diff --git a/.gitattributes b/.gitattributes index 461090b7e..e389d472f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,14 @@ +/.gitattributes export-ignore +/.gitignore export-ignore +/.github export-ignore +/.phive export-ignore +/.php-cs-fixer.dist.php export-ignore +/build export-ignore +/build.xml export-ignore +/phpstan.neon export-ignore +/phpunit.xml export-ignore +/tests export-ignore +/tools export-ignore +/tools/* binary + *.php diff=php diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..ee242a803 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,28 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, such as physical or electronic + addresses, without explicit permission +* Other unethical or unprofessional conduct + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainer at sebastian@phpunit.de. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version] + +[homepage]: https://contributor-covenant.org +[version]: https://contributor-covenant.org/version/1/3/0/ diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 000000000..fedcef3bc --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,106 @@ +# Contributing to `phpunit/php-code-coverage` + +## Welcome! + +We look forward to your contributions! Here are some examples how you can contribute: + +* [Report a bug](https://github.com/sebastianbergmann/php-code-coverage/issues/new) +* [Send a pull request to fix a bug](https://github.com/sebastianbergmann/php-code-coverage/pulls) + + +## We have a Code of Conduct + +Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. + + +## Any contributions you make will be under the BSD-3-Clause License + +When you submit code changes, your submissions are understood to be under the same [BSD-3-Clause License](https://github.com/sebastianbergmann/php-code-coverage/blob/main/LICENSE) that covers the project. By contributing to this project, you agree that your contributions will be licensed under its BSD-3-Clause License. + + +### Do Not Violate Copyright + +Only submit a pull request with your own original code. Do NOT submit a pull request containing code which you have largely copied from +another project, unless you wrote the respective code yourself. + +Open Source does not mean that copyright does not apply. Copyright infringements will not be tolerated and can lead to you being banned from this project and repository. + + +### Do Not Submit AI-Generated Pull Requests + +The same goes for (largely) AI-generated pull requests. These are not welcome as they will be based on copyrighted code from others +without accreditation and without taking the license of the original code into account, let alone getting permission +for the use of the code or for re-licensing. + +Aside from that, the experience is that AI-generated pull requests will be incorrect 100% of the time and cost reviewers too much time. +Submitting a (largely) AI-generated pull request will lead to you being banned from this project and repository. + + +## Write bug reports with detail, background, and sample code + +[This is an example](https://github.com/sebastianbergmann/phpunit/issues/4376) of a bug report I wrote, and I think it's not too bad. + +In your bug report, please provide the following: + +* A quick summary and/or background +* Steps to reproduce + * Be specific! + * Give sample code if you can. +* What you expected would happen +* What actually happens +* Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) + +Please do not report a bug for a version of this library that is no longer supported. Please do not report a bug if you are using a version of PHP that is not supported by the version of this library you are using. + +The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. Support for this library follows the [support for the version of PHPUnit that uses a specific version of this library](https://phpunit.de/supported-versions.html). + +Please post code and output as text ([using proper markup](https://guides.github.com/features/mastering-markdown/)). Do not post screenshots of code or output. + + +## Workflow for Pull Requests + +1. Fork the repository. +2. Create your branch from `main` if you plan to implement new functionality or change existing code significantly; create your branch from the oldest branch that is affected by the bug if you plan to fix a bug. +3. Implement your change and add tests for it. +4. Ensure the test suite passes. +5. Ensure the code complies with our coding guidelines (see below). +6. Send that pull request! + +Please make sure you have [set up your username and email address](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup) for use with Git. Strings such as `silly nick name ` look really stupid in the commit history of a project. + +We encourage you to [sign your Git commits with your GPG key](https://docs.github.com/en/github/authenticating-to-github/signing-commits). + +Pull requests for bug fixes must be made for the oldest branch that is supported (see above). Pull requests for new features must be based on the `main` branch. + +We are trying to keep backwards compatibility breaks to an absolute minimum. Please take this into account when proposing changes. + +Due to time constraints, we are not always able to respond as quickly as we would like. Please do not take delays personal and feel free to remind us if you feel that we forgot to respond. + + +## Development + +This project uses [PHPUnit](https://phpunit.de/) for testing: + +```shell +./vendor/bin/phpunit +``` + +This project uses [PHPStan](https://phpstan.org/) for static analysis: + +```shell +./tools/phpstan +``` + +This project uses [PHP-CS-Fixer](https://cs.symfony.com/) to enforce coding guidelines: + +```shell +./tools/php-cs-fixer fix +``` + +The commands shown above require an autoloader script at `vendor/autoload.php`. This can be generated like so: + +```shell +./tools/composer dump-autoload +``` + +Please understand that we will not accept a pull request when its changes violate this project's coding guidelines or break the test suite. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..d40ffea35 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +github: sebastianbergmann +liberapay: sebastianbergmann +thanks_dev: u/gh/sebastianbergmann +tidelift: "packagist/phpunit/php-code-coverage" diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..5ca75c07d --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,20 @@ +| Q | A +| --------------------------| --------------- +| php-code-coverage version | x.y.z +| PHP version | x.y.z +| Driver | PCOV / Xdebug +| PCOV version (if used) | x.y.z +| Xdebug version (if used) | x.y.z +| Installation Method | Composer / PHPUnit PHAR +| Usage Method | PHPUnit / other +| PHPUnit version (if used) | x.y.z + + + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..2f9c008d6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,117 @@ +# https://help.github.com/en/categories/automating-your-workflow-with-github-actions + +on: + - pull_request + - push + +name: CI + +env: + COMPOSER_ROOT_VERSION: 12.3.x-dev + +jobs: + coding-guidelines: + name: Coding Guidelines + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + extensions: none, iconv, json, phar, tokenizer + coverage: none + tools: none + + - name: Run PHP-CS-Fixer + run: ./tools/php-cs-fixer fix --dry-run --show-progress=dots --using-cache=no --verbose + + static-analysis: + name: Static Analysis + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.4 + extensions: none, ctype, curl, dom, iconv, mbstring, opcache, simplexml, tokenizer, xml, xmlwriter + coverage: none + tools: none + + - name: Install dependencies with Composer + run: ./tools/composer update --no-interaction --no-ansi --no-progress + + - name: Run PHPStan + run: ./tools/phpstan analyse --no-progress --error-format=github + + tests: + name: Tests + + runs-on: ${{ matrix.os }} + + env: + PHP_EXTENSIONS: none, ctype, curl, dom, json, libxml, mbstring, openssl, pdo_sqlite, soap, tokenizer, xml, xmlwriter + PHP_INI_VALUES: memory_limit=-1, assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On + + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - windows-latest + + php-version: + - 8.3 + - 8.4 + - 8.5 + + coverage-driver: + - pcov + - xdebug3 + + steps: + - name: Configure Git to avoid issues with line endings + if: matrix.os == 'windows-latest' + run: git config --global core.autocrlf false + + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP with extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + coverage: ${{ matrix.coverage-driver }} + extensions: ${{ env.PHP_EXTENSIONS }} + ini-values: ${{ env.PHP_INI_VALUES }} + tools: none + + - name: Install dependencies with Composer + run: php ./tools/composer update --no-ansi --no-interaction --no-progress + + - name: Run tests with PHPUnit + run: ./vendor/bin/phpunit --log-junit test-results.xml --coverage-openclover=code-coverage.xml + + - name: Upload test results to Codecov.io + if: ${{ !cancelled() }} + uses: codecov/test-results-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + disable_search: true + files: ./test-results.xml + + - name: Upload code coverage data to Codecov.io + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + disable_search: true + files: ./code-coverage.xml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 000000000..11111a6b7 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,43 @@ +# https://docs.github.com/en/actions + +on: + push: + tags: + - "**" + +name: Release + +jobs: + release: + name: Release + + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP with extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + coverage: none + extensions: none + tools: none + + - name: Determine tag + run: echo "RELEASE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + + - name: Parse ChangeLog + run: build/scripts/extract-release-notes.php ${{ env.RELEASE_TAG }} > release-notes.md + + - name: Create release + uses: ncipollo/release-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ env.RELEASE_TAG }} + name: phpunit/php-code-coverage ${{ env.RELEASE_TAG }} + bodyFile: release-notes.md diff --git a/.gitignore b/.gitignore index a7419836b..5344939da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ -build/api -build/code-browser -build/coverage -build/logs -build/pdepend -cache.properties -phpunit.xml +/.idea +/.php-cs-fixer.php +/.php-cs-fixer.cache +/.phpunit.cache +/composer.lock +/vendor diff --git a/.phive/phars.xml b/.phive/phars.xml new file mode 100644 index 000000000..dac7fc73f --- /dev/null +++ b/.phive/phars.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 000000000..89883a868 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,369 @@ + + +For the full copyright and license information, please view the LICENSE +file that was distributed with this source code. +EOF; + +$finder = PhpCsFixer\Finder::create() + ->files() + ->in(__DIR__ . '/src') + ->in(__DIR__ . '/tests/src') + ->in(__DIR__ . '/tests/tests') +; + +$config = new PhpCsFixer\Config; +$config->setFinder($finder) + ->setUnsupportedPhpVersionAllowed(true) + ->setRiskyAllowed(true) + ->setRules([ + 'align_multiline_comment' => true, + 'array_indentation' => true, + 'array_push' => true, + 'array_syntax' => ['syntax' => 'short'], + 'attribute_empty_parentheses' => [ + 'use_parentheses' => false, + ], + 'backtick_to_shell_exec' => true, + 'binary_operator_spaces' => [ + 'operators' => [ + '*=' => 'align_single_space_minimal', + '+=' => 'align_single_space_minimal', + '-=' => 'align_single_space_minimal', + '/=' => 'align_single_space_minimal', + '=' => 'align_single_space_minimal', + '=>' => 'align_single_space_minimal', + ], + ], + 'blank_line_after_namespace' => true, + 'blank_line_before_statement' => [ + 'statements' => [ + 'break', + 'case', + 'continue', + 'declare', + 'default', + 'do', + 'exit', + 'for', + 'foreach', + 'goto', + 'if', + 'include', + 'include_once', + 'phpdoc', + 'require', + 'require_once', + 'return', + 'switch', + 'throw', + 'try', + 'while', + 'yield', + 'yield_from', + ], + ], + 'blank_lines_before_namespace' => [ + 'max_line_breaks' => 1, + 'min_line_breaks' => 0, + ], + 'braces_position' => [ + 'anonymous_classes_opening_brace' => 'next_line_unless_newline_at_signature_end', + 'anonymous_functions_opening_brace' => 'next_line_unless_newline_at_signature_end', + ], + 'cast_spaces' => true, + 'class_attributes_separation' => [ + 'elements' => [ + 'const' => 'none', + 'method' => 'one', + 'property' => 'only_if_meta' + ] + ], + 'class_definition' => true, + 'clean_namespace' => true, + 'combine_consecutive_issets' => true, + 'combine_consecutive_unsets' => true, + 'combine_nested_dirname' => true, + 'compact_nullable_type_declaration' => true, + 'concat_space' => ['spacing' => 'one'], + 'constant_case' => true, + 'control_structure_braces' => true, + 'control_structure_continuation_position' => true, + 'declare_equal_normalize' => ['space' => 'none'], + 'declare_parentheses' => true, + 'declare_strict_types' => true, + 'dir_constant' => true, + 'echo_tag_syntax' => true, + 'elseif' => true, + 'encoding' => true, + 'ereg_to_preg' => true, + 'explicit_indirect_variable' => true, + 'explicit_string_variable' => true, + 'fopen_flag_order' => true, + 'full_opening_tag' => true, + 'fully_qualified_strict_types' => ['import_symbols' => true], + 'function_declaration' => true, + 'function_to_constant' => true, + 'get_class_to_class_keyword' => true, + 'global_namespace_import' => [ + 'import_classes' => true, + 'import_constants' => true, + 'import_functions' => true, + ], + 'header_comment' => ['header' => $header, 'separate' => 'none'], + 'heredoc_to_nowdoc' => true, + 'implode_call' => true, + 'include' => true, + 'increment_style' => [ + 'style' => 'post', + ], + 'indentation_type' => true, + 'integer_literal_case' => true, + 'is_null' => true, + 'lambda_not_used_import' => true, + 'line_ending' => true, + 'list_syntax' => ['syntax' => 'short'], + 'logical_operators' => true, + 'lowercase_cast' => true, + 'lowercase_keywords' => true, + 'lowercase_static_reference' => true, + 'magic_constant_casing' => true, + 'magic_method_casing' => true, + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + ], + 'method_chaining_indentation' => true, + 'modernize_strpos' => true, + 'modernize_types_casting' => true, + 'multiline_comment_opening_closing' => true, + 'multiline_whitespace_before_semicolons' => true, + 'native_constant_invocation' => true, + 'native_function_casing' => false, + 'native_function_invocation' => [ + 'include' => [ + '@internal', + ], + ], + 'native_type_declaration_casing' => true, + 'new_with_parentheses' => [ + 'anonymous_class' => false, + 'named_class' => false, + ], + 'no_alias_functions' => true, + 'no_alias_language_construct_call' => true, + 'no_alternative_syntax' => true, + 'no_binary_string' => true, + 'no_blank_lines_after_class_opening' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_break_comment' => true, + 'no_closing_tag' => true, + 'no_empty_comment' => true, + 'no_empty_phpdoc' => true, + 'no_empty_statement' => true, + 'no_extra_blank_lines' => [ + 'tokens' => [ + 'attribute', + 'break', + 'case', + 'continue', + 'curly_brace_block', + 'default', + 'extra', + 'parenthesis_brace_block', + 'return', + 'square_brace_block', + 'switch', + 'throw', + 'use', + ], + ], + 'no_homoglyph_names' => true, + 'no_leading_import_slash' => true, + 'no_leading_namespace_whitespace' => true, + 'no_mixed_echo_print' => ['use' => 'print'], + 'no_multiline_whitespace_around_double_arrow' => true, + 'no_multiple_statements_per_line' => true, + 'no_null_property_initialization' => true, + 'no_php4_constructor' => true, + 'no_short_bool_cast' => true, + 'no_singleline_whitespace_before_semicolons' => true, + 'no_space_around_double_colon' => true, + 'no_spaces_after_function_name' => true, + 'no_spaces_around_offset' => true, + 'no_superfluous_elseif' => true, + 'no_superfluous_phpdoc_tags' => [ + 'allow_mixed' => true, + ], + 'no_trailing_comma_in_singleline' => true, + 'no_trailing_whitespace' => true, + 'no_trailing_whitespace_in_comment' => true, + 'no_trailing_whitespace_in_string' => true, + 'no_unneeded_braces' => true, + 'no_unneeded_control_parentheses' => true, + 'no_unneeded_final_method' => true, + 'no_unneeded_import_alias' => true, + 'no_unreachable_default_argument_value' => true, + 'no_unset_cast' => true, + 'no_unset_on_property' => true, + 'no_unused_imports' => true, + 'no_useless_concat_operator' => true, + 'no_useless_else' => true, + 'no_useless_nullsafe_operator' => true, + 'no_useless_return' => true, + 'no_useless_sprintf' => true, + 'no_whitespace_before_comma_in_array' => true, + 'no_whitespace_in_blank_line' => true, + 'non_printable_character' => true, + 'normalize_index_brace' => true, + 'nullable_type_declaration_for_default_null_value' => true, + 'object_operator_without_whitespace' => true, + 'octal_notation' => true, + 'operator_linebreak' => [ + 'only_booleans' => true, + 'position' => 'end', + ], + 'ordered_class_elements' => [ + 'order' => [ + 'use_trait', + 'constant_public', + 'constant_protected', + 'constant_private', + 'property_public_static', + 'property_protected_static', + 'property_private_static', + 'property_public', + 'property_protected', + 'property_private', + 'method_public_static', + 'construct', + 'destruct', + 'magic', + 'phpunit', + 'method_public', + 'method_protected', + 'method_private', + 'method_protected_static', + 'method_private_static', + ], + ], + 'ordered_imports' => [ + 'imports_order' => [ + 'const', + 'function', + 'class', + ] + ], + 'ordered_interfaces' => [ + 'direction' => 'ascend', + 'order' => 'alpha', + ], + 'ordered_traits' => true, + 'ordered_types' => true, + 'php_unit_set_up_tear_down_visibility' => true, + 'php_unit_test_case_static_method_calls' => [ + 'call_type' => 'this', + ], + 'phpdoc_add_missing_param_annotation' => false, + 'phpdoc_align' => true, + 'phpdoc_annotation_without_dot' => true, + 'phpdoc_indent' => true, + 'phpdoc_inline_tag_normalizer' => true, + 'phpdoc_no_access' => true, + 'phpdoc_no_alias_tag' => true, + 'phpdoc_no_empty_return' => true, + 'phpdoc_no_package' => true, + 'phpdoc_no_useless_inheritdoc' => true, + 'phpdoc_order' => true, + 'phpdoc_order_by_value' => [ + 'annotations' => [ + 'covers', + 'dataProvider', + 'throws', + 'uses', + ], + ], + 'phpdoc_param_order' => true, + 'phpdoc_return_self_reference' => true, + 'phpdoc_scalar' => true, + 'phpdoc_separation' => true, + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_summary' => true, + 'phpdoc_tag_casing' => true, + 'phpdoc_tag_type' => true, + 'phpdoc_to_comment' => false, + 'phpdoc_trim' => true, + 'phpdoc_trim_consecutive_blank_line_separation' => true, + 'phpdoc_types' => ['groups' => ['simple', 'meta']], + 'phpdoc_types_order' => true, + 'phpdoc_var_annotation_correct_order' => true, + 'phpdoc_var_without_name' => true, + 'pow_to_exponentiation' => true, + 'protected_to_private' => true, + 'return_assignment' => true, + 'return_type_declaration' => ['space_before' => 'none'], + 'self_accessor' => true, + 'self_static_accessor' => true, + 'semicolon_after_instruction' => true, + 'set_type_to_cast' => true, + 'short_scalar_cast' => true, + 'simple_to_complex_string_variable' => true, + 'simplified_null_return' => false, + 'single_blank_line_at_eof' => true, + 'single_class_element_per_statement' => true, + 'single_import_per_statement' => true, + 'single_line_after_imports' => true, + 'single_line_comment_spacing' => true, + 'single_quote' => true, + 'single_space_around_construct' => true, + 'single_trait_insert_per_statement' => true, + 'space_after_semicolon' => true, + 'spaces_inside_parentheses' => [ + 'space' => 'none', + ], + 'standardize_increment' => true, + 'standardize_not_equals' => true, + 'statement_indentation' => true, + 'static_lambda' => true, + 'strict_param' => true, + 'string_length_to_empty'=> true, + 'string_line_ending' => true, + 'switch_case_semicolon_to_colon' => true, + 'switch_case_space' => true, + 'switch_continue_to_break' => true, + 'ternary_operator_spaces' => true, + 'ternary_to_elvis_operator' => true, + 'ternary_to_null_coalescing' => true, + 'trailing_comma_in_multiline' => [ + 'elements' => [ + 'arguments', + 'arrays', + 'match', + ] + ], + 'trim_array_spaces' => true, + 'type_declaration_spaces' => [ + 'elements' => [ + 'function', + ], + ], + 'types_spaces' => true, + 'unary_operator_spaces' => true, + 'visibility_required' => [ + 'elements' => [ + 'const', + 'method', + 'property', + ], + ], + 'void_return' => true, + 'whitespace_after_comma_in_array' => true, + ]); + +$config->setCacheFile(__DIR__ . '/.php-cs-fixer.cache/' . json_decode((string) @file_get_contents('composer.json'), true)["extra"]["branch-alias"]["dev-main"] ?? 'unknown'); + +$config->setParallelConfig(\PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect()); + +return $config; diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ce3c047dd..000000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: php - -php: - - 5.4 - -before_script: - - mkdir -p vendor/SebastianBergmann - - git clone --branch=master --depth=100 --quiet git://github.com/sebastianbergmann/phpunit.git vendor/phpunit - - git clone --branch=master --depth=100 --quiet git://github.com/sebastianbergmann/phpunit-mock-objects.git vendor/phpunit-mock-objects - - git clone --branch=master --depth=100 --quiet git://github.com/sebastianbergmann/php-text-template.git vendor/php-text-template - - git clone --branch=master --depth=100 --quiet git://github.com/sebastianbergmann/php-token-stream.git vendor/php-token-stream - - git clone --branch=master --depth=100 --quiet git://github.com/sebastianbergmann/php-file-iterator.git vendor/php-file-iterator - - git clone --branch=master --depth=100 --quiet git://github.com/sebastianbergmann/php-timer.git vendor/php-timer - - git clone --branch=master --depth=100 --quiet git://github.com/sebastianbergmann/version.git vendor/version - - ln -s ~/builds/sebastianbergmann/php-code-coverage/vendor/version/src ~/builds/sebastianbergmann/php-code-coverage/vendor/SebastianBergmann/Version - - git clone --branch=master --depth=100 --quiet git://github.com/pear/pear-core.git vendor/pear-core - - git clone --branch=trunk --depth=100 --quiet git://github.com/pear/Console_Getopt.git vendor/console-getopt - -script: php -d include_path=vendor/php-text-template:vendor/php-token-stream:vendor/php-file-iterator:vendor/phpunit:vendor/phpunit-mock-objects:vendor/php-timer:vendor/php-invoker:vendor/pear-core:vendor/console-getopt:vendor:. ./vendor/phpunit/phpunit.php --configuration ./build/travis-ci.xml - -notifications: - email: false - irc: "irc.freenode.org#phpunit" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index b29053982..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,5 +0,0 @@ -Pull Requests for bug fixes should be made against the current release branch (1.2). - -Pull Requests for new features should be made against master. - -For further notes please refer to [https://github.com/sebastianbergmann/phpunit/blob/master/CONTRIBUTING.md](https://github.com/sebastianbergmann/phpunit/blob/master/CONTRIBUTING.md) diff --git a/ChangeLog-12.3.md b/ChangeLog-12.3.md new file mode 100644 index 000000000..f076381c5 --- /dev/null +++ b/ChangeLog-12.3.md @@ -0,0 +1,31 @@ +# ChangeLog + +All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. + +## [12.3.2] - 2025-07-29 + +### Changed + +* Add coverage and complexity columns to class and method complexity tables +* Add CRAP to graph tooltip + +### Fixed + +* [#1081](https://github.com/sebastianbergmann/php-code-coverage/issues/1081): Class complexity scatter chart tooltips show incorrect class + +## [12.3.1] - 2025-06-18 + +### Changed + +* Changed CSS for HTML report to not use common ligatures as this sometimes lead to hard-to-read code +* Updated Bootstrap to version 5.3.6 for HTML report + +## [12.3.0] - 2025-05-23 + +### Changed + +* [#1080](https://github.com/sebastianbergmann/php-code-coverage/pull/1080): Support for reporting code coverage information in OpenClover XML format; unlike the existing Clover XML reporter, which remains unchanged, the XML documents generated by this new reporter validate against the OpenClover project's XML schema definition, with one exception: we do not generate the `` element. This feature is experimental and the generated XML might change in order to improve compliance with the OpenClover project's XML schema definition further. Such changes will be made in bugfix and/or minor releases even if they break backward compatibility. + +[12.3.2]: https://github.com/sebastianbergmann/php-code-coverage/compare/12.3.1...12.3.2 +[12.3.1]: https://github.com/sebastianbergmann/php-code-coverage/compare/12.3.0...12.3.1 +[12.3.0]: https://github.com/sebastianbergmann/php-code-coverage/compare/12.2.1...12.3.0 diff --git a/ChangeLog.markdown b/ChangeLog.markdown deleted file mode 100644 index 0f3b0d7aa..000000000 --- a/ChangeLog.markdown +++ /dev/null @@ -1,11 +0,0 @@ -PHP_CodeCoverage 1.3 -==================== - -This is the list of changes for the PHP_CodeCoverage 1.3 release series. - -PHP_CodeCoverage 1.3.0 ----------------------- - -* Added an option to raise an exception when code is unintentionally covered. -* PHP_CodeCoverage 1.3 is only supported on PHP 5.4.7 (or later). - diff --git a/LICENSE b/LICENSE index 95d18222f..017eb48b1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,33 +1,29 @@ -PHP_CodeCoverage +BSD 3-Clause License -Copyright (c) 2009-2013, Sebastian Bergmann . +Copyright (c) 2009-2025, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: +modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/PHP/CodeCoverage.php b/PHP/CodeCoverage.php deleted file mode 100644 index cb599612f..000000000 --- a/PHP/CodeCoverage.php +++ /dev/null @@ -1,1025 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.0.0 - */ - -/** - * Provides collection functionality for PHP code coverage information. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.0.0 - */ -class PHP_CodeCoverage -{ - /** - * @var PHP_CodeCoverage_Driver - */ - protected $driver; - - /** - * @var PHP_CodeCoverage_Filter - */ - protected $filter; - - /** - * @var boolean - */ - protected $cacheTokens = FALSE; - - /** - * @var boolean - */ - protected $checkForUnintentionallyCoveredCode = FALSE; - - /** - * @var boolean - */ - protected $forceCoversAnnotation = FALSE; - - /** - * @var boolean - */ - protected $mapTestClassNameToCoveredClassName = FALSE; - - /** - * @var boolean - */ - protected $addUncoveredFilesFromWhitelist = TRUE; - - /** - * @var boolean - */ - protected $processUncoveredFilesFromWhitelist = FALSE; - - /** - * @var mixed - */ - protected $currentId; - - /** - * Code coverage data. - * - * @var array - */ - protected $data = array(); - - /** - * @var array - */ - protected $ignoredLines = array(); - - /** - * Test data. - * - * @var array - */ - protected $tests = array(); - - /** - * Constructor. - * - * @param PHP_CodeCoverage_Driver $driver - * @param PHP_CodeCoverage_Filter $filter - */ - public function __construct(PHP_CodeCoverage_Driver $driver = NULL, PHP_CodeCoverage_Filter $filter = NULL) - { - if ($driver === NULL) { - $driver = new PHP_CodeCoverage_Driver_Xdebug; - } - - if ($filter === NULL) { - $filter = new PHP_CodeCoverage_Filter; - } - - $this->driver = $driver; - $this->filter = $filter; - } - - /** - * Returns the PHP_CodeCoverage_Report_Node_* object graph - * for this PHP_CodeCoverage object. - * - * @return PHP_CodeCoverage_Report_Node_Directory - * @since Method available since Release 1.1.0 - */ - public function getReport() - { - $factory = new PHP_CodeCoverage_Report_Factory; - - return $factory->create($this); - } - - /** - * Clears collected code coverage data. - */ - public function clear() - { - $this->currentId = NULL; - $this->data = array(); - $this->tests = array(); - } - - /** - * Returns the PHP_CodeCoverage_Filter used. - * - * @return PHP_CodeCoverage_Filter - */ - public function filter() - { - return $this->filter; - } - - /** - * Returns the collected code coverage data. - * - * @return array - * @since Method available since Release 1.1.0 - */ - public function getData() - { - if ($this->addUncoveredFilesFromWhitelist) { - $this->addUncoveredFilesFromWhitelist(); - } - - // We need to apply the blacklist filter a second time - // when no whitelist is used. - if (!$this->filter->hasWhitelist()) { - $this->applyListsFilter($this->data); - } - - return $this->data; - } - - /** - * Returns the test data. - * - * @return array - * @since Method available since Release 1.1.0 - */ - public function getTests() - { - return $this->tests; - } - - /** - * Start collection of code coverage information. - * - * @param mixed $id - * @param boolean $clear - * @throws PHP_CodeCoverage_Exception - */ - public function start($id, $clear = FALSE) - { - if (!is_bool($clear)) { - throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( - 1, 'boolean' - ); - } - - if ($clear) { - $this->clear(); - } - - $this->currentId = $id; - - $this->driver->start(); - } - - /** - * Stop collection of code coverage information. - * - * @param boolean $append - * @return array - * @throws PHP_CodeCoverage_Exception - */ - public function stop($append = TRUE) - { - if (!is_bool($append)) { - throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( - 1, 'boolean' - ); - } - - $data = $this->driver->stop(); - $this->append($data, NULL, $append); - - $this->currentId = NULL; - - return $data; - } - - /** - * Appends code coverage data. - * - * @param array $data - * @param mixed $id - * @param boolean $append - */ - public function append(array $data, $id = NULL, $append = TRUE) - { - if ($id === NULL) { - $id = $this->currentId; - } - - if ($id === NULL) { - throw new PHP_CodeCoverage_Exception; - } - - $this->applyListsFilter($data); - $this->applyIgnoredLinesFilter($data); - $this->initializeFilesThatAreSeenTheFirstTime($data); - - if (!$append) { - return; - } - - if ($id != 'UNCOVERED_FILES_FROM_WHITELIST') { - $this->applyCoversAnnotationFilter($data, $id); - } - - if (empty($data)) { - return; - } - - $status = NULL; - - if ($id instanceof PHPUnit_Framework_TestCase) { - $status = $id->getStatus(); - $id = get_class($id) . '::' . $id->getName(); - } - - else if ($id instanceof PHPUnit_Extensions_PhptTestCase) { - $id = $id->getName(); - } - - $this->tests[$id] = $status; - - foreach ($data as $file => $lines) { - if (!$this->filter->isFile($file)) { - continue; - } - - foreach ($lines as $k => $v) { - if ($v == 1) { - $this->data[$file][$k][] = $id; - } - } - } - } - - /** - * Merges the data from another instance of PHP_CodeCoverage. - * - * @param PHP_CodeCoverage $that - */ - public function merge(PHP_CodeCoverage $that) - { - foreach ($that->data as $file => $lines) { - if (!isset($this->data[$file])) { - if (!$this->filter->isFiltered($file)) { - $this->data[$file] = $lines; - } - - continue; - } - - foreach ($lines as $line => $data) { - if ($data !== NULL) { - if (!isset($this->data[$file][$line])) { - $this->data[$file][$line] = $data; - } else { - $this->data[$file][$line] = array_unique( - array_merge($this->data[$file][$line], $data) - ); - } - } - } - } - - $this->tests = array_merge($this->tests, $that->getTests()); - } - - /** - * @param boolean $flag - * @throws PHP_CodeCoverage_Exception - * @since Method available since Release 1.1.0 - */ - public function setCacheTokens($flag) - { - if (!is_bool($flag)) { - throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( - 1, 'boolean' - ); - } - - $this->cacheTokens = $flag; - } - - /** - * @param boolean $flag - * @since Method available since Release 1.1.0 - */ - public function getCacheTokens() - { - return $this->cacheTokens; - } - - /** - * @param boolean $flag - * @throws PHP_CodeCoverage_Exception - * @since Method available since Release 1.3.0 - */ - public function setCheckForUnintentionallyCoveredCode($flag) - { - if (!is_bool($flag)) { - throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( - 1, 'boolean' - ); - } - - $this->checkForUnintentionallyCoveredCode = $flag; - } - - /** - * @param boolean $flag - * @throws PHP_CodeCoverage_Exception - */ - public function setForceCoversAnnotation($flag) - { - if (!is_bool($flag)) { - throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( - 1, 'boolean' - ); - } - - $this->forceCoversAnnotation = $flag; - } - - /** - * @param boolean $flag - * @throws PHP_CodeCoverage_Exception - */ - public function setMapTestClassNameToCoveredClassName($flag) - { - if (!is_bool($flag)) { - throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( - 1, 'boolean' - ); - } - - $this->mapTestClassNameToCoveredClassName = $flag; - } - - /** - * @param boolean $flag - * @throws PHP_CodeCoverage_Exception - */ - public function setAddUncoveredFilesFromWhitelist($flag) - { - if (!is_bool($flag)) { - throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( - 1, 'boolean' - ); - } - - $this->addUncoveredFilesFromWhitelist = $flag; - } - - /** - * @param boolean $flag - * @throws PHP_CodeCoverage_Exception - */ - public function setProcessUncoveredFilesFromWhitelist($flag) - { - if (!is_bool($flag)) { - throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( - 1, 'boolean' - ); - } - - $this->processUncoveredFilesFromWhitelist = $flag; - } - - /** - * Applies the @covers annotation filtering. - * - * @param array $data - * @param mixed $id - * @throws PHP_CodeCoverage_Exception_UnintentionallyCoveredCode - */ - protected function applyCoversAnnotationFilter(&$data, $id) - { - $unintentionallyCoveredCode = FALSE; - - if ($id instanceof PHPUnit_Framework_TestCase) { - $testClassName = get_class($id); - $linesToBeCovered = $this->getLinesToBeCovered( - $testClassName, $id->getName() - ); - - if ($linesToBeCovered === FALSE) { - $data = array(); - return; - } - - if ($this->mapTestClassNameToCoveredClassName && - empty($linesToBeCovered)) { - $testedClass = substr($testClassName, 0, -4); - - if (class_exists($testedClass)) { - $class = new ReflectionClass($testedClass); - - $linesToBeCovered = array( - $class->getFileName() => range( - $class->getStartLine(), $class->getEndLine() - ) - ); - } - } - } else { - $linesToBeCovered = array(); - } - - if (!empty($linesToBeCovered)) { - $count = count($data); - $data = array_intersect_key($data, $linesToBeCovered); - - if ($this->checkForUnintentionallyCoveredCode && - $count != count($data)) { - throw new PHP_CodeCoverage_Exception_UnintentionallyCoveredCode; - } - - foreach (array_keys($data) as $filename) { - $_linesToBeCovered = array_flip($linesToBeCovered[$filename]); - - if ($this->checkForUnintentionallyCoveredCode) { - foreach ($data[$filename] as $k => $v) { - if ($v == 1 && !isset($_linesToBeCovered[$k])) { - throw new PHP_CodeCoverage_Exception_UnintentionallyCoveredCode; - } - } - } - - $data[$filename] = array_intersect_key( - $data[$filename], $_linesToBeCovered - ); - } - } - - else if ($this->forceCoversAnnotation) { - $data = array(); - } - } - - /** - * Applies the blacklist/whitelist filtering. - * - * @param array $data - */ - protected function applyListsFilter(&$data) - { - foreach (array_keys($data) as $filename) { - if ($this->filter->isFiltered($filename)) { - unset($data[$filename]); - } - } - } - - /** - * Applies the "ignored lines" filtering. - * - * @param array $data - */ - protected function applyIgnoredLinesFilter(&$data) - { - foreach (array_keys($data) as $filename) { - foreach ($this->getLinesToBeIgnored($filename) as $line) { - unset($data[$filename][$line]); - } - - if (empty($data[$filename])) { - unset($data[$filename]); - } - } - } - - /** - * @since Method available since Release 1.1.0 - */ - protected function initializeFilesThatAreSeenTheFirstTime($data) - { - foreach ($data as $file => $lines) { - if ($this->filter->isFile($file) && !isset($this->data[$file])) { - $this->data[$file] = array(); - - foreach ($lines as $k => $v) { - $this->data[$file][$k] = $v == -2 ? NULL : array(); - } - } - } - } - - /** - * Processes whitelisted files that are not covered. - */ - protected function addUncoveredFilesFromWhitelist() - { - $data = array(); - $uncoveredFiles = array_diff( - $this->filter->getWhitelist(), array_keys($this->data) - ); - - foreach ($uncoveredFiles as $uncoveredFile) { - if (!file_exists($uncoveredFile)) { - continue; - } - - if ($this->processUncoveredFilesFromWhitelist) { - $this->processUncoveredFileFromWhitelist( - $uncoveredFile, $data, $uncoveredFiles - ); - } else { - $data[$uncoveredFile] = array(); - - $lines = count(file($uncoveredFile)); - - for ($i = 1; $i <= $lines; $i++) { - $data[$uncoveredFile][$i] = -1; - } - } - } - - $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); - } - - /** - * @param string $uncoveredFile - * @param array $data - * @param array $uncoveredFiles - */ - protected function processUncoveredFileFromWhitelist($uncoveredFile, array &$data, array $uncoveredFiles) - { - $this->driver->start(); - include_once $uncoveredFile; - $coverage = $this->driver->stop(); - - foreach ($coverage as $file => $fileCoverage) { - if (!isset($data[$file]) && - in_array($file, $uncoveredFiles)) { - foreach (array_keys($fileCoverage) as $key) { - if ($fileCoverage[$key] == 1) { - $fileCoverage[$key] = -1; - } - } - - $data[$file] = $fileCoverage; - } - } - } - - /** - * Returns the files and lines a test method wants to cover. - * - * @param string $className - * @param string $methodName - * @return array - * @since Method available since Release 1.2.0 - */ - protected function getLinesToBeCovered($className, $methodName) - { - $codeToCoverList = array(); - $result = array(); - - // @codeCoverageIgnoreStart - if (($pos = strpos($methodName, ' ')) !== FALSE) { - $methodName = substr($methodName, 0, $pos); - } - // @codeCoverageIgnoreEnd - - $class = new ReflectionClass($className); - - try { - $method = new ReflectionMethod($className, $methodName); - } - - catch (ReflectionException $e) { - return array(); - } - - $docComment = substr($class->getDocComment(), 3, -2) . PHP_EOL . substr($method->getDocComment(), 3, -2); - - $templateMethods = array( - 'setUp', 'assertPreConditions', 'assertPostConditions', 'tearDown' - ); - - foreach ($templateMethods as $templateMethod) { - if ($class->hasMethod($templateMethod)) { - $reflector = $class->getMethod($templateMethod); - $docComment .= PHP_EOL . substr($reflector->getDocComment(), 3, -2); - unset($reflector); - } - } - - if (strpos($docComment, '@coversNothing') !== FALSE) { - return FALSE; - } - - $classShortcut = preg_match_all( - '(@coversDefaultClass\s+(?P.*?)\s*$)m', - $class->getDocComment(), - $matches - ); - - if ($classShortcut) { - if ($classShortcut > 1) { - throw new PHP_CodeCoverage_Exception( - sprintf( - 'More than one @coversClass annotation in class or interface "%s".', - $className - ) - ); - } - - $classShortcut = $matches['coveredClass'][0]; - } - - $match = preg_match_all( - '(@covers\s+(?P.*?)\s*(\(\s*\))?\s*$)m', - $docComment, - $matches - ); - - if ($match) { - foreach ($matches['coveredElement'] as $coveredElement) { - if ($classShortcut && strncmp($coveredElement, '::', 2) === 0) { - $coveredElement = $classShortcut . $coveredElement; - } - - $codeToCoverList = array_merge( - $codeToCoverList, - $this->resolveCoversToReflectionObjects($coveredElement) - ); - } - - foreach ($codeToCoverList as $codeToCover) { - $fileName = $codeToCover->getFileName(); - - if (!isset($result[$fileName])) { - $result[$fileName] = array(); - } - - $result[$fileName] = array_unique( - array_merge( - $result[$fileName], - range( - $codeToCover->getStartLine(), $codeToCover->getEndLine() - ) - ) - ); - } - } - - return $result; - } - - /** - * @param string $coveredElement - * @return array - * @since Method available since Release 1.2.0 - */ - protected function resolveCoversToReflectionObjects($coveredElement) - { - $codeToCoverList = array(); - - if (strpos($coveredElement, '::') !== FALSE) { - list($className, $methodName) = explode('::', $coveredElement); - - if (isset($methodName[0]) && $methodName[0] == '<') { - $classes = array($className); - - foreach ($classes as $className) { - if (!class_exists($className) && - !interface_exists($className)) { - throw new PHP_CodeCoverage_Exception_InvalidCoversTarget( - sprintf( - 'Trying to @cover not existing class or ' . - 'interface "%s".', - $className - ) - ); - } - - $class = new ReflectionClass($className); - $methods = $class->getMethods(); - $inverse = isset($methodName[1]) && $methodName[1] == '!'; - - if (strpos($methodName, 'protected')) { - $visibility = 'isProtected'; - } - - else if (strpos($methodName, 'private')) { - $visibility = 'isPrivate'; - } - - else if (strpos($methodName, 'public')) { - $visibility = 'isPublic'; - } - - foreach ($methods as $method) { - if ($inverse && !$method->$visibility()) { - $codeToCoverList[] = $method; - } - - else if (!$inverse && $method->$visibility()) { - $codeToCoverList[] = $method; - } - } - } - } else { - $classes = array($className); - - foreach ($classes as $className) { - if ($className == '' && function_exists($methodName)) { - $codeToCoverList[] = new ReflectionFunction( - $methodName - ); - } else { - if (!((class_exists($className) || - interface_exists($className) || - trait_exists($className)) && - method_exists($className, $methodName))) { - throw new PHP_CodeCoverage_Exception_InvalidCoversTarget( - sprintf( - 'Trying to @cover not existing method "%s::%s".', - $className, - $methodName - ) - ); - } - - $codeToCoverList[] = new ReflectionMethod( - $className, $methodName - ); - } - } - } - } else { - $extended = FALSE; - - if (strpos($coveredElement, '') !== FALSE) { - $coveredElement = str_replace( - '', '', $coveredElement - ); - - $extended = TRUE; - } - - $classes = array($coveredElement); - - if ($extended) { - $classes = array_merge( - $classes, - class_implements($coveredElement), - class_parents($coveredElement) - ); - } - - foreach ($classes as $className) { - if (!class_exists($className) && - !interface_exists($className) && - !trait_exists($className)) { - throw new PHP_CodeCoverage_Exception_InvalidCoversTarget( - sprintf( - 'Trying to @cover not existing class or ' . - 'interface "%s".', - $className - ) - ); - } - - $codeToCoverList[] = new ReflectionClass($className); - } - } - - return $codeToCoverList; - } - - /** - * Returns the lines of a source file that should be ignored. - * - * @param string $filename - * @return array - * @throws PHP_CodeCoverage_Exception - * @since Method available since Release 1.3.0 - */ - protected function getLinesToBeIgnored($filename) - { - if (!is_string($filename)) { - throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( - 1, 'string' - ); - } - - if (!isset($this->ignoredLines[$filename])) { - $this->ignoredLines[$filename] = array(); - $ignore = FALSE; - $stop = FALSE; - $lines = file($filename); - $numLines = count($lines); - - foreach ($lines as $index => $line) { - if (!trim($line)) { - $this->ignoredLines[$filename][] = $index + 1; - } - } - - if ($this->cacheTokens) { - $tokens = PHP_Token_Stream_CachingFactory::get($filename); - } else { - $tokens = new PHP_Token_Stream($filename); - } - - $classes = array_merge($tokens->getClasses(), $tokens->getTraits()); - $tokens = $tokens->tokens(); - - foreach ($tokens as $token) { - switch (get_class($token)) { - case 'PHP_Token_COMMENT': - case 'PHP_Token_DOC_COMMENT': { - $count = substr_count($token, "\n"); - $line = $token->getLine(); - - for ($i = $line; $i < $line + $count; $i++) { - $this->ignoredLines[$filename][] = $i; - } - - if ($token instanceof PHP_Token_DOC_COMMENT) { - // Workaround for the fact the DOC_COMMENT token - // does not include the final \n character in its - // text. - if (substr(trim($lines[$i-1]), -2) == '*/') { - $this->ignoredLines[$filename][] = $i; - } - - break; - } - - $_token = trim($token); - - if ($_token == '// @codeCoverageIgnore' || - $_token == '//@codeCoverageIgnore') { - $ignore = TRUE; - $stop = TRUE; - } - - else if ($_token == '// @codeCoverageIgnoreStart' || - $_token == '//@codeCoverageIgnoreStart') { - $ignore = TRUE; - } - - else if ($_token == '// @codeCoverageIgnoreEnd' || - $_token == '//@codeCoverageIgnoreEnd') { - $stop = TRUE; - } - } - break; - - case 'PHP_Token_INTERFACE': - case 'PHP_Token_TRAIT': - case 'PHP_Token_CLASS': - case 'PHP_Token_FUNCTION': { - $docblock = $token->getDocblock(); - - if (strpos($docblock, '@codeCoverageIgnore')) { - $endLine = $token->getEndLine(); - - for ($i = $token->getLine(); $i <= $endLine; $i++) { - $this->ignoredLines[$filename][] = $i; - } - } - - else if ($token instanceof PHP_Token_INTERFACE || - $token instanceof PHP_Token_TRAIT || - $token instanceof PHP_Token_CLASS) { - if (empty($classes[$token->getName()]['methods'])) { - for ($i = $token->getLine(); - $i <= $token->getEndLine(); - $i++) { - $this->ignoredLines[$filename][] = $i; - } - } else { - $firstMethod = array_shift( - $classes[$token->getName()]['methods'] - ); - - $lastMethod = array_pop( - $classes[$token->getName()]['methods'] - ); - - if ($lastMethod === NULL) { - $lastMethod = $firstMethod; - } - - for ($i = $token->getLine(); - $i < $firstMethod['startLine']; - $i++) { - $this->ignoredLines[$filename][] = $i; - } - - for ($i = $token->getEndLine(); - $i > $lastMethod['endLine']; - $i--) { - $this->ignoredLines[$filename][] = $i; - } - } - } - } - break; - - case 'PHP_Token_INTERFACE': { - $endLine = $token->getEndLine(); - - for ($i = $token->getLine(); $i <= $endLine; $i++) { - $this->ignoredLines[$filename][] = $i; - } - } - break; - - case 'PHP_Token_NAMESPACE': { - $this->ignoredLines[$filename][] = $token->getEndLine(); - } // Intentional fallthrough - case 'PHP_Token_OPEN_TAG': - case 'PHP_Token_CLOSE_TAG': - case 'PHP_Token_USE': { - $this->ignoredLines[$filename][] = $token->getLine(); - } - break; - } - - if ($ignore) { - $this->ignoredLines[$filename][] = $token->getLine(); - - if ($stop) { - $ignore = FALSE; - $stop = FALSE; - } - } - } - - $this->ignoredLines[$filename][] = $numLines + 1; - - $this->ignoredLines[$filename] = array_unique( - $this->ignoredLines[$filename] - ); - - sort($this->ignoredLines[$filename]); - } - - return $this->ignoredLines[$filename]; - } -} diff --git a/PHP/CodeCoverage/Autoload.php b/PHP/CodeCoverage/Autoload.php deleted file mode 100644 index 6312e8023..000000000 --- a/PHP/CodeCoverage/Autoload.php +++ /dev/null @@ -1,92 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.1.0 - */ - -require_once 'File/Iterator/Autoload.php'; -require_once 'PHP/Token/Stream/Autoload.php'; -require_once 'Text/Template/Autoload.php'; -require_once 'SebastianBergmann/Version/autoload.php'; - -spl_autoload_register( - function ($class) - { - static $classes = NULL; - static $path = NULL; - - if ($classes === NULL) { - $classes = array( - 'php_codecoverage' => '/CodeCoverage.php', - 'php_codecoverage_driver' => '/CodeCoverage/Driver.php', - 'php_codecoverage_driver_xdebug' => '/CodeCoverage/Driver/Xdebug.php', - 'php_codecoverage_exception' => '/CodeCoverage/Exception.php', - 'php_codecoverage_exception_invalidcoverstarget' => '/CodeCoverage/Exception/InvalidCoversTarget.php', - 'php_codecoverage_exception_unintentionallycoveredcode' => '/CodeCoverage/Exception/UnintentionallyCoveredCode.php', - 'php_codecoverage_filter' => '/CodeCoverage/Filter.php', - 'php_codecoverage_report_clover' => '/CodeCoverage/Report/Clover.php', - 'php_codecoverage_report_factory' => '/CodeCoverage/Report/Factory.php', - 'php_codecoverage_report_html' => '/CodeCoverage/Report/HTML.php', - 'php_codecoverage_report_html_renderer' => '/CodeCoverage/Report/HTML/Renderer.php', - 'php_codecoverage_report_html_renderer_dashboard' => '/CodeCoverage/Report/HTML/Renderer/Dashboard.php', - 'php_codecoverage_report_html_renderer_directory' => '/CodeCoverage/Report/HTML/Renderer/Directory.php', - 'php_codecoverage_report_html_renderer_file' => '/CodeCoverage/Report/HTML/Renderer/File.php', - 'php_codecoverage_report_node' => '/CodeCoverage/Report/Node.php', - 'php_codecoverage_report_node_directory' => '/CodeCoverage/Report/Node/Directory.php', - 'php_codecoverage_report_node_file' => '/CodeCoverage/Report/Node/File.php', - 'php_codecoverage_report_node_iterator' => '/CodeCoverage/Report/Node/Iterator.php', - 'php_codecoverage_report_php' => '/CodeCoverage/Report/PHP.php', - 'php_codecoverage_report_text' => '/CodeCoverage/Report/Text.php', - 'php_codecoverage_util' => '/CodeCoverage/Util.php', - 'php_codecoverage_util_invalidargumenthelper' => '/CodeCoverage/Util/InvalidArgumentHelper.php' - ); - - $path = dirname(dirname(__FILE__)); - } - - $cn = strtolower($class); - - if (isset($classes[$cn])) { - require $path . $classes[$cn]; - } - } -); diff --git a/PHP/CodeCoverage/Autoload.php.in b/PHP/CodeCoverage/Autoload.php.in deleted file mode 100644 index a363a4c27..000000000 --- a/PHP/CodeCoverage/Autoload.php.in +++ /dev/null @@ -1,71 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.1.0 - */ - -require_once 'File/Iterator/Autoload.php'; -require_once 'PHP/Token/Stream/Autoload.php'; -require_once 'Text/Template/Autoload.php'; -require_once 'SebastianBergmann/Version/autoload.php'; - -spl_autoload_register( - function ($class) - { - static $classes = NULL; - static $path = NULL; - - if ($classes === NULL) { - $classes = array( - ___CLASSLIST___ - ); - - $path = dirname(dirname(__FILE__)); - } - - $cn = strtolower($class); - - if (isset($classes[$cn])) { - require $path . $classes[$cn]; - } - } -); diff --git a/PHP/CodeCoverage/Driver.php b/PHP/CodeCoverage/Driver.php deleted file mode 100644 index 5cd79127f..000000000 --- a/PHP/CodeCoverage/Driver.php +++ /dev/null @@ -1,70 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.0.0 - */ - -/** - * Interface for code coverage drivers. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.0.0 - */ -interface PHP_CodeCoverage_Driver -{ - /** - * Start collection of code coverage information. - */ - public function start(); - - /** - * Stop collection of code coverage information. - * - * @return array - */ - public function stop(); -} diff --git a/PHP/CodeCoverage/Driver/Xdebug.php b/PHP/CodeCoverage/Driver/Xdebug.php deleted file mode 100644 index 59bbf00ca..000000000 --- a/PHP/CodeCoverage/Driver/Xdebug.php +++ /dev/null @@ -1,96 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.0.0 - */ - -/** - * Driver for Xdebug's code coverage functionality. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.0.0 - * @codeCoverageIgnore - */ -class PHP_CodeCoverage_Driver_Xdebug implements PHP_CodeCoverage_Driver -{ - /** - * Constructor. - */ - public function __construct() - { - if (!extension_loaded('xdebug')) { - throw new PHP_CodeCoverage_Exception('Xdebug is not loaded.'); - } - - if (!ini_get('xdebug.coverage_enable')) { - throw new PHP_CodeCoverage_Exception( - 'You need to set xdebug.coverage_enable=On in your php.ini.' - ); - } - } - - /** - * Start collection of code coverage information. - */ - public function start() - { - xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); - } - - /** - * Stop collection of code coverage information. - * - * @return array - */ - public function stop() - { - $codeCoverage = xdebug_get_code_coverage(); - xdebug_stop_code_coverage(); - - return $codeCoverage; - } -} diff --git a/PHP/CodeCoverage/Exception.php b/PHP/CodeCoverage/Exception.php deleted file mode 100644 index 412a4e12d..000000000 --- a/PHP/CodeCoverage/Exception.php +++ /dev/null @@ -1,59 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.1.0 - */ - -/** - * Exception class for PHP_CodeCoverage component. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.1.0 - */ -class PHP_CodeCoverage_Exception extends RuntimeException -{ -} diff --git a/PHP/CodeCoverage/Exception/InvalidCoversTarget.php b/PHP/CodeCoverage/Exception/InvalidCoversTarget.php deleted file mode 100644 index 185a953d4..000000000 --- a/PHP/CodeCoverage/Exception/InvalidCoversTarget.php +++ /dev/null @@ -1,59 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.3.0 - */ - -/** - * Exception that is raised when code is unintentionally covered. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.3.0 - */ -class PHP_CodeCoverage_Exception_InvalidCoversTarget extends PHP_CodeCoverage_Exception -{ -} diff --git a/PHP/CodeCoverage/Exception/UnintentionallyCoveredCode.php b/PHP/CodeCoverage/Exception/UnintentionallyCoveredCode.php deleted file mode 100644 index 7f9260f10..000000000 --- a/PHP/CodeCoverage/Exception/UnintentionallyCoveredCode.php +++ /dev/null @@ -1,59 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.3.0 - */ - -/** - * Exception that is raised when code is unintentionally covered. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.3.0 - */ -class PHP_CodeCoverage_Exception_UnintentionallyCoveredCode extends PHP_CodeCoverage_Exception -{ -} diff --git a/PHP/CodeCoverage/Filter.php b/PHP/CodeCoverage/Filter.php deleted file mode 100644 index 1796cf1dd..000000000 --- a/PHP/CodeCoverage/Filter.php +++ /dev/null @@ -1,344 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.0.0 - */ - -/** - * Filter for blacklisting and whitelisting of code coverage information. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.0.0 - */ -class PHP_CodeCoverage_Filter -{ - /** - * Source files that are blacklisted. - * - * @var array - */ - protected $blacklistedFiles = array(); - - /** - * Source files that are whitelisted. - * - * @var array - */ - protected $whitelistedFiles = array(); - - /** - * @var boolean - */ - protected $blacklistPrefilled = FALSE; - - /** - * Adds a directory to the blacklist (recursively). - * - * @param string $directory - * @param string $suffix - * @param string $prefix - */ - public function addDirectoryToBlacklist($directory, $suffix = '.php', $prefix = '') - { - $facade = new File_Iterator_Facade; - $files = $facade->getFilesAsArray( - $directory, $suffix, $prefix - ); - - foreach ($files as $file) { - $this->addFileToBlacklist($file); - } - } - - /** - * Adds a file to the blacklist. - * - * @param string $filename - */ - public function addFileToBlacklist($filename) - { - $this->blacklistedFiles[realpath($filename)] = TRUE; - } - - /** - * Adds files to the blacklist. - * - * @param array $files - */ - public function addFilesToBlacklist(array $files) - { - foreach ($files as $file) { - $this->addFileToBlacklist($file); - } - } - - /** - * Removes a directory from the blacklist (recursively). - * - * @param string $directory - * @param string $suffix - * @param string $prefix - */ - public function removeDirectoryFromBlacklist($directory, $suffix = '.php', $prefix = '') - { - $facade = new File_Iterator_Facade; - $files = $facade->getFilesAsArray( - $directory, $suffix, $prefix - ); - - foreach ($files as $file) { - $this->removeFileFromBlacklist($file); - } - } - - /** - * Removes a file from the blacklist. - * - * @param string $filename - */ - public function removeFileFromBlacklist($filename) - { - $filename = realpath($filename); - - if (isset($this->blacklistedFiles[$filename])) { - unset($this->blacklistedFiles[$filename]); - } - } - - /** - * Adds a directory to the whitelist (recursively). - * - * @param string $directory - * @param string $suffix - * @param string $prefix - */ - public function addDirectoryToWhitelist($directory, $suffix = '.php', $prefix = '') - { - $facade = new File_Iterator_Facade; - $files = $facade->getFilesAsArray( - $directory, $suffix, $prefix - ); - - foreach ($files as $file) { - $this->addFileToWhitelist($file); - } - } - - /** - * Adds a file to the whitelist. - * - * @param string $filename - */ - public function addFileToWhitelist($filename) - { - $this->whitelistedFiles[realpath($filename)] = TRUE; - } - - /** - * Adds files to the whitelist. - * - * @param array $files - */ - public function addFilesToWhitelist(array $files) - { - foreach ($files as $file) { - $this->addFileToWhitelist($file); - } - } - - /** - * Removes a directory from the whitelist (recursively). - * - * @param string $directory - * @param string $suffix - * @param string $prefix - */ - public function removeDirectoryFromWhitelist($directory, $suffix = '.php', $prefix = '') - { - $facade = new File_Iterator_Facade; - $files = $facade->getFilesAsArray( - $directory, $suffix, $prefix - ); - - foreach ($files as $file) { - $this->removeFileFromWhitelist($file); - } - } - - /** - * Removes a file from the whitelist. - * - * @param string $filename - */ - public function removeFileFromWhitelist($filename) - { - $filename = realpath($filename); - - if (isset($this->whitelistedFiles[$filename])) { - unset($this->whitelistedFiles[$filename]); - } - } - - /** - * Checks whether a filename is a real filename. - * - * @param string $filename - */ - public function isFile($filename) - { - if ($filename == '-' || - strpos($filename, 'eval()\'d code') !== FALSE || - strpos($filename, 'runtime-created function') !== FALSE || - strpos($filename, 'runkit created function') !== FALSE || - strpos($filename, 'assert code') !== FALSE || - strpos($filename, 'regexp code') !== FALSE) { - return FALSE; - } - - return TRUE; - } - - /** - * Checks whether or not a file is filtered. - * - * When the whitelist is empty (default), blacklisting is used. - * When the whitelist is not empty, whitelisting is used. - * - * @param string $filename - * @param boolean $ignoreWhitelist - * @return boolean - * @throws PHP_CodeCoverage_Exception - */ - public function isFiltered($filename) - { - $filename = realpath($filename); - - if (!empty($this->whitelistedFiles)) { - return !isset($this->whitelistedFiles[$filename]); - } - - if (!$this->blacklistPrefilled) { - $this->prefillBlacklist(); - } - - return isset($this->blacklistedFiles[$filename]); - } - - /** - * Returns the list of blacklisted files. - * - * @return array - */ - public function getBlacklist() - { - return array_keys($this->blacklistedFiles); - } - - /** - * Returns the list of whitelisted files. - * - * @return array - */ - public function getWhitelist() - { - return array_keys($this->whitelistedFiles); - } - - /** - * Returns whether this filter has a whitelist. - * - * @return boolean - * @since Method available since Release 1.1.0 - */ - public function hasWhitelist() - { - return !empty($this->whitelistedFiles); - } - - /** - * @since Method available since Release 1.2.3 - */ - protected function prefillBlacklist() - { - $this->addDirectoryContainingClassToBlacklist('File_Iterator'); - $this->addDirectoryContainingClassToBlacklist('PHP_CodeCoverage'); - $this->addDirectoryContainingClassToBlacklist('PHP_Invoker'); - $this->addDirectoryContainingClassToBlacklist('PHP_Timer'); - $this->addDirectoryContainingClassToBlacklist('PHP_Token'); - $this->addDirectoryContainingClassToBlacklist('PHPUnit_Framework_TestCase', 2); - $this->addDirectoryContainingClassToBlacklist('PHPUnit_Extensions_Database_TestCase', 2); - $this->addDirectoryContainingClassToBlacklist('PHPUnit_Framework_MockObject_Generator', 2); - $this->addDirectoryContainingClassToBlacklist('PHPUnit_Extensions_SeleniumTestCase', 2); - $this->addDirectoryContainingClassToBlacklist('PHPUnit_Extensions_Story_TestCase', 2); - $this->addDirectoryContainingClassToBlacklist('Text_Template'); - $this->addDirectoryContainingClassToBlacklist('Symfony\Component\Yaml\Yaml'); - $this->addDirectoryContainingClassToBlacklist('SebastianBergmann\Version'); - - $this->blacklistPrefilled = TRUE; - } - - /** - * @param string $className - * @param integer $parent - * @since Method available since Release 1.2.3 - */ - protected function addDirectoryContainingClassToBlacklist($className, $parent = 1) - { - if (!class_exists($className)) { - return; - } - - $reflector = new ReflectionClass($className); - $directory = $reflector->getFileName(); - - for ($i = 0; $i < $parent; $i++) { - $directory = dirname($directory); - } - - $this->addDirectoryToBlacklist($directory); - } -} diff --git a/PHP/CodeCoverage/Report/Clover.php b/PHP/CodeCoverage/Report/Clover.php deleted file mode 100644 index 05f9eee43..000000000 --- a/PHP/CodeCoverage/Report/Clover.php +++ /dev/null @@ -1,335 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.0.0 - */ - -/** - * Generates a Clover XML logfile from an PHP_CodeCoverage object. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.0.0 - */ -class PHP_CodeCoverage_Report_Clover -{ - /** - * @param PHP_CodeCoverage $coverage - * @param string $target - * @param string $name - * @return string - */ - public function process(PHP_CodeCoverage $coverage, $target = NULL, $name = NULL) - { - $xmlDocument = new DOMDocument('1.0', 'UTF-8'); - $xmlDocument->formatOutput = TRUE; - - $xmlCoverage = $xmlDocument->createElement('coverage'); - $xmlCoverage->setAttribute('generated', (int)$_SERVER['REQUEST_TIME']); - $xmlDocument->appendChild($xmlCoverage); - - $xmlProject = $xmlDocument->createElement('project'); - $xmlProject->setAttribute('timestamp', (int)$_SERVER['REQUEST_TIME']); - - if (is_string($name)) { - $xmlProject->setAttribute('name', $name); - } - - $xmlCoverage->appendChild($xmlProject); - - $packages = array(); - $report = $coverage->getReport(); - unset($coverage); - - foreach ($report as $item) { - $namespace = 'global'; - - if (!$item instanceof PHP_CodeCoverage_Report_Node_File) { - continue; - } - - $xmlFile = $xmlDocument->createElement('file'); - $xmlFile->setAttribute('name', $item->getPath()); - - $classes = $item->getClassesAndTraits(); - $coverage = $item->getCoverageData(); - $lines = array(); - - foreach ($classes as $className => $class) { - $classStatements = 0; - $coveredClassStatements = 0; - $coveredMethods = 0; - - foreach ($class['methods'] as $methodName => $method) { - $methodCount = 0; - $methodLines = 0; - $methodLinesCovered = 0; - - for ($i = $method['startLine']; - $i <= $method['endLine']; - $i++) { - $add = TRUE; - $count = 0; - - if (isset($coverage[$i])) { - if ($coverage[$i] !== NULL) { - $classStatements++; - $methodLines++; - } else { - $add = FALSE; - } - - $count = count($coverage[$i]); - - if ($count > 0) { - $coveredClassStatements++; - $methodLinesCovered++; - } - } else { - $add = FALSE; - } - - $methodCount = max($methodCount, $count); - - if ($add) { - $lines[$i] = array( - 'count' => $count, 'type' => 'stmt' - ); - } - } - - if ($methodCount > 0) { - $coveredMethods++; - } - - $lines[$method['startLine']] = array( - 'count' => $methodCount, - 'crap' => $method['crap'], - 'type' => 'method', - 'name' => $methodName - ); - } - - if (!empty($class['package']['namespace'])) { - $namespace = $class['package']['namespace']; - } - - $xmlClass = $xmlDocument->createElement('class'); - $xmlClass->setAttribute('name', $className); - $xmlClass->setAttribute('namespace', $namespace); - - if (!empty($class['package']['fullPackage'])) { - $xmlClass->setAttribute( - 'fullPackage', $class['package']['fullPackage'] - ); - } - - if (!empty($class['package']['category'])) { - $xmlClass->setAttribute( - 'category', $class['package']['category'] - ); - } - - if (!empty($class['package']['package'])) { - $xmlClass->setAttribute( - 'package', $class['package']['package'] - ); - } - - if (!empty($class['package']['subpackage'])) { - $xmlClass->setAttribute( - 'subpackage', $class['package']['subpackage'] - ); - } - - $xmlFile->appendChild($xmlClass); - - $xmlMetrics = $xmlDocument->createElement('metrics'); - $xmlMetrics->setAttribute('methods', count($class['methods'])); - $xmlMetrics->setAttribute('coveredmethods', $coveredMethods); - $xmlMetrics->setAttribute('conditionals', 0); - $xmlMetrics->setAttribute('coveredconditionals', 0); - $xmlMetrics->setAttribute('statements', $classStatements); - $xmlMetrics->setAttribute( - 'coveredstatements', $coveredClassStatements - ); - $xmlMetrics->setAttribute( - 'elements', - count($class['methods']) + - $classStatements - /* + conditionals */); - $xmlMetrics->setAttribute( - 'coveredelements', - $coveredMethods + - $coveredClassStatements - /* + coveredconditionals */ - ); - $xmlClass->appendChild($xmlMetrics); - } - - foreach ($coverage as $line => $data) { - if ($data === NULL || isset($lines[$line])) { - continue; - } - - $lines[$line] = array( - 'count' => count($data), 'type' => 'stmt' - ); - } - - ksort($lines); - - foreach ($lines as $line => $data) { - $xmlLine = $xmlDocument->createElement('line'); - $xmlLine->setAttribute('num', $line); - $xmlLine->setAttribute('type', $data['type']); - - if (isset($data['name'])) { - $xmlLine->setAttribute('name', $data['name']); - } - - if (isset($data['crap'])) { - $xmlLine->setAttribute('crap', $data['crap']); - } - - $xmlLine->setAttribute('count', $data['count']); - $xmlFile->appendChild($xmlLine); - } - - $linesOfCode = $item->getLinesOfCode(); - - $xmlMetrics = $xmlDocument->createElement('metrics'); - $xmlMetrics->setAttribute('loc', $linesOfCode['loc']); - $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']); - $xmlMetrics->setAttribute('classes', $item->getNumClassesAndTraits()); - $xmlMetrics->setAttribute('methods', $item->getNumMethods()); - $xmlMetrics->setAttribute( - 'coveredmethods', $item->getNumTestedMethods() - ); - $xmlMetrics->setAttribute('conditionals', 0); - $xmlMetrics->setAttribute('coveredconditionals', 0); - $xmlMetrics->setAttribute( - 'statements', $item->getNumExecutableLines() - ); - $xmlMetrics->setAttribute( - 'coveredstatements', $item->getNumExecutedLines() - ); - $xmlMetrics->setAttribute( - 'elements', - $item->getNumMethods() + - $item->getNumExecutableLines() - /* + conditionals */ - ); - $xmlMetrics->setAttribute( - 'coveredelements', - $item->getNumTestedMethods() + - $item->getNumExecutedLines() - /* + coveredconditionals */ - ); - $xmlFile->appendChild($xmlMetrics); - - if ($namespace == 'global') { - $xmlProject->appendChild($xmlFile); - } else { - if (!isset($packages[$namespace])) { - $packages[$namespace] = $xmlDocument->createElement( - 'package' - ); - - $packages[$namespace]->setAttribute('name', $namespace); - $xmlProject->appendChild($packages[$namespace]); - } - - $packages[$namespace]->appendChild($xmlFile); - } - } - - $linesOfCode = $report->getLinesOfCode(); - - $xmlMetrics = $xmlDocument->createElement('metrics'); - $xmlMetrics->setAttribute('files', count($report)); - $xmlMetrics->setAttribute('loc', $linesOfCode['loc']); - $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']); - $xmlMetrics->setAttribute( - 'classes', $report->getNumClassesAndTraits() - ); - $xmlMetrics->setAttribute('methods', $report->getNumMethods()); - $xmlMetrics->setAttribute( - 'coveredmethods', $report->getNumTestedMethods() - ); - $xmlMetrics->setAttribute('conditionals', 0); - $xmlMetrics->setAttribute('coveredconditionals', 0); - $xmlMetrics->setAttribute( - 'statements', $report->getNumExecutableLines() - ); - $xmlMetrics->setAttribute( - 'coveredstatements', $report->getNumExecutedLines() - ); - $xmlMetrics->setAttribute( - 'elements', - $report->getNumMethods() + - $report->getNumExecutableLines() - /* + conditionals */ - ); - $xmlMetrics->setAttribute( - 'coveredelements', - $report->getNumTestedMethods() + - $report->getNumExecutedLines() - /* + coveredconditionals */ - ); - $xmlProject->appendChild($xmlMetrics); - - if ($target !== NULL) { - if (!is_dir(dirname($target))) { - mkdir(dirname($target), 0777, TRUE); - } - - return $xmlDocument->save($target); - } else { - return $xmlDocument->saveXML(); - } - } -} diff --git a/PHP/CodeCoverage/Report/Factory.php b/PHP/CodeCoverage/Report/Factory.php deleted file mode 100644 index f2780fc8a..000000000 --- a/PHP/CodeCoverage/Report/Factory.php +++ /dev/null @@ -1,281 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.1.0 - */ - -/** - * Factory for PHP_CodeCoverage_Report_Node_* object graphs. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.1.0 - */ -class PHP_CodeCoverage_Report_Factory -{ - /** - * @param PHP_CodeCoverage $coverage - */ - public function create(PHP_CodeCoverage $coverage) - { - $files = $coverage->getData(); - $commonPath = $this->reducePaths($files); - $root = new PHP_CodeCoverage_Report_Node_Directory( - $commonPath, NULL - ); - - $this->addItems( - $root, - $this->buildDirectoryStructure($files), - $coverage->getTests(), - $coverage->getCacheTokens() - ); - - return $root; - } - - /** - * @param PHP_CodeCoverage_Report_Node_Directory $root - * @param array $items - * @param array $tests - * @param boolean $cacheTokens - */ - protected function addItems(PHP_CodeCoverage_Report_Node_Directory $root, array $items, array $tests, $cacheTokens) - { - foreach ($items as $key => $value) { - if (substr($key, -2) == '/f') { - $key = substr($key, 0, -2); - - if (file_exists($root->getPath() . DIRECTORY_SEPARATOR . $key)) { - $root->addFile($key, $value, $tests, $cacheTokens); - } - } else { - $child = $root->addDirectory($key); - $this->addItems($child, $value, $tests, $cacheTokens); - } - } - } - - /** - * Builds an array representation of the directory structure. - * - * For instance, - * - * - * Array - * ( - * [Money.php] => Array - * ( - * ... - * ) - * - * [MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * - * - * is transformed into - * - * - * Array - * ( - * [.] => Array - * ( - * [Money.php] => Array - * ( - * ... - * ) - * - * [MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * ) - * - * - * @param array $files - * @return array - */ - protected function buildDirectoryStructure($files) - { - $result = array(); - - foreach ($files as $path => $file) { - $path = explode('/', $path); - $pointer = &$result; - $max = count($path); - - for ($i = 0; $i < $max; $i++) { - if ($i == ($max - 1)) { - $type = '/f'; - } else { - $type = ''; - } - - $pointer = &$pointer[$path[$i] . $type]; - } - - $pointer = $file; - } - - return $result; - } - - /** - * Reduces the paths by cutting the longest common start path. - * - * For instance, - * - * - * Array - * ( - * [/home/sb/Money/Money.php] => Array - * ( - * ... - * ) - * - * [/home/sb/Money/MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * - * - * is reduced to - * - * - * Array - * ( - * [Money.php] => Array - * ( - * ... - * ) - * - * [MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * - * - * @param array $files - * @return string - */ - protected function reducePaths(&$files) - { - if (empty($files)) { - return '.'; - } - - $commonPath = ''; - $paths = array_keys($files); - - if (count($files) == 1) { - $commonPath = dirname($paths[0]) . '/'; - $files[basename($paths[0])] = $files[$paths[0]]; - - unset($files[$paths[0]]); - - return $commonPath; - } - - $max = count($paths); - - for ($i = 0; $i < $max; $i++) { - // strip phar:// prefixes - if (strpos($paths[$i], 'phar://') === 0) { - $paths[$i] = substr($paths[$i], 7); - $paths[$i] = strtr($paths[$i], '/', DIRECTORY_SEPARATOR); - } - $paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]); - - if (empty($paths[$i][0])) { - $paths[$i][0] = DIRECTORY_SEPARATOR; - } - } - - $done = FALSE; - $max = count($paths); - - while (!$done) { - for ($i = 0; $i < $max - 1; $i++) { - if (!isset($paths[$i][0]) || - !isset($paths[$i+1][0]) || - $paths[$i][0] != $paths[$i+1][0]) { - $done = TRUE; - break; - } - } - - if (!$done) { - $commonPath .= $paths[0][0]; - - if ($paths[0][0] != DIRECTORY_SEPARATOR) { - $commonPath .= DIRECTORY_SEPARATOR; - } - - for ($i = 0; $i < $max; $i++) { - array_shift($paths[$i]); - } - } - } - - $original = array_keys($files); - $max = count($original); - - for ($i = 0; $i < $max; $i++) { - $files[join('/', $paths[$i])] = $files[$original[$i]]; - unset($files[$original[$i]]); - } - - ksort($files); - - return substr($commonPath, 0, -1); - } -} diff --git a/PHP/CodeCoverage/Report/HTML.php b/PHP/CodeCoverage/Report/HTML.php deleted file mode 100644 index b2546259e..000000000 --- a/PHP/CodeCoverage/Report/HTML.php +++ /dev/null @@ -1,222 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.0.0 - */ - -/** - * Generates an HTML report from an PHP_CodeCoverage object. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.0.0 - */ -class PHP_CodeCoverage_Report_HTML -{ - /** - * @var string - */ - protected $templatePath; - - /** - * @var string - */ - protected $charset; - - /** - * @var string - */ - protected $generator; - - /** - * @var integer - */ - protected $lowUpperBound; - - /** - * @var integer - */ - protected $highLowerBound; - - /** - * @var boolean - */ - protected $highlight; - - /** - * Constructor. - * - * @param array $options - */ - public function __construct($charset = 'UTF-8', $highlight = FALSE, $lowUpperBound = 35, $highLowerBound = 70, $generator = '') - { - $this->charset = $charset; - $this->generator = $generator; - $this->highLowerBound = $highLowerBound; - $this->highlight = $highlight; - $this->lowUpperBound = $lowUpperBound; - - $this->templatePath = sprintf( - '%s%sHTML%sRenderer%sTemplate%s', - - dirname(__FILE__), - DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR - ); - } - - /** - * @param PHP_CodeCoverage $coverage - * @param string $target - */ - public function process(PHP_CodeCoverage $coverage, $target) - { - $target = $this->getDirectory($target); - $report = $coverage->getReport(); - unset($coverage); - - if (!isset($_SERVER['REQUEST_TIME'])) { - $_SERVER['REQUEST_TIME'] = time(); - } - - $date = date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']); - - $dashboard = new PHP_CodeCoverage_Report_HTML_Renderer_Dashboard( - $this->templatePath, - $this->charset, - $this->generator, - $date, - $this->lowUpperBound, - $this->highLowerBound - ); - - $directory = new PHP_CodeCoverage_Report_HTML_Renderer_Directory( - $this->templatePath, - $this->charset, - $this->generator, - $date, - $this->lowUpperBound, - $this->highLowerBound - ); - - $file = new PHP_CodeCoverage_Report_HTML_Renderer_File( - $this->templatePath, - $this->charset, - $this->generator, - $date, - $this->lowUpperBound, - $this->highLowerBound, - $this->highlight - ); - - $dashboard->render($report, $target . 'index.dashboard.html'); - $directory->render($report, $target . 'index.html'); - - foreach ($report as $node) { - $id = $node->getId(); - - if ($node instanceof PHP_CodeCoverage_Report_Node_Directory) { - $dashboard->render($node, $target . $id . '.dashboard.html'); - $directory->render($node, $target . $id . '.html'); - } else { - $file->render($node, $target . $id . '.html'); - } - } - - $this->copyFiles($target); - } - - /** - * @param string $target - */ - protected function copyFiles($target) - { - $dir = $this->getDirectory($target . 'css'); - copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css'); - copy($this->templatePath . 'css/bootstrap-responsive.min.css', $dir . 'bootstrap-responsive.min.css'); - copy($this->templatePath . 'css/style.css', $dir . 'style.css'); - - $dir = $this->getDirectory($target . 'js'); - copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js'); - copy($this->templatePath . 'js/highcharts.js', $dir . 'highcharts.js'); - copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js'); - copy($this->templatePath . 'js/html5shiv.js', $dir . 'html5shiv.js'); - - $dir = $this->getDirectory($target . 'img'); - copy($this->templatePath . 'img/glyphicons-halflings.png', $dir . 'glyphicons-halflings.png'); - copy($this->templatePath . 'img/glyphicons-halflings-white.png', $dir . 'glyphicons-halflings-white.png'); - } - - /** - * @param string $directory - * @return string - * @throws PHP_CodeCoverage_Exception - * @since Method available since Release 1.2.0 - */ - protected function getDirectory($directory) - { - if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) { - $directory .= DIRECTORY_SEPARATOR; - } - - if (is_dir($directory)) { - return $directory; - } - - if (@mkdir($directory, 0777, TRUE)) { - return $directory; - } - - throw new PHP_CodeCoverage_Exception( - sprintf( - 'Directory "%s" does not exist.', - $directory - ) - ); - } -} diff --git a/PHP/CodeCoverage/Report/HTML/Renderer.php b/PHP/CodeCoverage/Report/HTML/Renderer.php deleted file mode 100644 index ac8500c0e..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer.php +++ /dev/null @@ -1,277 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.1.0 - */ - -/** - * Base class for PHP_CodeCoverage_Report_Node renderers. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.1.0 - */ -abstract class PHP_CodeCoverage_Report_HTML_Renderer -{ - /** - * @var string - */ - protected $templatePath; - - /** - * @var string - */ - protected $charset; - - /** - * @var string - */ - protected $generator; - - /** - * @var string - */ - protected $date; - - /** - * @var integer - */ - protected $lowUpperBound; - - /** - * @var integer - */ - protected $highLowerBound; - - /** - * @var string - */ - protected $version; - - /** - * Constructor. - * - * @param string $templatePath - * @param string $charset - * @param string $generator - * @param string $date - * @param integer $lowUpperBound - * @param integer $highLowerBound - */ - public function __construct($templatePath, $charset, $generator, $date, $lowUpperBound, $highLowerBound) - { - $version = new SebastianBergmann\Version('1.3', __DIR__); - - $this->templatePath = $templatePath; - $this->charset = $charset; - $this->generator = $generator; - $this->date = $date; - $this->lowUpperBound = $lowUpperBound; - $this->highLowerBound = $highLowerBound; - $this->version = $version->getVersion(); - } - - /** - * @param Text_Template $template - * @param array $data - * @return string - */ - protected function renderItemTemplate(Text_Template $template, array $data) - { - $classesBar = ' '; - $classesLevel = 'None'; - $classesNumber = ' '; - - if (isset($data['numClasses']) && $data['numClasses'] > 0) { - $classesLevel = $this->getColorLevel($data['testedClassesPercent']); - - $classesNumber = $data['numTestedClasses'] . ' / ' . - $data['numClasses']; - - $classesBar = $this->getCoverageBar( - $data['testedClassesPercent'] - ); - } - - $methodsBar = ' '; - $methodsLevel = 'None'; - $methodsNumber = ' '; - - if ($data['numMethods'] > 0) { - $methodsLevel = $this->getColorLevel($data['testedMethodsPercent']); - - $methodsNumber = $data['numTestedMethods'] . ' / ' . - $data['numMethods']; - - $methodsBar = $this->getCoverageBar( - $data['testedMethodsPercent'] - ); - } - - $linesBar = ' '; - $linesLevel = 'None'; - $linesNumber = ' '; - - if ($data['numExecutableLines'] > 0) { - $linesLevel = $this->getColorLevel($data['linesExecutedPercent']); - - $linesNumber = $data['numExecutedLines'] . ' / ' . - $data['numExecutableLines']; - - $linesBar = $this->getCoverageBar( - $data['linesExecutedPercent'] - ); - } - - $template->setVar( - array( - 'icon' => isset($data['icon']) ? $data['icon'] : '', - 'crap' => isset($data['crap']) ? $data['crap'] : '', - 'name' => $data['name'], - 'lines_bar' => $linesBar, - 'lines_executed_percent' => $data['linesExecutedPercentAsString'], - 'lines_level' => $linesLevel, - 'lines_number' => $linesNumber, - 'methods_bar' => $methodsBar, - 'methods_tested_percent' => $data['testedMethodsPercentAsString'], - 'methods_level' => $methodsLevel, - 'methods_number' => $methodsNumber, - 'classes_bar' => $classesBar, - 'classes_tested_percent' => isset($data['testedClassesPercentAsString']) ? $data['testedClassesPercentAsString'] : '', - 'classes_level' => $classesLevel, - 'classes_number' => $classesNumber - ) - ); - - return $template->render(); - } - - /** - * @param Text_Template $template - * @param PHP_CodeCoverage_Report_Node $node - */ - protected function setCommonTemplateVariables(Text_Template $template, PHP_CodeCoverage_Report_Node $node) - { - $template->setVar( - array( - 'id' => $node->getId(), - 'full_path' => $node->getPath(), - 'breadcrumbs' => $this->getBreadcrumbs($node), - 'charset' => $this->charset, - 'date' => $this->date, - 'version' => $this->version, - 'php_version' => PHP_VERSION, - 'generator' => $this->generator, - 'low_upper_bound' => $this->lowUpperBound, - 'high_lower_bound' => $this->highLowerBound - ) - ); - } - - protected function getBreadcrumbs(PHP_CodeCoverage_Report_Node $node) - { - $breadcrumbs = ''; - - $path = $node->getPathAsArray(); - - foreach ($path as $step) { - if ($step !== $node) { - $breadcrumbs .= sprintf( - '
  • %s /
  • ' . "\n", - $step->getId(), - $step->getName() - ); - } else { - $breadcrumbs .= sprintf( - '
  • %s
  • ' . "\n", - $step->getName() - ); - - if ($node instanceof PHP_CodeCoverage_Report_Node_Directory) { - $breadcrumbs .= sprintf( - '
  • (Dashboard)
  • ' . "\n", - $step->getId() - ); - } - } - } - - return $breadcrumbs; - } - - protected function getCoverageBar($percent) - { - $level = $this->getColorLevel($percent); - - $template = new Text_Template( - $this->templatePath . 'coverage_bar.html' - ); - - $template->setVar(array('level' => $level, 'percent' => sprintf("%.2F", $percent))); - - return $template->render(); - } - - /** - * @param integer $percent - * @return string - */ - protected function getColorLevel($percent) - { - if ($percent < $this->lowUpperBound) { - return 'danger'; - } - - else if ($percent >= $this->lowUpperBound && - $percent < $this->highLowerBound) { - return 'warning'; - } - - else { - return 'success'; - } - } -} diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php b/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php deleted file mode 100644 index f0767ac14..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php +++ /dev/null @@ -1,256 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.1.0 - */ - -/** - * Renders the dashboard for a PHP_CodeCoverage_Report_Node_Directory node. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.1.0 - */ -class PHP_CodeCoverage_Report_HTML_Renderer_Dashboard extends PHP_CodeCoverage_Report_HTML_Renderer -{ - /** - * @param PHP_CodeCoverage_Report_Node_Directory $node - * @param string $file - */ - public function render(PHP_CodeCoverage_Report_Node_Directory $node, $file) - { - $classes = $node->getClassesAndTraits(); - $template = new Text_Template( - $this->templatePath . 'dashboard.html' - ); - - $this->setCommonTemplateVariables($template, $node); - - $template->setVar( - array( - 'least_tested_methods' => $this->leastTestedMethods($classes), - 'top_project_risks' => $this->topProjectRisks($classes), - 'cc_values' => $this->classComplexity($classes), - 'ccd_values' => $this->classCoverageDistribution($classes), - 'backlink' => basename(str_replace('.dashboard', '', $file)) - ) - ); - - $template->renderTo($file); - } - - /** - * Returns the data for the Class Complexity chart. - * - * @param array $classes - * @return string - */ - protected function classComplexity(array $classes) - { - $data = array(); - - foreach ($classes as $name => $class) { - $data[] = array( - $class['coverage'], - $class['ccn'], - sprintf( - '%s', - $class['link'], - $name - ) - ); - } - - return json_encode($data); - } - - /** - * Returns the data for the Class Coverage Distribution chart. - * - * @param array $classes - * @return string - */ - protected function classCoverageDistribution(array $classes) - { - $data = array( - '0%' => 0, - '0-10%' => 0, - '10-20%' => 0, - '20-30%' => 0, - '30-40%' => 0, - '40-50%' => 0, - '50-60%' => 0, - '60-70%' => 0, - '70-80%' => 0, - '80-90%' => 0, - '90-100%' => 0, - '100%' => 0 - ); - - foreach ($classes as $class) { - if ($class['coverage'] == 0) { - $data['0%']++; - } - - else if ($class['coverage'] == 100) { - $data['100%']++; - } - - else { - $key = floor($class['coverage']/10)*10; - $key = $key . '-' . ($key + 10) . '%'; - $data[$key]++; - } - } - - return json_encode(array_values($data)); - } - - /** - * Returns the least tested methods. - * - * @param array $classes - * @param integer $max - * @return string - */ - protected function leastTestedMethods(array $classes, $max = 10) - { - $methods = array(); - - foreach ($classes as $className => $class) { - foreach ($class['methods'] as $methodName => $method) { - if ($method['coverage'] < 100) { - if ($className != '*') { - $key = $className . '::' . $methodName; - } else { - $key = $methodName; - } - - $methods[$key] = $method['coverage']; - } - } - } - - asort($methods); - - $methods = array_slice($methods, 0, min($max, count($methods))); - $buffer = ''; - - foreach ($methods as $name => $coverage) { - list($class, $method) = explode('::', $name); - - $buffer .= sprintf( - '
  • %s (%d%%)
  • ' . "\n", - $classes[$class]['methods'][$method]['link'], - $name, - $coverage - ); - } - - return $buffer; - } - - /** - * Returns the top project risks according to the CRAP index. - * - * @param array $classes - * @param integer $max - * @return string - */ - protected function topProjectRisks(array $classes, $max = 10) - { - $risks = array(); - - foreach ($classes as $className => $class) { - if ($class['coverage'] < 100 && - $class['ccn'] > count($class['methods'])) { - $risks[$className] = $class['crap']; - } - } - - arsort($risks); - - $buffer = ''; - $risks = array_slice($risks, 0, min($max, count($risks))); - - foreach ($risks as $name => $crap) { - $buffer .= sprintf( - '
  • %s (%d)
  • ' . "\n", - $classes[$name]['link'], - $name, - $crap - ); - } - - return $buffer; - } - - protected function getBreadcrumbs(PHP_CodeCoverage_Report_Node $node) - { - $breadcrumbs = ''; - - $path = $node->getPathAsArray(); - - foreach ($path as $step) { - if ($step !== $node) { - $breadcrumbs .= sprintf( - '
  • %s /
  • ' . "\n", - $step->getId(), - $step->getName() - ); - } else { - $breadcrumbs .= sprintf( - '
  • %s
  • ' . "\n" . - '
  • (Dashboard)
  • ' . "\n", - $step->getId(), - $step->getName() - ); - } - } - - return $breadcrumbs; - } -} diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php b/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php deleted file mode 100644 index c38f84489..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php +++ /dev/null @@ -1,132 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.1.0 - */ - -/** - * Renders a PHP_CodeCoverage_Report_Node_Directory node. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.1.0 - */ -class PHP_CodeCoverage_Report_HTML_Renderer_Directory extends PHP_CodeCoverage_Report_HTML_Renderer -{ - /** - * @param PHP_CodeCoverage_Report_Node_Directory $node - * @param string $file - */ - public function render(PHP_CodeCoverage_Report_Node_Directory $node, $file) - { - $template = new Text_Template($this->templatePath . 'directory.html'); - - $this->setCommonTemplateVariables($template, $node); - - $items = $this->renderItem($node, TRUE); - - foreach ($node->getDirectories() as $item) { - $items .= $this->renderItem($item); - } - - foreach ($node->getFiles() as $item) { - $items .= $this->renderItem($item); - } - - $template->setVar( - array( - 'id' => $node->getId(), - 'items' => $items - ) - ); - - $template->renderTo($file); - } - - /** - * @param PHP_CodeCoverage_Report_Node $item - * @param boolean $total - * @return string - */ - protected function renderItem(PHP_CodeCoverage_Report_Node $item, $total = FALSE) - { - $data = array( - 'numClasses' => $item->getNumClassesAndTraits(), - 'numTestedClasses' => $item->getNumTestedClassesAndTraits(), - 'numMethods' => $item->getNumMethods(), - 'numTestedMethods' => $item->getNumTestedMethods(), - 'linesExecutedPercent' => $item->getLineExecutedPercent(FALSE), - 'linesExecutedPercentAsString' => $item->getLineExecutedPercent(), - 'numExecutedLines' => $item->getNumExecutedLines(), - 'numExecutableLines' => $item->getNumExecutableLines(), - 'testedMethodsPercent' => $item->getTestedMethodsPercent(FALSE), - 'testedMethodsPercentAsString' => $item->getTestedMethodsPercent(), - 'testedClassesPercent' => $item->getTestedClassesAndTraitsPercent(FALSE), - 'testedClassesPercentAsString' => $item->getTestedClassesAndTraitsPercent() - ); - - if ($total) { - $data['name'] = 'Total'; - } else { - $data['name'] = sprintf( - '%s', - $item->getId(), - $item->getName() - ); - - if ($item instanceof PHP_CodeCoverage_Report_Node_Directory) { - $data['icon'] = ' '; - } else { - $data['icon'] = ' '; - } - } - - return $this->renderItemTemplate( - new Text_Template($this->templatePath . 'directory_item.html'), - $data - ); - } -} diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/File.php b/PHP/CodeCoverage/Report/HTML/Renderer/File.php deleted file mode 100644 index c03a84909..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/File.php +++ /dev/null @@ -1,568 +0,0 @@ -. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Sebastian Bergmann nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.1.0 - */ - -/** - * Renders a PHP_CodeCoverage_Report_Node_File node. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2013 Sebastian Bergmann - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.1.0 - */ -class PHP_CodeCoverage_Report_HTML_Renderer_File extends PHP_CodeCoverage_Report_HTML_Renderer -{ - /** - * @var boolean - */ - protected $highlight; - - /** - * Constructor. - * - * @param string $templatePath - * @param string $charset - * @param string $generator - * @param string $date - * @param integer $lowUpperBound - * @param integer $highLowerBound - * @param boolean $highlight - */ - public function __construct($templatePath, $charset, $generator, $date, $lowUpperBound, $highLowerBound, $highlight) - { - parent::__construct( - $templatePath, - $charset, - $generator, - $date, - $lowUpperBound, - $highLowerBound - ); - - $this->highlight = $highlight; - } - - /** - * @param PHP_CodeCoverage_Report_Node_File $node - * @param string $file - */ - public function render(PHP_CodeCoverage_Report_Node_File $node, $file) - { - $template = new Text_Template($this->templatePath . 'file.html'); - - $template->setVar( - array( - 'items' => $this->renderItems($node), - 'lines' => $this->renderSource($node) - ) - ); - - $this->setCommonTemplateVariables($template, $node); - - $template->renderTo($file); - } - - /** - * @param PHP_CodeCoverage_Report_Node_File $node - * @return string - */ - protected function renderItems(PHP_CodeCoverage_Report_Node_File $node) - { - $template = new Text_Template($this->templatePath . 'file_item.html'); - - $methodItemTemplate = new Text_Template( - $this->templatePath . 'method_item.html' - ); - - $items = $this->renderItemTemplate( - $template, - array( - 'name' => 'Total', - 'numClasses' => $node->getNumClassesAndTraits(), - 'numTestedClasses' => $node->getNumTestedClassesAndTraits(), - 'numMethods' => $node->getNumMethods(), - 'numTestedMethods' => $node->getNumTestedMethods(), - 'linesExecutedPercent' => $node->getLineExecutedPercent(FALSE), - 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(), - 'numExecutedLines' => $node->getNumExecutedLines(), - 'numExecutableLines' => $node->getNumExecutableLines(), - 'testedMethodsPercent' => $node->getTestedMethodsPercent(FALSE), - 'testedMethodsPercentAsString' => $node->getTestedMethodsPercent(), - 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(FALSE), - 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(), - 'crap' => 'CRAP' - ) - ); - - $items .= $this->renderFunctionItems( - $node->getFunctions(), $methodItemTemplate - ); - - $items .= $this->renderTraitOrClassItems( - $node->getTraits(), $template, $methodItemTemplate - ); - - $items .= $this->renderTraitOrClassItems( - $node->getClasses(), $template, $methodItemTemplate - ); - - return $items; - } - - /** - * @param array $items - * @param Text_Template $template - * @return string - */ - protected function renderTraitOrClassItems(array $items, Text_Template $template, Text_Template $methodItemTemplate) - { - if (empty($items)) { - return ''; - } - - $buffer = ''; - - foreach ($items as $name => $item) { - $numMethods = count($item['methods']); - $numTestedMethods = 0; - - foreach ($item['methods'] as $method) { - if ($method['executedLines'] == $method['executableLines']) { - $numTestedMethods++; - } - } - - $buffer .= $this->renderItemTemplate( - $template, - array( - 'name' => $name, - 'numClasses' => 1, - 'numTestedClasses' => $numTestedMethods == $numMethods ? 1 : 0, - 'numMethods' => $numMethods, - 'numTestedMethods' => $numTestedMethods, - 'linesExecutedPercent' => PHP_CodeCoverage_Util::percent( - $item['executedLines'], - $item['executableLines'], - FALSE - ), - 'linesExecutedPercentAsString' => PHP_CodeCoverage_Util::percent( - $item['executedLines'], - $item['executableLines'], - TRUE - ), - 'numExecutedLines' => $item['executedLines'], - 'numExecutableLines' => $item['executableLines'], - 'testedMethodsPercent' => PHP_CodeCoverage_Util::percent( - $numTestedMethods, - $numMethods, - FALSE - ), - 'testedMethodsPercentAsString' => PHP_CodeCoverage_Util::percent( - $numTestedMethods, - $numMethods, - TRUE - ), - 'testedClassesPercent' => PHP_CodeCoverage_Util::percent( - $numTestedMethods == $numMethods ? 1 : 0, - 1, - FALSE - ), - 'testedClassesPercentAsString' => PHP_CodeCoverage_Util::percent( - $numTestedMethods == $numMethods ? 1 : 0, - 1, - TRUE - ), - 'crap' => $item['crap'] - ) - ); - - foreach ($item['methods'] as $method) { - $buffer .= $this->renderFunctionOrMethodItem( - $methodItemTemplate, $method, ' ' - ); - } - } - - return $buffer; - } - - /** - * @param array $functions - * @param Text_Template $template - * @return string - */ - protected function renderFunctionItems(array $functions, Text_Template $template) - { - if (empty($functions)) { - return ''; - } - - $buffer = ''; - - foreach ($functions as $function) { - $buffer .= $this->renderFunctionOrMethodItem( - $template, $function - ); - } - - return $buffer; - } - - /** - * @param Text_Template $template - * @return string - */ - protected function renderFunctionOrMethodItem(Text_Template $template, array $item, $indent = '') - { - $numTestedItems = $item['executedLines'] == $item['executableLines'] ? 1 : 0; - - return $this->renderItemTemplate( - $template, - array( - 'name' => sprintf( - '%s%s', - $indent, - $item['startLine'], - htmlspecialchars($item['signature']) - ), - 'numMethods' => 1, - 'numTestedMethods' => $numTestedItems, - 'linesExecutedPercent' => PHP_CodeCoverage_Util::percent( - $item['executedLines'], - $item['executableLines'], - FALSE - ), - 'linesExecutedPercentAsString' => PHP_CodeCoverage_Util::percent( - $item['executedLines'], - $item['executableLines'], - TRUE - ), - 'numExecutedLines' => $item['executedLines'], - 'numExecutableLines' => $item['executableLines'], - 'testedMethodsPercent' => PHP_CodeCoverage_Util::percent( - $numTestedItems, - 1, - FALSE - ), - 'testedMethodsPercentAsString' => PHP_CodeCoverage_Util::percent( - $numTestedItems, - 1, - TRUE - ), - 'crap' => $item['crap'] - ) - ); - } - - /** - * @param PHP_CodeCoverage_Report_Node_File $node - * @return string - */ - protected function renderSource(PHP_CodeCoverage_Report_Node_File $node) - { - $coverageData = $node->getCoverageData(); - $testData = $node->getTestData(); - $codeLines = $this->loadFile($node->getPath()); - $lines = ''; - $i = 1; - - foreach ($codeLines as $line) { - $numTests = ''; - $trClass = ''; - $popoverContent = ''; - $popoverTitle = ''; - - if (isset($coverageData[$i])) { - $numTests = count($coverageData[$i]); - - if ($coverageData[$i] === NULL) { - $trClass = ' class="warning"'; - } - - else if ($numTests == 0) { - $trClass = ' class="danger"'; - } - - else { - $trClass = ' class="success popin"'; - $popoverContent = '
      '; - - if ($numTests > 1) { - $popoverTitle = $numTests . ' tests cover line ' . $i; - } else { - $popoverTitle = '1 test covers line ' . $i; - } - - foreach ($coverageData[$i] as $test) { - switch ($testData[$test]) { - case 0: { - $testCSS = ' class="success"'; - } - break; - - case 1: - case 2: { - $testCSS = ' class="warning"'; - } - break; - - case 3: { - $testCSS = ' class="danger"'; - } - break; - - case 4: { - $testCSS = ' class="danger"'; - } - break; - - default: { - $testCSS = ''; - } - } - - $popoverContent .= sprintf( - '%s', - - $testCSS, - htmlspecialchars($test) - ); - } - - $popoverContent .= '
    '; - } - } - - if (!empty($popoverTitle)) { - $popover = sprintf( - ' data-title="%s" data-content="%s" data-placement="bottom" data-html="true"', - $popoverTitle, - htmlspecialchars($popoverContent) - ); - } else { - $popover = ''; - } - - $lines .= sprintf( - ' %s' . "\n", - $trClass, - $popover, - $i, - $i, - $i, - !$this->highlight ? htmlspecialchars($line) : $line - ); - - $i++; - } - - return $lines; - } - - /** - * @param string $file - * @return array - */ - protected function loadFile($file) - { - $buffer = file_get_contents($file); - $lines = explode("\n", str_replace("\t", ' ', $buffer)); - $result = array(); - - if (count($lines) == 0) { - return $result; - } - - $lines = array_map('rtrim', $lines); - - if (!$this->highlight) { - unset($lines[count($lines)-1]); - return $lines; - } - - $tokens = token_get_all($buffer); - $stringFlag = FALSE; - $i = 0; - $result[$i] = ''; - - foreach ($tokens as $j => $token) { - if (is_string($token)) { - if ($token === '"' && $tokens[$j - 1] !== '\\') { - $result[$i] .= sprintf( - '%s', - - htmlspecialchars($token) - ); - - $stringFlag = !$stringFlag; - } else { - $result[$i] .= sprintf( - '%s', - - htmlspecialchars($token) - ); - } - - continue; - } - - list ($token, $value) = $token; - - $value = str_replace( - array("\t", ' '), - array('    ', ' '), - htmlspecialchars($value) - ); - - if ($value === "\n") { - $result[++$i] = ''; - } else { - $lines = explode("\n", $value); - - foreach ($lines as $jj => $line) { - $line = trim($line); - - if ($line !== '') { - if ($stringFlag) { - $colour = 'string'; - } else { - switch ($token) { - case T_INLINE_HTML: { - $colour = 'html'; - } - break; - - case T_COMMENT: - case T_DOC_COMMENT: { - $colour = 'comment'; - } - break; - - case T_ABSTRACT: - case T_ARRAY: - case T_AS: - case T_BREAK: - case T_CALLABLE: - case T_CASE: - case T_CATCH: - case T_CLASS: - case T_CLONE: - case T_CONTINUE: - case T_DEFAULT: - case T_ECHO: - case T_ELSE: - case T_ELSEIF: - case T_EMPTY: - case T_ENDDECLARE: - case T_ENDFOR: - case T_ENDFOREACH: - case T_ENDIF: - case T_ENDSWITCH: - case T_ENDWHILE: - case T_EXIT: - case T_EXTENDS: - case T_FINAL: - case T_FOREACH: - case T_FUNCTION: - case T_GLOBAL: - case T_IF: - case T_IMPLEMENTS: - case T_INCLUDE: - case T_INCLUDE_ONCE: - case T_INSTANCEOF: - case T_INSTEADOF: - case T_INTERFACE: - case T_ISSET: - case T_LOGICAL_AND: - case T_LOGICAL_OR: - case T_LOGICAL_XOR: - case T_NAMESPACE: - case T_NEW: - case T_PRIVATE: - case T_PROTECTED: - case T_PUBLIC: - case T_REQUIRE: - case T_REQUIRE_ONCE: - case T_RETURN: - case T_STATIC: - case T_THROW: - case T_TRAIT: - case T_TRY: - case T_UNSET: - case T_USE: - case T_VAR: - case T_WHILE: { - $colour = 'keyword'; - } - break; - - default: { - $colour = 'default'; - } - } - } - - $result[$i] .= sprintf( - '%s', - - $colour, - $line - ); - } - - if (isset($lines[$jj + 1])) { - $result[++$i] = ''; - } - } - } - } - - unset($result[count($result)-1]); - - return $result; - } -} diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist b/PHP/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist deleted file mode 100644 index 73a11a1a1..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist +++ /dev/null @@ -1,3 +0,0 @@ -
    -
    -
    diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap-responsive.min.css b/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap-responsive.min.css deleted file mode 100644 index 5cb833ff0..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap-responsive.min.css +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * Bootstrap Responsive v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */@-ms-viewport{width:device-width}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css b/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css deleted file mode 100644 index 140f731df..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * Bootstrap v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}a.muted:hover{color:#808080}.text-warning{color:#c09853}a.text-warning:hover{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover{color:#2d6987}.text-success{color:#468847}a.text-success:hover{color:#356635}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success td{background-color:#dff0d8}.table tbody tr.error td{background-color:#f2dede}.table tbody tr.warning td{background-color:#fcf8e3}.table tbody tr.info td{background-color:#d9edf7}.table-hover tbody tr.success:hover td{background-color:#d0e9c6}.table-hover tbody tr.error:hover td{background-color:#ebcccc}.table-hover tbody tr.warning:hover td{background-color:#faf2cc}.table-hover tbody tr.info:hover td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999}.dropdown-menu .disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #bbb;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#a2a2a2;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#c5c5c5;border-color:rgba(0,0,0,0.15) rgba(0,0,0,0.15) rgba(0,0,0,0.25)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info h4{color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret{border-top-color:#555;border-bottom-color:#555}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#fff}.navbar-inverse .brand{color:#999}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-3px}.tooltip.right{margin-left:3px}.tooltip.bottom{margin-top:3px}.tooltip.left{margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media .pull-left{margin-right:10px}.media .pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed} diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/style.css b/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/style.css deleted file mode 100644 index 2ef162894..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/style.css +++ /dev/null @@ -1,86 +0,0 @@ -body { - padding-top: 10px; -} - -.popover { - width: 600px; -} - -.table td { - padding-top: 3px; - padding-bottom: 3px; -} - -.table-condensed td { - padding-top: 0; - padding-bottom: 0; -} - -.table .progress { - margin-bottom: inherit; -} - -.table-borderless th, .table-borderless td { - border: 0 !important; -} - -.table tbody td.success, li.success, span.success { - background-color: #dff0d8; -} - -.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger { - background-color: #f2dede; -} - -.table tbody td.warning, li.warning, span.warning { - background-color: #fcf8e3; -} - -.table tbody td.info { - background-color: #d9edf7; -} - -td.big { - width: 100px; -} - -td.small { -} - -td.codeLine { - font-family: monospace; - white-space: pre; -} - -td span.comment { - color: #888a85; -} - -td span.default { - color: #2e3436; -} - -td span.html { - color: #888a85; -} - -td span.keyword { - color: #2e3436; - font-weight: bold; -} - -pre span.string { - color: #2e3436; -} - -span.success, span.warning, span.danger { - margin-right: 2px; - padding-left: 10px; - padding-right: 10px; - text-align: center; -} - -#classCoverageDistribution, #classComplexity { - height: 200px; - width: 475px; -} diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.dist b/PHP/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.dist deleted file mode 100644 index eb58b36f9..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.dist +++ /dev/null @@ -1,117 +0,0 @@ - - - - - Dashboard for {full_path} - - - - - - - -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -

    Class Coverage Distribution

    -
    -
    -
    -

    Class Complexity

    -
    -
    -
    -
    -
    -

    Top Project Risks

    -
      -{top_project_risks} -
    -
    -
    -

    Least Tested Methods

    -
      -{least_tested_methods} -
    -
    -
    - -
    - - - - - - diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory.html.dist b/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory.html.dist deleted file mode 100644 index 347440ce7..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory.html.dist +++ /dev/null @@ -1,58 +0,0 @@ - - - - - Code Coverage for {full_path} - - - - - - - -
    -
    -
    -
    - -
    -
    -
    -
    -
    - - - - - - - - - - - - - - -{items} - -
     
    Code Coverage
     
    Lines
    Functions and Methods
    Classes and Traits
    -
    -

    Legend

    -

    - Low: 0% to {low_upper_bound}% - Medium: {low_upper_bound}% to {high_lower_bound}% - High: {high_lower_bound}% to 100% -

    -

    - Generated by PHP_CodeCoverage {version} using PHP {php_version}{generator} at {date}. -

    -
    -
    - - - diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist b/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist deleted file mode 100644 index 4a19a06f4..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist +++ /dev/null @@ -1,13 +0,0 @@ - - {icon}{name} - {lines_bar} -
    {lines_executed_percent}
    -
    {lines_number}
    - {methods_bar} -
    {methods_tested_percent}
    -
    {methods_number}
    - {classes_bar} -
    {classes_tested_percent}
    -
    {classes_number}
    - - diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/file.html.dist b/PHP/CodeCoverage/Report/HTML/Renderer/Template/file.html.dist deleted file mode 100644 index 31a513ee2..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Template/file.html.dist +++ /dev/null @@ -1,65 +0,0 @@ - - - - - Code Coverage for {full_path} - - - - - - - -
    -
    -
    -
    - -
    -
    -
    -
    -
    - - - - - - - - - - - - - - -{items} - -
     
    Code Coverage
     
    Classes and Traits
    Functions and Methods
    Lines
    - - -{lines} - -
    - -
    - - - - - diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.dist b/PHP/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.dist deleted file mode 100644 index 7bff4e53e..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.dist +++ /dev/null @@ -1,14 +0,0 @@ - - {name} - {classes_bar} -
    {classes_tested_percent}
    -
    {classes_number}
    - {methods_bar} -
    {methods_tested_percent}
    -
    {methods_number}
    - {crap} - {lines_bar} -
    {lines_executed_percent}
    -
    {lines_number}
    - - diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings-white.png b/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings-white.png deleted file mode 100644 index 3bf6484a2..000000000 Binary files a/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings-white.png and /dev/null differ diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings.png b/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings.png deleted file mode 100644 index a99699932..000000000 Binary files a/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings.png and /dev/null differ diff --git a/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.js b/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.js deleted file mode 100644 index 6eeb15ce3..000000000 --- a/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! -* Bootstrap.js by @fat & @mdo -* Copyright 2012 Twitter, Inc. -* http://www.apache.org/licenses/LICENSE-2.0.txt -*/ -!function($){"use strict";$(function(){$.support.transition=function(){var transitionEnd=function(){var name,el=document.createElement("bootstrap"),transEndEventNames={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(name in transEndEventNames)if(void 0!==el.style[name])return transEndEventNames[name]}();return transitionEnd&&{end:transitionEnd}}()})}(window.jQuery),!function($){"use strict";var dismiss='[data-dismiss="alert"]',Alert=function(el){$(el).on("click",dismiss,this.close)};Alert.prototype.close=function(e){function removeElement(){$parent.trigger("closed").remove()}var $parent,$this=$(this),selector=$this.attr("data-target");selector||(selector=$this.attr("href"),selector=selector&&selector.replace(/.*(?=#[^\s]*$)/,"")),$parent=$(selector),e&&e.preventDefault(),$parent.length||($parent=$this.hasClass("alert")?$this:$this.parent()),$parent.trigger(e=$.Event("close")),e.isDefaultPrevented()||($parent.removeClass("in"),$.support.transition&&$parent.hasClass("fade")?$parent.on($.support.transition.end,removeElement):removeElement())};var old=$.fn.alert;$.fn.alert=function(option){return this.each(function(){var $this=$(this),data=$this.data("alert");data||$this.data("alert",data=new Alert(this)),"string"==typeof option&&data[option].call($this)})},$.fn.alert.Constructor=Alert,$.fn.alert.noConflict=function(){return $.fn.alert=old,this},$(document).on("click.alert.data-api",dismiss,Alert.prototype.close)}(window.jQuery),!function($){"use strict";var Button=function(element,options){this.$element=$(element),this.options=$.extend({},$.fn.button.defaults,options)};Button.prototype.setState=function(state){var d="disabled",$el=this.$element,data=$el.data(),val=$el.is("input")?"val":"html";state+="Text",data.resetText||$el.data("resetText",$el[val]()),$el[val](data[state]||this.options[state]),setTimeout(function(){"loadingText"==state?$el.addClass(d).attr(d,d):$el.removeClass(d).removeAttr(d)},0)},Button.prototype.toggle=function(){var $parent=this.$element.closest('[data-toggle="buttons-radio"]');$parent&&$parent.find(".active").removeClass("active"),this.$element.toggleClass("active")};var old=$.fn.button;$.fn.button=function(option){return this.each(function(){var $this=$(this),data=$this.data("button"),options="object"==typeof option&&option;data||$this.data("button",data=new Button(this,options)),"toggle"==option?data.toggle():option&&data.setState(option)})},$.fn.button.defaults={loadingText:"loading..."},$.fn.button.Constructor=Button,$.fn.button.noConflict=function(){return $.fn.button=old,this},$(document).on("click.button.data-api","[data-toggle^=button]",function(e){var $btn=$(e.target);$btn.hasClass("btn")||($btn=$btn.closest(".btn")),$btn.button("toggle")})}(window.jQuery),!function($){"use strict";var Carousel=function(element,options){this.$element=$(element),this.options=options,"hover"==this.options.pause&&this.$element.on("mouseenter",$.proxy(this.pause,this)).on("mouseleave",$.proxy(this.cycle,this))};Carousel.prototype={cycle:function(e){return e||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval($.proxy(this.next,this),this.options.interval)),this},to:function(pos){var $active=this.$element.find(".item.active"),children=$active.parent().children(),activePos=children.index($active),that=this;if(!(pos>children.length-1||0>pos))return this.sliding?this.$element.one("slid",function(){that.to(pos)}):activePos==pos?this.pause().cycle():this.slide(pos>activePos?"next":"prev",$(children[pos]))},pause:function(e){return e||(this.paused=!0),this.$element.find(".next, .prev").length&&$.support.transition.end&&(this.$element.trigger($.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){return this.sliding?void 0:this.slide("next")},prev:function(){return this.sliding?void 0:this.slide("prev")},slide:function(type,next){var e,$active=this.$element.find(".item.active"),$next=next||$active[type](),isCycling=this.interval,direction="next"==type?"left":"right",fallback="next"==type?"first":"last",that=this;if(this.sliding=!0,isCycling&&this.pause(),$next=$next.length?$next:this.$element.find(".item")[fallback](),e=$.Event("slide",{relatedTarget:$next[0]}),!$next.hasClass("active")){if($.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(e),e.isDefaultPrevented())return;$next.addClass(type),$next[0].offsetWidth,$active.addClass(direction),$next.addClass(direction),this.$element.one($.support.transition.end,function(){$next.removeClass([type,direction].join(" ")).addClass("active"),$active.removeClass(["active",direction].join(" ")),that.sliding=!1,setTimeout(function(){that.$element.trigger("slid")},0)})}else{if(this.$element.trigger(e),e.isDefaultPrevented())return;$active.removeClass("active"),$next.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return isCycling&&this.cycle(),this}}};var old=$.fn.carousel;$.fn.carousel=function(option){return this.each(function(){var $this=$(this),data=$this.data("carousel"),options=$.extend({},$.fn.carousel.defaults,"object"==typeof option&&option),action="string"==typeof option?option:options.slide;data||$this.data("carousel",data=new Carousel(this,options)),"number"==typeof option?data.to(option):action?data[action]():options.interval&&data.cycle()})},$.fn.carousel.defaults={interval:5e3,pause:"hover"},$.fn.carousel.Constructor=Carousel,$.fn.carousel.noConflict=function(){return $.fn.carousel=old,this},$(document).on("click.carousel.data-api","[data-slide]",function(e){var href,$this=$(this),$target=$($this.attr("data-target")||(href=$this.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,"")),options=$.extend({},$target.data(),$this.data());$target.carousel(options),e.preventDefault()})}(window.jQuery),!function($){"use strict";var Collapse=function(element,options){this.$element=$(element),this.options=$.extend({},$.fn.collapse.defaults,options),this.options.parent&&(this.$parent=$(this.options.parent)),this.options.toggle&&this.toggle()};Collapse.prototype={constructor:Collapse,dimension:function(){var hasWidth=this.$element.hasClass("width");return hasWidth?"width":"height"},show:function(){var dimension,scroll,actives,hasData;if(!this.transitioning){if(dimension=this.dimension(),scroll=$.camelCase(["scroll",dimension].join("-")),actives=this.$parent&&this.$parent.find("> .accordion-group > .in"),actives&&actives.length){if(hasData=actives.data("collapse"),hasData&&hasData.transitioning)return;actives.collapse("hide"),hasData||actives.data("collapse",null)}this.$element[dimension](0),this.transition("addClass",$.Event("show"),"shown"),$.support.transition&&this.$element[dimension](this.$element[0][scroll])}},hide:function(){var dimension;this.transitioning||(dimension=this.dimension(),this.reset(this.$element[dimension]()),this.transition("removeClass",$.Event("hide"),"hidden"),this.$element[dimension](0))},reset:function(size){var dimension=this.dimension();return this.$element.removeClass("collapse")[dimension](size||"auto")[0].offsetWidth,this.$element[null!==size?"addClass":"removeClass"]("collapse"),this},transition:function(method,startEvent,completeEvent){var that=this,complete=function(){"show"==startEvent.type&&that.reset(),that.transitioning=0,that.$element.trigger(completeEvent)};this.$element.trigger(startEvent),startEvent.isDefaultPrevented()||(this.transitioning=1,this.$element[method]("in"),$.support.transition&&this.$element.hasClass("collapse")?this.$element.one($.support.transition.end,complete):complete())},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var old=$.fn.collapse;$.fn.collapse=function(option){return this.each(function(){var $this=$(this),data=$this.data("collapse"),options="object"==typeof option&&option;data||$this.data("collapse",data=new Collapse(this,options)),"string"==typeof option&&data[option]()})},$.fn.collapse.defaults={toggle:!0},$.fn.collapse.Constructor=Collapse,$.fn.collapse.noConflict=function(){return $.fn.collapse=old,this},$(document).on("click.collapse.data-api","[data-toggle=collapse]",function(e){var href,$this=$(this),target=$this.attr("data-target")||e.preventDefault()||(href=$this.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,""),option=$(target).data("collapse")?"toggle":$this.data();$this[$(target).hasClass("in")?"addClass":"removeClass"]("collapsed"),$(target).collapse(option)})}(window.jQuery),!function($){"use strict";function clearMenus(){$(toggle).each(function(){getParent($(this)).removeClass("open")})}function getParent($this){var $parent,selector=$this.attr("data-target");return selector||(selector=$this.attr("href"),selector=selector&&/#/.test(selector)&&selector.replace(/.*(?=#[^\s]*$)/,"")),$parent=$(selector),$parent.length||($parent=$this.parent()),$parent}var toggle="[data-toggle=dropdown]",Dropdown=function(element){var $el=$(element).on("click.dropdown.data-api",this.toggle);$("html").on("click.dropdown.data-api",function(){$el.parent().removeClass("open")})};Dropdown.prototype={constructor:Dropdown,toggle:function(){var $parent,isActive,$this=$(this);if(!$this.is(".disabled, :disabled"))return $parent=getParent($this),isActive=$parent.hasClass("open"),clearMenus(),isActive||$parent.toggleClass("open"),$this.focus(),!1},keydown:function(e){var $this,$items,$parent,isActive,index;if(/(38|40|27)/.test(e.keyCode)&&($this=$(this),e.preventDefault(),e.stopPropagation(),!$this.is(".disabled, :disabled"))){if($parent=getParent($this),isActive=$parent.hasClass("open"),!isActive||isActive&&27==e.keyCode)return $this.click();$items=$("[role=menu] li:not(.divider):visible a",$parent),$items.length&&(index=$items.index($items.filter(":focus")),38==e.keyCode&&index>0&&index--,40==e.keyCode&&$items.length-1>index&&index++,~index||(index=0),$items.eq(index).focus())}}};var old=$.fn.dropdown;$.fn.dropdown=function(option){return this.each(function(){var $this=$(this),data=$this.data("dropdown");data||$this.data("dropdown",data=new Dropdown(this)),"string"==typeof option&&data[option].call($this)})},$.fn.dropdown.Constructor=Dropdown,$.fn.dropdown.noConflict=function(){return $.fn.dropdown=old,this},$(document).on("click.dropdown.data-api touchstart.dropdown.data-api",clearMenus).on("click.dropdown touchstart.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("touchstart.dropdown.data-api",".dropdown-menu",function(e){e.stopPropagation()}).on("click.dropdown.data-api touchstart.dropdown.data-api",toggle,Dropdown.prototype.toggle).on("keydown.dropdown.data-api touchstart.dropdown.data-api",toggle+", [role=menu]",Dropdown.prototype.keydown)}(window.jQuery),!function($){"use strict";var Modal=function(element,options){this.options=options,this.$element=$(element).delegate('[data-dismiss="modal"]',"click.dismiss.modal",$.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};Modal.prototype={constructor:Modal,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var that=this,e=$.Event("show");this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.escape(),this.backdrop(function(){var transition=$.support.transition&&that.$element.hasClass("fade");that.$element.parent().length||that.$element.appendTo(document.body),that.$element.show(),transition&&that.$element[0].offsetWidth,that.$element.addClass("in").attr("aria-hidden",!1),that.enforceFocus(),transition?that.$element.one($.support.transition.end,function(){that.$element.focus().trigger("shown")}):that.$element.focus().trigger("shown")}))},hide:function(e){e&&e.preventDefault(),e=$.Event("hide"),this.$element.trigger(e),this.isShown&&!e.isDefaultPrevented()&&(this.isShown=!1,this.escape(),$(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),$.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal())},enforceFocus:function(){var that=this;$(document).on("focusin.modal",function(e){that.$element[0]===e.target||that.$element.has(e.target).length||that.$element.focus()})},escape:function(){var that=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(e){27==e.which&&that.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var that=this,timeout=setTimeout(function(){that.$element.off($.support.transition.end),that.hideModal()},500);this.$element.one($.support.transition.end,function(){clearTimeout(timeout),that.hideModal()})},hideModal:function(){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(callback){var animate=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var doAnimate=$.support.transition&&animate;this.$backdrop=$(' + +
    +

    Project Risks

    +
    + + + + + + + + + + +{{project_risks_classes}} + +
    ClassCoverageComplexityCRAP
    +
    +
    + +
    +
    +

    Methods

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + +{{insufficient_coverage_methods}} + +
    MethodCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + + + +{{project_risks_methods}} + +
    MethodCoverageComplexityCRAP
    +
    +
    +
    + + + + + + + + diff --git a/src/Report/Html/Renderer/Template/dashboard_branch.html.dist b/src/Report/Html/Renderer/Template/dashboard_branch.html.dist new file mode 100644 index 000000000..eb003154a --- /dev/null +++ b/src/Report/Html/Renderer/Template/dashboard_branch.html.dist @@ -0,0 +1,293 @@ + + + + + Dashboard for {{full_path}} + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + +{{insufficient_coverage_classes}} + +
    ClassCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + +{{project_risks_classes}} + +
    ClassCRAP
    +
    +
    +
    +
    +
    +

    Methods

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + +{{insufficient_coverage_methods}} + +
    MethodCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + +{{project_risks_methods}} + +
    MethodCRAP
    +
    +
    +
    + +
    + + + + + + diff --git a/src/Report/Html/Renderer/Template/directory.html.dist b/src/Report/Html/Renderer/Template/directory.html.dist new file mode 100644 index 000000000..f769d2cae --- /dev/null +++ b/src/Report/Html/Renderer/Template/directory.html.dist @@ -0,0 +1,60 @@ + + + + + Code Coverage for {{full_path}} + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + +{{items}} + +
     
    Code Coverage
     
    Lines
    Functions and Methods
    Classes and Traits
    +
    +
    +
    +

    Legend

    +

    + Low: 0% to {{low_upper_bound}}% + Medium: {{low_upper_bound}}% to {{high_lower_bound}}% + High: {{high_lower_bound}}% to 100% +

    +

    + Generated by php-code-coverage {{version}} using {{runtime}}{{generator}} at {{date}}. +

    +
    +
    + + diff --git a/src/Report/Html/Renderer/Template/directory_branch.html.dist b/src/Report/Html/Renderer/Template/directory_branch.html.dist new file mode 100644 index 000000000..a40c2e128 --- /dev/null +++ b/src/Report/Html/Renderer/Template/directory_branch.html.dist @@ -0,0 +1,62 @@ + + + + + Code Coverage for {{full_path}} + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + +{{items}} + +
     
    Code Coverage
     
    Lines
    Branches
    Paths
    Functions and Methods
    Classes and Traits
    +
    +
    +
    +

    Legend

    +

    + Low: 0% to {{low_upper_bound}}% + Medium: {{low_upper_bound}}% to {{high_lower_bound}}% + High: {{high_lower_bound}}% to 100% +

    +

    + Generated by php-code-coverage {{version}} using {{runtime}}{{generator}} at {{date}}. +

    +
    +
    + + diff --git a/src/Report/Html/Renderer/Template/directory_item.html.dist b/src/Report/Html/Renderer/Template/directory_item.html.dist new file mode 100644 index 000000000..f6941a437 --- /dev/null +++ b/src/Report/Html/Renderer/Template/directory_item.html.dist @@ -0,0 +1,13 @@ + + {{icon}}{{name}} + {{lines_bar}} +
    {{lines_executed_percent}}
    +
    {{lines_number}}
    + {{methods_bar}} +
    {{methods_tested_percent}}
    +
    {{methods_number}}
    + {{classes_bar}} +
    {{classes_tested_percent}}
    +
    {{classes_number}}
    + + diff --git a/src/Report/Html/Renderer/Template/directory_item_branch.html.dist b/src/Report/Html/Renderer/Template/directory_item_branch.html.dist new file mode 100644 index 000000000..532a436c2 --- /dev/null +++ b/src/Report/Html/Renderer/Template/directory_item_branch.html.dist @@ -0,0 +1,19 @@ + + {{icon}}{{name}} + {{lines_bar}} +
    {{lines_executed_percent}}
    +
    {{lines_number}}
    + {{branches_bar}} +
    {{branches_executed_percent}}
    +
    {{branches_number}}
    + {{paths_bar}} +
    {{paths_executed_percent}}
    +
    {{paths_number}}
    + {{methods_bar}} +
    {{methods_tested_percent}}
    +
    {{methods_number}}
    + {{classes_bar}} +
    {{classes_tested_percent}}
    +
    {{classes_number}}
    + + diff --git a/src/Report/Html/Renderer/Template/file.html.dist b/src/Report/Html/Renderer/Template/file.html.dist new file mode 100644 index 000000000..d29103481 --- /dev/null +++ b/src/Report/Html/Renderer/Template/file.html.dist @@ -0,0 +1,64 @@ + + + + + Code Coverage for {{full_path}} + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + +{{items}} + +
     
    Code Coverage
     
    Lines
    Functions and Methods
    Classes and Traits
    +
    +{{lines}} +{{structure}} + +
    + + + + + diff --git a/src/Report/Html/Renderer/Template/file_branch.html.dist b/src/Report/Html/Renderer/Template/file_branch.html.dist new file mode 100644 index 000000000..b8bcf3747 --- /dev/null +++ b/src/Report/Html/Renderer/Template/file_branch.html.dist @@ -0,0 +1,66 @@ + + + + + Code Coverage for {{full_path}} + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + +{{items}} + +
     
    Code Coverage
     
    Lines
    Branches
    Paths
    Functions and Methods
    Classes and Traits
    +
    +{{lines}} +{{structure}} + +
    + + + + + diff --git a/src/Report/Html/Renderer/Template/file_item.html.dist b/src/Report/Html/Renderer/Template/file_item.html.dist new file mode 100644 index 000000000..b1c0fca48 --- /dev/null +++ b/src/Report/Html/Renderer/Template/file_item.html.dist @@ -0,0 +1,14 @@ + + {{name}} + {{lines_bar}} +
    {{lines_executed_percent}}
    +
    {{lines_number}}
    + {{methods_bar}} +
    {{methods_tested_percent}}
    +
    {{methods_number}}
    + {{crap}} + {{classes_bar}} +
    {{classes_tested_percent}}
    +
    {{classes_number}}
    + + diff --git a/src/Report/Html/Renderer/Template/file_item_branch.html.dist b/src/Report/Html/Renderer/Template/file_item_branch.html.dist new file mode 100644 index 000000000..505025179 --- /dev/null +++ b/src/Report/Html/Renderer/Template/file_item_branch.html.dist @@ -0,0 +1,20 @@ + + {{name}} + {{lines_bar}} +
    {{lines_executed_percent}}
    +
    {{lines_number}}
    + {{branches_bar}} +
    {{branches_executed_percent}}
    +
    {{branches_number}}
    + {{paths_bar}} +
    {{paths_executed_percent}}
    +
    {{paths_number}}
    + {{methods_bar}} +
    {{methods_tested_percent}}
    +
    {{methods_number}}
    + {{crap}} + {{classes_bar}} +
    {{classes_tested_percent}}
    +
    {{classes_number}}
    + + diff --git a/src/Report/Html/Renderer/Template/icons/file-code.svg b/src/Report/Html/Renderer/Template/icons/file-code.svg new file mode 100644 index 000000000..5b4b19953 --- /dev/null +++ b/src/Report/Html/Renderer/Template/icons/file-code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Report/Html/Renderer/Template/icons/file-directory.svg b/src/Report/Html/Renderer/Template/icons/file-directory.svg new file mode 100644 index 000000000..4bf1f1caa --- /dev/null +++ b/src/Report/Html/Renderer/Template/icons/file-directory.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Report/Html/Renderer/Template/js/billboard.pkgd.min.js b/src/Report/Html/Renderer/Template/js/billboard.pkgd.min.js new file mode 100644 index 000000000..52128a864 --- /dev/null +++ b/src/Report/Html/Renderer/Template/js/billboard.pkgd.min.js @@ -0,0 +1,57 @@ +/*! + * Copyright (c) 2017 ~ present NAVER Corp. + * billboard.js project is licensed under the MIT license + * + * billboard.js, JavaScript chart library + * https://naver.github.io/billboard.js/ + * + * @version 3.15.1 + * + * All-in-one packaged file for ease use of 'billboard.js' with dependant d3.js modules & polyfills. + * - @types/d3-selection ^3.0.11 + * - @types/d3-transition ^3.0.9 + * - d3-axis ^3.0.0 + * - d3-brush ^3.0.0 + * - d3-drag ^3.0.0 + * - d3-dsv ^3.0.1 + * - d3-ease ^3.0.1 + * - d3-hierarchy ^3.1.2 + * - d3-interpolate ^3.0.1 + * - d3-scale ^4.0.2 + * - d3-selection ^3.0.0 + * - d3-shape ^3.2.0 + * - d3-time-format ^4.1.0 + * - d3-transition ^3.0.1 + * - d3-zoom ^3.0.0 + */(function(Xa,zn){if(typeof exports=="object"&&typeof module=="object")module.exports=zn();else if(typeof define=="function"&&define.amd)define([],zn);else{var Ha=zn();for(var x in Ha)(typeof exports=="object"?exports:Xa)[x]=Ha[x]}})(this,function(){return function(){"use strict";var Cs=[function(x,b,r){r(1),r(97),r(98),r(99),r(100),r(101),r(102),r(103),r(104),r(105),r(106),r(107),r(108),r(109),r(110),r(111),r(124),r(126),r(136),r(137),r(139),r(143),r(146),r(148),r(150),r(151),r(152),r(153),r(155),r(156),r(158),r(159),r(161),r(165),r(166),r(167),r(168),r(173),r(174),r(176),r(177),r(178),r(180),r(184),r(185),r(186),r(187),r(188),r(193),r(195),r(196),r(198),r(201),r(202),r(203),r(204),r(205),r(207),r(218),r(220),r(221),r(223),r(224),r(227),r(230),r(236),r(237),r(238),r(239),r(240),r(241),r(245),r(246),r(248),r(249),r(250),r(252),r(253),r(254),r(255),r(256),r(261),r(262),r(263),r(264),r(266),r(267),r(268),r(270),r(271),r(272),r(273),r(93),r(274),r(275),r(283),r(285),r(287),r(288),r(289),r(290),r(291),r(293),r(294),r(295),r(296),r(297),r(298),r(300),r(301),r(302),r(303),r(304),r(305),r(306),r(307),r(311),r(312),r(314),r(316),r(317),r(318),r(319),r(320),r(322),r(324),r(325),r(326),r(327),r(329),r(330),r(332),r(333),r(334),r(335),r(337),r(338),r(339),r(340),r(341),r(342),r(343),r(344),r(345),r(347),r(348),r(349),r(350),r(351),r(352),r(353),r(354),r(355),r(356),r(357),r(359),r(360),r(361),r(362),r(386),r(387),r(388),r(389),r(390),r(391),r(392),r(393),r(394),r(395),r(397),r(398),r(399),r(400),r(401),r(402),r(403),r(404),r(405),r(406),r(413),r(415),r(416),r(418),r(419),r(420),r(421),r(422),r(424),r(434),r(436),r(438),r(440),r(442),r(444),r(446),r(447),r(449),r(452),r(453),r(454),r(455),r(456),r(460),r(461),r(463),r(464),r(465),r(466),r(468),r(469),r(470),r(471),r(472),r(473),r(474),r(476),r(479),r(482),r(485),r(486),r(487),r(488),r(489),r(490),r(491),r(492),r(493),r(494),r(495),r(496),r(497),r(505),r(506),r(507),r(508),r(509),r(510),r(511),r(512),r(513),r(514),r(515),r(516),r(517),r(519),r(520),r(521),r(522),r(523),r(524),r(525),r(526),r(527),r(528),r(529),r(530),r(531),r(532),r(533),r(534),r(535),r(536),r(537),r(538),r(539),r(540),r(541),r(542),r(543),r(544),r(545),r(546),r(549),r(551),r(553),r(554),r(557),r(558),r(560),r(561),r(562),r(566),r(567),r(568),r(569),r(572),r(577),r(578),r(579),r(580),r(581),r(582),r(583),r(80)},function(x,b,r){r(2),r(90),r(92),r(93),r(96)},function(x,b,r){var u=r(3),d=r(4),h=r(8),p=r(14),y=r(36),T=r(6),$=r(26),A=r(7),E=r(38),R=r(24),I=r(46),O=r(12),C=r(18),D=r(68),M=r(11),F=r(71),z=r(73),U=r(57),j=r(75),G=r(66),B=r(5),V=r(44),Y=r(72),Z=r(10),J=r(47),q=r(77),nt=r(34),rt=r(53),_=r(54),tt=r(40),et=r(33),lt=r(78),mt=r(79),gt=r(81),xt=r(82),yt=r(51),Ut=r(83).forEach,Dt=rt("hidden"),Xt="Symbol",Qt="prototype",kt=yt.set,me=yt.getterFor(Xt),ge=Object[Qt],ae=d.Symbol,Mt=ae&&ae[Qt],Ht=d.RangeError,re=d.TypeError,se=d.QObject,ee=B.f,fe=V.f,Pe=j.f,Me=Z.f,$e=p([].push),ce=nt("symbols"),Ae=nt("op-symbols"),Te=nt("wks"),de=!se||!se[Qt]||!se[Qt].findChild,bt=function(It,Pt,Ct){var Nt=ee(ge,Pt);Nt&&delete ge[Pt],fe(It,Pt,Ct),Nt&&It!==ge&&fe(ge,Pt,Nt)},Ft=T&&A(function(){return F(fe({},"a",{get:function(){return fe(this,"a",{value:7}).a}})).a!==7})?bt:fe,Tt=function(It,Pt){var Ct=ce[It]=F(Mt);return kt(Ct,{type:Xt,tag:It,description:Pt}),T||(Ct.description=Pt),Ct},qt=function(Pt,Ct,Nt){Pt===ge&&qt(Ae,Ct,Nt),I(Pt);var Et=C(Ct);return I(Nt),E(ce,Et)?(Nt.enumerable?(E(Pt,Dt)&&Pt[Dt][Et]&&(Pt[Dt][Et]=!1),Nt=F(Nt,{enumerable:M(0,!1)})):(E(Pt,Dt)||fe(Pt,Dt,M(1,F(null))),Pt[Dt][Et]=!0),Ft(Pt,Et,Nt)):fe(Pt,Et,Nt)},te=function(Pt,Ct){I(Pt);var Nt=O(Ct),Et=z(Nt).concat(ut(Nt));return Ut(Et,function(ie){(!T||h(Yt,Nt,ie))&&qt(Pt,ie,Nt[ie])}),Pt},Zt=function(Pt,Ct){return Ct===void 0?F(Pt):te(F(Pt),Ct)},Yt=function(Pt){var Ct=C(Pt),Nt=h(Me,this,Ct);return this===ge&&E(ce,Ct)&&!E(Ae,Ct)?!1:Nt||!E(this,Ct)||!E(ce,Ct)||E(this,Dt)&&this[Dt][Ct]?Nt:!0},Ye=function(Pt,Ct){var Nt=O(Pt),Et=C(Ct);if(!(Nt===ge&&E(ce,Et)&&!E(Ae,Et))){var ie=ee(Nt,Et);return ie&&E(ce,Et)&&!(E(Nt,Dt)&&Nt[Dt][Et])&&(ie.enumerable=!0),ie}},Ze=function(Pt){var Ct=Pe(O(Pt)),Nt=[];return Ut(Ct,function(Et){!E(ce,Et)&&!E(_,Et)&&$e(Nt,Et)}),Nt},ut=function(It){var Pt=It===ge,Ct=Pe(Pt?Ae:O(It)),Nt=[];return Ut(Ct,function(Et){E(ce,Et)&&(!Pt||E(ge,Et))&&$e(Nt,ce[Et])}),Nt};$||(ae=function(){if(R(Mt,this))throw new re("Symbol is not a constructor");var Pt=!arguments.length||arguments[0]===void 0?void 0:D(arguments[0]),Ct=tt(Pt),Nt=function(Et){var ie=this===void 0?d:this;ie===ge&&h(Nt,Ae,Et),E(ie,Dt)&&E(ie[Dt],Ct)&&(ie[Dt][Ct]=!1);var we=M(1,Et);try{Ft(ie,Ct,we)}catch(Rt){if(!(Rt instanceof Ht))throw Rt;bt(ie,Ct,we)}};return T&&de&&Ft(ge,Ct,{configurable:!0,set:Nt}),Tt(Ct,Pt)},Mt=ae[Qt],J(Mt,"toString",function(){return me(this).tag}),J(ae,"withoutSetter",function(It){return Tt(tt(It),It)}),Z.f=Yt,V.f=qt,Y.f=te,B.f=Ye,U.f=j.f=Ze,G.f=ut,lt.f=function(It){return Tt(et(It),It)},T&&(q(Mt,"description",{configurable:!0,get:function(){return me(this).description}}),y||J(ge,"propertyIsEnumerable",Yt,{unsafe:!0}))),u({global:!0,constructor:!0,wrap:!0,forced:!$,sham:!$},{Symbol:ae}),Ut(z(Te),function(It){mt(It)}),u({target:Xt,stat:!0,forced:!$},{useSetter:function(){de=!0},useSimple:function(){de=!1}}),u({target:"Object",stat:!0,forced:!$,sham:!T},{create:Zt,defineProperty:qt,defineProperties:te,getOwnPropertyDescriptor:Ye}),u({target:"Object",stat:!0,forced:!$},{getOwnPropertyNames:Ze}),gt(),xt(ae,Xt),_[Dt]=!0},function(x,b,r){var u=r(4),d=r(5).f,h=r(43),p=r(47),y=r(37),T=r(55),$=r(67);x.exports=function(A,E){var R=A.target,I=A.global,O=A.stat,C,D,M,F,z,U;if(I?D=u:O?D=u[R]||y(R,{}):D=u[R]&&u[R].prototype,D)for(M in E){if(z=E[M],A.dontCallGetSet?(U=d(D,M),F=U&&U.value):F=D[M],C=$(I?M:R+(O?".":"#")+M,A.forced),!C&&F!==void 0){if(typeof z==typeof F)continue;T(z,F)}(A.sham||F&&F.sham)&&h(z,"sham",!0),p(D,M,z,A)}}},function(x){var b=function(r){return r&&r.Math===Math&&r};x.exports=b(typeof globalThis=="object"&&globalThis)||b(typeof window=="object"&&window)||b(typeof self=="object"&&self)||b(typeof global=="object"&&global)||b(typeof this=="object"&&this)||function(){return this}()||Function("return this")()},function(x,b,r){var u=r(6),d=r(8),h=r(10),p=r(11),y=r(12),T=r(18),$=r(38),A=r(41),E=Object.getOwnPropertyDescriptor;b.f=u?E:function(I,O){if(I=y(I),O=T(O),A)try{return E(I,O)}catch(C){}if($(I,O))return p(!d(h.f,I,O),I[O])}},function(x,b,r){var u=r(7);x.exports=!u(function(){return Object.defineProperty({},1,{get:function(){return 7}})[1]!==7})},function(x){x.exports=function(b){try{return!!b()}catch(r){return!0}}},function(x,b,r){var u=r(9),d=Function.prototype.call;x.exports=u?d.bind(d):function(){return d.apply(d,arguments)}},function(x,b,r){var u=r(7);x.exports=!u(function(){var d=function(){}.bind();return typeof d!="function"||d.hasOwnProperty("prototype")})},function(x,b){var r={}.propertyIsEnumerable,u=Object.getOwnPropertyDescriptor,d=u&&!r.call({1:2},1);b.f=d?function(p){var y=u(this,p);return!!y&&y.enumerable}:r},function(x){x.exports=function(b,r){return{enumerable:!(b&1),configurable:!(b&2),writable:!(b&4),value:r}}},function(x,b,r){var u=r(13),d=r(16);x.exports=function(h){return u(d(h))}},function(x,b,r){var u=r(14),d=r(7),h=r(15),p=Object,y=u("".split);x.exports=d(function(){return!p("z").propertyIsEnumerable(0)})?function(T){return h(T)==="String"?y(T,""):p(T)}:p},function(x,b,r){var u=r(9),d=Function.prototype,h=d.call,p=u&&d.bind.bind(h,h);x.exports=u?p:function(y){return function(){return h.apply(y,arguments)}}},function(x,b,r){var u=r(14),d=u({}.toString),h=u("".slice);x.exports=function(p){return h(d(p),8,-1)}},function(x,b,r){var u=r(17),d=TypeError;x.exports=function(h){if(u(h))throw new d("Can't call method on "+h);return h}},function(x){x.exports=function(b){return b==null}},function(x,b,r){var u=r(19),d=r(22);x.exports=function(h){var p=u(h,"string");return d(p)?p:p+""}},function(x,b,r){var u=r(8),d=r(20),h=r(22),p=r(29),y=r(32),T=r(33),$=TypeError,A=T("toPrimitive");x.exports=function(E,R){if(!d(E)||h(E))return E;var I=p(E,A),O;if(I){if(R===void 0&&(R="default"),O=u(I,E,R),!d(O)||h(O))return O;throw new $("Can't convert object to primitive value")}return R===void 0&&(R="number"),y(E,R)}},function(x,b,r){var u=r(21);x.exports=function(d){return typeof d=="object"?d!==null:u(d)}},function(x){var b=typeof document=="object"&&document.all;x.exports=typeof b=="undefined"&&b!==void 0?function(r){return typeof r=="function"||r===b}:function(r){return typeof r=="function"}},function(x,b,r){var u=r(23),d=r(21),h=r(24),p=r(25),y=Object;x.exports=p?function(T){return typeof T=="symbol"}:function(T){var $=u("Symbol");return d($)&&h($.prototype,y(T))}},function(x,b,r){var u=r(4),d=r(21),h=function(p){return d(p)?p:void 0};x.exports=function(p,y){return arguments.length<2?h(u[p]):u[p]&&u[p][y]}},function(x,b,r){var u=r(14);x.exports=u({}.isPrototypeOf)},function(x,b,r){var u=r(26);x.exports=u&&!Symbol.sham&&typeof Symbol.iterator=="symbol"},function(x,b,r){var u=r(27),d=r(7),h=r(4),p=h.String;x.exports=!!Object.getOwnPropertySymbols&&!d(function(){var y=Symbol("symbol detection");return!p(y)||!(Object(y)instanceof Symbol)||!Symbol.sham&&u&&u<41})},function(x,b,r){var u=r(4),d=r(28),h=u.process,p=u.Deno,y=h&&h.versions||p&&p.version,T=y&&y.v8,$,A;T&&($=T.split("."),A=$[0]>0&&$[0]<4?1:+($[0]+$[1])),!A&&d&&($=d.match(/Edge\/(\d+)/),(!$||$[1]>=74)&&($=d.match(/Chrome\/(\d+)/),$&&(A=+$[1]))),x.exports=A},function(x,b,r){var u=r(4),d=u.navigator,h=d&&d.userAgent;x.exports=h?String(h):""},function(x,b,r){var u=r(30),d=r(17);x.exports=function(h,p){var y=h[p];return d(y)?void 0:u(y)}},function(x,b,r){var u=r(21),d=r(31),h=TypeError;x.exports=function(p){if(u(p))return p;throw new h(d(p)+" is not a function")}},function(x){var b=String;x.exports=function(r){try{return b(r)}catch(u){return"Object"}}},function(x,b,r){var u=r(8),d=r(21),h=r(20),p=TypeError;x.exports=function(y,T){var $,A;if(T==="string"&&d($=y.toString)&&!h(A=u($,y))||d($=y.valueOf)&&!h(A=u($,y))||T!=="string"&&d($=y.toString)&&!h(A=u($,y)))return A;throw new p("Can't convert object to primitive value")}},function(x,b,r){var u=r(4),d=r(34),h=r(38),p=r(40),y=r(26),T=r(25),$=u.Symbol,A=d("wks"),E=T?$.for||$:$&&$.withoutSetter||p;x.exports=function(R){return h(A,R)||(A[R]=y&&h($,R)?$[R]:E("Symbol."+R)),A[R]}},function(x,b,r){var u=r(35);x.exports=function(d,h){return u[d]||(u[d]=h||{})}},function(x,b,r){var u=r(36),d=r(4),h=r(37),p="__core-js_shared__",y=x.exports=d[p]||h(p,{});(y.versions||(y.versions=[])).push({version:"3.41.0",mode:u?"pure":"global",copyright:"\xA9 2014-2025 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.41.0/LICENSE",source:"https://github.com/zloirock/core-js"})},function(x){x.exports=!1},function(x,b,r){var u=r(4),d=Object.defineProperty;x.exports=function(h,p){try{d(u,h,{value:p,configurable:!0,writable:!0})}catch(y){u[h]=p}return p}},function(x,b,r){var u=r(14),d=r(39),h=u({}.hasOwnProperty);x.exports=Object.hasOwn||function(y,T){return h(d(y),T)}},function(x,b,r){var u=r(16),d=Object;x.exports=function(h){return d(u(h))}},function(x,b,r){var u=r(14),d=0,h=Math.random(),p=u(1 .toString);x.exports=function(y){return"Symbol("+(y===void 0?"":y)+")_"+p(++d+h,36)}},function(x,b,r){var u=r(6),d=r(7),h=r(42);x.exports=!u&&!d(function(){return Object.defineProperty(h("div"),"a",{get:function(){return 7}}).a!==7})},function(x,b,r){var u=r(4),d=r(20),h=u.document,p=d(h)&&d(h.createElement);x.exports=function(y){return p?h.createElement(y):{}}},function(x,b,r){var u=r(6),d=r(44),h=r(11);x.exports=u?function(p,y,T){return d.f(p,y,h(1,T))}:function(p,y,T){return p[y]=T,p}},function(x,b,r){var u=r(6),d=r(41),h=r(45),p=r(46),y=r(18),T=TypeError,$=Object.defineProperty,A=Object.getOwnPropertyDescriptor,E="enumerable",R="configurable",I="writable";b.f=u?h?function(C,D,M){if(p(C),D=y(D),p(M),typeof C=="function"&&D==="prototype"&&"value"in M&&I in M&&!M[I]){var F=A(C,D);F&&F[I]&&(C[D]=M.value,M={configurable:R in M?M[R]:F[R],enumerable:E in M?M[E]:F[E],writable:!1})}return $(C,D,M)}:$:function(C,D,M){if(p(C),D=y(D),p(M),d)try{return $(C,D,M)}catch(F){}if("get"in M||"set"in M)throw new T("Accessors not supported");return"value"in M&&(C[D]=M.value),C}},function(x,b,r){var u=r(6),d=r(7);x.exports=u&&d(function(){return Object.defineProperty(function(){},"prototype",{value:42,writable:!1}).prototype!==42})},function(x,b,r){var u=r(20),d=String,h=TypeError;x.exports=function(p){if(u(p))return p;throw new h(d(p)+" is not an object")}},function(x,b,r){var u=r(21),d=r(44),h=r(48),p=r(37);x.exports=function(y,T,$,A){A||(A={});var E=A.enumerable,R=A.name!==void 0?A.name:T;if(u($)&&h($,R,A),A.global)E?y[T]=$:p(T,$);else{try{A.unsafe?y[T]&&(E=!0):delete y[T]}catch(I){}E?y[T]=$:d.f(y,T,{value:$,enumerable:!1,configurable:!A.nonConfigurable,writable:!A.nonWritable})}return y}},function(x,b,r){var u=r(14),d=r(7),h=r(21),p=r(38),y=r(6),T=r(49).CONFIGURABLE,$=r(50),A=r(51),E=A.enforce,R=A.get,I=String,O=Object.defineProperty,C=u("".slice),D=u("".replace),M=u([].join),F=y&&!d(function(){return O(function(){},"length",{value:8}).length!==8}),z=String(String).split("String"),U=x.exports=function(j,G,B){C(I(G),0,7)==="Symbol("&&(G="["+D(I(G),/^Symbol\(([^)]*)\).*$/,"$1")+"]"),B&&B.getter&&(G="get "+G),B&&B.setter&&(G="set "+G),(!p(j,"name")||T&&j.name!==G)&&(y?O(j,"name",{value:G,configurable:!0}):j.name=G),F&&B&&p(B,"arity")&&j.length!==B.arity&&O(j,"length",{value:B.arity});try{B&&p(B,"constructor")&&B.constructor?y&&O(j,"prototype",{writable:!1}):j.prototype&&(j.prototype=void 0)}catch(Y){}var V=E(j);return p(V,"source")||(V.source=M(z,typeof G=="string"?G:"")),j};Function.prototype.toString=U(function(){return h(this)&&R(this).source||$(this)},"toString")},function(x,b,r){var u=r(6),d=r(38),h=Function.prototype,p=u&&Object.getOwnPropertyDescriptor,y=d(h,"name"),T=y&&function(){}.name==="something",$=y&&(!u||u&&p(h,"name").configurable);x.exports={EXISTS:y,PROPER:T,CONFIGURABLE:$}},function(x,b,r){var u=r(14),d=r(21),h=r(35),p=u(Function.toString);d(h.inspectSource)||(h.inspectSource=function(y){return p(y)}),x.exports=h.inspectSource},function(x,b,r){var u=r(52),d=r(4),h=r(20),p=r(43),y=r(38),T=r(35),$=r(53),A=r(54),E="Object already initialized",R=d.TypeError,I=d.WeakMap,O,C,D,M=function(j){return D(j)?C(j):O(j,{})},F=function(j){return function(G){var B;if(!h(G)||(B=C(G)).type!==j)throw new R("Incompatible receiver, "+j+" required");return B}};if(u||T.state){var z=T.state||(T.state=new I);z.get=z.get,z.has=z.has,z.set=z.set,O=function(j,G){if(z.has(j))throw new R(E);return G.facade=j,z.set(j,G),G},C=function(j){return z.get(j)||{}},D=function(j){return z.has(j)}}else{var U=$("state");A[U]=!0,O=function(j,G){if(y(j,U))throw new R(E);return G.facade=j,p(j,U,G),G},C=function(j){return y(j,U)?j[U]:{}},D=function(j){return y(j,U)}}x.exports={set:O,get:C,has:D,enforce:M,getterFor:F}},function(x,b,r){var u=r(4),d=r(21),h=u.WeakMap;x.exports=d(h)&&/native code/.test(String(h))},function(x,b,r){var u=r(34),d=r(40),h=u("keys");x.exports=function(p){return h[p]||(h[p]=d(p))}},function(x){x.exports={}},function(x,b,r){var u=r(38),d=r(56),h=r(5),p=r(44);x.exports=function(y,T,$){for(var A=d(T),E=p.f,R=h.f,I=0;IR;)d(E,O=A[R++])&&(~p(I,O)||T(I,O));return I}},function(x,b,r){var u=r(12),d=r(60),h=r(63),p=function(y){return function(T,$,A){var E=u(T),R=h(E);if(R===0)return!y&&-1;var I=d(A,R),O;if(y&&$!==$){for(;R>I;)if(O=E[I++],O!==O)return!0}else for(;R>I;I++)if((y||I in E)&&E[I]===$)return y||I||0;return!y&&-1}};x.exports={includes:p(!0),indexOf:p(!1)}},function(x,b,r){var u=r(61),d=Math.max,h=Math.min;x.exports=function(p,y){var T=u(p);return T<0?d(T+y,0):h(T,y)}},function(x,b,r){var u=r(62);x.exports=function(d){var h=+d;return h!==h||h===0?0:u(h)}},function(x){var b=Math.ceil,r=Math.floor;x.exports=Math.trunc||function(d){var h=+d;return(h>0?r:b)(h)}},function(x,b,r){var u=r(64);x.exports=function(d){return u(d.length)}},function(x,b,r){var u=r(61),d=Math.min;x.exports=function(h){var p=u(h);return p>0?d(p,9007199254740991):0}},function(x){x.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},function(x,b){b.f=Object.getOwnPropertySymbols},function(x,b,r){var u=r(7),d=r(21),h=/#|\.prototype\./,p=function(E,R){var I=T[y(E)];return I===A?!0:I===$?!1:d(R)?u(R):!!R},y=p.normalize=function(E){return String(E).replace(h,".").toLowerCase()},T=p.data={},$=p.NATIVE="N",A=p.POLYFILL="P";x.exports=p},function(x,b,r){var u=r(69),d=String;x.exports=function(h){if(u(h)==="Symbol")throw new TypeError("Cannot convert a Symbol value to a string");return d(h)}},function(x,b,r){var u=r(70),d=r(21),h=r(15),p=r(33),y=p("toStringTag"),T=Object,$=h(function(){return arguments}())==="Arguments",A=function(E,R){try{return E[R]}catch(I){}};x.exports=u?h:function(E){var R,I,O;return E===void 0?"Undefined":E===null?"Null":typeof(I=A(R=T(E),y))=="string"?I:$?h(R):(O=h(R))==="Object"&&d(R.callee)?"Arguments":O}},function(x,b,r){var u=r(33),d=u("toStringTag"),h={};h[d]="z",x.exports=String(h)==="[object z]"},function(x,b,r){var u=r(46),d=r(72),h=r(65),p=r(54),y=r(74),T=r(42),$=r(53),A=">",E="<",R="prototype",I="script",O=$("IE_PROTO"),C=function(){},D=function(j){return E+I+A+j+E+"/"+I+A},M=function(j){j.write(D("")),j.close();var G=j.parentWindow.Object;return j=null,G},F=function(){var j=T("iframe"),G="java"+I+":",B;return j.style.display="none",y.appendChild(j),j.src=String(G),B=j.contentWindow.document,B.open(),B.write(D("document.F=Object")),B.close(),B.F},z,U=function(){try{z=new ActiveXObject("htmlfile")}catch(G){}U=typeof document!="undefined"?document.domain&&z?M(z):F():M(z);for(var j=h.length;j--;)delete U[R][h[j]];return U()};p[O]=!0,x.exports=Object.create||function(G,B){var V;return G!==null?(C[R]=u(G),V=new C,C[R]=null,V[O]=G):V=U(),B===void 0?V:d.f(V,B)}},function(x,b,r){var u=r(6),d=r(45),h=r(44),p=r(46),y=r(12),T=r(73);b.f=u&&!d?Object.defineProperties:function(A,E){p(A);for(var R=y(E),I=T(E),O=I.length,C=0,D;O>C;)h.f(A,D=I[C++],R[D]);return A}},function(x,b,r){var u=r(58),d=r(65);x.exports=Object.keys||function(p){return u(p,d)}},function(x,b,r){var u=r(23);x.exports=u("document","documentElement")},function(x,b,r){var u=r(15),d=r(12),h=r(57).f,p=r(76),y=typeof window=="object"&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],T=function($){try{return h($)}catch(A){return p(y)}};x.exports.f=function(A){return y&&u(A)==="Window"?T(A):h(d(A))}},function(x,b,r){var u=r(14);x.exports=u([].slice)},function(x,b,r){var u=r(48),d=r(44);x.exports=function(h,p,y){return y.get&&u(y.get,p,{getter:!0}),y.set&&u(y.set,p,{setter:!0}),d.f(h,p,y)}},function(x,b,r){var u=r(33);b.f=u},function(x,b,r){var u=r(80),d=r(38),h=r(78),p=r(44).f;x.exports=function(y){var T=u.Symbol||(u.Symbol={});d(T,y)||p(T,y,{value:h.f(y)})}},function(x,b,r){var u=r(4);x.exports=u},function(x,b,r){var u=r(8),d=r(23),h=r(33),p=r(47);x.exports=function(){var y=d("Symbol"),T=y&&y.prototype,$=T&&T.valueOf,A=h("toPrimitive");T&&!T[A]&&p(T,A,function(E){return u($,this)},{arity:1})}},function(x,b,r){var u=r(44).f,d=r(38),h=r(33),p=h("toStringTag");x.exports=function(y,T,$){y&&!$&&(y=y.prototype),y&&!d(y,p)&&u(y,p,{configurable:!0,value:T})}},function(x,b,r){var u=r(84),d=r(14),h=r(13),p=r(39),y=r(63),T=r(86),$=d([].push),A=function(E){var R=E===1,I=E===2,O=E===3,C=E===4,D=E===6,M=E===7,F=E===5||D;return function(z,U,j,G){for(var B=p(z),V=h(B),Y=y(V),Z=u(U,j),J=0,q=G||T,nt=R?q(z,Y):I||M?q(z,0):void 0,rt,_;Y>J;J++)if((F||J in V)&&(rt=V[J],_=Z(rt,J,B),E))if(R)nt[J]=_;else if(_)switch(E){case 3:return!0;case 5:return rt;case 6:return J;case 2:$(nt,rt)}else switch(E){case 4:return!1;case 7:$(nt,rt)}return D?-1:O||C?C:nt}};x.exports={forEach:A(0),map:A(1),filter:A(2),some:A(3),every:A(4),find:A(5),findIndex:A(6),filterReject:A(7)}},function(x,b,r){var u=r(85),d=r(30),h=r(9),p=u(u.bind);x.exports=function(y,T){return d(y),T===void 0?y:h?p(y,T):function(){return y.apply(T,arguments)}}},function(x,b,r){var u=r(15),d=r(14);x.exports=function(h){if(u(h)==="Function")return d(h)}},function(x,b,r){var u=r(87);x.exports=function(d,h){return new(u(d))(h===0?0:h)}},function(x,b,r){var u=r(88),d=r(89),h=r(20),p=r(33),y=p("species"),T=Array;x.exports=function($){var A;return u($)&&(A=$.constructor,d(A)&&(A===T||u(A.prototype))?A=void 0:h(A)&&(A=A[y],A===null&&(A=void 0))),A===void 0?T:A}},function(x,b,r){var u=r(15);x.exports=Array.isArray||function(h){return u(h)==="Array"}},function(x,b,r){var u=r(14),d=r(7),h=r(21),p=r(69),y=r(23),T=r(50),$=function(){},A=y("Reflect","construct"),E=/^\s*(?:class|function)\b/,R=u(E.exec),I=!E.test($),O=function(M){if(!h(M))return!1;try{return A($,[],M),!0}catch(F){return!1}},C=function(M){if(!h(M))return!1;switch(p(M)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return I||!!R(E,T(M))}catch(F){return!0}};C.sham=!0,x.exports=!A||d(function(){var D;return O(O.call)||!O(Object)||!O(function(){D=!0})||D})?C:O},function(x,b,r){var u=r(3),d=r(23),h=r(38),p=r(68),y=r(34),T=r(91),$=y("string-to-symbol-registry"),A=y("symbol-to-string-registry");u({target:"Symbol",stat:!0,forced:!T},{for:function(E){var R=p(E);if(h($,R))return $[R];var I=d("Symbol")(R);return $[R]=I,A[I]=R,I}})},function(x,b,r){var u=r(26);x.exports=u&&!!Symbol.for&&!!Symbol.keyFor},function(x,b,r){var u=r(3),d=r(38),h=r(22),p=r(31),y=r(34),T=r(91),$=y("symbol-to-string-registry");u({target:"Symbol",stat:!0,forced:!T},{keyFor:function(E){if(!h(E))throw new TypeError(p(E)+" is not a symbol");if(d($,E))return $[E]}})},function(x,b,r){var u=r(3),d=r(23),h=r(94),p=r(8),y=r(14),T=r(7),$=r(21),A=r(22),E=r(76),R=r(95),I=r(26),O=String,C=d("JSON","stringify"),D=y(/./.exec),M=y("".charAt),F=y("".charCodeAt),z=y("".replace),U=y(1 .toString),j=/[\uD800-\uDFFF]/g,G=/^[\uD800-\uDBFF]$/,B=/^[\uDC00-\uDFFF]$/,V=!I||T(function(){var q=d("Symbol")("stringify detection");return C([q])!=="[null]"||C({a:q})!=="{}"||C(Object(q))!=="{}"}),Y=T(function(){return C("\uDF06\uD834")!=='"\\udf06\\ud834"'||C("\uDEAD")!=='"\\udead"'}),Z=function(q,nt){var rt=E(arguments),_=R(nt);if(!(!$(_)&&(q===void 0||A(q))))return rt[1]=function(tt,et){if($(_)&&(et=p(_,this,O(tt),et)),!A(et))return et},h(C,null,rt)},J=function(q,nt,rt){var _=M(rt,nt-1),tt=M(rt,nt+1);return D(G,q)&&!D(B,tt)||D(B,q)&&!D(G,_)?"\\u"+U(F(q,0),16):q};C&&u({target:"JSON",stat:!0,arity:3,forced:V||Y},{stringify:function(nt,rt,_){var tt=E(arguments),et=h(V?Z:C,null,tt);return Y&&typeof et=="string"?z(et,j,J):et}})},function(x,b,r){var u=r(9),d=Function.prototype,h=d.apply,p=d.call;x.exports=typeof Reflect=="object"&&Reflect.apply||(u?p.bind(h):function(){return p.apply(h,arguments)})},function(x,b,r){var u=r(14),d=r(88),h=r(21),p=r(15),y=r(68),T=u([].push);x.exports=function($){if(h($))return $;if(d($)){for(var A=$.length,E=[],R=0;Rj&&R(_,arguments[j]),_});if(J.prototype=Y,B!=="Error"?y?y(J,Z):T(J,Z,{name:!0}):O&&U in V&&($(J,V,U),$(J,V,"prepareStackTrace")),T(J,V),!C)try{Y.name!==B&&h(Y,"name",B),Y.constructor=J}catch(q){}return J}}},function(x,b,r){var u=r(114),d=r(20),h=r(16),p=r(115);x.exports=Object.setPrototypeOf||("__proto__"in{}?function(){var y=!1,T={},$;try{$=u(Object.prototype,"__proto__","set"),$(T,[]),y=T instanceof Array}catch(A){}return function(E,R){return h(E),p(R),d(E)&&(y?$(E,R):E.__proto__=R),E}}():void 0)},function(x,b,r){var u=r(14),d=r(30);x.exports=function(h,p,y){try{return u(d(Object.getOwnPropertyDescriptor(h,p)[y]))}catch(T){}}},function(x,b,r){var u=r(116),d=String,h=TypeError;x.exports=function(p){if(u(p))return p;throw new h("Can't set "+d(p)+" as a prototype")}},function(x,b,r){var u=r(20);x.exports=function(d){return u(d)||d===null}},function(x,b,r){var u=r(44).f;x.exports=function(d,h,p){p in d||u(d,p,{configurable:!0,get:function(){return h[p]},set:function(y){h[p]=y}})}},function(x,b,r){var u=r(21),d=r(20),h=r(113);x.exports=function(p,y,T){var $,A;return h&&u($=y.constructor)&&$!==T&&d(A=$.prototype)&&A!==T.prototype&&h(p,A),p}},function(x,b,r){var u=r(68);x.exports=function(d,h){return d===void 0?arguments.length<2?"":h:u(d)}},function(x,b,r){var u=r(20),d=r(43);x.exports=function(h,p){u(p)&&"cause"in p&&d(h,"cause",p.cause)}},function(x,b,r){var u=r(43),d=r(122),h=r(123),p=Error.captureStackTrace;x.exports=function(y,T,$,A){h&&(p?p(y,T):u(y,"stack",d($,A)))}},function(x,b,r){var u=r(14),d=Error,h=u("".replace),p=function($){return String(new d($).stack)}("zxcasd"),y=/\n\s*at [^:]*:[^\n]*/,T=y.test(p);x.exports=function($,A){if(T&&typeof $=="string"&&!d.prepareStackTrace)for(;A--;)$=h($,y,"");return $}},function(x,b,r){var u=r(7),d=r(11);x.exports=!u(function(){var h=new Error("a");return"stack"in h?(Object.defineProperty(h,"stack",d(1,7)),h.stack!==7):!0})},function(x,b,r){var u=r(47),d=r(125),h=Error.prototype;h.toString!==d&&u(h,"toString",d)},function(x,b,r){var u=r(6),d=r(7),h=r(46),p=r(119),y=Error.prototype.toString,T=d(function(){if(u){var $=Object.create(Object.defineProperty({},"name",{get:function(){return this===$}}));if(y.call($)!=="true")return!0}return y.call({message:1,name:2})!=="2: 1"||y.call({})!=="Error"});x.exports=T?function(){var A=h(this),E=p(A.name,"Error"),R=p(A.message);return E?R?E+": "+R:E:R}:y},function(x,b,r){r(127)},function(x,b,r){var u=r(3),d=r(24),h=r(128),p=r(113),y=r(55),T=r(71),$=r(43),A=r(11),E=r(120),R=r(121),I=r(130),O=r(119),C=r(33),D=C("toStringTag"),M=Error,F=[].push,z=function(G,B){var V=d(U,this),Y;p?Y=p(new M,V?h(this):U):(Y=V?this:T(U),$(Y,D,"Error")),B!==void 0&&$(Y,"message",O(B)),R(Y,z,Y.stack,1),arguments.length>2&&E(Y,arguments[2]);var Z=[];return I(G,F,{that:Z}),$(Y,"errors",Z),Y};p?p(z,M):y(z,M,{name:!0});var U=z.prototype=T(M.prototype,{constructor:A(1,z),message:A(1,""),name:A(1,"AggregateError")});u({global:!0,constructor:!0,arity:2},{AggregateError:z})},function(x,b,r){var u=r(38),d=r(21),h=r(39),p=r(53),y=r(129),T=p("IE_PROTO"),$=Object,A=$.prototype;x.exports=y?$.getPrototypeOf:function(E){var R=h(E);if(u(R,T))return R[T];var I=R.constructor;return d(I)&&R instanceof I?I.prototype:R instanceof $?A:null}},function(x,b,r){var u=r(7);x.exports=!u(function(){function d(){}return d.prototype.constructor=null,Object.getPrototypeOf(new d)!==d.prototype})},function(x,b,r){var u=r(84),d=r(8),h=r(46),p=r(31),y=r(131),T=r(63),$=r(24),A=r(133),E=r(134),R=r(135),I=TypeError,O=function(D,M){this.stopped=D,this.result=M},C=O.prototype;x.exports=function(D,M,F){var z=F&&F.that,U=!!(F&&F.AS_ENTRIES),j=!!(F&&F.IS_RECORD),G=!!(F&&F.IS_ITERATOR),B=!!(F&&F.INTERRUPTED),V=u(M,z),Y,Z,J,q,nt,rt,_,tt=function(lt){return Y&&R(Y,"normal",lt),new O(!0,lt)},et=function(lt){return U?(h(lt),B?V(lt[0],lt[1],tt):V(lt[0],lt[1])):B?V(lt,tt):V(lt)};if(j)Y=D.iterator;else if(G)Y=D;else{if(Z=E(D),!Z)throw new I(p(D)+" is not iterable");if(y(Z)){for(J=0,q=T(D);q>J;J++)if(nt=et(D[J]),nt&&$(C,nt))return nt;return new O(!1)}Y=A(D,Z)}for(rt=j?D.next:Y.next;!(_=d(rt,Y)).done;){try{nt=et(_.value)}catch(lt){R(Y,"throw",lt)}if(typeof nt=="object"&&nt&&$(C,nt))return nt}return new O(!1)}},function(x,b,r){var u=r(33),d=r(132),h=u("iterator"),p=Array.prototype;x.exports=function(y){return y!==void 0&&(d.Array===y||p[h]===y)}},function(x){x.exports={}},function(x,b,r){var u=r(8),d=r(30),h=r(46),p=r(31),y=r(134),T=TypeError;x.exports=function($,A){var E=arguments.length<2?y($):A;if(d(E))return h(u(E,$));throw new T(p($)+" is not iterable")}},function(x,b,r){var u=r(69),d=r(29),h=r(17),p=r(132),y=r(33),T=y("iterator");x.exports=function($){if(!h($))return d($,T)||d($,"@@iterator")||p[u($)]}},function(x,b,r){var u=r(8),d=r(46),h=r(29);x.exports=function(p,y,T){var $,A;d(p);try{if($=h(p,"return"),!$){if(y==="throw")throw T;return T}$=u($,p)}catch(E){A=!0,$=E}if(y==="throw")throw T;if(A)throw $;return d($),T}},function(x,b,r){var u=r(3),d=r(23),h=r(94),p=r(7),y=r(112),T="AggregateError",$=d(T),A=!p(function(){return $([1]).errors[0]!==1})&&p(function(){return $([1],T,{cause:7}).cause!==7});u({global:!0,constructor:!0,arity:2,forced:A},{AggregateError:y(T,function(E){return function(I,O){return h(E,this,arguments)}},A,!0)})},function(x,b,r){var u=r(3),d=r(39),h=r(63),p=r(61),y=r(138);u({target:"Array",proto:!0},{at:function($){var A=d(this),E=h(A),R=p($),I=R>=0?R:E+R;return I<0||I>=E?void 0:A[I]}}),y("at")},function(x,b,r){var u=r(33),d=r(71),h=r(44).f,p=u("unscopables"),y=Array.prototype;y[p]===void 0&&h(y,p,{configurable:!0,value:d(null)}),x.exports=function(T){y[p][T]=!0}},function(x,b,r){var u=r(3),d=r(7),h=r(88),p=r(20),y=r(39),T=r(63),$=r(140),A=r(141),E=r(86),R=r(142),I=r(33),O=r(27),C=I("isConcatSpreadable"),D=O>=51||!d(function(){var z=[];return z[C]=!1,z.concat()[0]!==z}),M=function(z){if(!p(z))return!1;var U=z[C];return U!==void 0?!!U:h(z)},F=!D||!R("concat");u({target:"Array",proto:!0,arity:1,forced:F},{concat:function(U){var j=y(this),G=E(j,0),B=0,V,Y,Z,J,q;for(V=-1,Z=arguments.length;Vr)throw b("Maximum allowed index exceeded");return u}},function(x,b,r){var u=r(6),d=r(44),h=r(11);x.exports=function(p,y,T){u?d.f(p,y,h(0,T)):p[y]=T}},function(x,b,r){var u=r(7),d=r(33),h=r(27),p=d("species");x.exports=function(y){return h>=51||!u(function(){var T=[],$=T.constructor={};return $[p]=function(){return{foo:1}},T[y](Boolean).foo!==1})}},function(x,b,r){var u=r(3),d=r(144),h=r(138);u({target:"Array",proto:!0},{copyWithin:d}),h("copyWithin")},function(x,b,r){var u=r(39),d=r(60),h=r(63),p=r(145),y=Math.min;x.exports=[].copyWithin||function($,A){var E=u(this),R=h(E),I=d($,R),O=d(A,R),C=arguments.length>2?arguments[2]:void 0,D=y((C===void 0?R:d(C,R))-O,R-I),M=1;for(O0;)O in E?E[I]=E[O]:p(E,I),I+=M,O+=M;return E}},function(x,b,r){var u=r(31),d=TypeError;x.exports=function(h,p){if(!delete h[p])throw new d("Cannot delete property "+u(p)+" of "+u(h))}},function(x,b,r){var u=r(3),d=r(83).every,h=r(147),p=h("every");u({target:"Array",proto:!0,forced:!p},{every:function(T){return d(this,T,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(7);x.exports=function(d,h){var p=[][d];return!!p&&u(function(){p.call(null,h||function(){return 1},1)})}},function(x,b,r){var u=r(3),d=r(149),h=r(138);u({target:"Array",proto:!0},{fill:d}),h("fill")},function(x,b,r){var u=r(39),d=r(60),h=r(63);x.exports=function(y){for(var T=u(this),$=h(T),A=arguments.length,E=d(A>1?arguments[1]:void 0,$),R=A>2?arguments[2]:void 0,I=R===void 0?$:d(R,$);I>E;)T[E++]=y;return T}},function(x,b,r){var u=r(3),d=r(83).filter,h=r(142),p=h("filter");u({target:"Array",proto:!0,forced:!p},{filter:function(T){return d(this,T,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(83).find,h=r(138),p="find",y=!0;p in[]&&Array(1)[p](function(){y=!1}),u({target:"Array",proto:!0,forced:y},{find:function($){return d(this,$,arguments.length>1?arguments[1]:void 0)}}),h(p)},function(x,b,r){var u=r(3),d=r(83).findIndex,h=r(138),p="findIndex",y=!0;p in[]&&Array(1)[p](function(){y=!1}),u({target:"Array",proto:!0,forced:y},{findIndex:function($){return d(this,$,arguments.length>1?arguments[1]:void 0)}}),h(p)},function(x,b,r){var u=r(3),d=r(154).findLast,h=r(138);u({target:"Array",proto:!0},{findLast:function(y){return d(this,y,arguments.length>1?arguments[1]:void 0)}}),h("findLast")},function(x,b,r){var u=r(84),d=r(13),h=r(39),p=r(63),y=function(T){var $=T===1;return function(A,E,R){for(var I=h(A),O=d(I),C=p(O),D=u(E,R),M,F;C-- >0;)if(M=O[C],F=D(M,C,I),F)switch(T){case 0:return M;case 1:return C}return $?-1:void 0}};x.exports={findLast:y(0),findLastIndex:y(1)}},function(x,b,r){var u=r(3),d=r(154).findLastIndex,h=r(138);u({target:"Array",proto:!0},{findLastIndex:function(y){return d(this,y,arguments.length>1?arguments[1]:void 0)}}),h("findLastIndex")},function(x,b,r){var u=r(3),d=r(157),h=r(39),p=r(63),y=r(61),T=r(86);u({target:"Array",proto:!0},{flat:function(){var A=arguments.length?arguments[0]:void 0,E=h(this),R=p(E),I=T(E,0);return I.length=d(I,E,E,R,0,A===void 0?1:y(A)),I}})},function(x,b,r){var u=r(88),d=r(63),h=r(140),p=r(84),y=function(T,$,A,E,R,I,O,C){for(var D=R,M=0,F=O?p(O,C):!1,z,U;M0&&u(z)?(U=d(z),D=y(T,$,z,U,D,I-1)-1):(h(D+1),T[D]=z),D++),M++;return D};x.exports=y},function(x,b,r){var u=r(3),d=r(157),h=r(30),p=r(39),y=r(63),T=r(86);u({target:"Array",proto:!0},{flatMap:function(A){var E=p(this),R=y(E),I;return h(A),I=T(E,0),I.length=d(I,E,E,R,0,1,A,arguments.length>1?arguments[1]:void 0),I}})},function(x,b,r){var u=r(3),d=r(160);u({target:"Array",proto:!0,forced:[].forEach!==d},{forEach:d})},function(x,b,r){var u=r(83).forEach,d=r(147),h=d("forEach");x.exports=h?[].forEach:function(y){return u(this,y,arguments.length>1?arguments[1]:void 0)}},function(x,b,r){var u=r(3),d=r(162),h=r(164),p=!h(function(y){Array.from(y)});u({target:"Array",stat:!0,forced:p},{from:d})},function(x,b,r){var u=r(84),d=r(8),h=r(39),p=r(163),y=r(131),T=r(89),$=r(63),A=r(141),E=r(133),R=r(134),I=Array;x.exports=function(C){var D=h(C),M=T(this),F=arguments.length,z=F>1?arguments[1]:void 0,U=z!==void 0;U&&(z=u(z,F>2?arguments[2]:void 0));var j=R(D),G=0,B,V,Y,Z,J,q;if(j&&!(this===I&&y(j)))for(V=M?new this:[],Z=E(D,j),J=Z.next;!(Y=d(J,Z)).done;G++)q=U?p(Z,z,[Y.value,G],!0):Y.value,A(V,G,q);else for(B=$(D),V=M?new this(B):I(B);B>G;G++)q=U?z(D[G],G):D[G],A(V,G,q);return V.length=G,V}},function(x,b,r){var u=r(46),d=r(135);x.exports=function(h,p,y,T){try{return T?p(u(y)[0],y[1]):p(y)}catch($){d(h,"throw",$)}}},function(x,b,r){var u=r(33),d=u("iterator"),h=!1;try{var p=0,y={next:function(){return{done:!!p++}},return:function(){h=!0}};y[d]=function(){return this},Array.from(y,function(){throw 2})}catch(T){}x.exports=function(T,$){try{if(!$&&!h)return!1}catch(R){return!1}var A=!1;try{var E={};E[d]=function(){return{next:function(){return{done:A=!0}}}},T(E)}catch(R){}return A}},function(x,b,r){var u=r(3),d=r(59).includes,h=r(7),p=r(138),y=h(function(){return!Array(1).includes()});u({target:"Array",proto:!0,forced:y},{includes:function($){return d(this,$,arguments.length>1?arguments[1]:void 0)}}),p("includes")},function(x,b,r){var u=r(3),d=r(85),h=r(59).indexOf,p=r(147),y=d([].indexOf),T=!!y&&1/y([1],1,-0)<0,$=T||!p("indexOf");u({target:"Array",proto:!0,forced:$},{indexOf:function(E){var R=arguments.length>1?arguments[1]:void 0;return T?y(this,E,R)||0:h(this,E,R)}})},function(x,b,r){var u=r(3),d=r(88);u({target:"Array",stat:!0},{isArray:d})},function(x,b,r){var u=r(12),d=r(138),h=r(132),p=r(51),y=r(44).f,T=r(169),$=r(172),A=r(36),E=r(6),R="Array Iterator",I=p.set,O=p.getterFor(R);x.exports=T(Array,"Array",function(D,M){I(this,{type:R,target:u(D),index:0,kind:M})},function(){var D=O(this),M=D.target,F=D.index++;if(!M||F>=M.length)return D.target=null,$(void 0,!0);switch(D.kind){case"keys":return $(F,!1);case"values":return $(M[F],!1)}return $([F,M[F]],!1)},"values");var C=h.Arguments=h.Array;if(d("keys"),d("values"),d("entries"),!A&&E&&C.name!=="values")try{y(C,"name",{value:"values"})}catch(D){}},function(x,b,r){var u=r(3),d=r(8),h=r(36),p=r(49),y=r(21),T=r(170),$=r(128),A=r(113),E=r(82),R=r(43),I=r(47),O=r(33),C=r(132),D=r(171),M=p.PROPER,F=p.CONFIGURABLE,z=D.IteratorPrototype,U=D.BUGGY_SAFARI_ITERATORS,j=O("iterator"),G="keys",B="values",V="entries",Y=function(){return this};x.exports=function(Z,J,q,nt,rt,_,tt){T(q,J,nt);var et=function(kt){if(kt===rt&&yt)return yt;if(!U&&kt&&kt in gt)return gt[kt];switch(kt){case G:return function(){return new q(this,kt)};case B:return function(){return new q(this,kt)};case V:return function(){return new q(this,kt)}}return function(){return new q(this)}},lt=J+" Iterator",mt=!1,gt=Z.prototype,xt=gt[j]||gt["@@iterator"]||rt&>[rt],yt=!U&&xt||et(rt),Ut=J==="Array"&>.entries||xt,Dt,Xt,Qt;if(Ut&&(Dt=$(Ut.call(new Z)),Dt!==Object.prototype&&Dt.next&&(!h&&$(Dt)!==z&&(A?A(Dt,z):y(Dt[j])||I(Dt,j,Y)),E(Dt,lt,!0,!0),h&&(C[lt]=Y))),M&&rt===B&&xt&&xt.name!==B&&(!h&&F?R(gt,"name",B):(mt=!0,yt=function(){return d(xt,this)})),rt)if(Xt={values:et(B),keys:_?yt:et(G),entries:et(V)},tt)for(Qt in Xt)(U||mt||!(Qt in gt))&&I(gt,Qt,Xt[Qt]);else u({target:J,proto:!0,forced:U||mt},Xt);return(!h||tt)&>[j]!==yt&&I(gt,j,yt,{name:rt}),C[J]=yt,Xt}},function(x,b,r){var u=r(171).IteratorPrototype,d=r(71),h=r(11),p=r(82),y=r(132),T=function(){return this};x.exports=function($,A,E,R){var I=A+" Iterator";return $.prototype=d(u,{next:h(+!R,E)}),p($,I,!1,!0),y[I]=T,$}},function(x,b,r){var u=r(7),d=r(21),h=r(20),p=r(71),y=r(128),T=r(47),$=r(33),A=r(36),E=$("iterator"),R=!1,I,O,C;[].keys&&(C=[].keys(),"next"in C?(O=y(y(C)),O!==Object.prototype&&(I=O)):R=!0);var D=!h(I)||u(function(){var M={};return I[E].call(M)!==M});D?I={}:A&&(I=p(I)),d(I[E])||T(I,E,function(){return this}),x.exports={IteratorPrototype:I,BUGGY_SAFARI_ITERATORS:R}},function(x){x.exports=function(b,r){return{value:b,done:r}}},function(x,b,r){var u=r(3),d=r(14),h=r(13),p=r(12),y=r(147),T=d([].join),$=h!==Object,A=$||!y("join",",");u({target:"Array",proto:!0,forced:A},{join:function(R){return T(p(this),R===void 0?",":R)}})},function(x,b,r){var u=r(3),d=r(175);u({target:"Array",proto:!0,forced:d!==[].lastIndexOf},{lastIndexOf:d})},function(x,b,r){var u=r(94),d=r(12),h=r(61),p=r(63),y=r(147),T=Math.min,$=[].lastIndexOf,A=!!$&&1/[1].lastIndexOf(1,-0)<0,E=y("lastIndexOf"),R=A||!E;x.exports=R?function(O){if(A)return u($,this,arguments)||0;var C=d(this),D=p(C);if(D===0)return-1;var M=D-1;for(arguments.length>1&&(M=T(M,h(arguments[1]))),M<0&&(M=D+M);M>=0;M--)if(M in C&&C[M]===O)return M||0;return-1}:$},function(x,b,r){var u=r(3),d=r(83).map,h=r(142),p=h("map");u({target:"Array",proto:!0,forced:!p},{map:function(T){return d(this,T,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(7),h=r(89),p=r(141),y=Array,T=d(function(){function $(){}return!(y.of.call($)instanceof $)});u({target:"Array",stat:!0,forced:T},{of:function(){for(var A=0,E=arguments.length,R=new(h(this)?this:y)(E);E>A;)p(R,A,arguments[A++]);return R.length=E,R}})},function(x,b,r){var u=r(3),d=r(39),h=r(63),p=r(179),y=r(140),T=r(7),$=T(function(){return[].push.call({length:4294967296},1)!==4294967297}),A=function(){try{Object.defineProperty([],"length",{writable:!1}).push()}catch(R){return R instanceof TypeError}},E=$||!A();u({target:"Array",proto:!0,arity:1,forced:E},{push:function(I){var O=d(this),C=h(O),D=arguments.length;y(C+D);for(var M=0;M79&&p<83,$=T||!h("reduce");u({target:"Array",proto:!0,forced:$},{reduce:function(E){var R=arguments.length;return d(this,E,R,R>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(30),d=r(39),h=r(13),p=r(63),y=TypeError,T="Reduce of empty array with no initial value",$=function(A){return function(E,R,I,O){var C=d(E),D=h(C),M=p(C);if(u(R),M===0&&I<2)throw new y(T);var F=A?M-1:0,z=A?-1:1;if(I<2)for(;;){if(F in D){O=D[F],F+=z;break}if(F+=z,A?F<0:M<=F)throw new y(T)}for(;A?F>=0:M>F;F+=z)F in D&&(O=R(O,D[F],F,C));return O}};x.exports={left:$(!1),right:$(!0)}},function(x,b,r){var u=r(183);x.exports=u==="NODE"},function(x,b,r){var u=r(4),d=r(28),h=r(15),p=function(y){return d.slice(0,y.length)===y};x.exports=function(){return p("Bun/")?"BUN":p("Cloudflare-Workers")?"CLOUDFLARE":p("Deno/")?"DENO":p("Node.js/")?"NODE":u.Bun&&typeof Bun.version=="string"?"BUN":u.Deno&&typeof Deno.version=="object"?"DENO":h(u.process)==="process"?"NODE":u.window&&u.document?"BROWSER":"REST"}()},function(x,b,r){var u=r(3),d=r(181).right,h=r(147),p=r(27),y=r(182),T=!y&&p>79&&p<83,$=T||!h("reduceRight");u({target:"Array",proto:!0,forced:$},{reduceRight:function(E){return d(this,E,arguments.length,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(14),h=r(88),p=d([].reverse),y=[1,2];u({target:"Array",proto:!0,forced:String(y)===String(y.reverse())},{reverse:function(){return h(this)&&(this.length=this.length),p(this)}})},function(x,b,r){var u=r(3),d=r(88),h=r(89),p=r(20),y=r(60),T=r(63),$=r(12),A=r(141),E=r(33),R=r(142),I=r(76),O=R("slice"),C=E("species"),D=Array,M=Math.max;u({target:"Array",proto:!0,forced:!O},{slice:function(z,U){var j=$(this),G=T(j),B=y(z,G),V=y(U===void 0?G:U,G),Y,Z,J;if(d(j)&&(Y=j.constructor,h(Y)&&(Y===D||d(Y.prototype))?Y=void 0:p(Y)&&(Y=Y[C],Y===null&&(Y=void 0)),Y===D||Y===void 0))return I(j,B,V);for(Z=new(Y===void 0?D:Y)(M(V-B,0)),J=0;B1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(14),h=r(30),p=r(39),y=r(63),T=r(145),$=r(68),A=r(7),E=r(189),R=r(147),I=r(190),O=r(191),C=r(27),D=r(192),M=[],F=d(M.sort),z=d(M.push),U=A(function(){M.sort(void 0)}),j=A(function(){M.sort(null)}),G=R("sort"),B=!A(function(){if(C)return C<70;if(!(I&&I>3)){if(O)return!0;if(D)return D<603;var Z="",J,q,nt,rt;for(J=65;J<76;J++){switch(q=String.fromCharCode(J),J){case 66:case 69:case 70:case 72:nt=3;break;case 68:case 71:nt=4;break;default:nt=2}for(rt=0;rt<47;rt++)M.push({k:q+rt,v:nt})}for(M.sort(function(_,tt){return tt.v-_.v}),rt=0;rt$(q)?1:-1}};u({target:"Array",proto:!0,forced:V},{sort:function(J){J!==void 0&&h(J);var q=p(this);if(B)return J===void 0?F(q):F(q,J);var nt=[],rt=y(q),_,tt;for(tt=0;tt0;)p[E]=p[--E];E!==$++&&(p[E]=A)}else for(var R=d(T/2),I=h(u(p,0,R),y),O=h(u(p,R),y),C=I.length,D=O.length,M=0,F=0;Mj-Y+V;J--)R(U,J-1)}else if(V>Y)for(J=j-Y;J>G;J--)q=J+Y-1,nt=J+V-1,q in U?U[nt]=U[q]:R(U,nt);for(J=0;J2?p:u(h),$=new d(T);T>y;)$[y]=h[y++];return $}},function(x,b,r){var u=r(4);x.exports=function(d,h){var p=u[d],y=p&&p.prototype;return y&&y[h]}},function(x,b,r){var u=r(3),d=r(138),h=r(140),p=r(63),y=r(60),T=r(12),$=r(61),A=Array,E=Math.max,R=Math.min;u({target:"Array",proto:!0},{toSpliced:function(O,C){var D=T(this),M=p(D),F=y(O,M),z=arguments.length,U=0,j,G,B,V;for(z===0?j=G=0:z===1?(j=0,G=M-F):(j=z-2,G=R(E($(C),0),M-F)),B=h(M+j-G),V=A(B);U=A||R<0)throw new h("Incorrect index");for(var I=new y(A),O=0;O>8&255]},se=function(bt){return[bt&255,bt>>8&255,bt>>16&255,bt>>24&255]},ee=function(bt){return bt[3]<<24|bt[2]<<16|bt[1]<<8|bt[0]},fe=function(bt){return ae(D(bt),23,4)},Pe=function(bt){return ae(bt,52,8)},Me=function(bt,Ft,Tt){$(bt[rt],Ft,{configurable:!0,get:function(){return Tt(this)[Ft]}})},$e=function(bt,Ft,Tt,qt){var te=lt(bt),Zt=C(Tt),Yt=!!qt;if(Zt+Ft>te.byteLength)throw new kt(tt);var Ye=te.bytes,Ze=Zt+te.byteOffset,ut=j(Ye,Ze,Ze+Ft);return Yt?ut:ge(ut)},ce=function(bt,Ft,Tt,qt,te,Zt){var Yt=lt(bt),Ye=C(Tt),Ze=qt(+te),ut=!!Zt;if(Ye+Ft>Yt.byteLength)throw new kt(tt);for(var It=Yt.bytes,Pt=Ye+Yt.byteOffset,Ct=0;CtZt)throw new kt("Wrong offset");if(qt=qt===void 0?Zt-Yt:O(qt),Yt+qt>Zt)throw new kt(_);mt(this,{type:nt,buffer:Ft,byteLength:qt,byteOffset:Yt,bytes:te.bytes}),h||(this.buffer=Ft,this.byteLength=qt,this.byteOffset=Yt)},Dt=Ut[rt],h&&(Me(xt,"byteLength",et),Me(Ut,"buffer",lt),Me(Ut,"byteLength",lt),Me(Ut,"byteOffset",lt)),A(Dt,{getInt8:function(Ft){return $e(this,1,Ft)[0]<<24>>24},getUint8:function(Ft){return $e(this,1,Ft)[0]},getInt16:function(Ft){var Tt=$e(this,2,Ft,arguments.length>1?arguments[1]:!1);return(Tt[1]<<8|Tt[0])<<16>>16},getUint16:function(Ft){var Tt=$e(this,2,Ft,arguments.length>1?arguments[1]:!1);return Tt[1]<<8|Tt[0]},getInt32:function(Ft){return ee($e(this,4,Ft,arguments.length>1?arguments[1]:!1))},getUint32:function(Ft){return ee($e(this,4,Ft,arguments.length>1?arguments[1]:!1))>>>0},getFloat32:function(Ft){return Mt($e(this,4,Ft,arguments.length>1?arguments[1]:!1),23)},getFloat64:function(Ft){return Mt($e(this,8,Ft,arguments.length>1?arguments[1]:!1),52)},setInt8:function(Ft,Tt){ce(this,1,Ft,Ht,Tt)},setUint8:function(Ft,Tt){ce(this,1,Ft,Ht,Tt)},setInt16:function(Ft,Tt){ce(this,2,Ft,re,Tt,arguments.length>2?arguments[2]:!1)},setUint16:function(Ft,Tt){ce(this,2,Ft,re,Tt,arguments.length>2?arguments[2]:!1)},setInt32:function(Ft,Tt){ce(this,4,Ft,se,Tt,arguments.length>2?arguments[2]:!1)},setUint32:function(Ft,Tt){ce(this,4,Ft,se,Tt,arguments.length>2?arguments[2]:!1)},setFloat32:function(Ft,Tt){ce(this,4,Ft,fe,Tt,arguments.length>2?arguments[2]:!1)},setFloat64:function(Ft,Tt){ce(this,8,Ft,Pe,Tt,arguments.length>2?arguments[2]:!1)}});else{var Ae=Z&>.name!==q;!E(function(){gt(1)})||!E(function(){new gt(-1)})||E(function(){return new gt,new gt(1.5),new gt(NaN),gt.length!==1||Ae&&!J})?(xt=function(Ft){return R(this,yt),G(new gt(C(Ft)),this,xt)},xt[rt]=yt,yt.constructor=xt,B(xt,gt)):Ae&&J&&T(gt,"name",q),z&&F(Dt)!==Xt&&z(Dt,Xt);var Te=new Ut(new xt(2)),de=d(Dt.setInt8);Te.setInt8(0,2147483648),Te.setInt8(1,2147483649),(Te.getInt8(0)||!Te.getInt8(1))&&A(Dt,{setInt8:function(Ft,Tt){de(this,Ft,Tt<<24>>24)},setUint8:function(Ft,Tt){de(this,Ft,Tt<<24>>24)}},{unsafe:!0})}V(xt,q),V(Ut,nt),x.exports={ArrayBuffer:xt,DataView:Ut}},function(x){x.exports=typeof ArrayBuffer!="undefined"&&typeof DataView!="undefined"},function(x,b,r){var u=r(47);x.exports=function(d,h,p){for(var y in h)u(d,y,h[y],p);return d}},function(x,b,r){var u=r(24),d=TypeError;x.exports=function(h,p){if(u(p,h))return h;throw new d("Incorrect invocation")}},function(x,b,r){var u=r(61),d=r(64),h=RangeError;x.exports=function(p){if(p===void 0)return 0;var y=u(p),T=d(y);if(y!==T)throw new h("Wrong length or index");return T}},function(x,b,r){var u=r(214),d=11920928955078125e-23,h=34028234663852886e22,p=11754943508222875e-54;x.exports=Math.fround||function(T){return u(T,d,h,p)}},function(x,b,r){var u=r(215),d=r(216),h=Math.abs,p=2220446049250313e-31;x.exports=function(y,T,$,A){var E=+y,R=h(E),I=u(E);if(R$||C!==C?I*(1/0):I*C}},function(x){x.exports=Math.sign||function(r){var u=+r;return u===0||u!==u?u:u<0?-1:1}},function(x){var b=2220446049250313e-31,r=1/b;x.exports=function(u){return u+r-r}},function(x){var b=Array,r=Math.abs,u=Math.pow,d=Math.floor,h=Math.log,p=Math.LN2,y=function($,A,E){var R=b(E),I=E*8-A-1,O=(1<>1,D=A===23?u(2,-24)-u(2,-77):0,M=$<0||$===0&&1/$<0?1:0,F=0,z,U,j;for($=r($),$!==$||$===1/0?(U=$!==$?1:0,z=O):(z=d(h($)/p),j=u(2,-z),$*j<1&&(z--,j*=2),z+C>=1?$+=D/j:$+=D*u(2,1-C),$*j>=2&&(z++,j/=2),z+C>=O?(U=0,z=O):z+C>=1?(U=($*j-1)*u(2,A),z+=C):(U=$*u(2,C-1)*u(2,A),z=0));A>=8;)R[F++]=U&255,U/=256,A-=8;for(z=z<0;)R[F++]=z&255,z/=256,I-=8;return R[F-1]|=M*128,R},T=function($,A){var E=$.length,R=E*8-A-1,I=(1<>1,C=R-7,D=E-1,M=$[D--],F=M&127,z;for(M>>=7;C>0;)F=F*256+$[D--],C-=8;for(z=F&(1<<-C)-1,F>>=-C,C+=A;C>0;)z=z*256+$[D--],C-=8;if(F===0)F=1-O;else{if(F===I)return z?NaN:M?-1/0:1/0;z+=u(2,A),F-=O}return(M?-1:1)*z*u(2,F-A)};x.exports={pack:y,unpack:T}},function(x,b,r){var u=r(3),d=r(219),h=d.NATIVE_ARRAY_BUFFER_VIEWS;u({target:"ArrayBuffer",stat:!0,forced:!h},{isView:d.isView})},function(x,b,r){var u=r(209),d=r(6),h=r(4),p=r(21),y=r(20),T=r(38),$=r(69),A=r(31),E=r(43),R=r(47),I=r(77),O=r(24),C=r(128),D=r(113),M=r(33),F=r(40),z=r(51),U=z.enforce,j=z.get,G=h.Int8Array,B=G&&G.prototype,V=h.Uint8ClampedArray,Y=V&&V.prototype,Z=G&&C(G),J=B&&C(B),q=Object.prototype,nt=h.TypeError,rt=M("toStringTag"),_=F("TYPED_ARRAY_TAG"),tt="TypedArrayConstructor",et=u&&!!D&&$(h.opera)!=="Opera",lt=!1,mt,gt,xt,yt={Int8Array:1,Uint8Array:1,Uint8ClampedArray:1,Int16Array:2,Uint16Array:2,Int32Array:4,Uint32Array:4,Float32Array:4,Float64Array:8},Ut={BigInt64Array:8,BigUint64Array:8},Dt=function(Ht){if(!y(Ht))return!1;var re=$(Ht);return re==="DataView"||T(yt,re)||T(Ut,re)},Xt=function(Mt){var Ht=C(Mt);if(y(Ht)){var re=j(Ht);return re&&T(re,tt)?re[tt]:Xt(Ht)}},Qt=function(Mt){if(!y(Mt))return!1;var Ht=$(Mt);return T(yt,Ht)||T(Ut,Ht)},kt=function(Mt){if(Qt(Mt))return Mt;throw new nt("Target is not a typed array")},me=function(Mt){if(p(Mt)&&(!D||O(Z,Mt)))return Mt;throw new nt(A(Mt)+" is not a typed array constructor")},ge=function(Mt,Ht,re,se){if(d){if(re)for(var ee in yt){var fe=h[ee];if(fe&&T(fe.prototype,Mt))try{delete fe.prototype[Mt]}catch(Pe){try{fe.prototype[Mt]=Ht}catch(Me){}}}(!J[Mt]||re)&&R(J,Mt,re?Ht:et&&B[Mt]||Ht,se)}},ae=function(Mt,Ht,re){var se,ee;if(d){if(D){if(re){for(se in yt)if(ee=h[se],ee&&T(ee,Mt))try{delete ee[Mt]}catch(fe){}}if(!Z[Mt]||re)try{return R(Z,Mt,re?Ht:et&&Z[Mt]||Ht)}catch(fe){}else return}for(se in yt)ee=h[se],ee&&(!ee[Mt]||re)&&R(ee,Mt,Ht)}};for(mt in yt)gt=h[mt],xt=gt&>.prototype,xt?U(xt)[tt]=gt:et=!1;for(mt in Ut)gt=h[mt],xt=gt&>.prototype,xt&&(U(xt)[tt]=gt);if((!et||!p(Z)||Z===Function.prototype)&&(Z=function(){throw new nt("Incorrect invocation")},et))for(mt in yt)h[mt]&&D(h[mt],Z);if((!et||!J||J===q)&&(J=Z.prototype,et))for(mt in yt)h[mt]&&D(h[mt].prototype,J);if(et&&C(Y)!==J&&D(Y,J),d&&!T(J,rt)){lt=!0,I(J,rt,{configurable:!0,get:function(){return y(this)?this[_]:void 0}});for(mt in yt)h[mt]&&E(h[mt],_,mt)}x.exports={NATIVE_ARRAY_BUFFER_VIEWS:et,TYPED_ARRAY_TAG:lt&&_,aTypedArray:kt,aTypedArrayConstructor:me,exportTypedArrayMethod:ge,exportTypedArrayStaticMethod:ae,getTypedArrayConstructor:Xt,isView:Dt,isTypedArray:Qt,TypedArray:Z,TypedArrayPrototype:J}},function(x,b,r){var u=r(3),d=r(85),h=r(7),p=r(208),y=r(46),T=r(60),$=r(64),A=p.ArrayBuffer,E=p.DataView,R=E.prototype,I=d(A.prototype.slice),O=d(R.getUint8),C=d(R.setUint8),D=h(function(){return!new A(2).slice(1,void 0).byteLength});u({target:"ArrayBuffer",proto:!0,unsafe:!0,forced:D},{slice:function(F,z){if(I&&z===void 0)return I(y(this),F);for(var U=y(this).byteLength,j=T(F,U),G=T(z===void 0?U:z,U),B=new A($(G-j)),V=new E(this),Y=new E(B),Z=0;j>>15,O=R>>>10&p,C=R&y;return O===p?C===0?I===0?1/0:-1/0:NaN:O===0?C*(I===0?T:-T):h(2,O-15)*(I===0?1+C*$:-1-C*$)},E=d(DataView.prototype.getUint16);u({target:"DataView",proto:!0},{getFloat16:function(I){var O=E(this,I,arguments.length>1?arguments[1]:!1);return A(O)}})},function(x,b,r){var u=r(3),d=r(14),h=r(225),p=r(212),y=r(226),T=r(216),$=Math.pow,A=65520,E=61005353927612305e-21,R=16777216,I=1024,O=function(D){if(D!==D)return 32256;if(D===0)return(1/D===-1/0)<<15;var M=D<0;if(M&&(D=-D),D>=A)return M<<15|31744;if(D2?arguments[2]:!1)}})},function(x,b,r){var u=r(69),d=TypeError;x.exports=function(h){if(u(h)==="DataView")return h;throw new d("Argument is not a DataView")}},function(x){var b=Math.log,r=Math.LN2;x.exports=Math.log2||function(d){return b(d)/r}},function(x,b,r){var u=r(6),d=r(77),h=r(228),p=ArrayBuffer.prototype;u&&!("detached"in p)&&d(p,"detached",{configurable:!0,get:function(){return h(this)}})},function(x,b,r){var u=r(4),d=r(209),h=r(229),p=u.DataView;x.exports=function(y){if(!d||h(y)!==0)return!1;try{return new p(y),!1}catch(T){return!0}}},function(x,b,r){var u=r(4),d=r(114),h=r(15),p=u.ArrayBuffer,y=u.TypeError;x.exports=p&&d(p.prototype,"byteLength","get")||function(T){if(h(T)!=="ArrayBuffer")throw new y("ArrayBuffer expected");return T.byteLength}},function(x,b,r){var u=r(3),d=r(231);d&&u({target:"ArrayBuffer",proto:!0},{transfer:function(){return d(this,arguments.length?arguments[0]:void 0,!0)}})},function(x,b,r){var u=r(4),d=r(14),h=r(114),p=r(212),y=r(232),T=r(229),$=r(233),A=r(235),E=u.structuredClone,R=u.ArrayBuffer,I=u.DataView,O=Math.min,C=R.prototype,D=I.prototype,M=d(C.slice),F=h(C,"resizable","get"),z=h(C,"maxByteLength","get"),U=d(D.getInt8),j=d(D.setInt8);x.exports=(A||$)&&function(G,B,V){var Y=T(G),Z=B===void 0?Y:p(B),J=!F||!F(G),q;if(y(G),A&&(G=E(G,{transfer:[G]}),Y===Z&&(V||J)))return G;if(Y>=Z&&(!V||J))q=M(G,0,Z);else{var nt=V&&!J&&z?{maxByteLength:z(G)}:void 0;q=new R(Z,nt);for(var rt=new I(G),_=new I(q),tt=O(Z,Y),et=0;et92||p==="NODE"&&h>94||p==="BROWSER"&&h>97)return!1;var T=new ArrayBuffer(8),$=y(T,{transfer:[T]});return T.byteLength!==0||$.byteLength!==8})},function(x,b,r){var u=r(3),d=r(231);d&&u({target:"ArrayBuffer",proto:!0},{transferToFixedLength:function(){return d(this,arguments.length?arguments[0]:void 0,!1)}})},function(x,b,r){var u=r(3),d=r(14),h=r(7),p=h(function(){return new Date(16e11).getYear()!==120}),y=d(Date.prototype.getFullYear);u({target:"Date",proto:!0,forced:p},{getYear:function(){return y(this)-1900}})},function(x,b,r){var u=r(3),d=r(14),h=Date,p=d(h.prototype.getTime);u({target:"Date",stat:!0},{now:function(){return p(new h)}})},function(x,b,r){var u=r(3),d=r(14),h=r(61),p=Date.prototype,y=d(p.getTime),T=d(p.setFullYear);u({target:"Date",proto:!0},{setYear:function(A){y(this);var E=h(A),R=E>=0&&E<=99?E+1900:E;return T(this,R)}})},function(x,b,r){var u=r(3);u({target:"Date",proto:!0},{toGMTString:Date.prototype.toUTCString})},function(x,b,r){var u=r(3),d=r(242);u({target:"Date",proto:!0,forced:Date.prototype.toISOString!==d},{toISOString:d})},function(x,b,r){var u=r(14),d=r(7),h=r(243).start,p=RangeError,y=isFinite,T=Math.abs,$=Date.prototype,A=$.toISOString,E=u($.getTime),R=u($.getUTCDate),I=u($.getUTCFullYear),O=u($.getUTCHours),C=u($.getUTCMilliseconds),D=u($.getUTCMinutes),M=u($.getUTCMonth),F=u($.getUTCSeconds);x.exports=d(function(){return A.call(new Date(-50000000000001))!=="0385-07-25T07:06:39.999Z"})||!d(function(){A.call(new Date(NaN))})?function(){if(!y(E(this)))throw new p("Invalid time value");var U=this,j=I(U),G=C(U),B=j<0?"-":j>9999?"+":"";return B+h(T(j),B?6:4,0)+"-"+h(M(U)+1,2,0)+"-"+h(R(U),2,0)+"T"+h(O(U),2,0)+":"+h(D(U),2,0)+":"+h(F(U),2,0)+"."+h(G,3,0)+"Z"}:A},function(x,b,r){var u=r(14),d=r(64),h=r(68),p=r(244),y=r(16),T=u(p),$=u("".slice),A=Math.ceil,E=function(R){return function(I,O,C){var D=h(y(I)),M=d(O),F=D.length,z=C===void 0?" ":h(C),U,j;return M<=F||z===""?D:(U=M-F,j=T(z,A(U/z.length)),j.length>U&&(j=$(j,0,U)),R?D+j:j+D)}};x.exports={start:E(!1),end:E(!0)}},function(x,b,r){var u=r(61),d=r(68),h=r(16),p=RangeError;x.exports=function(T){var $=d(h(this)),A="",E=u(T);if(E<0||E===1/0)throw new p("Wrong number of repetitions");for(;E>0;(E>>>=1)&&($+=$))E&1&&(A+=$);return A}},function(x,b,r){var u=r(3),d=r(7),h=r(39),p=r(19),y=d(function(){return new Date(NaN).toJSON()!==null||Date.prototype.toJSON.call({toISOString:function(){return 1}})!==1});u({target:"Date",proto:!0,arity:1,forced:y},{toJSON:function($){var A=h(this),E=p(A,"number");return typeof E=="number"&&!isFinite(E)?null:A.toISOString()}})},function(x,b,r){var u=r(38),d=r(47),h=r(247),p=r(33),y=p("toPrimitive"),T=Date.prototype;u(T,y)||d(T,y,h)},function(x,b,r){var u=r(46),d=r(32),h=TypeError;x.exports=function(p){if(u(this),p==="string"||p==="default")p="string";else if(p!=="number")throw new h("Incorrect hint");return d(this,p)}},function(x,b,r){var u=r(14),d=r(47),h=Date.prototype,p="Invalid Date",y="toString",T=u(h[y]),$=u(h.getTime);String(new Date(NaN))!==p&&d(h,y,function(){var E=$(this);return E===E?T(this):p})},function(x,b,r){var u=r(3),d=r(14),h=r(68),p=d("".charAt),y=d("".charCodeAt),T=d(/./.exec),$=d(1 .toString),A=d("".toUpperCase),E=/[\w*+\-./@]/,R=function(I,O){for(var C=$(I,16);C.length1?arguments[1]:void 0),_;_=_?_.next:nt.first;)for(rt(_.value,_.key,this);_&&_.removed;)_=_.previous},has:function(q){return!!Z(this,q)}}),h(B,U?{get:function(q){var nt=Z(this,q);return nt&&nt.value},set:function(q,nt){return Y(this,q===0?0:q,nt)}}:{add:function(q){return Y(this,q=q===0?0:q,q)}}),I&&d(B,"size",{configurable:!0,get:function(){return V(this).size}}),G},setStrong:function(F,z,U){var j=z+" Iterator",G=M(z),B=M(j);A(F,z,function(V,Y){D(this,{type:j,target:V,state:G(V),kind:Y,last:null})},function(){for(var V=B(this),Y=V.kind,Z=V.last;Z&&Z.removed;)Z=Z.previous;return!V.target||!(V.last=Z=Z?Z.next:V.state.first)?(V.target=null,E(void 0,!0)):E(Y==="keys"?Z.key:Y==="values"?Z.value:[Z.key,Z.value],!1)},U?"entries":"values",!U,!0),R(z)}}},function(x,b,r){var u=r(3),d=r(14),h=r(30),p=r(16),y=r(130),T=r(284),$=r(36),A=r(7),E=T.Map,R=T.has,I=T.get,O=T.set,C=d([].push),D=$||A(function(){return E.groupBy("ab",function(M){return M}).get("a").length!==1});u({target:"Map",stat:!0,forced:$||D},{groupBy:function(F,z){p(F),h(z);var U=new E,j=0;return y(F,function(G){var B=z(G,j++);R(U,B)?C(I(U,B),G):O(U,B,[G])}),U}})},function(x,b,r){var u=r(14),d=Map.prototype;x.exports={Map,set:u(d.set),get:u(d.get),has:u(d.has),remove:u(d.delete),proto:d}},function(x,b,r){var u=r(3),d=r(286),h=Math.acosh,p=Math.log,y=Math.sqrt,T=Math.LN2,$=!h||Math.floor(h(Number.MAX_VALUE))!==710||h(1/0)!==1/0;u({target:"Math",stat:!0,forced:$},{acosh:function(E){var R=+E;return R<1?NaN:R>9490626562425156e-8?p(R)+T:d(R-1+y(R-1)*y(R+1))}})},function(x){var b=Math.log;x.exports=Math.log1p||function(u){var d=+u;return d>-1e-8&&d<1e-8?d-d*d/2:b(1+d)}},function(x,b,r){var u=r(3),d=Math.asinh,h=Math.log,p=Math.sqrt;function y($){var A=+$;return!isFinite(A)||A===0?A:A<0?-y(-A):h(A+p(A*A+1))}var T=!(d&&1/d(0)>0);u({target:"Math",stat:!0,forced:T},{asinh:y})},function(x,b,r){var u=r(3),d=Math.atanh,h=Math.log,p=!(d&&1/d(-0)<0);u({target:"Math",stat:!0,forced:p},{atanh:function(T){var $=+T;return $===0?$:h((1+$)/(1-$))/2}})},function(x,b,r){var u=r(3),d=r(215),h=Math.abs,p=Math.pow;u({target:"Math",stat:!0},{cbrt:function(T){var $=+T;return d($)*p(h($),.3333333333333333)}})},function(x,b,r){var u=r(3),d=Math.floor,h=Math.log,p=Math.LOG2E;u({target:"Math",stat:!0},{clz32:function(T){var $=T>>>0;return $?31-d(h($+.5)*p):32}})},function(x,b,r){var u=r(3),d=r(292),h=Math.cosh,p=Math.abs,y=Math.E,T=!h||h(710)===1/0;u({target:"Math",stat:!0,forced:T},{cosh:function(A){var E=d(p(A)-1)+1;return(E+1/(E*y*y))*(y/2)}})},function(x){var b=Math.expm1,r=Math.exp;x.exports=!b||b(10)>22025.465794806718||b(10)<22025.465794806718||b(-2e-17)!==-2e-17?function(d){var h=+d;return h===0?h:h>-1e-6&&h<1e-6?h+h*h/2:r(h)-1}:b},function(x,b,r){var u=r(3),d=r(292);u({target:"Math",stat:!0,forced:d!==Math.expm1},{expm1:d})},function(x,b,r){var u=r(3),d=r(213);u({target:"Math",stat:!0},{fround:d})},function(x,b,r){var u=r(3),d=r(214),h=.0009765625,p=65504,y=6103515625e-14;u({target:"Math",stat:!0},{f16round:function($){return d($,h,p,y)}})},function(x,b,r){var u=r(3),d=Math.hypot,h=Math.abs,p=Math.sqrt,y=!!d&&d(1/0,NaN)!==1/0;u({target:"Math",stat:!0,arity:2,forced:y},{hypot:function($,A){for(var E=0,R=0,I=arguments.length,O=0,C,D;R0?(D=C/O,E+=D*D):E+=C;return O===1/0?1/0:O*p(E)}})},function(x,b,r){var u=r(3),d=r(7),h=Math.imul,p=d(function(){return h(4294967295,5)!==-5||h.length!==2});u({target:"Math",stat:!0,forced:p},{imul:function(T,$){var A=65535,E=+T,R=+$,I=A&E,O=A&R;return 0|I*O+((A&E>>>16)*O+I*(A&R>>>16)<<16>>>0)}})},function(x,b,r){var u=r(3),d=r(299);u({target:"Math",stat:!0},{log10:d})},function(x){var b=Math.log,r=Math.LOG10E;x.exports=Math.log10||function(d){return b(d)*r}},function(x,b,r){var u=r(3),d=r(286);u({target:"Math",stat:!0},{log1p:d})},function(x,b,r){var u=r(3),d=r(226);u({target:"Math",stat:!0},{log2:d})},function(x,b,r){var u=r(3),d=r(215);u({target:"Math",stat:!0},{sign:d})},function(x,b,r){var u=r(3),d=r(7),h=r(292),p=Math.abs,y=Math.exp,T=Math.E,$=d(function(){return Math.sinh(-2e-17)!==-2e-17});u({target:"Math",stat:!0,forced:$},{sinh:function(E){var R=+E;return p(R)<1?(h(R)-h(-R))/2:(y(R-1)-y(-R-1))*(T/2)}})},function(x,b,r){var u=r(3),d=r(292),h=Math.exp;u({target:"Math",stat:!0},{tanh:function(y){var T=+y,$=d(T),A=d(-T);return $===1/0?1:A===1/0?-1:($-A)/(h(T)+h(-T))}})},function(x,b,r){var u=r(82);u(Math,"Math",!0)},function(x,b,r){var u=r(3),d=r(62);u({target:"Math",stat:!0},{trunc:d})},function(x,b,r){var u=r(3),d=r(36),h=r(6),p=r(4),y=r(80),T=r(14),$=r(67),A=r(38),E=r(118),R=r(24),I=r(22),O=r(19),C=r(7),D=r(57).f,M=r(5).f,F=r(44).f,z=r(308),U=r(309).trim,j="Number",G=p[j],B=y[j],V=G.prototype,Y=p.TypeError,Z=T("".slice),J=T("".charCodeAt),q=function(lt){var mt=O(lt,"number");return typeof mt=="bigint"?mt:nt(mt)},nt=function(lt){var mt=O(lt,"number"),gt,xt,yt,Ut,Dt,Xt,Qt,kt;if(I(mt))throw new Y("Cannot convert a Symbol value to a number");if(typeof mt=="string"&&mt.length>2){if(mt=U(mt),gt=J(mt,0),gt===43||gt===45){if(xt=J(mt,2),xt===88||xt===120)return NaN}else if(gt===48){switch(J(mt,1)){case 66:case 98:yt=2,Ut=49;break;case 79:case 111:yt=8,Ut=55;break;default:return+mt}for(Dt=Z(mt,2),Xt=Dt.length,Qt=0;QtUt)return NaN;return parseInt(Dt,yt)}}return+mt},rt=$(j,!G(" 0o1")||!G("0b1")||G("+0x1")),_=function(lt){return R(V,lt)&&C(function(){z(lt)})},tt=function(mt){var gt=arguments.length<1?0:G(q(mt));return _(this)?E(Object(gt),this,tt):gt};tt.prototype=V,rt&&!d&&(V.constructor=tt),u({global:!0,constructor:!0,wrap:!0,forced:rt},{Number:tt});var et=function(lt,mt){for(var gt=h?D(mt):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,isFinite,isInteger,isNaN,isSafeInteger,parseFloat,parseInt,fromString,range".split(","),xt=0,yt;gt.length>xt;xt++)A(mt,yt=gt[xt])&&!A(lt,yt)&&F(lt,yt,M(mt,yt))};d&&B&&et(y[j],B),(rt||d)&&et(y[j],G)},function(x,b,r){var u=r(14);x.exports=u(1 .valueOf)},function(x,b,r){var u=r(14),d=r(16),h=r(68),p=r(310),y=u("".replace),T=RegExp("^["+p+"]+"),$=RegExp("(^|[^"+p+"])["+p+"]+$"),A=function(E){return function(R){var I=h(d(R));return E&1&&(I=y(I,T,"")),E&2&&(I=y(I,$,"$1")),I}};x.exports={start:A(1),end:A(2),trim:A(3)}},function(x){x.exports=` +\v\f\r \xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF`},function(x,b,r){var u=r(3);u({target:"Number",stat:!0,nonConfigurable:!0,nonWritable:!0},{EPSILON:Math.pow(2,-52)})},function(x,b,r){var u=r(3),d=r(313);u({target:"Number",stat:!0},{isFinite:d})},function(x,b,r){var u=r(4),d=u.isFinite;x.exports=Number.isFinite||function(p){return typeof p=="number"&&d(p)}},function(x,b,r){var u=r(3),d=r(315);u({target:"Number",stat:!0},{isInteger:d})},function(x,b,r){var u=r(20),d=Math.floor;x.exports=Number.isInteger||function(p){return!u(p)&&isFinite(p)&&d(p)===p}},function(x,b,r){var u=r(3);u({target:"Number",stat:!0},{isNaN:function(h){return h!==h}})},function(x,b,r){var u=r(3),d=r(315),h=Math.abs;u({target:"Number",stat:!0},{isSafeInteger:function(y){return d(y)&&h(y)<=9007199254740991}})},function(x,b,r){var u=r(3);u({target:"Number",stat:!0,nonConfigurable:!0,nonWritable:!0},{MAX_SAFE_INTEGER:9007199254740991})},function(x,b,r){var u=r(3);u({target:"Number",stat:!0,nonConfigurable:!0,nonWritable:!0},{MIN_SAFE_INTEGER:-9007199254740991})},function(x,b,r){var u=r(3),d=r(321);u({target:"Number",stat:!0,forced:Number.parseFloat!==d},{parseFloat:d})},function(x,b,r){var u=r(4),d=r(7),h=r(14),p=r(68),y=r(309).trim,T=r(310),$=h("".charAt),A=u.parseFloat,E=u.Symbol,R=E&&E.iterator,I=1/A(T+"-0")!==-1/0||R&&!d(function(){A(Object(R))});x.exports=I?function(C){var D=y(p(C)),M=A(D);return M===0&&$(D,0)==="-"?-0:M}:A},function(x,b,r){var u=r(3),d=r(323);u({target:"Number",stat:!0,forced:Number.parseInt!==d},{parseInt:d})},function(x,b,r){var u=r(4),d=r(7),h=r(14),p=r(68),y=r(309).trim,T=r(310),$=u.parseInt,A=u.Symbol,E=A&&A.iterator,R=/^[+-]?0x/i,I=h(R.exec),O=$(T+"08")!==8||$(T+"0x16")!==22||E&&!d(function(){$(Object(E))});x.exports=O?function(D,M){var F=y(p(D));return $(F,M>>>0||(I(R,F)?16:10))}:$},function(x,b,r){var u=r(3),d=r(14),h=r(61),p=r(308),y=r(244),T=r(299),$=r(7),A=RangeError,E=String,R=isFinite,I=Math.abs,O=Math.floor,C=Math.pow,D=Math.round,M=d(1 .toExponential),F=d(y),z=d("".slice),U=M(-69e-12,4)==="-6.9000e-11"&&M(1.255,2)==="1.25e+0"&&M(12345,3)==="1.235e+4"&&M(25,0)==="3e+1",j=function(){return $(function(){M(1,1/0)})&&$(function(){M(1,-1/0)})},G=function(){return!$(function(){M(1/0,1/0),M(NaN,1/0)})},B=!U||!j()||!G();u({target:"Number",proto:!0,forced:B},{toExponential:function(Y){var Z=p(this);if(Y===void 0)return M(Z);var J=h(Y);if(!R(Z))return String(Z);if(J<0||J>20)throw new A("Incorrect fraction digits");if(U)return M(Z,J);var q="",nt,rt,_,tt;if(Z<0&&(q="-",Z=-Z),Z===0)rt=0,nt=F("0",J+1);else{var et=T(Z);rt=O(et);var lt=C(10,rt-J),mt=D(Z/lt);2*Z>=(2*mt+1)*lt&&(mt+=1),mt>=C(10,J+1)&&(mt/=10,rt+=1),nt=E(mt)}return J!==0&&(nt=z(nt,0,1)+"."+z(nt,1)),rt===0?(_="+",tt="0"):(_=rt>0?"+":"-",tt=E(I(rt))),nt+="e"+_+tt,q+nt}})},function(x,b,r){var u=r(3),d=r(14),h=r(61),p=r(308),y=r(244),T=r(7),$=RangeError,A=String,E=Math.floor,R=d(y),I=d("".slice),O=d(1 .toFixed),C=function(j,G,B){return G===0?B:G%2===1?C(j,G-1,B*j):C(j*j,G/2,B)},D=function(j){for(var G=0,B=j;B>=4096;)G+=12,B/=4096;for(;B>=2;)G+=1,B/=2;return G},M=function(j,G,B){for(var V=-1,Y=B;++V<6;)Y+=G*j[V],j[V]=Y%1e7,Y=E(Y/1e7)},F=function(j,G){for(var B=6,V=0;--B>=0;)V+=j[B],j[B]=E(V/G),V=V%G*1e7},z=function(j){for(var G=6,B="";--G>=0;)if(B!==""||G===0||j[G]!==0){var V=A(j[G]);B=B===""?V:B+R("0",7-V.length)+V}return B},U=T(function(){return O(8e-5,3)!=="0.000"||O(.9,0)!=="1"||O(1.255,2)!=="1.25"||O(0xde0b6b3a7640080,0)!=="1000000000000000128"})||!T(function(){O({})});u({target:"Number",proto:!0,forced:U},{toFixed:function(G){var B=p(this),V=h(G),Y=[0,0,0,0,0,0],Z="",J="0",q,nt,rt,_;if(V<0||V>20)throw new $("Incorrect fraction digits");if(B!==B)return"NaN";if(B<=-1e21||B>=1e21)return A(B);if(B<0&&(Z="-",B=-B),B>1e-21)if(q=D(B*C(2,69,1))-69,nt=q<0?B*C(2,-q,1):B/C(2,q,1),nt*=4503599627370496,q=52-q,q>0){for(M(Y,0,nt),rt=V;rt>=7;)M(Y,1e7,0),rt-=7;for(M(Y,C(10,rt,1),0),rt=q-1;rt>=23;)F(Y,8388608),rt-=23;F(Y,1<0?(_=J.length,J=Z+(_<=V?"0."+R("0",V-_)+J:I(J,0,_-V)+"."+I(J,_-V))):J=Z+J,J}})},function(x,b,r){var u=r(3),d=r(14),h=r(7),p=r(308),y=d(1 .toPrecision),T=h(function(){return y(1,void 0)!=="1"})||!h(function(){y({})});u({target:"Number",proto:!0,forced:T},{toPrecision:function(A){return A===void 0?y(p(this)):y(p(this),A)}})},function(x,b,r){var u=r(3),d=r(328);u({target:"Object",stat:!0,arity:2,forced:Object.assign!==d},{assign:d})},function(x,b,r){var u=r(6),d=r(14),h=r(8),p=r(7),y=r(73),T=r(66),$=r(10),A=r(39),E=r(13),R=Object.assign,I=Object.defineProperty,O=d([].concat);x.exports=!R||p(function(){if(u&&R({b:1},R(I({},"a",{enumerable:!0,get:function(){I(this,"b",{value:3,enumerable:!1})}}),{b:2})).b!==1)return!0;var C={},D={},M=Symbol("assign detection"),F="abcdefghijklmnopqrst";return C[M]=7,F.split("").forEach(function(z){D[z]=z}),R({},C)[M]!==7||y(R({},D)).join("")!==F})?function(D,M){for(var F=A(D),z=arguments.length,U=1,j=T.f,G=$.f;z>U;)for(var B=E(arguments[U++]),V=j?O(y(B),j(B)):y(B),Y=V.length,Z=0,J;Y>Z;)J=V[Z++],(!u||h(G,B,J))&&(F[J]=B[J]);return F}:R},function(x,b,r){var u=r(3),d=r(6),h=r(71);u({target:"Object",stat:!0,sham:!d},{create:h})},function(x,b,r){var u=r(3),d=r(6),h=r(331),p=r(30),y=r(39),T=r(44);d&&u({target:"Object",proto:!0,forced:h},{__defineGetter__:function(A,E){T.f(y(this),A,{get:p(E),enumerable:!0,configurable:!0})}})},function(x,b,r){var u=r(36),d=r(4),h=r(7),p=r(192);x.exports=u||!h(function(){if(!(p&&p<535)){var y=Math.random();__defineSetter__.call(null,y,function(){}),delete d[y]}})},function(x,b,r){var u=r(3),d=r(6),h=r(72).f;u({target:"Object",stat:!0,forced:Object.defineProperties!==h,sham:!d},{defineProperties:h})},function(x,b,r){var u=r(3),d=r(6),h=r(44).f;u({target:"Object",stat:!0,forced:Object.defineProperty!==h,sham:!d},{defineProperty:h})},function(x,b,r){var u=r(3),d=r(6),h=r(331),p=r(30),y=r(39),T=r(44);d&&u({target:"Object",proto:!0,forced:h},{__defineSetter__:function(A,E){T.f(y(this),A,{set:p(E),enumerable:!0,configurable:!0})}})},function(x,b,r){var u=r(3),d=r(336).entries;u({target:"Object",stat:!0},{entries:function(p){return d(p)}})},function(x,b,r){var u=r(6),d=r(7),h=r(14),p=r(128),y=r(73),T=r(12),$=r(10).f,A=h($),E=h([].push),R=u&&d(function(){var O=Object.create(null);return O[2]=2,!A(O,2)}),I=function(O){return function(C){for(var D=T(C),M=y(D),F=R&&p(D)===null,z=M.length,U=0,j=[],G;z>U;)G=M[U++],(!u||(F?G in D:A(D,G)))&&E(j,O?[G,D[G]]:D[G]);return j}};x.exports={entries:I(!0),values:I(!1)}},function(x,b,r){var u=r(3),d=r(281),h=r(7),p=r(20),y=r(278).onFreeze,T=Object.freeze,$=h(function(){T(1)});u({target:"Object",stat:!0,forced:$,sham:!d},{freeze:function(E){return T&&p(E)?T(y(E)):E}})},function(x,b,r){var u=r(3),d=r(130),h=r(141);u({target:"Object",stat:!0},{fromEntries:function(y){var T={};return d(y,function($,A){h(T,$,A)},{AS_ENTRIES:!0}),T}})},function(x,b,r){var u=r(3),d=r(7),h=r(12),p=r(5).f,y=r(6),T=!y||d(function(){p(1)});u({target:"Object",stat:!0,forced:T,sham:!y},{getOwnPropertyDescriptor:function(A,E){return p(h(A),E)}})},function(x,b,r){var u=r(3),d=r(6),h=r(56),p=r(12),y=r(5),T=r(141);u({target:"Object",stat:!0,sham:!d},{getOwnPropertyDescriptors:function(A){for(var E=p(A),R=y.f,I=h(E),O={},C=0,D,M;I.length>C;)M=R(E,D=I[C++]),M!==void 0&&T(O,D,M);return O}})},function(x,b,r){var u=r(3),d=r(7),h=r(75).f,p=d(function(){return!Object.getOwnPropertyNames(1)});u({target:"Object",stat:!0,forced:p},{getOwnPropertyNames:h})},function(x,b,r){var u=r(3),d=r(7),h=r(39),p=r(128),y=r(129),T=d(function(){p(1)});u({target:"Object",stat:!0,forced:T,sham:!y},{getPrototypeOf:function(A){return p(h(A))}})},function(x,b,r){var u=r(3),d=r(23),h=r(14),p=r(30),y=r(16),T=r(18),$=r(130),A=r(7),E=Object.groupBy,R=d("Object","create"),I=h([].push),O=!E||A(function(){return E("ab",function(C){return C}).a.length!==1});u({target:"Object",stat:!0,forced:O},{groupBy:function(D,M){y(D),p(M);var F=R(null),z=0;return $(D,function(U){var j=T(M(U,z++));j in F?I(F[j],U):F[j]=[U]}),F}})},function(x,b,r){var u=r(3),d=r(38);u({target:"Object",stat:!0},{hasOwn:d})},function(x,b,r){var u=r(3),d=r(346);u({target:"Object",stat:!0},{is:d})},function(x){x.exports=Object.is||function(r,u){return r===u?r!==0||1/r===1/u:r!==r&&u!==u}},function(x,b,r){var u=r(3),d=r(279);u({target:"Object",stat:!0,forced:Object.isExtensible!==d},{isExtensible:d})},function(x,b,r){var u=r(3),d=r(7),h=r(20),p=r(15),y=r(280),T=Object.isFrozen,$=y||d(function(){T(1)});u({target:"Object",stat:!0,forced:$},{isFrozen:function(E){return!h(E)||y&&p(E)==="ArrayBuffer"?!0:T?T(E):!1}})},function(x,b,r){var u=r(3),d=r(7),h=r(20),p=r(15),y=r(280),T=Object.isSealed,$=y||d(function(){T(1)});u({target:"Object",stat:!0,forced:$},{isSealed:function(E){return!h(E)||y&&p(E)==="ArrayBuffer"?!0:T?T(E):!1}})},function(x,b,r){var u=r(3),d=r(39),h=r(73),p=r(7),y=p(function(){h(1)});u({target:"Object",stat:!0,forced:y},{keys:function($){return h(d($))}})},function(x,b,r){var u=r(3),d=r(6),h=r(331),p=r(39),y=r(18),T=r(128),$=r(5).f;d&&u({target:"Object",proto:!0,forced:h},{__lookupGetter__:function(E){var R=p(this),I=y(E),O;do if(O=$(R,I))return O.get;while(R=T(R))}})},function(x,b,r){var u=r(3),d=r(6),h=r(331),p=r(39),y=r(18),T=r(128),$=r(5).f;d&&u({target:"Object",proto:!0,forced:h},{__lookupSetter__:function(E){var R=p(this),I=y(E),O;do if(O=$(R,I))return O.set;while(R=T(R))}})},function(x,b,r){var u=r(3),d=r(20),h=r(278).onFreeze,p=r(281),y=r(7),T=Object.preventExtensions,$=y(function(){T(1)});u({target:"Object",stat:!0,forced:$,sham:!p},{preventExtensions:function(E){return T&&d(E)?T(h(E)):E}})},function(x,b,r){var u=r(6),d=r(77),h=r(20),p=r(116),y=r(39),T=r(16),$=Object.getPrototypeOf,A=Object.setPrototypeOf,E=Object.prototype,R="__proto__";if(u&&$&&A&&!(R in E))try{d(E,R,{configurable:!0,get:function(){return $(y(this))},set:function(O){var C=T(this);p(O)&&h(C)&&A(C,O)}})}catch(I){}},function(x,b,r){var u=r(3),d=r(20),h=r(278).onFreeze,p=r(281),y=r(7),T=Object.seal,$=y(function(){T(1)});u({target:"Object",stat:!0,forced:$,sham:!p},{seal:function(E){return T&&d(E)?T(h(E)):E}})},function(x,b,r){var u=r(3),d=r(113);u({target:"Object",stat:!0},{setPrototypeOf:d})},function(x,b,r){var u=r(70),d=r(47),h=r(358);u||d(Object.prototype,"toString",h,{unsafe:!0})},function(x,b,r){var u=r(70),d=r(69);x.exports=u?{}.toString:function(){return"[object "+d(this)+"]"}},function(x,b,r){var u=r(3),d=r(336).values;u({target:"Object",stat:!0},{values:function(p){return d(p)}})},function(x,b,r){var u=r(3),d=r(321);u({global:!0,forced:parseFloat!==d},{parseFloat:d})},function(x,b,r){var u=r(3),d=r(323);u({global:!0,forced:parseInt!==d},{parseInt:d})},function(x,b,r){r(363),r(379),r(381),r(382),r(383),r(384)},function(x,b,r){var u=r(3),d=r(36),h=r(182),p=r(4),y=r(8),T=r(47),$=r(113),A=r(82),E=r(194),R=r(30),I=r(21),O=r(20),C=r(211),D=r(364),M=r(366).set,F=r(369),z=r(374),U=r(375),j=r(371),G=r(51),B=r(376),V=r(377),Y=r(378),Z="Promise",J=V.CONSTRUCTOR,q=V.REJECTION_EVENT,nt=V.SUBCLASSING,rt=G.getterFor(Z),_=G.set,tt=B&&B.prototype,et=B,lt=tt,mt=p.TypeError,gt=p.document,xt=p.process,yt=Y.f,Ut=yt,Dt=!!(gt&>.createEvent&&p.dispatchEvent),Xt="unhandledrejection",Qt="rejectionhandled",kt=0,me=1,ge=2,ae=1,Mt=2,Ht,re,se,ee,fe=function(Tt){var qt;return O(Tt)&&I(qt=Tt.then)?qt:!1},Pe=function(Tt,qt){var te=qt.value,Zt=qt.state===me,Yt=Zt?Tt.ok:Tt.fail,Ye=Tt.resolve,Ze=Tt.reject,ut=Tt.domain,It,Pt,Ct;try{Yt?(Zt||(qt.rejection===Mt&&Te(qt),qt.rejection=ae),Yt===!0?It=te:(ut&&ut.enter(),It=Yt(te),ut&&(ut.exit(),Ct=!0)),It===Tt.promise?Ze(new mt("Promise-chain cycle")):(Pt=fe(It))?y(Pt,It,Ye,Ze):Ye(It)):Ze(te)}catch(Nt){ut&&!Ct&&ut.exit(),Ze(Nt)}},Me=function(Tt,qt){Tt.notified||(Tt.notified=!0,F(function(){for(var te=Tt.reactions,Zt;Zt=te.get();)Pe(Zt,Tt);Tt.notified=!1,qt&&!Tt.rejection&&ce(Tt)}))},$e=function(Tt,qt,te){var Zt,Yt;Dt?(Zt=gt.createEvent("Event"),Zt.promise=qt,Zt.reason=te,Zt.initEvent(Tt,!1,!0),p.dispatchEvent(Zt)):Zt={promise:qt,reason:te},!q&&(Yt=p["on"+Tt])?Yt(Zt):Tt===Xt&&z("Unhandled promise rejection",te)},ce=function(Tt){y(M,p,function(){var qt=Tt.facade,te=Tt.value,Zt=Ae(Tt),Yt;if(Zt&&(Yt=U(function(){h?xt.emit("unhandledRejection",te,qt):$e(Xt,qt,te)}),Tt.rejection=h||Ae(Tt)?Mt:ae,Yt.error))throw Yt.value})},Ae=function(Tt){return Tt.rejection!==ae&&!Tt.parent},Te=function(Tt){y(M,p,function(){var qt=Tt.facade;h?xt.emit("rejectionHandled",qt):$e(Qt,qt,Tt.value)})},de=function(Tt,qt,te){return function(Zt){Tt(qt,Zt,te)}},bt=function(Tt,qt,te){Tt.done||(Tt.done=!0,te&&(Tt=te),Tt.value=qt,Tt.state=ge,Me(Tt,!0))},Ft=function(Tt,qt,te){if(!Tt.done){Tt.done=!0,te&&(Tt=te);try{if(Tt.facade===qt)throw new mt("Promise can't be resolved itself");var Zt=fe(qt);Zt?F(function(){var Yt={done:!1};try{y(Zt,qt,de(Ft,Yt,Tt),de(bt,Yt,Tt))}catch(Ye){bt(Yt,Ye,Tt)}}):(Tt.value=qt,Tt.state=me,Me(Tt,!1))}catch(Yt){bt({done:!1},Yt,Tt)}}};if(J&&(et=function(qt){C(this,lt),R(qt),y(Ht,this);var te=rt(this);try{qt(de(Ft,te),de(bt,te))}catch(Zt){bt(te,Zt)}},lt=et.prototype,Ht=function(qt){_(this,{type:Z,done:!1,notified:!1,parent:!1,reactions:new j,rejection:!1,state:kt,value:null})},Ht.prototype=T(lt,"then",function(qt,te){var Zt=rt(this),Yt=yt(D(this,et));return Zt.parent=!0,Yt.ok=I(qt)?qt:!0,Yt.fail=I(te)&&te,Yt.domain=h?xt.domain:void 0,Zt.state===kt?Zt.reactions.add(Yt):F(function(){Pe(Yt,Zt)}),Yt.promise}),re=function(){var Tt=new Ht,qt=rt(Tt);this.promise=Tt,this.resolve=de(Ft,qt),this.reject=de(bt,qt)},Y.f=yt=function(Tt){return Tt===et||Tt===se?new re(Tt):Ut(Tt)},!d&&I(B)&&tt!==Object.prototype)){ee=tt.then,nt||T(tt,"then",function(qt,te){var Zt=this;return new et(function(Yt,Ye){y(ee,Zt,Yt,Ye)}).then(qt,te)},{unsafe:!0});try{delete tt.constructor}catch(Tt){}$&&$(tt,lt)}u({global:!0,constructor:!0,wrap:!0,forced:J},{Promise:et}),A(et,Z,!1,!0),E(Z)},function(x,b,r){var u=r(46),d=r(365),h=r(17),p=r(33),y=p("species");x.exports=function(T,$){var A=u(T).constructor,E;return A===void 0||h(E=u(A)[y])?$:d(E)}},function(x,b,r){var u=r(89),d=r(31),h=TypeError;x.exports=function(p){if(u(p))return p;throw new h(d(p)+" is not a constructor")}},function(x,b,r){var u=r(4),d=r(94),h=r(84),p=r(21),y=r(38),T=r(7),$=r(74),A=r(76),E=r(42),R=r(367),I=r(368),O=r(182),C=u.setImmediate,D=u.clearImmediate,M=u.process,F=u.Dispatch,z=u.Function,U=u.MessageChannel,j=u.String,G=0,B={},V="onreadystatechange",Y,Z,J,q;T(function(){Y=u.location});var nt=function(et){if(y(B,et)){var lt=B[et];delete B[et],lt()}},rt=function(et){return function(){nt(et)}},_=function(et){nt(et.data)},tt=function(et){u.postMessage(j(et),Y.protocol+"//"+Y.host)};(!C||!D)&&(C=function(lt){R(arguments.length,1);var mt=p(lt)?lt:z(lt),gt=A(arguments,1);return B[++G]=function(){d(mt,void 0,gt)},Z(G),G},D=function(lt){delete B[lt]},O?Z=function(et){M.nextTick(rt(et))}:F&&F.now?Z=function(et){F.now(rt(et))}:U&&!I?(J=new U,q=J.port2,J.port1.onmessage=_,Z=h(q.postMessage,q)):u.addEventListener&&p(u.postMessage)&&!u.importScripts&&Y&&Y.protocol!=="file:"&&!T(tt)?(Z=tt,u.addEventListener("message",_,!1)):V in E("script")?Z=function(et){$.appendChild(E("script"))[V]=function(){$.removeChild(this),nt(et)}}:Z=function(et){setTimeout(rt(et),0)}),x.exports={set:C,clear:D}},function(x){var b=TypeError;x.exports=function(r,u){if(r1?p(arguments,1):[],C=y.f(this),D=$(function(){return h(T(I),void 0,O)});return(D.error?C.reject:C.resolve)(D.value),C.promise}})},function(x,b,r){var u=r(3),d=r(378);u({target:"Promise",stat:!0},{withResolvers:function(){var p=d.f(this);return{promise:p.promise,resolve:p.resolve,reject:p.reject}}})},function(x,b,r){var u=r(3),d=r(94),h=r(30),p=r(46),y=r(7),T=!y(function(){Reflect.apply(function(){})});u({target:"Reflect",stat:!0,forced:T},{apply:function(A,E,R){return d(h(A),E,p(R))}})},function(x,b,r){var u=r(3),d=r(23),h=r(94),p=r(251),y=r(365),T=r(46),$=r(20),A=r(71),E=r(7),R=d("Reflect","construct"),I=Object.prototype,O=[].push,C=E(function(){function F(){}return!(R(function(){},[],F)instanceof F)}),D=!E(function(){R(function(){})}),M=C||D;u({target:"Reflect",stat:!0,forced:M,sham:M},{construct:function(z,U){y(z),T(U);var j=arguments.length<3?z:y(arguments[2]);if(D&&!C)return R(z,U,j);if(z===j){switch(U.length){case 0:return new z;case 1:return new z(U[0]);case 2:return new z(U[0],U[1]);case 3:return new z(U[0],U[1],U[2]);case 4:return new z(U[0],U[1],U[2],U[3])}var G=[null];return h(O,G,U),new(h(p,z,G))}var B=j.prototype,V=A($(B)?B:I),Y=h(z,V,U);return $(Y)?Y:V}})},function(x,b,r){var u=r(3),d=r(6),h=r(46),p=r(18),y=r(44),T=r(7),$=T(function(){Reflect.defineProperty(y.f({},1,{value:1}),1,{value:2})});u({target:"Reflect",stat:!0,forced:$,sham:!d},{defineProperty:function(E,R,I){h(E);var O=p(R);h(I);try{return y.f(E,O,I),!0}catch(C){return!1}}})},function(x,b,r){var u=r(3),d=r(46),h=r(5).f;u({target:"Reflect",stat:!0},{deleteProperty:function(y,T){var $=h(d(y),T);return $&&!$.configurable?!1:delete y[T]}})},function(x,b,r){var u=r(3),d=r(8),h=r(20),p=r(46),y=r(396),T=r(5),$=r(128);function A(E,R){var I=arguments.length<3?E:arguments[2],O,C;if(p(E)===I)return E[R];if(O=T.f(E,R),O)return y(O)?O.value:O.get===void 0?void 0:d(O.get,I);if(h(C=$(E)))return A(C,R,I)}u({target:"Reflect",stat:!0},{get:A})},function(x,b,r){var u=r(38);x.exports=function(d){return d!==void 0&&(u(d,"value")||u(d,"writable"))}},function(x,b,r){var u=r(3),d=r(6),h=r(46),p=r(5);u({target:"Reflect",stat:!0,sham:!d},{getOwnPropertyDescriptor:function(T,$){return p.f(h(T),$)}})},function(x,b,r){var u=r(3),d=r(46),h=r(128),p=r(129);u({target:"Reflect",stat:!0,sham:!p},{getPrototypeOf:function(T){return h(d(T))}})},function(x,b,r){var u=r(3);u({target:"Reflect",stat:!0},{has:function(h,p){return p in h}})},function(x,b,r){var u=r(3),d=r(46),h=r(279);u({target:"Reflect",stat:!0},{isExtensible:function(y){return d(y),h(y)}})},function(x,b,r){var u=r(3),d=r(56);u({target:"Reflect",stat:!0},{ownKeys:d})},function(x,b,r){var u=r(3),d=r(23),h=r(46),p=r(281);u({target:"Reflect",stat:!0,sham:!p},{preventExtensions:function(T){h(T);try{var $=d("Object","preventExtensions");return $&&$(T),!0}catch(A){return!1}}})},function(x,b,r){var u=r(3),d=r(8),h=r(46),p=r(20),y=r(396),T=r(7),$=r(44),A=r(5),E=r(128),R=r(11);function I(C,D,M){var F=arguments.length<4?C:arguments[3],z=A.f(h(C),D),U,j,G;if(!z){if(p(j=E(C)))return I(j,D,M,F);z=R(0)}if(y(z)){if(z.writable===!1||!p(F))return!1;if(U=A.f(F,D)){if(U.get||U.set||U.writable===!1)return!1;U.value=M,$.f(F,D,U)}else $.f(F,D,R(0,M))}else{if(G=z.set,G===void 0)return!1;d(G,F,M)}return!0}var O=T(function(){var C=function(){},D=$.f(new C,"a",{configurable:!0});return Reflect.set(C.prototype,"a",1,D)!==!1});u({target:"Reflect",stat:!0,forced:O},{set:I})},function(x,b,r){var u=r(3),d=r(46),h=r(115),p=r(113);p&&u({target:"Reflect",stat:!0},{setPrototypeOf:function(T,$){d(T),h($);try{return p(T,$),!0}catch(A){return!1}}})},function(x,b,r){var u=r(3),d=r(4),h=r(82);u({global:!0},{Reflect:{}}),h(d.Reflect,"Reflect",!0)},function(x,b,r){var u=r(6),d=r(4),h=r(14),p=r(67),y=r(118),T=r(43),$=r(71),A=r(57).f,E=r(24),R=r(407),I=r(68),O=r(408),C=r(410),D=r(117),M=r(47),F=r(7),z=r(38),U=r(51).enforce,j=r(194),G=r(33),B=r(411),V=r(412),Y=G("match"),Z=d.RegExp,J=Z.prototype,q=d.SyntaxError,nt=h(J.exec),rt=h("".charAt),_=h("".replace),tt=h("".indexOf),et=h("".slice),lt=/^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/,mt=/a/g,gt=/a/g,xt=new Z(mt)!==mt,yt=C.MISSED_STICKY,Ut=C.UNSUPPORTED_Y,Dt=u&&(!xt||yt||B||V||F(function(){return gt[Y]=!1,Z(mt)!==mt||Z(gt)===gt||String(Z(mt,"i"))!=="/a/i"})),Xt=function(ae){for(var Mt=ae.length,Ht=0,re="",se=!1,ee;Ht<=Mt;Ht++){if(ee=rt(ae,Ht),ee==="\\"){re+=ee+rt(ae,++Ht);continue}!se&&ee==="."?re+="[\\s\\S]":(ee==="["?se=!0:ee==="]"&&(se=!1),re+=ee)}return re},Qt=function(ae){for(var Mt=ae.length,Ht=0,re="",se=[],ee=$(null),fe=!1,Pe=!1,Me=0,$e="",ce;Ht<=Mt;Ht++){if(ce=rt(ae,Ht),ce==="\\")ce+=rt(ae,++Ht);else if(ce==="]")fe=!1;else if(!fe)switch(!0){case ce==="[":fe=!0;break;case ce==="(":if(re+=ce,et(ae,Ht+1,Ht+3)==="?:")continue;nt(lt,et(ae,Ht+1))&&(Ht+=2,Pe=!0),Me++;continue;case(ce===">"&&Pe):if($e===""||z(ee,$e))throw new q("Invalid capture group name");ee[$e]=!0,se[se.length]=[$e,Me],Pe=!1,$e="";continue}Pe?$e+=ce:re+=ce}return[re,se]};if(p("RegExp",Dt)){for(var kt=function(Mt,Ht){var re=E(J,this),se=R(Mt),ee=Ht===void 0,fe=[],Pe=Mt,Me,$e,ce,Ae,Te,de;if(!re&&se&&ee&&Mt.constructor===kt)return Mt;if((se||E(J,Mt))&&(Mt=Mt.source,ee&&(Ht=O(Pe))),Mt=Mt===void 0?"":I(Mt),Ht=Ht===void 0?"":I(Ht),Pe=Mt,B&&"dotAll"in mt&&($e=!!Ht&&tt(Ht,"s")>-1,$e&&(Ht=_(Ht,/s/g,""))),Me=Ht,yt&&"sticky"in mt&&(ce=!!Ht&&tt(Ht,"y")>-1,ce&&Ut&&(Ht=_(Ht,/y/g,""))),V&&(Ae=Qt(Mt),Mt=Ae[0],fe=Ae[1]),Te=y(Z(Mt,Ht),re?this:J,kt),($e||ce||fe.length)&&(de=U(Te),$e&&(de.dotAll=!0,de.raw=kt(Xt(Mt),Me)),ce&&(de.sticky=!0),fe.length&&(de.groups=fe)),Mt!==Pe)try{T(Te,"source",Pe===""?"(?:)":Pe)}catch(bt){}return Te},me=A(Z),ge=0;me.length>ge;)D(kt,Z,me[ge++]);J.constructor=kt,kt.prototype=J,M(d,"RegExp",kt,{constructor:!0})}j("RegExp")},function(x,b,r){var u=r(20),d=r(15),h=r(33),p=h("match");x.exports=function(y){var T;return u(y)&&((T=y[p])!==void 0?!!T:d(y)==="RegExp")}},function(x,b,r){var u=r(8),d=r(38),h=r(24),p=r(409),y=RegExp.prototype;x.exports=function(T){var $=T.flags;return $===void 0&&!("flags"in y)&&!d(T,"flags")&&h(y,T)?u(p,T):$}},function(x,b,r){var u=r(46);x.exports=function(){var d=u(this),h="";return d.hasIndices&&(h+="d"),d.global&&(h+="g"),d.ignoreCase&&(h+="i"),d.multiline&&(h+="m"),d.dotAll&&(h+="s"),d.unicode&&(h+="u"),d.unicodeSets&&(h+="v"),d.sticky&&(h+="y"),h}},function(x,b,r){var u=r(7),d=r(4),h=d.RegExp,p=u(function(){var $=h("a","y");return $.lastIndex=2,$.exec("abcd")!==null}),y=p||u(function(){return!h("a","y").sticky}),T=p||u(function(){var $=h("^r","gy");return $.lastIndex=2,$.exec("str")!==null});x.exports={BROKEN_CARET:T,MISSED_STICKY:y,UNSUPPORTED_Y:p}},function(x,b,r){var u=r(7),d=r(4),h=d.RegExp;x.exports=u(function(){var p=h(".","s");return!(p.dotAll&&p.test(` +`)&&p.flags==="s")})},function(x,b,r){var u=r(7),d=r(4),h=d.RegExp;x.exports=u(function(){var p=h("(?b)","g");return p.exec("b").groups.a!=="b"||"b".replace(p,"$c")!=="bc"})},function(x,b,r){var u=r(3),d=r(14),h=r(414),p=r(38),y=r(243).start,T=r(310),$=Array,A=RegExp.escape,E=d("".charAt),R=d("".charCodeAt),I=d(1.1.toString),O=d([].join),C=/^[0-9a-z]/i,D=/^[$()*+./?[\\\]^{|}]/,M=RegExp("^[!\"#%&',\\-:;<=>@`~"+T+"]"),F=d(C.exec),z={" ":"t","\n":"n","\v":"v","\f":"f","\r":"r"},U=function(G){var B=I(R(G,0),16);return B.length<3?"\\x"+y(B,2,"0"):"\\u"+y(B,4,"0")},j=!A||A("ab")!=="\\x61b";u({target:"RegExp",stat:!0,forced:j},{escape:function(B){h(B);for(var V=B.length,Y=$(V),Z=0;Z=56320||Z+1>=V||(R(B,Z+1)&64512)!==56320?Y[Z]=U(J):(Y[Z]=J,Y[++Z]=E(B,Z))}}return O(Y,"")}})},function(x){var b=TypeError;x.exports=function(r){if(typeof r=="string")return r;throw new b("Argument is not a string")}},function(x,b,r){var u=r(6),d=r(411),h=r(15),p=r(77),y=r(51).get,T=RegExp.prototype,$=TypeError;u&&d&&p(T,"dotAll",{configurable:!0,get:function(){if(this!==T){if(h(this)==="RegExp")return!!y(this).dotAll;throw new $("Incompatible receiver, RegExp required")}}})},function(x,b,r){var u=r(3),d=r(417);u({target:"RegExp",proto:!0,forced:/./.exec!==d},{exec:d})},function(x,b,r){var u=r(8),d=r(14),h=r(68),p=r(409),y=r(410),T=r(34),$=r(71),A=r(51).get,E=r(411),R=r(412),I=T("native-string-replace",String.prototype.replace),O=RegExp.prototype.exec,C=O,D=d("".charAt),M=d("".indexOf),F=d("".replace),z=d("".slice),U=function(){var V=/a/,Y=/b*/g;return u(O,V,"a"),u(O,Y,"a"),V.lastIndex!==0||Y.lastIndex!==0}(),j=y.BROKEN_CARET,G=/()??/.exec("")[1]!==void 0,B=U||G||j||E||R;B&&(C=function(Y){var Z=this,J=A(Z),q=h(Y),nt=J.raw,rt,_,tt,et,lt,mt,gt;if(nt)return nt.lastIndex=Z.lastIndex,rt=u(C,nt,q),Z.lastIndex=nt.lastIndex,rt;var xt=J.groups,yt=j&&Z.sticky,Ut=u(p,Z),Dt=Z.source,Xt=0,Qt=q;if(yt&&(Ut=F(Ut,"y",""),M(Ut,"g")===-1&&(Ut+="g"),Qt=z(q,Z.lastIndex),Z.lastIndex>0&&(!Z.multiline||Z.multiline&&D(q,Z.lastIndex-1)!==` +`)&&(Dt="(?: "+Dt+")",Qt=" "+Qt,Xt++),_=new RegExp("^(?:"+Dt+")",Ut)),G&&(_=new RegExp("^"+Dt+"$(?!\\s)",Ut)),U&&(tt=Z.lastIndex),et=u(O,yt?_:Z,Qt),yt?et?(et.input=z(et.input,Xt),et[0]=z(et[0],Xt),et.index=Z.lastIndex,Z.lastIndex+=et[0].length):Z.lastIndex=0:U&&et&&(Z.lastIndex=Z.global?et.index+et[0].length:tt),G&&et&&et.length>1&&u(I,et[0],_,function(){for(lt=1;ltC.size?T(C.getIterator(),function(M){E(O,M)&&A(D,M)}):y(O,function(M){C.includes(M)&&A(D,M)}),D}},function(x,b,r){var u=r(3),d=r(437),h=r(433),p=!h("isDisjointFrom",function(y){return!y});u({target:"Set",proto:!0,real:!0,forced:p},{isDisjointFrom:d})},function(x,b,r){var u=r(426),d=r(427).has,h=r(431),p=r(432),y=r(429),T=r(430),$=r(135);x.exports=function(E){var R=u(this),I=p(E);if(h(R)<=I.size)return y(R,function(C){if(I.includes(C))return!1},!0)!==!1;var O=I.getIterator();return T(O,function(C){if(d(R,C))return $(O,"normal",!1)})!==!1}},function(x,b,r){var u=r(3),d=r(439),h=r(433),p=!h("isSubsetOf",function(y){return y});u({target:"Set",proto:!0,real:!0,forced:p},{isSubsetOf:d})},function(x,b,r){var u=r(426),d=r(431),h=r(429),p=r(432);x.exports=function(T){var $=u(this),A=p(T);return d($)>A.size?!1:h($,function(E){if(!A.includes(E))return!1},!0)!==!1}},function(x,b,r){var u=r(3),d=r(441),h=r(433),p=!h("isSupersetOf",function(y){return!y});u({target:"Set",proto:!0,real:!0,forced:p},{isSupersetOf:d})},function(x,b,r){var u=r(426),d=r(427).has,h=r(431),p=r(432),y=r(430),T=r(135);x.exports=function(A){var E=u(this),R=p(A);if(h(E)=0?C:O+C;return D<0||D>=O?void 0:$(I,D)}})},function(x,b,r){var u=r(3),d=r(448).codeAt;u({target:"String",proto:!0},{codePointAt:function(p){return d(this,p)}})},function(x,b,r){var u=r(14),d=r(61),h=r(68),p=r(16),y=u("".charAt),T=u("".charCodeAt),$=u("".slice),A=function(E){return function(R,I){var O=h(p(R)),C=d(I),D=O.length,M,F;return C<0||C>=D?E?"":void 0:(M=T(O,C),M<55296||M>56319||C+1===D||(F=T(O,C+1))<56320||F>57343?E?y(O,C):M:E?$(O,C,C+2):(M-55296<<10)+(F-56320)+65536)}};x.exports={codeAt:A(!1),charAt:A(!0)}},function(x,b,r){var u=r(3),d=r(85),h=r(5).f,p=r(64),y=r(68),T=r(450),$=r(16),A=r(451),E=r(36),R=d("".slice),I=Math.min,O=A("endsWith"),C=!E&&!O&&!!function(){var D=h(String.prototype,"endsWith");return D&&!D.writable}();u({target:"String",proto:!0,forced:!C&&!O},{endsWith:function(M){var F=y($(this));T(M);var z=arguments.length>1?arguments[1]:void 0,U=F.length,j=z===void 0?U:I(p(z),U),G=y(M);return R(F,j-G.length,j)===G}})},function(x,b,r){var u=r(407),d=TypeError;x.exports=function(h){if(u(h))throw new d("The method doesn't accept regular expressions");return h}},function(x,b,r){var u=r(33),d=u("match");x.exports=function(h){var p=/./;try{"/./"[h](p)}catch(y){try{return p[d]=!1,"/./"[h](p)}catch(T){}}return!1}},function(x,b,r){var u=r(3),d=r(14),h=r(60),p=RangeError,y=String.fromCharCode,T=String.fromCodePoint,$=d([].join),A=!!T&&T.length!==1;u({target:"String",stat:!0,arity:1,forced:A},{fromCodePoint:function(R){for(var I=[],O=arguments.length,C=0,D;O>C;){if(D=+arguments[C++],h(D,1114111)!==D)throw new p(D+" is not a valid code point");I[C]=D<65536?y(D):y(((D-=65536)>>10)+55296,D%1024+56320)}return $(I,"")}})},function(x,b,r){var u=r(3),d=r(14),h=r(450),p=r(16),y=r(68),T=r(451),$=d("".indexOf);u({target:"String",proto:!0,forced:!T("includes")},{includes:function(E){return!!~$(y(p(this)),y(h(E)),arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(14),h=r(16),p=r(68),y=d("".charCodeAt);u({target:"String",proto:!0},{isWellFormed:function(){for(var $=p(h(this)),A=$.length,E=0;E=56320||++E>=A||(y($,E)&64512)!==56320))return!1}return!0}})},function(x,b,r){var u=r(448).charAt,d=r(68),h=r(51),p=r(169),y=r(172),T="String Iterator",$=h.set,A=h.getterFor(T);p(String,"String",function(E){$(this,{type:T,string:d(E),index:0})},function(){var R=A(this),I=R.string,O=R.index,C;return O>=I.length?y(void 0,!0):(C=u(I,O),R.index+=C.length,y(C,!1))})},function(x,b,r){var u=r(8),d=r(457),h=r(46),p=r(17),y=r(64),T=r(68),$=r(16),A=r(29),E=r(458),R=r(459);d("match",function(I,O,C){return[function(M){var F=$(this),z=p(M)?void 0:A(M,I);return z?u(z,M,F):new RegExp(M)[I](T(F))},function(D){var M=h(this),F=T(D),z=C(O,M,F);if(z.done)return z.value;if(!M.global)return R(M,F);var U=M.unicode;M.lastIndex=0;for(var j=[],G=0,B;(B=R(M,F))!==null;){var V=T(B[0]);j[G]=V,V===""&&(M.lastIndex=E(F,y(M.lastIndex),U)),G++}return G===0?null:j}]})},function(x,b,r){r(416);var u=r(8),d=r(47),h=r(417),p=r(7),y=r(33),T=r(43),$=y("species"),A=RegExp.prototype;x.exports=function(E,R,I,O){var C=y(E),D=!p(function(){var U={};return U[C]=function(){return 7},""[E](U)!==7}),M=D&&!p(function(){var U=!1,j=/a/;return E==="split"&&(j={},j.constructor={},j.constructor[$]=function(){return j},j.flags="",j[C]=/./[C]),j.exec=function(){return U=!0,null},j[C](""),!U});if(!D||!M||I){var F=/./[C],z=R(C,""[E],function(U,j,G,B,V){var Y=j.exec;return Y===h||Y===A.exec?D&&!V?{done:!0,value:u(F,j,G,B)}:{done:!0,value:u(U,G,j,B)}:{done:!1}});d(String.prototype,E,z[0]),d(A,C,z[1])}O&&T(A[C],"sham",!0)}},function(x,b,r){var u=r(448).charAt;x.exports=function(d,h,p){return h+(p?u(d,h).length:1)}},function(x,b,r){var u=r(8),d=r(46),h=r(21),p=r(15),y=r(417),T=TypeError;x.exports=function($,A){var E=$.exec;if(h(E)){var R=u(E,$,A);return R!==null&&d(R),R}if(p($)==="RegExp")return u(y,$,A);throw new T("RegExp#exec called on incompatible receiver")}},function(x,b,r){var u=r(3),d=r(8),h=r(85),p=r(170),y=r(172),T=r(16),$=r(64),A=r(68),E=r(46),R=r(17),I=r(15),O=r(407),C=r(408),D=r(29),M=r(47),F=r(7),z=r(33),U=r(364),j=r(458),G=r(459),B=r(51),V=r(36),Y=z("matchAll"),Z="RegExp String",J=Z+" Iterator",q=B.set,nt=B.getterFor(J),rt=RegExp.prototype,_=TypeError,tt=h("".indexOf),et=h("".matchAll),lt=!!et&&!F(function(){et("a",/./)}),mt=p(function(yt,Ut,Dt,Xt){q(this,{type:J,regexp:yt,string:Ut,global:Dt,unicode:Xt,done:!1})},Z,function(){var yt=nt(this);if(yt.done)return y(void 0,!0);var Ut=yt.regexp,Dt=yt.string,Xt=G(Ut,Dt);return Xt===null?(yt.done=!0,y(void 0,!0)):yt.global?(A(Xt[0])===""&&(Ut.lastIndex=j(Dt,$(Ut.lastIndex),yt.unicode)),y(Xt,!1)):(yt.done=!0,y(Xt,!1))}),gt=function(xt){var yt=E(this),Ut=A(xt),Dt=U(yt,RegExp),Xt=A(C(yt)),Qt,kt,me;return Qt=new Dt(Dt===RegExp?yt.source:yt,Xt),kt=!!~tt(Xt,"g"),me=!!~tt(Xt,"u"),Qt.lastIndex=$(yt.lastIndex),new mt(Qt,Ut,kt,me)};u({target:"String",proto:!0,forced:lt},{matchAll:function(yt){var Ut=T(this),Dt,Xt,Qt,kt;if(R(yt)){if(lt)return et(Ut,yt)}else{if(O(yt)&&(Dt=A(T(C(yt))),!~tt(Dt,"g")))throw new _("`.matchAll` does not allow non-global regexes");if(lt)return et(Ut,yt);if(Qt=D(yt,Y),Qt===void 0&&V&&I(yt)==="RegExp"&&(Qt=gt),Qt)return d(Qt,yt,Ut)}return Xt=A(Ut),kt=new RegExp(yt,"g"),V?d(gt,kt,Xt):kt[Y](Xt)}}),V||Y in rt||M(rt,Y,gt)},function(x,b,r){var u=r(3),d=r(243).end,h=r(462);u({target:"String",proto:!0,forced:h},{padEnd:function(y){return d(this,y,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(28);x.exports=/Version\/10(?:\.\d+){1,2}(?: [\w./]+)?(?: Mobile\/\w+)? Safari\//.test(u)},function(x,b,r){var u=r(3),d=r(243).start,h=r(462);u({target:"String",proto:!0,forced:h},{padStart:function(y){return d(this,y,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(14),h=r(12),p=r(39),y=r(68),T=r(63),$=d([].push),A=d([].join);u({target:"String",stat:!0},{raw:function(R){var I=h(p(R).raw),O=T(I);if(!O)return"";for(var C=arguments.length,D=[],M=0;;){if($(D,y(I[M++])),M===O)return A(D,"");M")!=="7"});p("replace",function(_,tt,et){var lt=nt?"$":"$0";return[function(gt,xt){var yt=O(this),Ut=A(gt)?void 0:D(gt,U);return Ut?d(Ut,gt,yt,xt):d(tt,I(yt),gt,xt)},function(mt,gt){var xt=T(this),yt=I(mt);if(typeof gt=="string"&&Y(gt,lt)===-1&&Y(gt,"$<")===-1){var Ut=et(tt,xt,yt,gt);if(Ut.done)return Ut.value}var Dt=$(gt);Dt||(gt=I(gt));var Xt=xt.global,Qt;Xt&&(Qt=xt.unicode,xt.lastIndex=0);for(var kt=[],me;me=F(xt,yt),!(me===null||(V(kt,me),!Xt));){var ge=I(me[0]);ge===""&&(xt.lastIndex=C(yt,R(xt.lastIndex),Qt))}for(var ae="",Mt=0,Ht=0;Ht=Mt&&(ae+=Z(yt,Mt,se)+fe,Mt=se+re.length)}return ae+Z(yt,Mt)}]},!rt||!q||nt)},function(x,b,r){var u=r(14),d=r(39),h=Math.floor,p=u("".charAt),y=u("".replace),T=u("".slice),$=/\$([$&'`]|\d{1,2}|<[^>]*>)/g,A=/\$([$&'`]|\d{1,2})/g;x.exports=function(E,R,I,O,C,D){var M=I+E.length,F=O.length,z=A;return C!==void 0&&(C=d(C),z=$),y(D,z,function(U,j){var G;switch(p(j,0)){case"$":return"$";case"&":return E;case"`":return T(R,0,I);case"'":return T(R,M);case"<":G=C[T(j,1,-1)];break;default:var B=+j;if(B===0)return U;if(B>F){var V=h(B/10);return V===0?U:V<=F?O[V-1]===void 0?p(j,1):O[V-1]+p(j,1):U}G=O[B-1]}return G===void 0?"":G})}},function(x,b,r){var u=r(3),d=r(8),h=r(14),p=r(16),y=r(21),T=r(17),$=r(407),A=r(68),E=r(29),R=r(408),I=r(467),O=r(33),C=r(36),D=O("replace"),M=TypeError,F=h("".indexOf),z=h("".replace),U=h("".slice),j=Math.max;u({target:"String",proto:!0},{replaceAll:function(B,V){var Y=p(this),Z,J,q,nt,rt,_,tt,et,lt,mt,gt=0,xt="";if(!T(B)){if(Z=$(B),Z&&(J=A(p(R(B))),!~F(J,"g")))throw new M("`.replaceAll` does not allow non-global regexes");if(q=E(B,D),q)return d(q,B,Y,V);if(C&&Z)return z(A(Y),B,V)}for(nt=A(Y),rt=A(B),_=y(V),_||(V=A(V)),tt=rt.length,et=j(1,tt),lt=F(nt,rt);lt!==-1;)mt=_?A(V(rt,lt,nt)):I(rt,nt,lt,[],void 0,V),xt+=U(nt,gt,lt)+mt,gt=lt+tt,lt=lt+et>nt.length?-1:F(nt,rt,lt+et);return gt1||"".split(/.?/).length;h("split",function(V,Y,Z){var J="0".split(void 0,0).length?function(q,nt){return q===void 0&&nt===0?[]:u(Y,this,q,nt)}:Y;return[function(nt,rt){var _=T(this),tt=y(nt)?void 0:I(nt,V);return tt?u(tt,nt,_,rt):u(J,R(_),nt,rt)},function(q,nt){var rt=p(this),_=R(q);if(!B){var tt=Z(J,rt,_,nt,J!==Y);if(tt.done)return tt.value}var et=$(rt,RegExp),lt=rt.unicode,mt=(rt.ignoreCase?"i":"")+(rt.multiline?"m":"")+(rt.unicode?"u":"")+(M?"g":"y"),gt=new et(M?"^(?:"+rt.source+")":rt,mt),xt=nt===void 0?F:nt>>>0;if(xt===0)return[];if(_.length===0)return O(gt,_)===null?[_]:[];for(var yt=0,Ut=0,Dt=[];Ut<_.length;){gt.lastIndex=M?0:Ut;var Xt=O(gt,M?j(_,Ut):_),Qt;if(Xt===null||(Qt=z(E(gt.lastIndex+(M?Ut:0)),_.length))===yt)Ut=A(_,Ut,lt);else{if(U(Dt,j(_,yt,Ut)),Dt.length===xt)return Dt;for(var kt=1;kt<=Xt.length-1;kt++)if(U(Dt,Xt[kt]),Dt.length===xt)return Dt;Ut=yt=Qt}}return U(Dt,j(_,yt)),Dt}]},B||!G,M)},function(x,b,r){var u=r(3),d=r(85),h=r(5).f,p=r(64),y=r(68),T=r(450),$=r(16),A=r(451),E=r(36),R=d("".slice),I=Math.min,O=A("startsWith"),C=!E&&!O&&!!function(){var D=h(String.prototype,"startsWith");return D&&!D.writable}();u({target:"String",proto:!0,forced:!C&&!O},{startsWith:function(M){var F=y($(this));T(M);var z=p(I(arguments.length>1?arguments[1]:void 0,F.length)),U=y(M);return R(F,z,z+U.length)===U}})},function(x,b,r){var u=r(3),d=r(14),h=r(16),p=r(61),y=r(68),T=d("".slice),$=Math.max,A=Math.min,E=!"".substr||"ab".substr(-1)!=="b";u({target:"String",proto:!0,forced:E},{substr:function(I,O){var C=y(h(this)),D=C.length,M=p(I),F,z;return M===1/0&&(M=0),M<0&&(M=$(D+M,0)),F=O===void 0?D:p(O),F<=0||F===1/0?"":(z=A(M+F,D),M>=z?"":T(C,M,z))}})},function(x,b,r){var u=r(3),d=r(8),h=r(14),p=r(16),y=r(68),T=r(7),$=Array,A=h("".charAt),E=h("".charCodeAt),R=h([].join),I="".toWellFormed,O="\uFFFD",C=I&&T(function(){return d(I,1)!=="1"});u({target:"String",proto:!0,forced:C},{toWellFormed:function(){var M=y(p(this));if(C)return d(I,M);for(var F=M.length,z=$(F),U=0;U=56320||U+1>=F||(E(M,U+1)&64512)!==56320?z[U]=O:(z[U]=A(M,U),z[++U]=A(M,U))}return R(z,"")}})},function(x,b,r){var u=r(3),d=r(309).trim,h=r(475);u({target:"String",proto:!0,forced:h("trim")},{trim:function(){return d(this)}})},function(x,b,r){var u=r(49).PROPER,d=r(7),h=r(310),p="\u200B\x85\u180E";x.exports=function(y){return d(function(){return!!h[y]()||p[y]()!==p||u&&h[y].name!==y})}},function(x,b,r){r(477);var u=r(3),d=r(478);u({target:"String",proto:!0,name:"trimEnd",forced:"".trimEnd!==d},{trimEnd:d})},function(x,b,r){var u=r(3),d=r(478);u({target:"String",proto:!0,name:"trimEnd",forced:"".trimRight!==d},{trimRight:d})},function(x,b,r){var u=r(309).end,d=r(475);x.exports=d("trimEnd")?function(){return u(this)}:"".trimEnd},function(x,b,r){r(480);var u=r(3),d=r(481);u({target:"String",proto:!0,name:"trimStart",forced:"".trimStart!==d},{trimStart:d})},function(x,b,r){var u=r(3),d=r(481);u({target:"String",proto:!0,name:"trimStart",forced:"".trimLeft!==d},{trimLeft:d})},function(x,b,r){var u=r(309).start,d=r(475);x.exports=d("trimStart")?function(){return u(this)}:"".trimStart},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("anchor")},{anchor:function(y){return d(this,"a","name",y)}})},function(x,b,r){var u=r(14),d=r(16),h=r(68),p=/"/g,y=u("".replace);x.exports=function(T,$,A,E){var R=h(d(T)),I="<"+$;return A!==""&&(I+=" "+A+'="'+y(h(E),p,""")+'"'),I+">"+R+""}},function(x,b,r){var u=r(7);x.exports=function(d){return u(function(){var h=""[d]('"');return h!==h.toLowerCase()||h.split('"').length>3})}},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("big")},{big:function(){return d(this,"big","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("blink")},{blink:function(){return d(this,"blink","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("bold")},{bold:function(){return d(this,"b","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("fixed")},{fixed:function(){return d(this,"tt","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("fontcolor")},{fontcolor:function(y){return d(this,"font","color",y)}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("fontsize")},{fontsize:function(y){return d(this,"font","size",y)}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("italics")},{italics:function(){return d(this,"i","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("link")},{link:function(y){return d(this,"a","href",y)}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("small")},{small:function(){return d(this,"small","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("strike")},{strike:function(){return d(this,"strike","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("sub")},{sub:function(){return d(this,"sub","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("sup")},{sup:function(){return d(this,"sup","","")}})},function(x,b,r){var u=r(498);u("Float32",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(3),d=r(4),h=r(8),p=r(6),y=r(499),T=r(219),$=r(208),A=r(211),E=r(11),R=r(43),I=r(315),O=r(64),C=r(212),D=r(500),M=r(501),F=r(18),z=r(38),U=r(69),j=r(20),G=r(22),B=r(71),V=r(24),Y=r(113),Z=r(57).f,J=r(502),q=r(83).forEach,nt=r(194),rt=r(77),_=r(44),tt=r(5),et=r(199),lt=r(51),mt=r(118),gt=lt.get,xt=lt.set,yt=lt.enforce,Ut=_.f,Dt=tt.f,Xt=d.RangeError,Qt=$.ArrayBuffer,kt=Qt.prototype,me=$.DataView,ge=T.NATIVE_ARRAY_BUFFER_VIEWS,ae=T.TYPED_ARRAY_TAG,Mt=T.TypedArray,Ht=T.TypedArrayPrototype,re=T.isTypedArray,se="BYTES_PER_ELEMENT",ee="Wrong length",fe=function(Ae,Te){rt(Ae,Te,{configurable:!0,get:function(){return gt(this)[Te]}})},Pe=function(Ae){var Te;return V(kt,Ae)||(Te=U(Ae))==="ArrayBuffer"||Te==="SharedArrayBuffer"},Me=function(Ae,Te){return re(Ae)&&!G(Te)&&Te in Ae&&I(+Te)&&Te>=0},$e=function(Te,de){return de=F(de),Me(Te,de)?E(2,Te[de]):Dt(Te,de)},ce=function(Te,de,bt){return de=F(de),Me(Te,de)&&j(bt)&&z(bt,"value")&&!z(bt,"get")&&!z(bt,"set")&&!bt.configurable&&(!z(bt,"writable")||bt.writable)&&(!z(bt,"enumerable")||bt.enumerable)?(Te[de]=bt.value,Te):Ut(Te,de,bt)};p?(ge||(tt.f=$e,_.f=ce,fe(Ht,"buffer"),fe(Ht,"byteOffset"),fe(Ht,"byteLength"),fe(Ht,"length")),u({target:"Object",stat:!0,forced:!ge},{getOwnPropertyDescriptor:$e,defineProperty:ce}),x.exports=function(Ae,Te,de){var bt=Ae.match(/\d+/)[0]/8,Ft=Ae+(de?"Clamped":"")+"Array",Tt="get"+Ae,qt="set"+Ae,te=d[Ft],Zt=te,Yt=Zt&&Zt.prototype,Ye={},Ze=function(Ct,Nt){var Et=gt(Ct);return Et.view[Tt](Nt*bt+Et.byteOffset,!0)},ut=function(Ct,Nt,Et){var ie=gt(Ct);ie.view[qt](Nt*bt+ie.byteOffset,de?M(Et):Et,!0)},It=function(Ct,Nt){Ut(Ct,Nt,{get:function(){return Ze(this,Nt)},set:function(Et){return ut(this,Nt,Et)},enumerable:!0})};ge?y&&(Zt=Te(function(Ct,Nt,Et,ie){return A(Ct,Yt),mt(function(){return j(Nt)?Pe(Nt)?ie!==void 0?new te(Nt,D(Et,bt),ie):Et!==void 0?new te(Nt,D(Et,bt)):new te(Nt):re(Nt)?et(Zt,Nt):h(J,Zt,Nt):new te(C(Nt))}(),Ct,Zt)}),Y&&Y(Zt,Mt),q(Z(te),function(Ct){Ct in Zt||R(Zt,Ct,te[Ct])}),Zt.prototype=Yt):(Zt=Te(function(Ct,Nt,Et,ie){A(Ct,Yt);var we=0,Rt=0,zt,jt,Wt;if(!j(Nt))Wt=C(Nt),jt=Wt*bt,zt=new Qt(jt);else if(Pe(Nt)){zt=Nt,Rt=D(Et,bt);var ue=Nt.byteLength;if(ie===void 0){if(ue%bt)throw new Xt(ee);if(jt=ue-Rt,jt<0)throw new Xt(ee)}else if(jt=O(ie)*bt,jt+Rt>ue)throw new Xt(ee);Wt=jt/bt}else return re(Nt)?et(Zt,Nt):h(J,Zt,Nt);for(xt(Ct,{buffer:zt,byteOffset:Rt,byteLength:jt,length:Wt,view:new me(zt)});we255?255:u&255}},function(x,b,r){var u=r(84),d=r(8),h=r(365),p=r(39),y=r(63),T=r(133),$=r(134),A=r(131),E=r(503),R=r(219).aTypedArrayConstructor,I=r(504);x.exports=function(C){var D=h(this),M=p(C),F=arguments.length,z=F>1?arguments[1]:void 0,U=z!==void 0,j=$(M),G,B,V,Y,Z,J,q,nt;if(j&&!A(j))for(q=T(M,j),nt=q.next,M=[];!(J=d(nt,q)).done;)M.push(J.value);for(U&&F>2&&(z=u(z,arguments[2])),B=y(M),V=new(R(D))(B),Y=E(V),G=0;B>G;G++)Z=U?z(M[G],G):M[G],V[G]=Y?I(Z):+Z;return V}},function(x,b,r){var u=r(69);x.exports=function(d){var h=u(d);return h==="BigInt64Array"||h==="BigUint64Array"}},function(x,b,r){var u=r(19),d=TypeError;x.exports=function(h){var p=u(h,"number");if(typeof p=="number")throw new d("Can't convert number to bigint");return BigInt(p)}},function(x,b,r){var u=r(498);u("Float64",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Int8",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Int16",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Int32",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Uint8",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Uint8",function(d){return function(p,y,T){return d(this,p,y,T)}},!0)},function(x,b,r){var u=r(498);u("Uint16",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Uint32",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(219),d=r(63),h=r(61),p=u.aTypedArray,y=u.exportTypedArrayMethod;y("at",function($){var A=p(this),E=d(A),R=h($),I=R>=0?R:E+R;return I<0||I>=E?void 0:A[I]})},function(x,b,r){var u=r(14),d=r(219),h=r(144),p=u(h),y=d.aTypedArray,T=d.exportTypedArrayMethod;T("copyWithin",function(A,E){return p(y(this),A,E,arguments.length>2?arguments[2]:void 0)})},function(x,b,r){var u=r(219),d=r(83).every,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("every",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(149),h=r(504),p=r(69),y=r(8),T=r(14),$=r(7),A=u.aTypedArray,E=u.exportTypedArrayMethod,R=T("".slice),I=$(function(){var O=0;return new Int8Array(2).fill({valueOf:function(){return O++}}),O!==1});E("fill",function(C){var D=arguments.length;A(this);var M=R(p(this),0,3)==="Big"?h(C):+C;return y(d,this,M,D>1?arguments[1]:void 0,D>2?arguments[2]:void 0)},I)},function(x,b,r){var u=r(219),d=r(83).filter,h=r(518),p=u.aTypedArray,y=u.exportTypedArrayMethod;y("filter",function($){var A=d(p(this),$,arguments.length>1?arguments[1]:void 0);return h(this,A)})},function(x,b,r){var u=r(199),d=r(219).getTypedArrayConstructor;x.exports=function(h,p){return u(d(h),p)}},function(x,b,r){var u=r(219),d=r(83).find,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("find",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(83).findIndex,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("findIndex",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(154).findLast,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("findLast",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(154).findLastIndex,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("findLastIndex",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(83).forEach,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("forEach",function(T){d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(499),d=r(219).exportTypedArrayStaticMethod,h=r(502);d("from",h,u)},function(x,b,r){var u=r(219),d=r(59).includes,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("includes",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(59).indexOf,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("indexOf",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(4),d=r(7),h=r(14),p=r(219),y=r(168),T=r(33),$=T("iterator"),A=u.Uint8Array,E=h(y.values),R=h(y.keys),I=h(y.entries),O=p.aTypedArray,C=p.exportTypedArrayMethod,D=A&&A.prototype,M=!d(function(){D[$].call([1])}),F=!!D&&D.values&&D[$]===D.values&&D.values.name==="values",z=function(){return E(O(this))};C("entries",function(){return I(O(this))},M),C("keys",function(){return R(O(this))},M),C("values",z,M||!F,{name:"values"}),C($,z,M||!F,{name:"values"})},function(x,b,r){var u=r(219),d=r(14),h=u.aTypedArray,p=u.exportTypedArrayMethod,y=d([].join);p("join",function($){return y(h(this),$)})},function(x,b,r){var u=r(219),d=r(94),h=r(175),p=u.aTypedArray,y=u.exportTypedArrayMethod;y("lastIndexOf",function($){var A=arguments.length;return d(h,p(this),A>1?[$,arguments[1]]:[$])})},function(x,b,r){var u=r(219),d=r(83).map,h=u.aTypedArray,p=u.getTypedArrayConstructor,y=u.exportTypedArrayMethod;y("map",function($){return d(h(this),$,arguments.length>1?arguments[1]:void 0,function(A,E){return new(p(A))(E)})})},function(x,b,r){var u=r(219),d=r(499),h=u.aTypedArrayConstructor,p=u.exportTypedArrayStaticMethod;p("of",function(){for(var T=0,$=arguments.length,A=new(h(this))($);$>T;)A[T]=arguments[T++];return A},d)},function(x,b,r){var u=r(219),d=r(181).left,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("reduce",function(T){var $=arguments.length;return d(h(this),T,$,$>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(181).right,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("reduceRight",function(T){var $=arguments.length;return d(h(this),T,$,$>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=u.aTypedArray,h=u.exportTypedArrayMethod,p=Math.floor;h("reverse",function(){for(var T=this,$=d(T).length,A=p($/2),E=0,R;E1?arguments[1]:void 0,1),j=T(z);if(D)return d(I,this,j,U);var G=this.length,B=p(j),V=0;if(B+U>G)throw new A("Wrong length");for(;VC;)M[C]=I[C++];return M},$)},function(x,b,r){var u=r(219),d=r(83).some,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("some",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(4),d=r(85),h=r(7),p=r(30),y=r(189),T=r(219),$=r(190),A=r(191),E=r(27),R=r(192),I=T.aTypedArray,O=T.exportTypedArrayMethod,C=u.Uint16Array,D=C&&d(C.prototype.sort),M=!!D&&!(h(function(){D(new C(2),null)})&&h(function(){D(new C(2),{})})),F=!!D&&!h(function(){if(E)return E<74;if($)return $<67;if(A)return!0;if(R)return R<602;var U=new C(516),j=Array(516),G,B;for(G=0;G<516;G++)B=G%4,U[G]=515-G,j[G]=G-2*B+3;for(D(U,function(V,Y){return(V/4|0)-(Y/4|0)}),G=0;G<516;G++)if(U[G]!==j[G])return!0}),z=function(U){return function(j,G){return U!==void 0?+U(j,G)||0:G!==G?-1:j!==j?1:j===0&&G===0?1/j>0&&1/G<0?1:-1:j>G}};O("sort",function(j){return j!==void 0&&p(j),F?D(this,j):y(I(this),z(j))},!F||M)},function(x,b,r){var u=r(219),d=r(64),h=r(60),p=u.aTypedArray,y=u.getTypedArrayConstructor,T=u.exportTypedArrayMethod;T("subarray",function(A,E){var R=p(this),I=R.length,O=h(A,I),C=y(R);return new C(R.buffer,R.byteOffset+O*R.BYTES_PER_ELEMENT,d((E===void 0?I:h(E,I))-O))})},function(x,b,r){var u=r(4),d=r(94),h=r(219),p=r(7),y=r(76),T=u.Int8Array,$=h.aTypedArray,A=h.exportTypedArrayMethod,E=[].toLocaleString,R=!!T&&p(function(){E.call(new T(1))}),I=p(function(){return[1,2].toLocaleString()!==new T([1,2]).toLocaleString()})||!p(function(){T.prototype.toLocaleString.call([1,2])});A("toLocaleString",function(){return d(E,R?y($(this)):$(this),y(arguments))},I)},function(x,b,r){var u=r(197),d=r(219),h=d.aTypedArray,p=d.exportTypedArrayMethod,y=d.getTypedArrayConstructor;p("toReversed",function(){return u(h(this),y(this))})},function(x,b,r){var u=r(219),d=r(14),h=r(30),p=r(199),y=u.aTypedArray,T=u.getTypedArrayConstructor,$=u.exportTypedArrayMethod,A=d(u.TypedArrayPrototype.sort);$("toSorted",function(R){R!==void 0&&h(R);var I=y(this),O=p(T(I),I);return A(O,R)})},function(x,b,r){var u=r(219).exportTypedArrayMethod,d=r(7),h=r(4),p=r(14),y=h.Uint8Array,T=y&&y.prototype||{},$=[].toString,A=p([].join);d(function(){$.call({})})&&($=function(){return A(this)});var E=T.toString!==$;u("toString",$,E)},function(x,b,r){var u=r(206),d=r(219),h=r(503),p=r(61),y=r(504),T=d.aTypedArray,$=d.getTypedArrayConstructor,A=d.exportTypedArrayMethod,E=!!function(){try{new Int8Array(1).with(2,{valueOf:function(){throw 8}})}catch(R){return R===8}}();A("with",function(R,I){var O=T(this),C=p(R),D=h(O)?y(I):+I;return u(O,$(O),C,D)},!E)},function(x,b,r){var u=r(3),d=r(14),h=r(68),p=String.fromCharCode,y=d("".charAt),T=d(/./.exec),$=d("".slice),A=/^[\da-f]{2}$/i,E=/^[\da-f]{4}$/i;u({global:!0},{unescape:function(I){for(var O=h(I),C="",D=O.length,M=0,F,z;M>(-2*_&6)));return nt}})},function(x){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",r=b+"+/",u=b+"-_",d=function(h){for(var p={},y=0;y<64;y++)p[h.charAt(y)]=y;return p};x.exports={i2c:r,c2i:d(r),i2cUrl:u,c2iUrl:d(u)}},function(x,b,r){var u=r(3),d=r(4),h=r(23),p=r(14),y=r(8),T=r(7),$=r(68),A=r(367),E=r(552).i2c,R=h("btoa"),I=p("".charAt),O=p("".charCodeAt),C=!!R&&!T(function(){return R("hi")!=="aGk="}),D=C&&!T(function(){R()}),M=C&&T(function(){return R(null)!=="bnVsbA=="}),F=C&&R.length!==1;u({global:!0,bind:!0,enumerable:!0,forced:!C||D||M||F},{btoa:function(U){if(A(arguments.length,1),C)return y(R,d,$(U));for(var j=$(U),G="",B=0,V=E,Y,Z;I(j,B)||(V="=",B%1);){if(Z=O(j,B+=.75),Z>255)throw new(h("DOMException"))("The string contains characters outside of the Latin1 range","InvalidCharacterError");Y=Y<<8|Z,G+=I(V,63&Y>>8-B%1*8)}return G}})},function(x,b,r){var u=r(4),d=r(555),h=r(556),p=r(160),y=r(43),T=function(A){if(A&&A.forEach!==p)try{y(A,"forEach",p)}catch(E){A.forEach=p}};for(var $ in d)d[$]&&T(u[$]&&u[$].prototype);T(h)},function(x){x.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},function(x,b,r){var u=r(42),d=u("span").classList,h=d&&d.constructor&&d.constructor.prototype;x.exports=h===Object.prototype?void 0:h},function(x,b,r){var u=r(4),d=r(555),h=r(556),p=r(168),y=r(43),T=r(82),$=r(33),A=$("iterator"),E=p.values,R=function(O,C){if(O){if(O[A]!==E)try{y(O,A,E)}catch(M){O[A]=E}if(T(O,C,!0),d[C]){for(var D in p)if(O[D]!==p[D])try{y(O,D,p[D])}catch(M){O[D]=p[D]}}}};for(var I in d)R(u[I]&&u[I].prototype,I);R(h,"DOMTokenList")},function(x,b,r){var u=r(3),d=r(23),h=r(234),p=r(7),y=r(71),T=r(11),$=r(44).f,A=r(47),E=r(77),R=r(38),I=r(211),O=r(46),C=r(125),D=r(119),M=r(559),F=r(122),z=r(51),U=r(6),j=r(36),G="DOMException",B="DATA_CLONE_ERR",V=d("Error"),Y=d(G)||function(){try{var Mt=d("MessageChannel")||h("worker_threads").MessageChannel;new Mt().port1.postMessage(new WeakMap)}catch(Ht){if(Ht.name===B&&Ht.code===25)return Ht.constructor}}(),Z=Y&&Y.prototype,J=V.prototype,q=z.set,nt=z.getterFor(G),rt="stack"in new V(G),_=function(Mt){return R(M,Mt)&&M[Mt].m?M[Mt].c:0},tt=function(){I(this,et);var Ht=arguments.length,re=D(Ht<1?void 0:arguments[0]),se=D(Ht<2?void 0:arguments[1],"Error"),ee=_(se);if(q(this,{type:G,name:se,message:re,code:ee}),U||(this.name=se,this.message=re,this.code=ee),rt){var fe=new V(re);fe.name=G,$(this,"stack",T(1,F(fe.stack,1)))}},et=tt.prototype=y(J),lt=function(Mt){return{enumerable:!0,configurable:!0,get:Mt}},mt=function(Mt){return lt(function(){return nt(this)[Mt]})};U&&(E(et,"code",mt("code")),E(et,"message",mt("message")),E(et,"name",mt("name"))),$(et,"constructor",T(1,tt));var gt=p(function(){return!(new Y instanceof V)}),xt=gt||p(function(){return J.toString!==C||String(new Y(1,2))!=="2: 1"}),yt=gt||p(function(){return new Y(1,"DataCloneError").code!==25}),Ut=gt||Y[B]!==25||Z[B]!==25,Dt=j?xt||yt||Ut:gt;u({global:!0,constructor:!0,forced:Dt},{DOMException:Dt?tt:Y});var Xt=d(G),Qt=Xt.prototype;xt&&(j||Y===Xt)&&A(Qt,"toString",C),yt&&U&&Y===Xt&&E(Qt,"code",lt(function(){return _(O(this).name)}));for(var kt in M)if(R(M,kt)){var me=M[kt],ge=me.s,ae=T(6,me.c);R(Xt,ge)||$(Xt,ge,ae),R(Qt,ge)||$(Qt,ge,ae)}},function(x){x.exports={IndexSizeError:{s:"INDEX_SIZE_ERR",c:1,m:1},DOMStringSizeError:{s:"DOMSTRING_SIZE_ERR",c:2,m:0},HierarchyRequestError:{s:"HIERARCHY_REQUEST_ERR",c:3,m:1},WrongDocumentError:{s:"WRONG_DOCUMENT_ERR",c:4,m:1},InvalidCharacterError:{s:"INVALID_CHARACTER_ERR",c:5,m:1},NoDataAllowedError:{s:"NO_DATA_ALLOWED_ERR",c:6,m:0},NoModificationAllowedError:{s:"NO_MODIFICATION_ALLOWED_ERR",c:7,m:1},NotFoundError:{s:"NOT_FOUND_ERR",c:8,m:1},NotSupportedError:{s:"NOT_SUPPORTED_ERR",c:9,m:1},InUseAttributeError:{s:"INUSE_ATTRIBUTE_ERR",c:10,m:1},InvalidStateError:{s:"INVALID_STATE_ERR",c:11,m:1},SyntaxError:{s:"SYNTAX_ERR",c:12,m:1},InvalidModificationError:{s:"INVALID_MODIFICATION_ERR",c:13,m:1},NamespaceError:{s:"NAMESPACE_ERR",c:14,m:1},InvalidAccessError:{s:"INVALID_ACCESS_ERR",c:15,m:1},ValidationError:{s:"VALIDATION_ERR",c:16,m:0},TypeMismatchError:{s:"TYPE_MISMATCH_ERR",c:17,m:1},SecurityError:{s:"SECURITY_ERR",c:18,m:1},NetworkError:{s:"NETWORK_ERR",c:19,m:1},AbortError:{s:"ABORT_ERR",c:20,m:1},URLMismatchError:{s:"URL_MISMATCH_ERR",c:21,m:1},QuotaExceededError:{s:"QUOTA_EXCEEDED_ERR",c:22,m:1},TimeoutError:{s:"TIMEOUT_ERR",c:23,m:1},InvalidNodeTypeError:{s:"INVALID_NODE_TYPE_ERR",c:24,m:1},DataCloneError:{s:"DATA_CLONE_ERR",c:25,m:1}}},function(x,b,r){var u=r(3),d=r(4),h=r(23),p=r(11),y=r(44).f,T=r(38),$=r(211),A=r(118),E=r(119),R=r(559),I=r(122),O=r(6),C=r(36),D="DOMException",M=h("Error"),F=h(D),z=function(){$(this,U);var tt=arguments.length,et=E(tt<1?void 0:arguments[0]),lt=E(tt<2?void 0:arguments[1],"Error"),mt=new F(et,lt),gt=new M(et);return gt.name=D,y(mt,"stack",p(1,I(gt.stack,1))),A(mt,this,z),mt},U=z.prototype=F.prototype,j="stack"in new M(D),G="stack"in new F(1,2),B=F&&O&&Object.getOwnPropertyDescriptor(d,D),V=!!B&&!(B.writable&&B.configurable),Y=j&&!V&&!G;u({global:!0,constructor:!0,forced:C||Y},{DOMException:Y?z:F});var Z=h(D),J=Z.prototype;if(J.constructor!==Z){C||y(J,"constructor",p(1,Z));for(var q in R)if(T(R,q)){var nt=R[q],rt=nt.s;T(Z,rt)||y(Z,rt,p(6,nt.c))}}},function(x,b,r){var u=r(23),d=r(82),h="DOMException";d(u(h),h)},function(x,b,r){r(563),r(564)},function(x,b,r){var u=r(3),d=r(4),h=r(366).clear;u({global:!0,bind:!0,enumerable:!0,forced:d.clearImmediate!==h},{clearImmediate:h})},function(x,b,r){var u=r(3),d=r(4),h=r(366).set,p=r(565),y=d.setImmediate?p(h,!1):h;u({global:!0,bind:!0,enumerable:!0,forced:d.setImmediate!==y},{setImmediate:y})},function(x,b,r){var u=r(4),d=r(94),h=r(21),p=r(183),y=r(28),T=r(76),$=r(367),A=u.Function,E=/MSIE .\./.test(y)||p==="BUN"&&function(){var R=u.Bun.version.split(".");return R.length<3||R[0]==="0"&&(R[1]<3||R[1]==="3"&&R[2]==="0")}();x.exports=function(R,I){var O=I?2:1;return E?function(C,D){var M=$(arguments.length,1)>O,F=h(C)?C:A(C),z=M?T(arguments,O):[],U=M?function(){d(F,this,z)}:F;return I?R(U,D):R(U)}:R}},function(x,b,r){var u=r(3),d=r(4),h=r(369),p=r(30),y=r(367),T=r(7),$=r(6),A=T(function(){return $&&Object.getOwnPropertyDescriptor(d,"queueMicrotask").value.length!==1});u({global:!0,enumerable:!0,dontCallGetSet:!0,forced:A},{queueMicrotask:function(R){y(arguments.length,1),h(p(R))}})},function(x,b,r){var u=r(3),d=r(4),h=r(77),p=r(6),y=TypeError,T=Object.defineProperty,$=d.self!==d;try{if(p){var A=Object.getOwnPropertyDescriptor(d,"self");($||!A||!A.get||!A.enumerable)&&h(d,"self",{get:function(){return d},set:function(R){if(this!==d)throw new y("Illegal invocation");T(d,"self",{value:R,writable:!0,configurable:!0,enumerable:!0})},configurable:!0,enumerable:!0})}else u({global:!0,simple:!0,forced:$},{self:d})}catch(E){}},function(x,b,r){var u=r(36),d=r(3),h=r(4),p=r(23),y=r(14),T=r(7),$=r(40),A=r(21),E=r(89),R=r(17),I=r(20),O=r(22),C=r(130),D=r(46),M=r(69),F=r(38),z=r(141),U=r(43),j=r(63),G=r(367),B=r(408),V=r(284),Y=r(427),Z=r(429),J=r(233),q=r(123),nt=r(235),rt=h.Object,_=h.Array,tt=h.Date,et=h.Error,lt=h.TypeError,mt=h.PerformanceMark,gt=p("DOMException"),xt=V.Map,yt=V.has,Ut=V.get,Dt=V.set,Xt=Y.Set,Qt=Y.add,kt=Y.has,me=p("Object","keys"),ge=y([].push),ae=y((!0).valueOf),Mt=y(1 .valueOf),Ht=y("".valueOf),re=y(tt.prototype.getTime),se=$("structuredClone"),ee="DataCloneError",fe="Transferring",Pe=function(ut){return!T(function(){var It=new h.Set([7]),Pt=ut(It),Ct=ut(rt(7));return Pt===It||!Pt.has(7)||!I(Ct)||+Ct!=7})&&ut},Me=function(ut,It){return!T(function(){var Pt=new It,Ct=ut({a:Pt,b:Pt});return!(Ct&&Ct.a===Ct.b&&Ct.a instanceof It&&Ct.a.stack===Pt.stack)})},$e=function(ut){return!T(function(){var It=ut(new h.AggregateError([1],se,{cause:3}));return It.name!=="AggregateError"||It.errors[0]!==1||It.message!==se||It.cause!==3})},ce=h.structuredClone,Ae=u||!Me(ce,et)||!Me(ce,gt)||!$e(ce),Te=!ce&&Pe(function(ut){return new mt(se,{detail:ut}).detail}),de=Pe(ce)||Te,bt=function(ut){throw new gt("Uncloneable type: "+ut,ee)},Ft=function(ut,It){throw new gt((It||"Cloning")+" of "+ut+" cannot be properly polyfilled in this engine",ee)},Tt=function(ut,It){return de||Ft(It),de(ut)},qt=function(){var ut;try{ut=new h.DataTransfer}catch(It){try{ut=new h.ClipboardEvent("").clipboardData}catch(Pt){}}return ut&&ut.items&&ut.files?ut:null},te=function(ut,It,Pt){if(yt(It,ut))return Ut(It,ut);var Ct=Pt||M(ut),Nt,Et,ie,we,Rt,zt;if(Ct==="SharedArrayBuffer")de?Nt=de(ut):Nt=ut;else{var jt=h.DataView;!jt&&!A(ut.slice)&&Ft("ArrayBuffer");try{if(A(ut.slice)&&!ut.resizable)Nt=ut.slice(0);else for(Et=ut.byteLength,ie=("maxByteLength"in ut)?{maxByteLength:ut.maxByteLength}:void 0,Nt=new ArrayBuffer(Et,ie),we=new jt(ut),Rt=new jt(Nt),zt=0;zt1&&!R(arguments[1])?D(arguments[1]):void 0,Ct=Pt?Pt.transfer:void 0,Nt,Et;Ct!==void 0&&(Nt=new xt,Et=Ye(Ct,Nt));var ie=Yt(It,Nt);return Et&&Ze(Et),ie}})},function(x,b,r){r(570),r(571)},function(x,b,r){var u=r(3),d=r(4),h=r(565),p=h(d.setInterval,!0);u({global:!0,bind:!0,forced:d.setInterval!==p},{setInterval:p})},function(x,b,r){var u=r(3),d=r(4),h=r(565),p=h(d.setTimeout,!0);u({global:!0,bind:!0,forced:d.setTimeout!==p},{setTimeout:p})},function(x,b,r){r(573)},function(x,b,r){r(455);var u=r(3),d=r(6),h=r(574),p=r(4),y=r(84),T=r(14),$=r(47),A=r(77),E=r(211),R=r(38),I=r(328),O=r(162),C=r(76),D=r(448).codeAt,M=r(575),F=r(68),z=r(82),U=r(367),j=r(576),G=r(51),B=G.set,V=G.getterFor("URL"),Y=j.URLSearchParams,Z=j.getState,J=p.URL,q=p.TypeError,nt=p.parseInt,rt=Math.floor,_=Math.pow,tt=T("".charAt),et=T(/./.exec),lt=T([].join),mt=T(1 .toString),gt=T([].pop),xt=T([].push),yt=T("".replace),Ut=T([].shift),Dt=T("".split),Xt=T("".slice),Qt=T("".toLowerCase),kt=T([].unshift),me="Invalid authority",ge="Invalid scheme",ae="Invalid host",Mt="Invalid port",Ht=/[a-z]/i,re=/[\d+-.a-z]/i,se=/\d/,ee=/^0x/i,fe=/^[0-7]+$/,Pe=/^\d+$/,Me=/^[\da-f]+$/i,$e=/[\0\t\n\r #%/:<>?@[\\\]^|]/,ce=/[\0\t\n\r #/:<>?@[\\\]^|]/,Ae=/^[\u0000-\u0020]+/,Te=/(^|[^\u0000-\u0020])[\u0000-\u0020]+$/,de=/[\t\n\r]/g,bt,Ft=function(ft){var wt=Dt(ft,"."),pt,it,Ot,ye,_t,Ie,rn;if(wt.length&&wt[wt.length-1]===""&&wt.length--,pt=wt.length,pt>4)return ft;for(it=[],Ot=0;Ot1&&tt(ye,0)==="0"&&(_t=et(ee,ye)?16:8,ye=Xt(ye,_t===8?1:2)),ye==="")Ie=0;else{if(!et(_t===10?Pe:_t===8?fe:Me,ye))return ft;Ie=nt(ye,_t)}xt(it,Ie)}for(Ot=0;Ot=_(256,5-pt))return null}else if(Ie>255)return null;for(rn=gt(it),Ot=0;Ot6))return;for(Ie=0;an();){if(rn=null,Ie>0)if(an()==="."&&Ie<4)Ot++;else return;if(!et(se,an()))return;for(;et(se,an());){if(hn=nt(an(),10),rn===null)rn=hn;else{if(rn===0)return;rn=rn*10+hn}if(rn>255)return;Ot++}wt[pt]=wt[pt]*256+rn,Ie++,(Ie===2||Ie===4)&&pt++}if(Ie!==4)return;break}else if(an()===":"){if(Ot++,!an())return}else if(an())return;wt[pt++]=ye}if(it!==null)for(pn=pt-it,pt=7;pt!==0&&pn>0;)ot=wt[pt],wt[pt--]=wt[it+pn-1],wt[it+--pn]=ot;else if(pt!==8)return;return wt},qt=function(ft){for(var wt=null,pt=1,it=null,Ot=0,ye=0;ye<8;ye++)ft[ye]!==0?(Ot>pt&&(wt=it,pt=Ot),it=null,Ot=0):(it===null&&(it=ye),++Ot);return Ot>pt?it:wt},te=function(ft){var wt,pt,it,Ot;if(typeof ft=="number"){for(wt=[],pt=0;pt<4;pt++)kt(wt,ft%256),ft=rt(ft/256);return lt(wt,".")}if(typeof ft=="object"){for(wt="",it=qt(ft),pt=0;pt<8;pt++)Ot&&ft[pt]===0||(Ot&&(Ot=!1),it===pt?(wt+=pt?":":"::",Ot=!0):(wt+=mt(ft[pt],16),pt<7&&(wt+=":")));return"["+wt+"]"}return ft},Zt={},Yt=I({},Zt,{" ":1,'"':1,"<":1,">":1,"`":1}),Ye=I({},Yt,{"#":1,"?":1,"{":1,"}":1}),Ze=I({},Ye,{"/":1,":":1,";":1,"=":1,"@":1,"[":1,"\\":1,"]":1,"^":1,"|":1}),ut=function(ft,wt){var pt=D(ft,0);return pt>32&&pt<127&&!R(wt,ft)?ft:encodeURIComponent(ft)},It={ftp:21,file:null,http:80,https:443,ws:80,wss:443},Pt=function(ft,wt){var pt;return ft.length===2&&et(Ht,tt(ft,0))&&((pt=tt(ft,1))===":"||!wt&&pt==="|")},Ct=function(ft){var wt;return ft.length>1&&Pt(Xt(ft,0,2))&&(ft.length===2||(wt=tt(ft,2))==="/"||wt==="\\"||wt==="?"||wt==="#")},Nt=function(ft){return ft==="."||Qt(ft)==="%2e"},Et=function(ft){return ft=Qt(ft),ft===".."||ft==="%2e."||ft===".%2e"||ft==="%2e%2e"},ie={},we={},Rt={},zt={},jt={},Wt={},ue={},Ee={},Xe={},Je={},nn={},vn={},jn={},Hr={},Ya={},ga={},yr={},Vn={},Wa={},ir={},Wn={},va=function(ft,wt,pt){var it=F(ft),Ot,ye,_t;if(wt){if(ye=this.parse(it),ye)throw new q(ye);this.searchParams=null}else{if(pt!==void 0&&(Ot=new va(pt,!0)),ye=this.parse(it,null,Ot),ye)throw new q(ye);_t=Z(new Y),_t.bindURL(this),this.searchParams=_t}};va.prototype={type:"URL",parse:function(ft,wt,pt){var it=this,Ot=wt||ie,ye=0,_t="",Ie=!1,rn=!1,hn=!1,pn,ot,an,Fn;for(ft=F(ft),wt||(it.scheme="",it.username="",it.password="",it.host=null,it.port=null,it.path=[],it.query=null,it.fragment=null,it.cannotBeABaseURL=!1,ft=yt(ft,Ae,""),ft=yt(ft,Te,"$1")),ft=yt(ft,de,""),pn=O(ft);ye<=pn.length;){switch(ot=pn[ye],Ot){case ie:if(ot&&et(Ht,ot))_t+=Qt(ot),Ot=we;else{if(wt)return ge;Ot=Rt;continue}break;case we:if(ot&&(et(re,ot)||ot==="+"||ot==="-"||ot==="."))_t+=Qt(ot);else if(ot===":"){if(wt&&(it.isSpecial()!==R(It,_t)||_t==="file"&&(it.includesCredentials()||it.port!==null)||it.scheme==="file"&&!it.host))return;if(it.scheme=_t,wt){it.isSpecial()&&It[it.scheme]===it.port&&(it.port=null);return}_t="",it.scheme==="file"?Ot=Hr:it.isSpecial()&&pt&&pt.scheme===it.scheme?Ot=zt:it.isSpecial()?Ot=Ee:pn[ye+1]==="/"?(Ot=jt,ye++):(it.cannotBeABaseURL=!0,xt(it.path,""),Ot=Wa)}else{if(wt)return ge;_t="",Ot=Rt,ye=0;continue}break;case Rt:if(!pt||pt.cannotBeABaseURL&&ot!=="#")return ge;if(pt.cannotBeABaseURL&&ot==="#"){it.scheme=pt.scheme,it.path=C(pt.path),it.query=pt.query,it.fragment="",it.cannotBeABaseURL=!0,Ot=Wn;break}Ot=pt.scheme==="file"?Hr:Wt;continue;case zt:if(ot==="/"&&pn[ye+1]==="/")Ot=Xe,ye++;else{Ot=Wt;continue}break;case jt:if(ot==="/"){Ot=Je;break}else{Ot=Vn;continue}case Wt:if(it.scheme=pt.scheme,ot===bt)it.username=pt.username,it.password=pt.password,it.host=pt.host,it.port=pt.port,it.path=C(pt.path),it.query=pt.query;else if(ot==="/"||ot==="\\"&&it.isSpecial())Ot=ue;else if(ot==="?")it.username=pt.username,it.password=pt.password,it.host=pt.host,it.port=pt.port,it.path=C(pt.path),it.query="",Ot=ir;else if(ot==="#")it.username=pt.username,it.password=pt.password,it.host=pt.host,it.port=pt.port,it.path=C(pt.path),it.query=pt.query,it.fragment="",Ot=Wn;else{it.username=pt.username,it.password=pt.password,it.host=pt.host,it.port=pt.port,it.path=C(pt.path),it.path.length--,Ot=Vn;continue}break;case ue:if(it.isSpecial()&&(ot==="/"||ot==="\\"))Ot=Xe;else if(ot==="/")Ot=Je;else{it.username=pt.username,it.password=pt.password,it.host=pt.host,it.port=pt.port,Ot=Vn;continue}break;case Ee:if(Ot=Xe,ot!=="/"||tt(_t,ye+1)!=="/")continue;ye++;break;case Xe:if(ot!=="/"&&ot!=="\\"){Ot=Je;continue}break;case Je:if(ot==="@"){Ie&&(_t="%40"+_t),Ie=!0,an=O(_t);for(var en=0;en65535)return Mt;it.port=it.isSpecial()&&Bn===It[it.scheme]?null:Bn,_t=""}if(wt)return;Ot=yr;continue}else return Mt;break;case Hr:if(it.scheme="file",ot==="/"||ot==="\\")Ot=Ya;else if(pt&&pt.scheme==="file")switch(ot){case bt:it.host=pt.host,it.path=C(pt.path),it.query=pt.query;break;case"?":it.host=pt.host,it.path=C(pt.path),it.query="",Ot=ir;break;case"#":it.host=pt.host,it.path=C(pt.path),it.query=pt.query,it.fragment="",Ot=Wn;break;default:Ct(lt(C(pn,ye),""))||(it.host=pt.host,it.path=C(pt.path),it.shortenPath()),Ot=Vn;continue}else{Ot=Vn;continue}break;case Ya:if(ot==="/"||ot==="\\"){Ot=ga;break}pt&&pt.scheme==="file"&&!Ct(lt(C(pn,ye),""))&&(Pt(pt.path[0],!0)?xt(it.path,pt.path[0]):it.host=pt.host),Ot=Vn;continue;case ga:if(ot===bt||ot==="/"||ot==="\\"||ot==="?"||ot==="#"){if(!wt&&Pt(_t))Ot=Vn;else if(_t===""){if(it.host="",wt)return;Ot=yr}else{if(Fn=it.parseHost(_t),Fn)return Fn;if(it.host==="localhost"&&(it.host=""),wt)return;_t="",Ot=yr}continue}else _t+=ot;break;case yr:if(it.isSpecial()){if(Ot=Vn,ot!=="/"&&ot!=="\\")continue}else if(!wt&&ot==="?")it.query="",Ot=ir;else if(!wt&&ot==="#")it.fragment="",Ot=Wn;else if(ot!==bt&&(Ot=Vn,ot!=="/"))continue;break;case Vn:if(ot===bt||ot==="/"||ot==="\\"&&it.isSpecial()||!wt&&(ot==="?"||ot==="#")){if(Et(_t)?(it.shortenPath(),ot!=="/"&&!(ot==="\\"&&it.isSpecial())&&xt(it.path,"")):Nt(_t)?ot!=="/"&&!(ot==="\\"&&it.isSpecial())&&xt(it.path,""):(it.scheme==="file"&&!it.path.length&&Pt(_t)&&(it.host&&(it.host=""),_t=tt(_t,0)+":"),xt(it.path,_t)),_t="",it.scheme==="file"&&(ot===bt||ot==="?"||ot==="#"))for(;it.path.length>1&&it.path[0]==="";)Ut(it.path);ot==="?"?(it.query="",Ot=ir):ot==="#"&&(it.fragment="",Ot=Wn)}else _t+=ut(ot,Ye);break;case Wa:ot==="?"?(it.query="",Ot=ir):ot==="#"?(it.fragment="",Ot=Wn):ot!==bt&&(it.path[0]+=ut(ot,Zt));break;case ir:!wt&&ot==="#"?(it.fragment="",Ot=Wn):ot!==bt&&(ot==="'"&&it.isSpecial()?it.query+="%27":ot==="#"?it.query+="%23":it.query+=ut(ot,Zt));break;case Wn:ot!==bt&&(it.fragment+=ut(ot,Yt));break}ye++}},parseHost:function(ft){var wt,pt,it;if(tt(ft,0)==="["){if(tt(ft,ft.length-1)!=="]"||(wt=Tt(Xt(ft,1,-1)),!wt))return ae;this.host=wt}else if(this.isSpecial()){if(ft=M(ft),et($e,ft)||(wt=Ft(ft),wt===null))return ae;this.host=wt}else{if(et(ce,ft))return ae;for(wt="",pt=O(ft),it=0;it1?arguments[1]:void 0,Ot=B(pt,new va(wt,!1,it));d||(pt.href=Ot.serialize(),pt.origin=Ot.getOrigin(),pt.protocol=Ot.getProtocol(),pt.username=Ot.getUsername(),pt.password=Ot.getPassword(),pt.host=Ot.getHost(),pt.hostname=Ot.getHostname(),pt.port=Ot.getPort(),pt.pathname=Ot.getPathname(),pt.search=Ot.getSearch(),pt.searchParams=Ot.getSearchParams(),pt.hash=Ot.getHash())},xn=xr.prototype,Mn=function(ft,wt){return{get:function(){return V(this)[ft]()},set:wt&&function(pt){return V(this)[wt](pt)},configurable:!0,enumerable:!0}};if(d&&(A(xn,"href",Mn("serialize","setHref")),A(xn,"origin",Mn("getOrigin")),A(xn,"protocol",Mn("getProtocol","setProtocol")),A(xn,"username",Mn("getUsername","setUsername")),A(xn,"password",Mn("getPassword","setPassword")),A(xn,"host",Mn("getHost","setHost")),A(xn,"hostname",Mn("getHostname","setHostname")),A(xn,"port",Mn("getPort","setPort")),A(xn,"pathname",Mn("getPathname","setPathname")),A(xn,"search",Mn("getSearch","setSearch")),A(xn,"searchParams",Mn("getSearchParams")),A(xn,"hash",Mn("getHash","setHash"))),$(xn,"toJSON",function(){return V(this).serialize()},{enumerable:!0}),$(xn,"toString",function(){return V(this).serialize()},{enumerable:!0}),J){var Ka=J.createObjectURL,Za=J.revokeObjectURL;Ka&&$(xr,"createObjectURL",y(Ka,J)),Za&&$(xr,"revokeObjectURL",y(Za,J))}z(xr,"URL"),u({global:!0,constructor:!0,forced:!h,sham:!d},{URL:xr})},function(x,b,r){var u=r(7),d=r(33),h=r(6),p=r(36),y=d("iterator");x.exports=!u(function(){var T=new URL("b?a=1&b=2&c=3","https://a"),$=T.searchParams,A=new URLSearchParams("a=1&a=2&b=3"),E="";return T.pathname="c%20d",$.forEach(function(R,I){$.delete("b"),E+=I+R}),A.delete("a",2),A.delete("b",void 0),p&&(!T.toJSON||!A.has("a",1)||A.has("a",2)||!A.has("a",void 0)||A.has("b"))||!$.size&&(p||!h)||!$.sort||T.href!=="https://a/c%20d?a=1&c=3"||$.get("c")!=="3"||String(new URLSearchParams("?a=1"))!=="a=1"||!$[y]||new URL("https://a@b").username!=="a"||new URLSearchParams(new URLSearchParams("a=b")).get("a")!=="b"||new URL("https://\u0442\u0435\u0441\u0442").host!=="xn--e1aybc"||new URL("https://a#\u0431").hash!=="#%D0%B1"||E!=="a1c3"||new URL("https://x",void 0).host!=="x"})},function(x,b,r){var u=r(14),d=2147483647,h=36,p=1,y=26,T=38,$=700,A=72,E=128,R="-",I=/[^\0-\u007E]/,O=/[.\u3002\uFF0E\uFF61]/g,C="Overflow: input needs wider integers to process",D=h-p,M=RangeError,F=u(O.exec),z=Math.floor,U=String.fromCharCode,j=u("".charCodeAt),G=u([].join),B=u([].push),V=u("".replace),Y=u("".split),Z=u("".toLowerCase),J=function(_){for(var tt=[],et=0,lt=_.length;et=55296&&mt<=56319&&et>1,_+=z(_/tt);_>D*y>>1;)_=z(_/D),lt+=h;return z(lt+(D+1)*_/(_+T))},rt=function(_){var tt=[];_=J(_);var et=_.length,lt=E,mt=0,gt=A,xt,yt;for(xt=0;xt<_.length;xt++)yt=_[xt],yt<128&&B(tt,U(yt));var Ut=tt.length,Dt=Ut;for(Ut&&B(tt,R);Dt=lt&&ytz((d-mt)/Qt))throw new M(C);for(mt+=(Xt-lt)*Qt,lt=Xt,xt=0;xt<_.length;xt++){if(yt=_[xt],ytd)throw new M(C);if(yt===lt){for(var kt=mt,me=h;;){var ge=me<=gt?p:me>=gt+y?y:me-gt;if(kt0&&(Rt&jt)!==0;jt>>=1)zt++;return zt},qt=function(Rt){var zt=null;switch(Rt.length){case 1:zt=Rt[0];break;case 2:zt=(Rt[0]&31)<<6|Rt[1]&63;break;case 3:zt=(Rt[0]&15)<<12|(Rt[1]&63)<<6|Rt[2]&63;break;case 4:zt=(Rt[0]&7)<<18|(Rt[1]&63)<<12|(Rt[2]&63)<<6|Rt[3]&63;break}return zt>1114111?null:zt},te=function(Rt){Rt=fe(Rt,Te," ");for(var zt=Rt.length,jt="",Wt=0;Wtzt){jt+="%",Wt++;continue}var Ee=Ft(Rt,Wt+1);if(Ee!==Ee){jt+=ue,Wt++;continue}Wt+=2;var Xe=Tt(Ee);if(Xe===0)ue=ae(Ee);else{if(Xe===1||Xe>4){jt+=de,Wt++;continue}for(var Je=[Ee],nn=1;nnzt||re(Rt,Wt)!=="%"));){var vn=Ft(Rt,Wt+1);if(vn!==vn){Wt+=3;break}if(vn>191||vn<128)break;ee(Je,vn),Wt+=2,nn++}if(Je.length!==Xe){jt+=de;continue}var jn=qt(Je);jn===null?jt+=de:ue=Mt(jn)}}jt+=ue,Wt++}return jt},Zt=/[!'()~]|%20/g,Yt={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+"},Ye=function(Rt){return Yt[Rt]},Ze=function(Rt){return fe(ge(Rt),Zt,Ye)},ut=C(function(zt,jt){gt(this,{type:mt,target:xt(zt).entries,index:0,kind:jt})},lt,function(){var zt=yt(this),jt=zt.target,Wt=zt.index++;if(!jt||Wt>=jt.length)return zt.target=null,nt(void 0,!0);var ue=jt[Wt];switch(zt.kind){case"keys":return nt(ue.key,!1);case"values":return nt(ue.value,!1)}return nt([ue.key,ue.value],!1)},!0),It=function(Rt){this.entries=[],this.url=null,Rt!==void 0&&(B(Rt)?this.parseObject(Rt):this.parseQuery(typeof Rt=="string"?re(Rt,0)==="?"?ce(Rt,1):Rt:V(Rt)))};It.prototype={type:lt,bindURL:function(Rt){this.url=Rt,this.update()},parseObject:function(Rt){var zt=this.entries,jt=q(Rt),Wt,ue,Ee,Xe,Je,nn,vn;if(jt)for(Wt=J(Rt,jt),ue=Wt.next;!(Ee=y(ue,Wt)).done;){if(Xe=J(G(Ee.value)),Je=Xe.next,(nn=y(Je,Xe)).done||(vn=y(Je,Xe)).done||!y(Je,Xe).done)throw new me("Expected sequence with length 2");ee(zt,{key:V(nn.value),value:V(vn.value)})}else for(var jn in Rt)z(Rt,jn)&&ee(zt,{key:jn,value:V(Rt[jn])})},parseQuery:function(Rt){if(Rt)for(var zt=this.entries,jt=$e(Rt,"&"),Wt=0,ue,Ee;Wt0?arguments[0]:void 0,jt=gt(this,new It(zt));$||(this.size=jt.entries.length)},Ct=Pt.prototype;if(I(Ct,{append:function(zt,jt){var Wt=xt(this);rt(arguments.length,2),ee(Wt.entries,{key:V(zt),value:V(jt)}),$||this.length++,Wt.updateURL()},delete:function(Rt){for(var zt=xt(this),jt=rt(arguments.length,1),Wt=zt.entries,ue=V(Rt),Ee=jt<2?void 0:arguments[1],Xe=Ee===void 0?Ee:V(Ee),Je=0;JeWt.key?1:-1}),zt.updateURL()},forEach:function(zt){for(var jt=xt(this).entries,Wt=U(zt,arguments.length>1?arguments[1]:void 0),ue=0,Ee;ue1?ie(arguments[1]):{})}}),F(Dt)){var we=function(zt){return M(this,Qt),new Dt(zt,arguments.length>1?ie(arguments[1]):{})};Qt.constructor=we,we.prototype=Qt,u({global:!0,constructor:!0,dontCallGetSet:!0,forced:!0},{Request:we})}}x.exports={URLSearchParams:Pt,getState:xt}},function(x,b,r){var u=r(3),d=r(23),h=r(7),p=r(367),y=r(68),T=r(574),$=d("URL"),A=T&&h(function(){$.canParse()}),E=h(function(){return $.canParse.length!==1});u({target:"URL",stat:!0,forced:!A||E},{canParse:function(I){var O=p(arguments.length,1),C=y(I),D=O<2||arguments[1]===void 0?void 0:y(arguments[1]);try{return!!new $(C,D)}catch(M){return!1}}})},function(x,b,r){var u=r(3),d=r(23),h=r(367),p=r(68),y=r(574),T=d("URL");u({target:"URL",stat:!0,forced:!y},{parse:function(A){var E=h(arguments.length,1),R=p(A),I=E<2||arguments[1]===void 0?void 0:p(arguments[1]);try{return new T(R,I)}catch(O){return null}}})},function(x,b,r){var u=r(3),d=r(8);u({target:"URL",proto:!0,enumerable:!0},{toJSON:function(){return d(URL.prototype.toString,this)}})},function(x,b,r){r(576)},function(x,b,r){var u=r(47),d=r(14),h=r(68),p=r(367),y=URLSearchParams,T=y.prototype,$=d(T.append),A=d(T.delete),E=d(T.forEach),R=d([].push),I=new y("a=1&a=2&b=3");I.delete("a",1),I.delete("b",void 0),I+""!="a=2"&&u(T,"delete",function(O){var C=arguments.length,D=C<2?void 0:arguments[1];if(C&&D===void 0)return A(this,O);var M=[];E(this,function(Y,Z){R(M,{key:Z,value:Y})}),p(C,1);for(var F=h(O),z=h(D),U=0,j=0,G=!1,B=M.length,V;U=W&&(W=X+1);!(k=L[W])&&++W=0;)(s=a[i])&&(o&&s.compareDocumentPosition(o)^4&&o.parentNode.insertBefore(s,o),o=s);return this}function xt(t){t||(t=yt);function e(v,m){return v&&m?t(v.__data__,m.__data__):!v-!m}for(var n=this._groups,a=n.length,i=new Array(a),o=0;oe?1:t>=e?0:NaN}function Ut(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function Dt(){return Array.from(this)}function Xt(){for(var t=this._groups,e=0,n=t.length;e=0&&(e=t.slice(0,n))!=="xmlns"&&(t=t.slice(n+1)),ae.hasOwnProperty(e)?{space:ae[e],local:t}:t}function Ht(t){return function(){this.removeAttribute(t)}}function re(t){return function(){this.removeAttributeNS(t.space,t.local)}}function se(t,e){return function(){this.setAttribute(t,e)}}function ee(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function fe(t,e){return function(){var n=e.apply(this,arguments);n==null?this.removeAttribute(t):this.setAttribute(t,n)}}function Pe(t,e){return function(){var n=e.apply(this,arguments);n==null?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}function Me(t,e){var n=Mt(t);if(arguments.length<2){var a=this.node();return n.local?a.getAttributeNS(n.space,n.local):a.getAttribute(n)}return this.each((e==null?n.local?re:Ht:typeof e=="function"?n.local?Pe:fe:n.local?ee:se)(n,e))}function $e(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function ce(t){return function(){this.style.removeProperty(t)}}function Ae(t,e,n){return function(){this.style.setProperty(t,e,n)}}function Te(t,e,n){return function(){var a=e.apply(this,arguments);a==null?this.style.removeProperty(t):this.style.setProperty(t,a,n)}}function de(t,e,n){return arguments.length>1?this.each((e==null?ce:typeof e=="function"?Te:Ae)(t,e,n==null?"":n)):bt(this.node(),t)}function bt(t,e){return t.style.getPropertyValue(e)||$e(t).getComputedStyle(t,null).getPropertyValue(e)}function Ft(t){return function(){delete this[t]}}function Tt(t,e){return function(){this[t]=e}}function qt(t,e){return function(){var n=e.apply(this,arguments);n==null?delete this[t]:this[t]=n}}function te(t,e){return arguments.length>1?this.each((e==null?Ft:typeof e=="function"?qt:Tt)(t,e)):this.node()[t]}function Zt(t){return t.trim().split(/^|\s+/)}function Yt(t){return t.classList||new Ye(t)}function Ye(t){this._node=t,this._names=Zt(t.getAttribute("class")||"")}Ye.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function Ze(t,e){for(var n=Yt(t),a=-1,i=e.length;++a=0&&(n=e.slice(a+1),e=e.slice(0,a)),{type:e,name:n}})}function Ka(t){return function(){var e=this.__on;if(e){for(var n=0,a=-1,i=e.length,o;n(t(o=new Date(+o)),o),i.ceil=o=>(t(o=new Date(o-1)),e(o,1),t(o),o),i.round=o=>{const s=i(o),l=i.ceil(o);return o-s(e(o=new Date(+o),s==null?1:Math.floor(s)),o),i.range=(o,s,l)=>{const c=[];if(o=i.ceil(o),l=l==null?1:Math.floor(l),!(o0))return c;let f;do c.push(f=new Date(+o)),e(o,l),t(o);while(fen(s=>{if(s>=s)for(;t(s),!o(s);)s.setTime(s-1)},(s,l)=>{if(s>=s)if(l<0)for(;++l<=0;)for(;e(s,-1),!o(s););else for(;--l>=0;)for(;e(s,1),!o(s););}),n&&(i.count=(o,s)=>(an.setTime(+o),Fn.setTime(+s),t(an),t(Fn),Math.floor(n(an,Fn))),i.every=o=>(o=Math.floor(o),!isFinite(o)||!(o>0)?null:o>1?i.filter(a?s=>a(s)%o===0:s=>i.count(0,s)%o===0):i)),i}const Gn=1e3,In=Gn*60,Bn=In*60,or=Bn*24,to=or*7,Ps=or*30,eo=or*365;function Rr(t){return en(e=>{e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)},(e,n)=>{e.setDate(e.getDate()+n*7)},(e,n)=>(n-e-(n.getTimezoneOffset()-e.getTimezoneOffset())*In)/to)}const Ja=Rr(0),Qa=Rr(1),Rf=Rr(2),If=Rr(3),Yr=Rr(4),Of=Rr(5),Cf=Rr(6),I0=Ja.range,O0=Qa.range,C0=Rf.range,P0=If.range,w0=Yr.range,M0=Of.range,D0=Cf.range;function Ir(t){return en(e=>{e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)},(e,n)=>{e.setUTCDate(e.getUTCDate()+n*7)},(e,n)=>(n-e)/to)}const ka=Ir(0),qa=Ir(1),Pf=Ir(2),wf=Ir(3),Wr=Ir(4),Mf=Ir(5),Df=Ir(6),L0=ka.range,N0=qa.range,F0=Pf.range,B0=wf.range,U0=Wr.range,z0=Mf.range,j0=Df.range,pa=en(t=>t.setHours(0,0,0,0),(t,e)=>t.setDate(t.getDate()+e),(t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*In)/or,t=>t.getDate()-1),V0=pa.range,_a=en(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/or,t=>t.getUTCDate()-1),G0=_a.range,ws=en(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/or,t=>Math.floor(t/or)),X0=ws.range,sr=en(t=>{t.setMonth(0,1),t.setHours(0,0,0,0)},(t,e)=>{t.setFullYear(t.getFullYear()+e)},(t,e)=>e.getFullYear()-t.getFullYear(),t=>t.getFullYear());sr.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:en(e=>{e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)},(e,n)=>{e.setFullYear(e.getFullYear()+n*t)});const H0=sr.range,lr=en(t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCFullYear(t.getUTCFullYear()+e)},(t,e)=>e.getUTCFullYear()-t.getUTCFullYear(),t=>t.getUTCFullYear());lr.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:en(e=>{e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,n)=>{e.setUTCFullYear(e.getUTCFullYear()+n*t)});const Y0=lr.range;function no(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function ro(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function ma(t,e,n){return{y:t,m:e,d:n,H:0,M:0,S:0,L:0}}function Lf(t){var e=t.dateTime,n=t.date,a=t.time,i=t.periods,o=t.days,s=t.shortDays,l=t.months,c=t.shortMonths,f=ya(i),g=xa(i),v=ya(o),m=xa(o),S=ya(s),P=xa(s),N=ya(l),L=xa(l),w=ya(c),X=xa(c),W={a:At,A:Gt,b:Bt,B:Kt,c:null,d:Bs,e:Bs,f:rd,g:hd,G:vd,H:td,I:ed,j:nd,L:Us,m:ad,M:id,p:ne,q:le,Q:Hs,s:Ys,S:od,u:sd,U:ld,V:cd,w:ud,W:fd,x:null,X:null,y:dd,Y:gd,Z:pd,"%":Xs},H={a:be,A:Oe,b:Ce,B:He,c:null,d:js,e:js,f:Td,g:Pd,G:Md,H:md,I:yd,j:xd,L:Vs,m:$d,M:Sd,p:Fe,q:dn,Q:Hs,s:Ys,S:Ad,u:Ed,U:bd,V:Rd,w:Id,W:Od,x:null,X:null,y:Cd,Y:wd,Z:Dd,"%":Xs},k={a:dt,A:st,b:Vt,B:vt,c:Q,d:Ns,e:Ns,f:Qf,g:Ls,G:Ds,H:Fs,I:Fs,j:Wf,L:Jf,m:Yf,M:Kf,p:$t,q:Hf,Q:qf,s:_f,S:Zf,u:zf,U:jf,V:Vf,w:Uf,W:Gf,x:St,X:ct,y:Ls,Y:Ds,Z:Xf,"%":kf};W.x=K(n,W),W.X=K(a,W),W.c=K(e,W),H.x=K(n,H),H.X=K(a,H),H.c=K(e,H);function K(Jt,xe){return function(Re){var Lt=[],un=-1,Ge=0,Pn=Jt.length,wn,pe,fn;for(Re instanceof Date||(Re=new Date(+Re));++un53)return null;"w"in Lt||(Lt.w=1),"Z"in Lt?(Ge=ro(ma(Lt.y,0,1)),Pn=Ge.getUTCDay(),Ge=Pn>4||Pn===0?qa.ceil(Ge):qa(Ge),Ge=_a.offset(Ge,(Lt.V-1)*7),Lt.y=Ge.getUTCFullYear(),Lt.m=Ge.getUTCMonth(),Lt.d=Ge.getUTCDate()+(Lt.w+6)%7):(Ge=no(ma(Lt.y,0,1)),Pn=Ge.getDay(),Ge=Pn>4||Pn===0?Qa.ceil(Ge):Qa(Ge),Ge=pa.offset(Ge,(Lt.V-1)*7),Lt.y=Ge.getFullYear(),Lt.m=Ge.getMonth(),Lt.d=Ge.getDate()+(Lt.w+6)%7)}else("W"in Lt||"U"in Lt)&&("w"in Lt||(Lt.w="u"in Lt?Lt.u%7:"W"in Lt?1:0),Pn="Z"in Lt?ro(ma(Lt.y,0,1)).getUTCDay():no(ma(Lt.y,0,1)).getDay(),Lt.m=0,Lt.d="W"in Lt?(Lt.w+6)%7+Lt.W*7-(Pn+5)%7:Lt.w+Lt.U*7-(Pn+6)%7);return"Z"in Lt?(Lt.H+=Lt.Z/100|0,Lt.M+=Lt.Z%100,ro(Lt)):no(Lt)}}function ht(Jt,xe,Re,Lt){for(var un=0,Ge=xe.length,Pn=Re.length,wn,pe;un=Pn)return-1;if(wn=xe.charCodeAt(un++),wn===37){if(wn=xe.charAt(un++),pe=k[wn in Ms?xe.charAt(un++):wn],!pe||(Lt=pe(Jt,Re,Lt))<0)return-1}else if(wn!=Re.charCodeAt(Lt++))return-1}return Lt}function $t(Jt,xe,Re){var Lt=f.exec(xe.slice(Re));return Lt?(Jt.p=g.get(Lt[0].toLowerCase()),Re+Lt[0].length):-1}function dt(Jt,xe,Re){var Lt=S.exec(xe.slice(Re));return Lt?(Jt.w=P.get(Lt[0].toLowerCase()),Re+Lt[0].length):-1}function st(Jt,xe,Re){var Lt=v.exec(xe.slice(Re));return Lt?(Jt.w=m.get(Lt[0].toLowerCase()),Re+Lt[0].length):-1}function Vt(Jt,xe,Re){var Lt=w.exec(xe.slice(Re));return Lt?(Jt.m=X.get(Lt[0].toLowerCase()),Re+Lt[0].length):-1}function vt(Jt,xe,Re){var Lt=N.exec(xe.slice(Re));return Lt?(Jt.m=L.get(Lt[0].toLowerCase()),Re+Lt[0].length):-1}function Q(Jt,xe,Re){return ht(Jt,e,xe,Re)}function St(Jt,xe,Re){return ht(Jt,n,xe,Re)}function ct(Jt,xe,Re){return ht(Jt,a,xe,Re)}function At(Jt){return s[Jt.getDay()]}function Gt(Jt){return o[Jt.getDay()]}function Bt(Jt){return c[Jt.getMonth()]}function Kt(Jt){return l[Jt.getMonth()]}function ne(Jt){return i[+(Jt.getHours()>=12)]}function le(Jt){return 1+~~(Jt.getMonth()/3)}function be(Jt){return s[Jt.getUTCDay()]}function Oe(Jt){return o[Jt.getUTCDay()]}function Ce(Jt){return c[Jt.getUTCMonth()]}function He(Jt){return l[Jt.getUTCMonth()]}function Fe(Jt){return i[+(Jt.getUTCHours()>=12)]}function dn(Jt){return 1+~~(Jt.getUTCMonth()/3)}return{format:function(Jt){var xe=K(Jt+="",W);return xe.toString=function(){return Jt},xe},parse:function(Jt){var xe=at(Jt+="",!1);return xe.toString=function(){return Jt},xe},utcFormat:function(Jt){var xe=K(Jt+="",H);return xe.toString=function(){return Jt},xe},utcParse:function(Jt){var xe=at(Jt+="",!0);return xe.toString=function(){return Jt},xe}}}var Ms={"-":"",_:" ",0:"0"},mn=/^\s*\d+/,Nf=/^%/,Ff=/[\\^$*+?|[\]().{}]/g;function Ne(t,e,n){var a=t<0?"-":"",i=(a?-t:t)+"",o=i.length;return a+(o[e.toLowerCase(),n]))}function Uf(t,e,n){var a=mn.exec(e.slice(n,n+1));return a?(t.w=+a[0],n+a[0].length):-1}function zf(t,e,n){var a=mn.exec(e.slice(n,n+1));return a?(t.u=+a[0],n+a[0].length):-1}function jf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.U=+a[0],n+a[0].length):-1}function Vf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.V=+a[0],n+a[0].length):-1}function Gf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.W=+a[0],n+a[0].length):-1}function Ds(t,e,n){var a=mn.exec(e.slice(n,n+4));return a?(t.y=+a[0],n+a[0].length):-1}function Ls(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.y=+a[0]+(+a[0]>68?1900:2e3),n+a[0].length):-1}function Xf(t,e,n){var a=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n+6));return a?(t.Z=a[1]?0:-(a[2]+(a[3]||"00")),n+a[0].length):-1}function Hf(t,e,n){var a=mn.exec(e.slice(n,n+1));return a?(t.q=a[0]*3-3,n+a[0].length):-1}function Yf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.m=a[0]-1,n+a[0].length):-1}function Ns(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.d=+a[0],n+a[0].length):-1}function Wf(t,e,n){var a=mn.exec(e.slice(n,n+3));return a?(t.m=0,t.d=+a[0],n+a[0].length):-1}function Fs(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.H=+a[0],n+a[0].length):-1}function Kf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.M=+a[0],n+a[0].length):-1}function Zf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.S=+a[0],n+a[0].length):-1}function Jf(t,e,n){var a=mn.exec(e.slice(n,n+3));return a?(t.L=+a[0],n+a[0].length):-1}function Qf(t,e,n){var a=mn.exec(e.slice(n,n+6));return a?(t.L=Math.floor(a[0]/1e3),n+a[0].length):-1}function kf(t,e,n){var a=Nf.exec(e.slice(n,n+1));return a?n+a[0].length:-1}function qf(t,e,n){var a=mn.exec(e.slice(n));return a?(t.Q=+a[0],n+a[0].length):-1}function _f(t,e,n){var a=mn.exec(e.slice(n));return a?(t.s=+a[0],n+a[0].length):-1}function Bs(t,e){return Ne(t.getDate(),e,2)}function td(t,e){return Ne(t.getHours(),e,2)}function ed(t,e){return Ne(t.getHours()%12||12,e,2)}function nd(t,e){return Ne(1+pa.count(sr(t),t),e,3)}function Us(t,e){return Ne(t.getMilliseconds(),e,3)}function rd(t,e){return Us(t,e)+"000"}function ad(t,e){return Ne(t.getMonth()+1,e,2)}function id(t,e){return Ne(t.getMinutes(),e,2)}function od(t,e){return Ne(t.getSeconds(),e,2)}function sd(t){var e=t.getDay();return e===0?7:e}function ld(t,e){return Ne(Ja.count(sr(t)-1,t),e,2)}function zs(t){var e=t.getDay();return e>=4||e===0?Yr(t):Yr.ceil(t)}function cd(t,e){return t=zs(t),Ne(Yr.count(sr(t),t)+(sr(t).getDay()===4),e,2)}function ud(t){return t.getDay()}function fd(t,e){return Ne(Qa.count(sr(t)-1,t),e,2)}function dd(t,e){return Ne(t.getFullYear()%100,e,2)}function hd(t,e){return t=zs(t),Ne(t.getFullYear()%100,e,2)}function gd(t,e){return Ne(t.getFullYear()%1e4,e,4)}function vd(t,e){var n=t.getDay();return t=n>=4||n===0?Yr(t):Yr.ceil(t),Ne(t.getFullYear()%1e4,e,4)}function pd(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+Ne(e/60|0,"0",2)+Ne(e%60,"0",2)}function js(t,e){return Ne(t.getUTCDate(),e,2)}function md(t,e){return Ne(t.getUTCHours(),e,2)}function yd(t,e){return Ne(t.getUTCHours()%12||12,e,2)}function xd(t,e){return Ne(1+_a.count(lr(t),t),e,3)}function Vs(t,e){return Ne(t.getUTCMilliseconds(),e,3)}function Td(t,e){return Vs(t,e)+"000"}function $d(t,e){return Ne(t.getUTCMonth()+1,e,2)}function Sd(t,e){return Ne(t.getUTCMinutes(),e,2)}function Ad(t,e){return Ne(t.getUTCSeconds(),e,2)}function Ed(t){var e=t.getUTCDay();return e===0?7:e}function bd(t,e){return Ne(ka.count(lr(t)-1,t),e,2)}function Gs(t){var e=t.getUTCDay();return e>=4||e===0?Wr(t):Wr.ceil(t)}function Rd(t,e){return t=Gs(t),Ne(Wr.count(lr(t),t)+(lr(t).getUTCDay()===4),e,2)}function Id(t){return t.getUTCDay()}function Od(t,e){return Ne(qa.count(lr(t)-1,t),e,2)}function Cd(t,e){return Ne(t.getUTCFullYear()%100,e,2)}function Pd(t,e){return t=Gs(t),Ne(t.getUTCFullYear()%100,e,2)}function wd(t,e){return Ne(t.getUTCFullYear()%1e4,e,4)}function Md(t,e){var n=t.getUTCDay();return t=n>=4||n===0?Wr(t):Wr.ceil(t),Ne(t.getUTCFullYear()%1e4,e,4)}function Dd(){return"+0000"}function Xs(){return"%"}function Hs(t){return+t}function Ys(t){return Math.floor(+t/1e3)}var Kr,ao,Ws,io,Ks;Ld({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function Ld(t){return Kr=Lf(t),ao=Kr.format,Ws=Kr.parse,io=Kr.utcFormat,Ks=Kr.utcParse,Kr}var Nd=Object.defineProperty,Zs=Object.getOwnPropertySymbols,Fd=Object.prototype.hasOwnProperty,Bd=Object.prototype.propertyIsEnumerable,Js=(t,e,n)=>e in t?Nd(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,ke=(t,e)=>{for(var n in e||(e={}))Fd.call(e,n)&&Js(t,n,e[n]);if(Zs)for(var n of Zs(e))Bd.call(e,n)&&Js(t,n,e[n]);return t};const Se={button:"bb-button",chart:"bb-chart",empty:"bb-empty",main:"bb-main",target:"bb-target",EXPANDED:"_expanded_"},Ve={arc:"bb-arc",arcLabelLine:"bb-arc-label-line",arcRange:"bb-arc-range",arcs:"bb-arcs",chartArc:"bb-chart-arc",chartArcs:"bb-chart-arcs",chartArcsBackground:"bb-chart-arcs-background",chartArcsTitle:"bb-chart-arcs-title",needle:"bb-needle"},ti={area:"bb-area",areas:"bb-areas"},Tn={axis:"bb-axis",axisX:"bb-axis-x",axisXLabel:"bb-axis-x-label",axisY:"bb-axis-y",axisY2:"bb-axis-y2",axisY2Label:"bb-axis-y2-label",axisYLabel:"bb-axis-y-label",axisXTooltip:"bb-axis-x-tooltip",axisYTooltip:"bb-axis-y-tooltip",axisY2Tooltip:"bb-axis-y2-tooltip"},Kn={bar:"bb-bar",bars:"bb-bars",chartBar:"bb-chart-bar",chartBars:"bb-chart-bars"},cr={candlestick:"bb-candlestick",candlesticks:"bb-candlesticks",chartCandlestick:"bb-chart-candlestick",chartCandlesticks:"bb-chart-candlesticks",valueDown:"bb-value-down",valueUp:"bb-value-up"},$n={chartCircles:"bb-chart-circles",circle:"bb-circle",circles:"bb-circles"},oo={colorPattern:"bb-color-pattern",colorScale:"bb-colorscale"},Or={dragarea:"bb-dragarea",INCLUDED:"_included_"},Ta={funnel:"bb-funnel",chartFunnel:"bb-chart-funnel",chartFunnels:"bb-chart-funnels",funnelBackground:"bb-funnel-background"},Un={chartArcsGaugeMax:"bb-chart-arcs-gauge-max",chartArcsGaugeMin:"bb-chart-arcs-gauge-min",chartArcsGaugeUnit:"bb-chart-arcs-gauge-unit",chartArcsGaugeTitle:"bb-chart-arcs-gauge-title",gaugeValue:"bb-gauge-value"},We={legend:"bb-legend",legendBackground:"bb-legend-background",legendItem:"bb-legend-item",legendItemEvent:"bb-legend-item-event",legendItemHidden:"bb-legend-item-hidden",legendItemPoint:"bb-legend-item-point",legendItemTile:"bb-legend-item-tile"},ur={chartLine:"bb-chart-line",chartLines:"bb-chart-lines",line:"bb-line",lines:"bb-lines"},Zn={eventRect:"bb-event-rect",eventRects:"bb-event-rects",eventRectsMultiple:"bb-event-rects-multiple",eventRectsSingle:"bb-event-rects-single"},qe={focused:"bb-focused",defocused:"bb-defocused",legendItemFocused:"bb-legend-item-focused",xgridFocus:"bb-xgrid-focus",ygridFocus:"bb-ygrid-focus"},on={grid:"bb-grid",gridLines:"bb-grid-lines",xgrid:"bb-xgrid",xgridLine:"bb-xgrid-line",xgridLines:"bb-xgrid-lines",xgrids:"bb-xgrids",ygrid:"bb-ygrid",ygridLine:"bb-ygrid-line",ygridLines:"bb-ygrid-lines",ygrids:"bb-ygrids"},Tr={level:"bb-level",levels:"bb-levels"},Qs={chartRadar:"bb-chart-radar",chartRadars:"bb-chart-radars"},$a={region:"bb-region",regions:"bb-regions"},tn={selectedCircle:"bb-selected-circle",selectedCircles:"bb-selected-circles",SELECTED:"_selected_"},sn={shape:"bb-shape",shapes:"bb-shapes"},ks={brush:"bb-brush",subchart:"bb-subchart"},On={chartText:"bb-chart-text",chartTexts:"bb-chart-texts",text:"bb-text",texts:"bb-texts",title:"bb-title",TextOverlapping:"text-overlapping"},ei={tooltip:"bb-tooltip",tooltipContainer:"bb-tooltip-container",tooltipName:"bb-tooltip-name"},qs={treemap:"bb-treemap",chartTreemap:"bb-chart-treemap",chartTreemaps:"bb-chart-treemaps"},so={buttonZoomReset:"bb-zoom-reset",zoomBrush:"bb-zoom-brush"};var Ue=ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke({},Se),Ve),ti),Tn),Kn),cr),$n),oo),Or),Un),We),ur),Zn),qe),Ta),on),Qs),$a),tn),sn),ks),On),ei),qs),so),Ud={boost_useCssRule:!1,boost_useWorker:!1},zd={color_pattern:[],color_tiles:void 0,color_threshold:{},color_onover:void 0},jd={legend_contents_bindto:void 0,legend_contents_template:"{=TITLE}",legend_equally:!1,legend_hide:!1,legend_inset_anchor:"top-left",legend_inset_x:10,legend_inset_y:0,legend_inset_step:void 0,legend_item_interaction:!0,legend_item_dblclick:!1,legend_item_onclick:void 0,legend_item_onover:void 0,legend_item_onout:void 0,legend_item_tile_width:10,legend_item_tile_height:10,legend_item_tile_r:5,legend_item_tile_type:"rectangle",legend_format:void 0,legend_padding:0,legend_position:"bottom",legend_show:!0,legend_tooltip:!1,legend_usePoint:!1},Vd={bindto:"#chart",background:{},clipPath:!0,svg_classname:void 0,size_width:void 0,size_height:void 0,padding:!0,padding_mode:void 0,padding_left:void 0,padding_right:void 0,padding_top:void 0,padding_bottom:void 0,resize_auto:!0,resize_timer:!0,onclick:void 0,onover:void 0,onout:void 0,onresize:void 0,onresized:void 0,onbeforeinit:void 0,oninit:void 0,onafterinit:void 0,onrendered:void 0,transition_duration:250,plugins:[],render:{},regions:[]},Gd={title_text:void 0,title_padding:{top:0,right:0,bottom:0,left:0},title_position:"center"},Xd={tooltip_show:!0,tooltip_doNotHide:!1,tooltip_grouped:!0,tooltip_format_title:void 0,tooltip_format_name:void 0,tooltip_format_value:void 0,tooltip_position:void 0,tooltip_contents:{},tooltip_init_show:!1,tooltip_init_x:0,tooltip_init_position:void 0,tooltip_linked:!1,tooltip_linked_name:"",tooltip_onshow:()=>{},tooltip_onhide:()=>{},tooltip_onshown:()=>{},tooltip_onhidden:()=>{},tooltip_order:null},Hd={data_x:void 0,data_idConverter:t=>t,data_names:{},data_classes:{},data_type:void 0,data_types:{},data_order:"desc",data_groups:[],data_groupsZeroAs:"positive",data_color:void 0,data_colors:{},data_labels:{},data_labels_backgroundColors:void 0,data_labels_colors:void 0,data_labels_position:{},data_hide:!1,data_filter:void 0,data_onclick:()=>{},data_onover:()=>{},data_onout:()=>{},data_onshown:void 0,data_onhidden:void 0,data_onmin:void 0,data_onmax:void 0,data_url:void 0,data_headers:void 0,data_json:void 0,data_rows:void 0,data_columns:void 0,data_mimeType:"csv",data_keys:void 0,data_empty_label_text:""},Yd={interaction_enabled:!0,interaction_brighten:!0,interaction_inputType_mouse:!0,interaction_inputType_touch:{},interaction_onout:!0},Wd={value:()=>{}};function _s(){for(var t=0,e=arguments.length,n={},a;t=0&&(a=n.slice(i+1),n=n.slice(0,i)),n&&!e.hasOwnProperty(n))throw new Error("unknown type: "+n);return{type:n,name:a}})}ni.prototype=_s.prototype={constructor:ni,on:function(t,e){var n=this._,a=Kd(t+"",n),i,o=-1,s=a.length;if(arguments.length<2){for(;++o0)for(var n=new Array(i),a=0,i,o;a>8&15|e>>4&240,e>>4&15|e&240,(e&15)<<4|e&15,1):n===8?ii(e>>24&255,e>>16&255,e>>8&255,(e&255)/255):n===4?ii(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|e&240,((e&15)<<4|e&15)/255):null):(e=kd.exec(t))?new Dn(e[1],e[2],e[3],1):(e=qd.exec(t))?new Dn(e[1]*255/100,e[2]*255/100,e[3]*255/100,1):(e=_d.exec(t))?ii(e[1],e[2],e[3],e[4]):(e=th.exec(t))?ii(e[1]*255/100,e[2]*255/100,e[3]*255/100,e[4]):(e=eh.exec(t))?ll(e[1],e[2]/100,e[3]/100,1):(e=nh.exec(t))?ll(e[1],e[2]/100,e[3]/100,e[4]):nl.hasOwnProperty(t)?il(nl[t]):t==="transparent"?new Dn(NaN,NaN,NaN,0):null}function il(t){return new Dn(t>>16&255,t>>8&255,t&255,1)}function ii(t,e,n,a){return a<=0&&(t=e=n=NaN),new Dn(t,e,n,a)}function ih(t){return t instanceof Aa||(t=Cr(t)),t?(t=t.rgb(),new Dn(t.r,t.g,t.b,t.opacity)):new Dn}function oi(t,e,n,a){return arguments.length===1?ih(t):new Dn(t,e,n,a==null?1:a)}function Dn(t,e,n,a){this.r=+t,this.g=+e,this.b=+n,this.opacity=+a}fo(Dn,oi,el(Aa,{brighter(t){return t=t==null?ai:Math.pow(ai,t),new Dn(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=t==null?Ea:Math.pow(Ea,t),new Dn(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new Dn(Pr(this.r),Pr(this.g),Pr(this.b),si(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:ol,formatHex:ol,formatHex8:oh,formatRgb:sl,toString:sl}));function ol(){return`#${wr(this.r)}${wr(this.g)}${wr(this.b)}`}function oh(){return`#${wr(this.r)}${wr(this.g)}${wr(this.b)}${wr((isNaN(this.opacity)?1:this.opacity)*255)}`}function sl(){const t=si(this.opacity);return`${t===1?"rgb(":"rgba("}${Pr(this.r)}, ${Pr(this.g)}, ${Pr(this.b)}${t===1?")":`, ${t})`}`}function si(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function Pr(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function wr(t){return t=Pr(t),(t<16?"0":"")+t.toString(16)}function ll(t,e,n,a){return a<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new Jn(t,e,n,a)}function cl(t){if(t instanceof Jn)return new Jn(t.h,t.s,t.l,t.opacity);if(t instanceof Aa||(t=Cr(t)),!t)return new Jn;if(t instanceof Jn)return t;t=t.rgb();var e=t.r/255,n=t.g/255,a=t.b/255,i=Math.min(e,n,a),o=Math.max(e,n,a),s=NaN,l=o-i,c=(o+i)/2;return l?(e===o?s=(n-a)/l+(n0&&c<1?0:s,new Jn(s,l,c,t.opacity)}function sh(t,e,n,a){return arguments.length===1?cl(t):new Jn(t,e,n,a==null?1:a)}function Jn(t,e,n,a){this.h=+t,this.s=+e,this.l=+n,this.opacity=+a}fo(Jn,sh,el(Aa,{brighter(t){return t=t==null?ai:Math.pow(ai,t),new Jn(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=t==null?Ea:Math.pow(Ea,t),new Jn(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+(this.h<0)*360,e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,a=n+(n<.5?n:1-n)*e,i=2*n-a;return new Dn(ho(t>=240?t-240:t+120,i,a),ho(t,i,a),ho(t<120?t+240:t-120,i,a),this.opacity)},clamp(){return new Jn(ul(this.h),li(this.s),li(this.l),si(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const t=si(this.opacity);return`${t===1?"hsl(":"hsla("}${ul(this.h)}, ${li(this.s)*100}%, ${li(this.l)*100}%${t===1?")":`, ${t})`}`}}));function ul(t){return t=(t||0)%360,t<0?t+360:t}function li(t){return Math.max(0,Math.min(1,t||0))}function ho(t,e,n){return(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)*255}function fl(t,e,n,a,i){var o=t*t,s=o*t;return((1-3*t+3*o-s)*e+(4-6*o+3*s)*n+(1+3*t+3*o-3*s)*a+s*i)/6}function lh(t){var e=t.length-1;return function(n){var a=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[a],o=t[a+1],s=a>0?t[a-1]:2*i-o,l=a()=>t;function dl(t,e){return function(n){return t+n*e}}function uh(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(a){return Math.pow(t+a*e,n)}}function W0(t,e){var n=e-t;return n?dl(t,n>180||n<-180?n-360*Math.round(n/360):n):ci(isNaN(t)?e:t)}function fh(t){return(t=+t)==1?hl:function(e,n){return n-e?uh(e,n,t):ci(isNaN(e)?n:e)}}function hl(t,e){var n=e-t;return n?dl(t,n):ci(isNaN(t)?e:t)}var ui=function t(e){var n=fh(e);function a(i,o){var s=n((i=oi(i)).r,(o=oi(o)).r),l=n(i.g,o.g),c=n(i.b,o.b),f=hl(i.opacity,o.opacity);return function(g){return i.r=s(g),i.g=l(g),i.b=c(g),i.opacity=f(g),i+""}}return a.gamma=t,a}(1);function gl(t){return function(e){var n=e.length,a=new Array(n),i=new Array(n),o=new Array(n),s,l;for(s=0;sn&&(o=e.slice(n,o),l[s]?l[s]+=o:l[++s]=o),(a=a[0])===(i=i[0])?l[s]?l[s]+=i:l[++s]=i:(l[++s]=null,c.push({i:s,x:Qn(a,i)})),n=vo.lastIndex;return n=0&&t._call.call(void 0,e),t=t._next;--kr}function Sl(){Mr=(di=Ca.now())+hi,kr=Ra=0;try{yh()}finally{kr=0,Th(),Mr=0}}function xh(){var t=Ca.now(),e=t-di;e>xl&&(hi-=e,di=t)}function Th(){for(var t,e=fi,n,a=1/0;e;)e._call?(a>e._time&&(a=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:fi=n);Oa=t,mo(a)}function mo(t){if(!kr){Ra&&(Ra=clearTimeout(Ra));var e=t-Mr;e>24?(t<1/0&&(Ra=setTimeout(Sl,t-Ca.now()-hi)),Ia&&(Ia=clearInterval(Ia))):(Ia||(di=Ca.now(),Ia=setInterval(xh,xl)),kr=1,Tl(Sl))}}function Al(t,e,n){var a=new gi;return e=e==null?0:+e,a.restart(i=>{a.stop(),t(i+e)},e,n),a}var $h=ri("start","end","cancel","interrupt"),Sh=[],El=0,bl=1,yo=2,vi=3,Rl=4,xo=5,pi=6;function mi(t,e,n,a,i,o){var s=t.__transition;if(!s)t.__transition={};else if(n in s)return;Ah(t,n,{name:e,index:a,group:i,on:$h,tween:Sh,time:o.time,delay:o.delay,duration:o.duration,ease:o.ease,timer:null,state:El})}function To(t,e){var n=kn(t,e);if(n.state>El)throw new Error("too late; already scheduled");return n}function er(t,e){var n=kn(t,e);if(n.state>vi)throw new Error("too late; already running");return n}function kn(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}function Ah(t,e,n){var a=t.__transition,i;a[e]=n,n.timer=$l(o,0,n.time);function o(f){n.state=bl,n.timer.restart(s,n.delay,n.time),n.delay<=f&&s(f-n.delay)}function s(f){var g,v,m,S;if(n.state!==bl)return c();for(g in a)if(S=a[g],S.name===n.name){if(S.state===vi)return Al(s);S.state===Rl?(S.state=pi,S.timer.stop(),S.on.call("interrupt",t,t.__data__,S.index,S.group),delete a[g]):+gyo&&a.state180?g+=360:g-f>180&&(f+=360),m.push({i:v.push(i(v)+"rotate(",null,a)-2,x:Qn(f,g)})):g&&v.push(i(v)+"rotate("+g+a)}function l(f,g,v,m){f!==g?m.push({i:v.push(i(v)+"skewX(",null,a)-2,x:Qn(f,g)}):g&&v.push(i(v)+"skewX("+g+a)}function c(f,g,v,m,S,P){if(f!==v||g!==m){var N=S.push(i(S)+"scale(",null,",",null,")");P.push({i:N-4,x:Qn(f,v)},{i:N-2,x:Qn(g,m)})}else(v!==1||m!==1)&&S.push(i(S)+"scale("+v+","+m+")")}return function(f,g){var v=[],m=[];return f=t(f),g=t(g),o(f.translateX,f.translateY,g.translateX,g.translateY,v,m),s(f.rotate,g.rotate,v,m),l(f.skewX,g.skewX,v,m),c(f.scaleX,f.scaleY,g.scaleX,g.scaleY,v,m),f=g=null,function(S){for(var P=-1,N=m.length,L;++P=0&&(e=e.slice(0,n)),!e||e==="start"})}function rg(t,e,n){var a,i,o=ng(e)?To:er;return function(){var s=o(this,t),l=s.on;l!==a&&(i=(a=l).copy()).on(e,n),s.on=i}}function ag(t,e){var n=this._id;return arguments.length<2?kn(this.node(),n).on.on(t):this.each(rg(n,t,e))}function ig(t){return function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}}function og(){return this.on("end.remove",ig(this._id))}function sg(t){var e=this._name,n=this._id;typeof t!="function"&&(t=p(t));for(var a=this._groups,i=a.length,o=new Array(i),s=0;s()=>t;function Mg(t,{sourceEvent:e,target:n,selection:a,mode:i,dispatch:o}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},target:{value:n,enumerable:!0,configurable:!0},selection:{value:a,enumerable:!0,configurable:!0},mode:{value:i,enumerable:!0,configurable:!0},_:{value:o}})}function Dg(t){t.stopImmediatePropagation()}function Eo(t){t.preventDefault(),t.stopImmediatePropagation()}var Ll={name:"drag"},bo={name:"space"},_r={name:"handle"},ta={name:"center"};const{abs:Nl,max:Sn,min:An}=Math;function Fl(t){return[+t[0],+t[1]]}function Ro(t){return[Fl(t[0]),Fl(t[1])]}var xi={name:"x",handles:["w","e"].map(Pa),input:function(t,e){return t==null?null:[[+t[0],e[0][1]],[+t[1],e[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},Ti={name:"y",handles:["n","s"].map(Pa),input:function(t,e){return t==null?null:[[e[0][0],+t[0]],[e[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},Lg={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(Pa),input:function(t){return t==null?null:Ro(t)},output:function(t){return t}},hr={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},Ul={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},Ng={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},Fg={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function Pa(t){return{type:t}}function Bg(t){return!t.ctrlKey&&!t.button}function Ug(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?(t=t.viewBox.baseVal,[[t.x,t.y],[t.x+t.width,t.y+t.height]]):[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function zg(){return navigator.maxTouchPoints||"ontouchstart"in this}function Io(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function jg(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function zl(t){var e=t.__brush;return e?e.dim.output(e.selection):null}function Vg(){return Oo(xi)}function Gg(){return Oo(Ti)}function q0(){return Oo(Lg)}function Oo(t){var e=Ug,n=Bg,a=zg,i=!0,o=ri("start","brush","end"),s=6,l;function c(L){var w=L.property("__brush",N).selectAll(".overlay").data([Pa("overlay")]);w.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",hr.overlay).merge(w).each(function(){var W=Io(this).extent;ot(this).attr("x",W[0][0]).attr("y",W[0][1]).attr("width",W[1][0]-W[0][0]).attr("height",W[1][1]-W[0][1])}),L.selectAll(".selection").data([Pa("selection")]).enter().append("rect").attr("class","selection").attr("cursor",hr.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var X=L.selectAll(".handle").data(t.handles,function(W){return W.type});X.exit().remove(),X.enter().append("rect").attr("class",function(W){return"handle handle--"+W.type}).attr("cursor",function(W){return hr[W.type]}),L.each(f).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",m).filter(a).on("touchstart.brush",m).on("touchmove.brush",S).on("touchend.brush touchcancel.brush",P).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}c.move=function(L,w,X){L.tween?L.on("start.brush",function(W){g(this,arguments).beforestart().start(W)}).on("interrupt.brush end.brush",function(W){g(this,arguments).end(W)}).tween("brush",function(){var W=this,H=W.__brush,k=g(W,arguments),K=H.selection,at=t.input(typeof w=="function"?w.apply(this,arguments):w,H.extent),ht=Qr(K,at);function $t(dt){H.selection=dt===1&&at===null?null:ht(dt),f.call(W),k.brush()}return K!==null&&at!==null?$t:$t(1)}):L.each(function(){var W=this,H=arguments,k=W.__brush,K=t.input(typeof w=="function"?w.apply(W,H):w,k.extent),at=g(W,H).beforestart();qr(W),k.selection=K===null?null:K,f.call(W),at.start(X).brush(X).end(X)})},c.clear=function(L,w){c.move(L,null,w)};function f(){var L=ot(this),w=Io(this).selection;w?(L.selectAll(".selection").style("display",null).attr("x",w[0][0]).attr("y",w[0][1]).attr("width",w[1][0]-w[0][0]).attr("height",w[1][1]-w[0][1]),L.selectAll(".handle").style("display",null).attr("x",function(X){return X.type[X.type.length-1]==="e"?w[1][0]-s/2:w[0][0]-s/2}).attr("y",function(X){return X.type[0]==="s"?w[1][1]-s/2:w[0][1]-s/2}).attr("width",function(X){return X.type==="n"||X.type==="s"?w[1][0]-w[0][0]+s:s}).attr("height",function(X){return X.type==="e"||X.type==="w"?w[1][1]-w[0][1]+s:s})):L.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function g(L,w,X){var W=L.__brush.emitter;return W&&(!X||!W.clean)?W:new v(L,w,X)}function v(L,w,X){this.that=L,this.args=w,this.state=L.__brush,this.active=0,this.clean=X}v.prototype={beforestart:function(){return++this.active===1&&(this.state.emitter=this,this.starting=!0),this},start:function(L,w){return this.starting?(this.starting=!1,this.emit("start",L,w)):this.emit("brush",L),this},brush:function(L,w){return this.emit("brush",L,w),this},end:function(L,w){return--this.active===0&&(delete this.state.emitter,this.emit("end",L,w)),this},emit:function(L,w,X){var W=ot(this.that).datum();o.call(L,this.that,new Mg(L,{sourceEvent:w,target:c,selection:t.output(this.state.selection),mode:X,dispatch:o}),W)}};function m(L){if(l&&!L.touches||!n.apply(this,arguments))return;var w=this,X=L.target.__data__.type,W=(i&&L.metaKey?X="overlay":X)==="selection"?Ll:i&&L.altKey?ta:_r,H=t===Ti?null:Ng[X],k=t===xi?null:Fg[X],K=Io(w),at=K.extent,ht=K.selection,$t=at[0][0],dt,st,Vt=at[0][1],vt,Q,St=at[1][0],ct,At,Gt=at[1][1],Bt,Kt,ne=0,le=0,be,Oe=H&&k&&i&&L.shiftKey,Ce,He,Fe=Array.from(L.touches||[L],pe=>{const fn=pe.identifier;return pe=Xn(pe,w),pe.point0=pe.slice(),pe.identifier=fn,pe});qr(w);var dn=g(w,arguments,!0).beforestart();if(X==="overlay"){ht&&(be=!0);const pe=[Fe[0],Fe[1]||Fe[0]];K.selection=ht=[[dt=t===Ti?$t:An(pe[0][0],pe[1][0]),vt=t===xi?Vt:An(pe[0][1],pe[1][1])],[ct=t===Ti?St:Sn(pe[0][0],pe[1][0]),Bt=t===xi?Gt:Sn(pe[0][1],pe[1][1])]],Fe.length>1&&un(L)}else dt=ht[0][0],vt=ht[0][1],ct=ht[1][0],Bt=ht[1][1];st=dt,Q=vt,At=ct,Kt=Bt;var Jt=ot(w).attr("pointer-events","none"),xe=Jt.selectAll(".overlay").attr("cursor",hr[X]);if(L.touches)dn.moved=Lt,dn.ended=Ge;else{var Re=ot(L.view).on("mousemove.brush",Lt,!0).on("mouseup.brush",Ge,!0);i&&Re.on("keydown.brush",Pn,!0).on("keyup.brush",wn,!0),co(L.view)}f.call(w),dn.start(L,W.name);function Lt(pe){for(const fn of pe.changedTouches||[pe])for(const Ga of Fe)Ga.identifier===fn.identifier&&(Ga.cur=Xn(fn,w));if(Oe&&!Ce&&!He&&Fe.length===1){const fn=Fe[0];Nl(fn.cur[0]-fn[0])>Nl(fn.cur[1]-fn[1])?He=!0:Ce=!0}for(const fn of Fe)fn.cur&&(fn[0]=fn.cur[0],fn[1]=fn.cur[1]);be=!0,Eo(pe),un(pe)}function un(pe){const fn=Fe[0],Ga=fn.point0;var br;switch(ne=fn[0]-Ga[0],le=fn[1]-Ga[1],W){case bo:case Ll:{H&&(ne=Sn($t-dt,An(St-ct,ne)),st=dt+ne,At=ct+ne),k&&(le=Sn(Vt-vt,An(Gt-Bt,le)),Q=vt+le,Kt=Bt+le);break}case _r:{Fe[1]?(H&&(st=Sn($t,An(St,Fe[0][0])),At=Sn($t,An(St,Fe[1][0])),H=1),k&&(Q=Sn(Vt,An(Gt,Fe[0][1])),Kt=Sn(Vt,An(Gt,Fe[1][1])),k=1)):(H<0?(ne=Sn($t-dt,An(St-dt,ne)),st=dt+ne,At=ct):H>0&&(ne=Sn($t-ct,An(St-ct,ne)),st=dt,At=ct+ne),k<0?(le=Sn(Vt-vt,An(Gt-vt,le)),Q=vt+le,Kt=Bt):k>0&&(le=Sn(Vt-Bt,An(Gt-Bt,le)),Q=vt,Kt=Bt+le));break}case ta:{H&&(st=Sn($t,An(St,dt-ne*H)),At=Sn($t,An(St,ct+ne*H))),k&&(Q=Sn(Vt,An(Gt,vt-le*k)),Kt=Sn(Vt,An(Gt,Bt+le*k)));break}}At0&&(dt=st-ne),k<0?Bt=Kt-le:k>0&&(vt=Q-le),W=bo,xe.attr("cursor",hr.selection),un(pe));break}default:return}Eo(pe)}function wn(pe){switch(pe.keyCode){case 16:{Oe&&(Ce=He=Oe=!1,un(pe));break}case 18:{W===ta&&(H<0?ct=At:H>0&&(dt=st),k<0?Bt=Kt:k>0&&(vt=Q),W=_r,un(pe));break}case 32:{W===bo&&(pe.altKey?(H&&(ct=At-ne*H,dt=st+ne*H),k&&(Bt=Kt-le*k,vt=Q+le*k),W=ta):(H<0?ct=At:H>0&&(dt=st),k<0?Bt=Kt:k>0&&(vt=Q),W=_r),xe.attr("cursor",hr[X]),un(pe));break}default:return}Eo(pe)}}function S(L){g(this,arguments).moved(L)}function P(L){g(this,arguments).ended(L)}function N(){var L=this.__brush||{selection:null};return L.extent=Ro(e.apply(this,arguments)),L.dim=t,L}return c.extent=function(L){return arguments.length?(e=typeof L=="function"?L:Ao(Ro(L)),c):e},c.filter=function(L){return arguments.length?(n=typeof L=="function"?L:Ao(!!L),c):n},c.touchable=function(L){return arguments.length?(a=typeof L=="function"?L:Ao(!!L),c):a},c.handleSize=function(L){return arguments.length?(s=+L,c):s},c.keyModifiers=function(L){return arguments.length?(i=!!L,c):i},c.on=function(){var L=o.on.apply(o,arguments);return L===o?c:L},c}function Xg(){return typeof globalThis=="object"&&globalThis!==null&&globalThis.Object===Object&&globalThis||typeof global=="object"&&global!==null&&global.Object===Object&&global||typeof self=="object"&&self!==null&&self.Object===Object&&self||Function("return this")()}function Hg(t){const e=typeof(t==null?void 0:t.requestAnimationFrame)=="function"&&typeof(t==null?void 0:t.cancelAnimationFrame)=="function",n=typeof(t==null?void 0:t.requestIdleCallback)=="function"&&typeof(t==null?void 0:t.cancelIdleCallback)=="function",a=o=>setTimeout(o,1),i=o=>clearTimeout(o);return[e?t.requestAnimationFrame:a,e?t.cancelAnimationFrame:i,n?t.requestIdleCallback:a,n?t.cancelIdleCallback:i]}const Ke=Xg(),gn=Ke==null?void 0:Ke.document,[Yg,_0,jl,t1]=Hg(Ke);var Wg=Object.defineProperty,Vl=Object.getOwnPropertySymbols,Kg=Object.prototype.hasOwnProperty,Zg=Object.prototype.propertyIsEnumerable,Gl=(t,e,n)=>e in t?Wg(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Xl=(t,e)=>{for(var n in e||(e={}))Kg.call(e,n)&&Gl(t,n,e[n]);if(Vl)for(var n of Vl(e))Zg.call(e,n)&&Gl(t,n,e[n]);return t};const De=t=>t||t===0,ve=t=>typeof t=="function",ze=t=>typeof t=="string",he=t=>typeof t=="number",ln=t=>typeof t=="undefined",Qe=t=>typeof t!="undefined",Co=t=>typeof t=="boolean",Jg=t=>Math.ceil(t/10)*10,$i=t=>Math.ceil(t)+.5,Dr=t=>t[1]-t[0],nr=t=>typeof t=="object",qn=t=>ln(t)||t===null||ze(t)&&t.length===0||nr(t)&&!(t instanceof Date)&&Object.keys(t).length===0||he(t)&&isNaN(t),cn=t=>!qn(t),je=t=>Array.isArray(t),Be=t=>t&&!(t!=null&&t.nodeType)&&nr(t)&&!je(t);function $r(t,e,n){return Qe(t[e])?t[e]:n}function Qg(t,e){let n=!1;return Object.keys(t).forEach(a=>t[a]===e&&(n=!0)),n}function _e(t,e,...n){const a=ve(t);return a&&t.call(e,...n),a}function Si(t,e){let n=0;const a=function(...i){!--n&&e.apply(this,...i)};"duration"in t?t.each(()=>++n).on("end",a):(++n,t.call(a))}function Po(t){return ze(t)?t.replace(/<(script|img)?/ig,"<").replace(/(script)?>/ig,">"):t}function wa(t,e,n=[-1,1],a=!1){if(!(!t||!ze(e)))if(e.indexOf(` +`)===-1)t.text(e);else{const i=[t.text(),e].map(o=>o.replace(/[\s\n]/g,""));if(i[0]!==i[1]){const o=e.split(` +`),s=a?o.length-1:1;t.html(""),o.forEach((l,c)=>{t.append("tspan").attr("x",0).attr("dy",`${c===0?n[0]*s:n[1]}em`).text(l)})}}}function Hl(t){const{x:e,y:n,width:a,height:i}=t.getBBox();return[{x:e,y:n+i},{x:e,y:n},{x:e+a,y:n},{x:e+a,y:n+i}]}function Yl(t){const{width:e,height:n}=t.getBoundingClientRect(),a=Hl(t),i=a[0].x,o=Math.min(a[0].y,a[1].y);return{x:i,y:o,width:e,height:n}}function Hn(t,e){var n;const a=t&&((n=t.touches||t.sourceEvent&&t.sourceEvent.touches)==null?void 0:n[0]);let i=[0,0];try{i=Xn(a||t,e)}catch(o){}return i.map(o=>isNaN(o)?0:o)}function Wl(t){const{event:e,$el:n}=t,a=n.subchart.main||n.main;let i;return e&&e.type==="brush"?i=e.selection:a&&(i=a.select(".bb-brush").node())&&(i=zl(i)),i}function Ma(t){return!("rect"in t)||"rect"in t&&t.hasAttribute("width")&&t.rect.width!==+t.getAttribute("width")?t.rect=t.getBoundingClientRect():t.rect}function gr(t=!0,e=0,n=1e4){const a=Ke.crypto||Ke.msCrypto,i=a?e+a.getRandomValues(new Uint32Array(1))[0]%(n-e+1):Math.floor(Math.random()*(n-e)+e);return t?String(i):i}function wo(t,e,n,a,i){if(n>a)return-1;const o=Math.floor((n+a)/2);let{x:s,w:l=0}=t[o];return i&&(s=t[o].y,l=t[o].h),e>=s&&e<=s+l?o:e{if(Be(n)&&n.constructor){const a=new n.constructor;for(const i in n)a[i]=e(n[i]);return a}return n};return t.map(n=>e(n)).reduce((n,a)=>Xl(Xl({},n),a))}function yn(t={},e){je(e)&&e.forEach(n=>yn(t,n));for(const n in e)/^\d+$/.test(n)||n in t||(t[n]=e[n]);return t}const Cn=t=>t.charAt(0).toUpperCase()+t.slice(1);function qg(t,e="-"){return t.split(e).map((n,a)=>a?n.charAt(0).toUpperCase()+n.slice(1).toLowerCase():n.toLowerCase()).join("")}const Lr=t=>[].slice.call(t);function _g(t,e,n){const{rootSelector:a="",sheet:i}=t,s=`${a} ${(l=>l.replace(/\s?(bb-)/g,".$1").replace(/\.+/g,"."))(e)} {${n.join(";")}}`;return i[i.insertRule?"insertRule":"addRule"](s,i.cssRules.length)}function tv(t){let e=[];return t.forEach(n=>{var a;try{n.cssRules&&n.cssRules.length&&(e=e.concat(Lr(n.cssRules)))}catch(i){(a=Ke.console)==null||a.warn(`Error while reading rules from ${n.href}: ${i.toString()}`)}}),e}function Zl(t){var e,n,a,i,o,s;return{x:((n=(e=Ke.pageXOffset)!=null?e:Ke.scrollX)!=null?n:0)+((a=t.scrollLeft)!=null?a:0),y:((o=(i=Ke.pageYOffset)!=null?i:Ke.scrollY)!=null?o:0)+((s=t.scrollTop)!=null?s:0)}}function Ai(t,e=0,n=0,a=!0){const i=new DOMPoint(e,n),o=t.getScreenCTM(),s=i.matrixTransform(a?o==null?void 0:o.inverse():o);if(a===!1){const l=t.getBoundingClientRect();s.x-=l.x,s.y-=l.y}return s}function Jl(t){const e=t?t.transform:null,n=e&&e.baseVal;return n&&n.numberOfItems?n.getItem(0).matrix:{a:0,b:0,c:0,d:0,e:0,f:0}}function Mo(t){const e=t[0]instanceof Date,n=(e?t.map(Number):t).filter((a,i,o)=>o.indexOf(a)===i);return e?n.map(a=>new Date(a)):n}function Do(t){return t&&t.length?t.reduce((e,n)=>e.concat(n)):[]}function ea(t,...e){if(!e.length||e.length===1&&!e[0])return t;const n=e.shift();return Be(t)&&Be(n)&&Object.keys(n).forEach(a=>{if(!/^(__proto__|constructor|prototype)$/i.test(a)){const i=n[a];Be(i)?(!t[a]&&(t[a]={}),t[a]=ea(t[a],i)):t[a]=je(i)?i.concat():i}}),ea(t,...e)}function na(t,e=!0){let n;return t[0]instanceof Date?n=e?(a,i)=>a-i:(a,i)=>i-a:e&&!t.every(isNaN)?n=(a,i)=>a-i:e||(n=(a,i)=>a>i&&-1||acn(a));return n.length?he(n[0])?n=Math[t](...n):n[0]instanceof Date&&(n=na(n,t==="min")[0]):n=void 0,n}const Ei=(t,e,n=1)=>{const a=[],i=Math.max(0,Math.ceil((e-t)/n))|0;for(let o=t;o{const t=()=>({bubbles:!1,cancelable:!1,screenX:0,screenY:0,clientX:0,clientY:0});try{return new MouseEvent("t"),(e,n,a=t())=>{e.dispatchEvent(new MouseEvent(n,a))}}catch(e){return(n,a,i=t())=>{const o=gn.createEvent("MouseEvent");o.initMouseEvent(a,i.bubbles,i.cancelable,Ke,0,i.screenX,i.screenY,i.clientX,i.clientY,!1,!1,!1,!1,0,null),n.dispatchEvent(o)}}})(),touch:(t,e,n)=>{const a=new Touch(ea({identifier:Date.now(),target:t,radiusX:2.5,radiusY:2.5,rotationAngle:10,force:.5},n));t.dispatchEvent(new TouchEvent(e,{cancelable:!0,bubbles:!0,shiftKey:!0,touches:[a],targetTouches:[],changedTouches:[a]}))}};function bi(t,e){let n=t;for(const a in e)n=n.replace(new RegExp(`{=${a}}`,"g"),e[a]);return n}function Yn(t){var e;let n;if(t instanceof Date)n=t;else if(ze(t)){const{config:a,format:i}=this;n=(e=i.dataTime(a.data_xFormat)(t))!=null?e:new Date(t)}else he(t)&&!isNaN(t)&&(n=new Date(+t));return(!n||isNaN(+n))&&console&&console.error&&console.error(`Failed to parse x '${t}' to Date object`),n}function Lo(t){const e=t.attr("viewBox");return e?/(\d+(\.\d+)?){3}/.test(e):!1}function nv(t,e,n=!1){const a=!!t.node;let i=!1;for(const[o,s]of Object.entries(e))if(i=a?t.style(o)===s:t.style[o]===s,n===!1&&i)break;return i}function Da(){var t,e;return((t=gn)==null?void 0:t.hidden)===!1||((e=gn)==null?void 0:e.visibilityState)==="visible"}function rv(t,e){const{DocumentTouch:n,matchMedia:a,navigator:i}=Ke,o=a==null?void 0:a("(pointer:coarse)").matches;let s=!1;if(e)if(i&&"maxTouchPoints"in i)s=i.maxTouchPoints>0;else if("ontouchmove"in Ke||n&&gn instanceof n)s=!0;else if(o)s=!0;else{const c=i.userAgent;s=/\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(c)||/\b(Android|Windows Phone|iPad|iPod)\b/i.test(c)}return t&&!o&&(a==null?void 0:a("(pointer:fine)").matches)&&"mouse"||s&&"touch"||"mouse"}function Ql(t,e){e()===!1?Yg(()=>Ql(t,e)):t()}var av=Object.defineProperty,kl=Object.getOwnPropertySymbols,iv=Object.prototype.hasOwnProperty,ov=Object.prototype.propertyIsEnumerable,No=(t,e,n)=>e in t?av(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,ql=(t,e)=>{for(var n in e||(e={}))iv.call(e,n)&&No(t,n,e[n]);if(kl)for(var n of kl(e))ov.call(e,n)&&No(t,n,e[n]);return t},sv=(t,e,n)=>No(t,typeof e!="symbol"?e+"":e,n);const _l=class bf{static setOptions(e){this.data=e.reduce((n,a)=>ql(ql({},n),a),this.data)}constructor(){return kg(Vd,Ud,Hd,zd,Yd,jd,Gd,Xd,bf.data)}};sv(_l,"data",{});let Nr=_l;class lv{constructor(){return{chart:null,main:null,svg:null,axis:{x:null,y:null,y2:null,subX:null},axisTooltip:{x:null,y:null,y2:null},defs:null,tooltip:null,legend:null,title:null,subchart:{main:null,bar:null,line:null,area:null},arcs:null,bar:null,candlestick:null,line:null,area:null,circle:null,radar:null,text:null,grid:{main:null,x:null,y:null},gridLines:{main:null,x:null,y:null},region:{main:null,list:null},eventRect:null,zoomResetBtn:null}}}class cv{constructor(){return{width:0,width2:0,height:0,height2:0,margin:{top:0,bottom:0,left:0,right:0},margin2:{top:0,bottom:0,left:0,right:0},margin3:{top:0,bottom:0,left:0,right:0},arcWidth:0,arcHeight:0,xAxisHeight:0,hasAxis:!1,hasFunnel:!1,hasRadar:!1,hasTreemap:!1,cssRule:{},current:{domain:void 0,width:0,height:0,dataMax:0,maxTickSize:{x:{width:0,height:0,ticks:[],clipPath:0,domain:""},y:{width:0,height:0,domain:""},y2:{width:0,height:0,domain:""}},types:[],needle:void 0},isLegendRight:!1,isLegendInset:!1,isLegendTop:!1,isLegendLeft:!1,legendStep:0,legendItemWidth:0,legendItemHeight:0,legendHasRendered:!1,eventReceiver:{currentIdx:-1,rect:{},data:[],coords:[]},axis:{x:{padding:{left:0,right:0},tickCount:0}},rotatedPadding:{left:30,right:0,top:5},withoutFadeIn:{},inputType:"",datetimeId:"",clip:{id:"",idXAxis:"",idYAxis:"",idXAxisTickTexts:"",idGrid:"",idSubchart:"",path:"",pathXAxis:"",pathYAxis:"",pathXAxisTickTexts:"",pathGrid:""},event:null,dragStart:null,dragging:!1,flowing:!1,cancelClick:!1,mouseover:!1,rendered:!1,transiting:!1,redrawing:!1,resizing:!1,toggling:!1,zooming:!1,hasNegativeValue:!1,hasPositiveValue:!0,orgAreaOpacity:"0.2",orgConfig:{},hiddenTargetIds:[],hiddenLegendIds:[],focusedTargetIds:[],defocusedTargetIds:[],radius:0,innerRadius:0,outerRadius:void 0,innerRadiusRatio:0,gaugeArcWidth:0,radiusExpanded:0,xgridAttr:{x1:null,x2:null,y1:null,y2:null}}}}const tc={element:lv,state:cv};class uv{constructor(){Object.keys(tc).forEach(e=>{this[e]=new tc[e]})}getStore(e){return this[e]}}var fv=Object.defineProperty,dv=(t,e,n)=>e in t?fv(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,hv=(t,e,n)=>dv(t,typeof e!="symbol"?e+"":e,n);const Ln={bubbleBaseLength:"$baseLength",colorPattern:"__colorPattern__",dataMinMax:"$dataMinMax",dataTotalSum:"$dataTotalSum",dataTotalPerIndex:"$totalPerIndex",legendItemTextBox:"legendItemTextBox",radarPoints:"$radarPoints",radarTextWidth:"$radarTextWidth",setOverOut:"setOverOut",callOverOutForTouch:"callOverOutForTouch",textRect:"textRect"};class gv{constructor(){hv(this,"cache",{})}add(e,n,a=!1){return this.cache[e]=a?this.cloneTarget(n):n,this.cache[e]}remove(e){(ze(e)?[e]:e).forEach(n=>delete this.cache[n])}get(e,n=!1){if(n&&Array.isArray(e)){const a=[];for(let i=0,o;o=e[i];i++)o in this.cache&&a.push(this.cloneTarget(this.cache[o]));return a}else{const a=this.cache[e];return De(a)?a:null}}reset(e){const n=this;for(const a in n.cache)(e||/^\$/.test(a))&&(n.cache[a]=null)}cloneTarget(e){return{id:e.id,id_org:e.id_org,values:e.values.map(n=>({x:n.x,value:n.value,id:n.id}))}}}const oe={AREA:"area",AREA_LINE_RANGE:"area-line-range",AREA_SPLINE:"area-spline",AREA_SPLINE_RANGE:"area-spline-range",AREA_STEP:"area-step",AREA_STEP_RANGE:"area-step-range",BAR:"bar",BUBBLE:"bubble",CANDLESTICK:"candlestick",DONUT:"donut",FUNNEL:"funnel",GAUGE:"gauge",LINE:"line",PIE:"pie",POLAR:"polar",RADAR:"radar",SCATTER:"scatter",SPLINE:"spline",STEP:"step",TREEMAP:"treemap"},Fo={AREA:"initArea",AREA_LINE_RANGE:"initArea",AREA_SPLINE:"initArea",AREA_SPLINE_RANGE:"initArea",AREA_STEP:"initArea",AREA_STEP_RANGE:"initArea",BAR:"initBar",BUBBLE:"initCircle",CANDLESTICK:"initCandlestick",DONUT:"initArc",FUNNEL:"initFunnel",GAUGE:"initArc",LINE:"initLine",PIE:"initArc",POLAR:"initPolar",RADAR:"initCircle",SCATTER:"initCircle",SPLINE:"initLine",STEP:"initLine",TREEMAP:"initTreemap"},Sr={Area:[oe.AREA,oe.AREA_SPLINE,oe.AREA_SPLINE_RANGE,oe.AREA_LINE_RANGE,oe.AREA_STEP,oe.AREA_STEP_RANGE],AreaRange:[oe.AREA_SPLINE_RANGE,oe.AREA_LINE_RANGE,oe.AREA_STEP_RANGE],Arc:[oe.PIE,oe.DONUT,oe.GAUGE,oe.POLAR,oe.RADAR],Line:[oe.LINE,oe.SPLINE,oe.AREA,oe.AREA_SPLINE,oe.AREA_SPLINE_RANGE,oe.AREA_LINE_RANGE,oe.STEP,oe.AREA_STEP,oe.AREA_STEP_RANGE],Step:[oe.STEP,oe.AREA_STEP,oe.AREA_STEP_RANGE],Spline:[oe.SPLINE,oe.AREA_SPLINE,oe.AREA_SPLINE_RANGE]};function vv(t){const e=t,{config:n}=e;let a="";if(qn(n.data_type||n.data_types)&&!e[Fo.LINE])a="line";else for(const i in Fo){const o=oe[i];if(e.hasType(o)&&!e[Fo[i]]){a=o;break}}a&&pv(`Please, make sure if %c${qg(a)}`,"module has been imported and specified correctly.","https://github.com/naver/billboard.js/wiki/CHANGELOG-v2#modularization-by-its-functionality")}function pv(t,e,n){var a;const i="[billboard.js]";if((a=Ke.console)==null?void 0:a.error){const s=e?["background:red;color:white;display:block;font-size:15px",e]:[];console.error(`\u274C ${i} ${t}`,"background:red;color:white;display:block;font-size:15px",...s),n&&console.info("%c\u2139\uFE0F","font-size:15px",n)}throw Error(`${i} ${t.replace(/\%c([a-z-]+)/i,"'$1' ")} ${e!=null?e:""}`)}const{setTimeout:mv,clearTimeout:yv}=Ke;function xv(t){const e=[];let n;const a=function(){a.clear(),t===!1?jl(()=>{e.forEach(i=>i())},{timeout:200}):n=mv(()=>{e.forEach(i=>i())},he(t)?t:200)};return a.clear=()=>{n&&(yv(n),n=null)},a.add=i=>e.push(i),a.remove=i=>e.splice(e.indexOf(i),1),a}function ec(){let t=[];const e=function(n,a){function i(){var o;let s=0;for(let l=0,c;c=t[l];l++){if(c===!0||(o=c.empty)!=null&&o.call(c)){s++;continue}if(Da()===!1){s=t.length;break}try{c.transition()}catch(f){s++}}return s===t.length}Ql(()=>{a==null||a()},i)};return e.add=function(n){je(n)?t=t.concat(n):t.push(n)},e}const Bo={};function Tv(t,e){var n;const a=t.toString(),i=a.replace(/(function|[\s\W\n])/g,"").substring(0,15);return i in Bo||(Bo[i]=new Ke.Blob([`${(n=e==null?void 0:e.map(String).join(";"))!=null?n:""} + + self.onmessage=function({data}) { + const result = (${a}).apply(null, data); + self.postMessage(result); + };`],{type:"text/javascript"})),Ke.URL.createObjectURL(Bo[i])}function $v(t){const e=new Ke.Worker(t);return e.onerror=function(n){console.error?console.error(n):console.log(n)},e}function Uo(t=!0,e,n,a){let i=function(...o){const s=e(...o);n(s)};if(Ke.Worker&&t){const o=Tv(e,a),s=$v(o);i=function(...l){s.postMessage(l),s.onmessage=function(c){return Ke.URL.revokeObjectURL(o),n(c.data)}}}return i}var nc={},zo={},jo=34,La=10,Vo=13;function rc(t){return new Function("d","return {"+t.map(function(e,n){return JSON.stringify(e)+": d["+n+'] || ""'}).join(",")+"}")}function Sv(t,e){var n=rc(t);return function(a,i){return e(n(a),i,t)}}function ac(t){var e=Object.create(null),n=[];return t.forEach(function(a){for(var i in a)i in e||n.push(e[i]=i)}),n}function Nn(t,e){var n=t+"",a=n.length;return a9999?"+"+Nn(t,6):Nn(t,4)}function Ev(t){var e=t.getUTCHours(),n=t.getUTCMinutes(),a=t.getUTCSeconds(),i=t.getUTCMilliseconds();return isNaN(t)?"Invalid Date":Av(t.getUTCFullYear(),4)+"-"+Nn(t.getUTCMonth()+1,2)+"-"+Nn(t.getUTCDate(),2)+(i?"T"+Nn(e,2)+":"+Nn(n,2)+":"+Nn(a,2)+"."+Nn(i,3)+"Z":a?"T"+Nn(e,2)+":"+Nn(n,2)+":"+Nn(a,2)+"Z":n||e?"T"+Nn(e,2)+":"+Nn(n,2)+"Z":"")}function ic(t){var e=new RegExp('["'+t+` +\r]`),n=t.charCodeAt(0);function a(v,m){var S,P,N=i(v,function(L,w){if(S)return S(L,w-1);P=L,S=m?Sv(L,m):rc(L)});return N.columns=P||[],N}function i(v,m){var S=[],P=v.length,N=0,L=0,w,X=P<=0,W=!1;v.charCodeAt(P-1)===La&&--P,v.charCodeAt(P-1)===Vo&&--P;function H(){if(X)return zo;if(W)return W=!1,nc;var K,at=N,ht;if(v.charCodeAt(at)===jo){for(;N++=P?X=!0:(ht=v.charCodeAt(N++))===La?W=!0:ht===Vo&&(W=!0,v.charCodeAt(N)===La&&++N),v.slice(at+1,K-1).replace(/""/g,'"')}for(;N0){if(typeof e[s-1]=="undefined"&&(e[s-1]={}),typeof o=="undefined")throw new Error(`Source data is missing a component at (${a}, ${s})!`);e[s-1][i]=o}})}),e}function Xo(t){const e=t[0],n=[];return t.forEach(function(a,i){if(i>0){const o={};a.forEach(function(s,l){if(typeof s=="undefined")throw new Error(`Source data is missing a component at (${i}, ${l})!`);o[e[l]]=s}),n.push(o)}}),n}function oc(t,e){const n=[];let a,i;if(Array.isArray(t)){const o=function(s,l){if(s[l]!==void 0)return s[l];const f=l.replace(/\[(\w+)\]/g,".$1").replace(/^\./,"").split(".");let g=s;return f.some(function(v){return!(g=g&&v in g?g[v]:void 0)}),g};e.x?a=e.value.concat(e.x):a=e.value,n.push(a),t.forEach(function(s){const l=a.map(function(c){let f=o(s,c);return typeof f=="undefined"&&(f=null),f});n.push(l)}),i=Xo(n)}else Object.keys(t).forEach(function(o){var s;const l=t[o].concat();(s=l.unshift)==null||s.call(l,o),n.push(l)}),i=Go(n);return i}function Cv(t,e="csv",n,a,i){const o=new XMLHttpRequest,s={csv:Pv,tsv:wv,json:oc};o.open("GET",t),n&&Object.keys(n).forEach(function(l){o.setRequestHeader(l,n[l])}),o.onreadystatechange=function(){if(o.readyState===4)if(o.status===200){const l=o.responseText;l&&i.call(this,s[e](e==="json"?JSON.parse(l):l,a))}else throw new Error(`${t}: Something went wrong loading!`)},o.send()}function sc(t,e){const n=t.rows(e);let a;return n.length===1?(a=[{}],n[0].forEach(i=>{a[0][i]=null})):a=t.parse(e),a}function Pv(t){return sc({rows:Rv,parse:bv},t)}function wv(t){return sc({rows:Ov,parse:Iv},t)}function lc(t,e){const n=t||(e==null?void 0:e.data_keys);return n!=null&&n.x&&(e.data_x=n.x),n}var Mv={convertData(t,e){const{config:n}=this,a=n.boost_useWorker;let i=t;if(t.bindto&&(i={},["url","mimeType","headers","keys","json","keys","rows","columns"].forEach(o=>{const s=`data_${o}`;s in t&&(i[o]=t[s])})),i.url&&e)Cv(i.url,i.mimeType,i.headers,lc(i.keys,n),e);else if(i.json)Uo(a,oc,e,[Go,Xo])(i.json,lc(i.keys,n));else if(i.rows)Uo(a,Xo,e)(i.rows);else if(i.columns)Uo(a,Go,e)(i.columns);else if(t.bindto)throw Error("url or json or rows or columns is required.")},convertDataToTargets(t,e){const n=this,{axis:a,config:i,state:o}=n,s=i.data_type;let l=!1,c=!1,f=!1;a&&(l=a.isCategorized(),c=a.isTimeSeries(),f=a.isCustomX());const g=Object.keys(t[0]||{}),v=g.length?g.filter(n.isNotX,n):[],m=g.length?g.filter(n.isX,n):[];let S;v.forEach(N=>{const L=this.getXKey(N);f||c?m.indexOf(L)>=0?S=(e&&n.data.xs[N]||[]).concat(t.map(w=>w[L]).filter(De).map((w,X)=>n.generateTargetX(w,N,X))):i.data_x?S=this.getOtherTargetXs():cn(i.data_xs)&&(S=n.getXValuesOfXKey(L,n.data.targets)):S=t.map((w,X)=>X),S&&(this.data.xs[N]=S)}),v.forEach(N=>{if(!this.data.xs[N])throw new Error(`x is not defined for id = "${N}".`)});const P=v.map((N,L)=>{const w=i.data_idConverter.bind(n.api)(N),X=n.getXKey(N),W=f&&l,H=W&&t.map(at=>at.x).every(at=>i.axis_x_categories.indexOf(at)>-1),k=t.__append__,K=X===null&&k?n.api.data.values(N).length:0;return{id:w,id_org:N,values:t.map((at,ht)=>{const $t=at[X];let dt=at[N],st;return dt=dt!==null&&!isNaN(dt)&&!Be(dt)?+dt:je(dt)||Be(dt)?dt:null,(W||o.hasRadar)&&L===0&&!ln($t)?(!H&&L===0&&ht===0&&!k&&(i.axis_x_categories=[]),st=i.axis_x_categories.indexOf($t),st===-1&&(st=i.axis_x_categories.length,i.axis_x_categories.push($t))):st=n.generateTargetX($t,N,K+ht),(ln(dt)||n.data.xs[N].length<=ht)&&(st=void 0),{x:st,value:dt,id:w,index:-1}}).filter(at=>Qe(at.x))}});if(P.forEach(N=>{var L;i.data_xSort&&(N.values=N.values.sort((w,X)=>{const W=w.x||w.x===0?w.x:1/0,H=X.x||X.x===0?X.x:1/0;return W-H})),N.values.forEach((w,X)=>w.index=X),(L=n.data.xs[N.id])==null||L.sort((w,X)=>w-X)}),o.hasNegativeValue=n.hasNegativeValueInTargets(P),o.hasPositiveValue=n.hasPositiveValueInTargets(P),s&&n.isValidChartType(s)){const N=n.mapToIds(P).filter(L=>!(L in i.data_types)||!n.isValidChartType(i.data_types[L]));n.setTargetType(N,s)}return P.forEach(N=>n.cache.add(N.id_org,N,!0)),P}},Dv={isX(t){const e=this,{config:n}=e,a=n.data_x&&t===n.data_x,i=cn(n.data_xs)&&Qg(n.data_xs,t);return a||i},isNotX(t){return!this.isX(t)},isStackNormalized(){const{config:t}=this;return!!(t.data_stack_normalize&&t.data_groups.length)},isGrouped(t){const e=this.config.data_groups;return t?e.some(n=>n.indexOf(t)>=0&&n.length>1):e.length>0},getXKey(t){const e=this,{config:n}=e;return n.data_x?n.data_x:cn(n.data_xs)?n.data_xs[t]:null},getXValuesOfXKey(t,e){const n=this,a=e&&cn(e)?n.mapToIds(e):[];let i;return a.forEach(o=>{n.getXKey(o)===t&&(i=n.data.xs[o])}),i},getIndexByX(t,e){const n=this;return e?e.indexOf(ze(t)?t:+t):(n.filterByX(n.data.targets,t)[0]||{index:null}).index},getXValue(t,e){const n=this;return t in n.data.xs&&n.data.xs[t]&&De(n.data.xs[t][e])?n.data.xs[t][e]:e},getOtherTargetXs(){const t=this,e=Object.keys(t.data.xs);return e.length?t.data.xs[e[0]]:null},getOtherTargetX(t){const e=this.getOtherTargetXs();return e&&t{n.data_xs[a]=t[a]})},isMultipleX(){return!this.config.axis_x_forceAsSingle&&(cn(this.config.data_xs)||this.hasType("bubble")||this.hasType("scatter"))},addName(t){const e=this,{config:n}=e;let a;return t&&(a=n.data_names[t.id],t.name=a!==void 0?a:t.id),t},getAllValuesOnIndex(t,e=!1){const n=this;let a=n.filterTargetsToShow(n.data.targets).map(i=>n.addName(n.getValueOnIndex(i.values,t)));return e&&(a=a.filter(i=>i&&"value"in i&&De(i.value))),a},getValueOnIndex(t,e){const n=t.filter(a=>a.index===e);return n.length?n[0]:null},updateTargetX(t,e){const n=this;t.forEach(a=>{a.values.forEach((i,o)=>{i.x=n.generateTargetX(e[o],a.id,o)}),n.data.xs[a.id]=e})},updateTargetXs(t,e){const n=this;t.forEach(a=>{e[a.id]&&n.updateTargetX([a],e[a.id])})},generateTargetX(t,e,n){const a=this,{axis:i}=a;let o=i!=null&&i.isCategorized()?n:t||n;if(i!=null&&i.isTimeSeries()){const s=Yn.bind(a);o=s(t||a.getXValue(e,n))}else i!=null&&i.isCustomX()&&!(i!=null&&i.isCategorized())&&(o=De(t)?+t:a.getXValue(e,n));return o},updateXs(t){t.length&&(this.axis.xs=t.map(e=>e.x))},getPrevX(t){const e=this.axis.xs[t-1];return Qe(e)?e:null},getNextX(t){const e=this.axis.xs[t+1];return Qe(e)?e:null},getBaseValue(t){const e=this,{hasAxis:n}=e.state;let{value:a}=t;return a&&n&&(e.isAreaRangeType(t)?a=e.getRangedData(t,"mid"):e.isBubbleZType(t)&&(a=e.getBubbleZData(a,"y"))),a},getMinMaxValue(t){const e=this.getBaseValue.bind(this);let n,a;return(t||this.data.targets.map(i=>i.values)).forEach((i,o)=>{const s=i.map(e).filter(he);n=Math.min(o?n:1/0,...s),a=Math.max(o?a:-1/0,...s)}),{min:n,max:a}},getMinMaxData(){const t=this,e=Ln.dataMinMax;let n=t.cache.get(e);if(!n){const a=t.data.targets.map(l=>l.values),i=t.getMinMaxValue(a);let o=[],s=[];a.forEach(l=>{const c=t.getFilteredDataByValue(l,i.min),f=t.getFilteredDataByValue(l,i.max);c.length&&(o=o.concat(c)),f.length&&(s=s.concat(f))}),t.cache.add(e,n={min:o,max:s})}return n},getTotalPerIndex(){const t=this,e=Ln.dataTotalPerIndex;let n=t.cache.get(e);return(t.config.data_groups.length||t.isStackNormalized())&&!n&&(n=[],t.data.targets.forEach(a=>{a.values.forEach((i,o)=>{n[o]||(n[o]=0),n[o]+=he(i.value)?i.value:0})})),n},getTotalDataSum(t){const e=this,n=Ln.dataTotalSum;let a=e.cache.get(n);if(!he(a)){const i=Do(e.data.targets.map(o=>o.values)).map(o=>o.value);a=i.length?i.reduce((o,s)=>o+s):0,e.cache.add(n,a)}return t&&(a-=e.getHiddenTotalDataSum()),a},getHiddenTotalDataSum(){const t=this,{api:e,state:{hiddenTargetIds:n}}=t;let a=0;return n.length&&(a=e.data.values.bind(e)(n).reduce((i,o)=>i+o)),a},getFilteredDataByValue(t,e){return t.filter(n=>this.getBaseValue(n)===e)},getMaxDataCount(){return Math.max(...this.data.targets.map(t=>t.values.length),0)},getMaxDataCountTarget(){let t=this.filterTargetsToShow()||[];const e=t.length,n=this.config.axis_x_inverted;return e>1?(t=t.map(a=>a.values).reduce((a,i)=>a.concat(i)).map(a=>a.x),t=na(Mo(t)).map((a,i,o)=>({x:a,index:n?o.length-i-1:i}))):e&&(t=t[0].values.concat()),t},mapToIds(t){return t.map(e=>e.id)},mapToTargetIds(t){const e=this;return t?je(t)?t.concat():[t]:e.mapToIds(e.data.targets)},hasTarget(t,e){const n=this.mapToIds(t);for(let a=0,i;i=n[a];a++)if(i===e)return!0;return!1},isTargetToShow(t){return this.state.hiddenTargetIds.indexOf(t)<0},isLegendToShow(t){return this.state.hiddenLegendIds.indexOf(t)<0},filterTargetsToShow(t){const e=this;return(t||e.data.targets).filter(n=>e.isTargetToShow(n.id))},mapTargetsToUniqueXs(t){const e=this,{axis:n}=e;let a=[];return t!=null&&t.length&&(a=Mo(Do(t.map(i=>i.values.map(o=>+o.x)))),a=n!=null&&n.isTimeSeries()?a.map(i=>new Date(+i)):a.map(Number)),na(a)},addTargetIds(t,e){const{state:n}=this;(je(e)?e:[e]).forEach(i=>{n[t].indexOf(i)<0&&n[t].push(i)})},removeTargetIds(t,e){const{state:n}=this;(je(e)?e:[e]).forEach(i=>{const o=n[t].indexOf(i);o>=0&&n[t].splice(o,1)})},addHiddenTargetIds(t){this.addTargetIds("hiddenTargetIds",t)},removeHiddenTargetIds(t){this.removeTargetIds("hiddenTargetIds",t)},addHiddenLegendIds(t){this.addTargetIds("hiddenLegendIds",t)},removeHiddenLegendIds(t){this.removeTargetIds("hiddenLegendIds",t)},getValuesAsIdKeyed(t){const e=this,{hasAxis:n}=e.state,a={},i=e.isMultipleX(),o=i?e.mapTargetsToUniqueXs(t).map(s=>ze(s)?s:+s):null;return t.forEach(s=>{const l=[];s.values.filter(({value:c})=>De(c)||c===null).forEach(c=>{let{value:f}=c;f!==null&&e.isCandlestickType(c)&&(f=je(f)?f.slice(0,4):[f.open,f.high,f.low,f.close]),je(f)?l.push(...f):Be(f)&&"high"in f?l.push(...Object.values(f)):e.isBubbleZType(c)?l.push(n&&e.getBubbleZData(f,"y")):i?l[e.getIndexByX(c.x,o)]=f:l.push(f)}),a[s.id]=l}),a},checkValueInTargets(t,e){const n=Object.keys(t);let a;for(let i=0;i1},hasNegativeValueInTargets(t){return this.checkValueInTargets(t,e=>e<0)},hasPositiveValueInTargets(t){return this.checkValueInTargets(t,e=>e>0)},orderTargets(t){const e=this,n=[...t],a=e.getSortCompareFn();return a&&n.sort(a),n},getSortCompareFn(t=!1){const e=this,{config:n}=e,a=n.data_order,i=/asc/i.test(a),o=/desc/i.test(a);let s;if(i||o){const l=(f,g)=>f+Math.abs(g.value),c=f=>he(f)?f:"values"in f?f.values.reduce(l,0):f.value;s=(f,g)=>{const v=c(f),m=c(g);return t?i?v-m:m-v:i?m-v:v-m}}else ve(a)&&(s=a.bind(e.api));return s||null},filterByX(t,e){return Do(t.map(n=>n.values)).filter(n=>n.x-e===0)},filterRemoveNull(t){return t.filter(e=>De(this.getBaseValue(e)))},filterByXDomain(t,e){return t.map(n=>({id:n.id,id_org:n.id_org,values:n.values.filter(a=>e[0]<=a.x&&a.x<=e[1])}))},hasDataLabel(){const t=this.config.data_labels;return Co(t)&&t||nr(t)&&cn(t)},hasNullDataValue(t){return t.some(({value:e})=>e===null)},getDataIndexFromEvent(t){const e=this,{$el:n,config:a,state:{hasRadar:i,inputType:o,eventReceiver:{coords:s,rect:l}}}=e;let c;if(i){let f=t.target;/tspan/i.test(f.tagName)&&(f=f.parentNode);const g=ot(f).datum();c=g&&Object.keys(g).length===1?g.index:void 0}else{const f=a.axis_rotated,g=Zl(n.chart.node()),v=o==="touch"&&t.changedTouches?t.changedTouches[0]:t;let m=f?v.clientY+g.y:v.clientX+g.x;if(Lo(n.svg)){const S=[m,0];f&&S.reverse(),m=Ai(n.eventRect.node(),...S)[f?"y":"x"]}else m-=f?l.top:l.left;c=wo(s,m,0,s.length-1,f)}return c},getDataLabelLength(t,e,n){const a=this,i=[0,0],o=1.3;return a.$el.chart.select("svg").selectAll(".dummy").data([t,e]).enter().append("text").text(s=>a.dataLabelFormat(s.id)(s)).each(function(s,l){i[l]=this.getBoundingClientRect()[n]*o}).remove(),i},isNoneArc(t){return this.hasTarget(this.data.targets,t.id)},isArc(t){return"data"in t&&this.hasTarget(this.data.targets,t.data.id)},findSameXOfValues(t,e){const n=t[e].x,a=[];let i;for(i=e-1;i>=0&&n===t[i].x;i--)a.push(t[i]);for(i=e;in.findClosest(i.values,e));return n.findClosest(a,e)},findClosest(t,e){const n=this,{$el:{main:a}}=n,i=t.filter(l=>l&&De(l.value));let o,s;return i.filter(l=>n.isBarType(l.id)||n.isCandlestickType(l.id)).forEach(l=>{const c=n.isBarType(l.id)?`.${Kn.chartBar}.${Se.target}${n.getTargetSelectorSuffix(l.id)} .${Kn.bar}-${l.index}`:`.${cr.chartCandlestick}.${Se.target}${n.getTargetSelectorSuffix(l.id)} .${cr.candlestick}-${l.index} path`;!s&&n.isWithinBar(a.select(c).node())&&(s=l)}),i.filter(l=>!n.isBarType(l.id)&&!n.isCandlestickType(l.id)).forEach(l=>{const c=n.dist(l,e);o=n.getPointSensitivity(l),c{const{x:i,id:o}=a;n.push({x:i,id:o,value:a.value[0]}),n.push({x:i,id:o,value:a.value[2]})}),n},updateDataAttributes(t,e){const n=this,{config:a}=n,i=a[`data_${t}`];return ln(e)||(Object.keys(e).forEach(o=>{i[o]=e[o]}),n.redraw({withLegend:!0})),i},getRangedData(t,e="",n="areaRange"){const a=t==null?void 0:t.value;if(je(a)){if(n==="bar")return a.reduce((i,o)=>o-i);{const i={areaRange:["high","mid","low"],candlestick:["open","high","low","close","volume"]}[n].indexOf(e);return i>=0&&a?a[i]:void 0}}else if(a&&e)return a[e];return a},setRatioForGroupedData(t){const e=this,{config:n}=e;if(n.data_groups.length&&t.some(a=>e.isGrouped(a.id))){const a=i=>e.getRatio("index",i,!0);t.forEach(i=>{"values"in i?i.values.forEach(a):a(i)})}},getRatio(t,e,n=!1){const a=this,{config:i,state:o}=a,s=a.api;let l=0;if(e&&s.data.shown().length)if(l=e.ratio||e.value,t==="arc")if(a.pie.padAngle()())l=e.value/a.getTotalDataSum(!0);else{const c=i.gauge_fullCircle?a.getArcLength():a.getStartingAngle()*-2,f=a.hasType("gauge")?c:Math.PI*2;l=(e.endAngle-e.startAngle)/f}else if(t==="index"){const c=s.data.values.bind(s);let f=this.getTotalPerIndex();if(o.hiddenTargetIds.length){let v=c(o.hiddenTargetIds,!1);v.length&&(v=v.reduce((m,S)=>m.map((P,N)=>(he(P)?P:0)+S[N])),f=f.map((m,S)=>m-v[S]))}const g=f[e.index];e.ratio=he(e.value)&&f&&g?e.value/g:0,l=e.ratio}else if(t==="radar")l=parseFloat(String(Math.max(e.value,0)))/o.current.dataMax*i.radar_size_ratio;else if(t==="bar"){const f=a.getYScaleById.bind(a)(e.id).domain().reduce((g,v)=>v-g);l=f===0?0:Math.abs(a.getRangedData(e,null,t)/f)}else t==="treemap"&&(l/=a.getTotalDataSum(!0));return n&&l?l*100:l},updateDataIndexByX(t){const e=this,n=t.reduce((a,i,o)=>(a[Number(i.x)]=o,a),{});e.data.targets.forEach(a=>{a.values.forEach((i,o)=>{let s=n[Number(i.x)];s===void 0&&(s=o),i.index=s})})},isBubbleZType(t){return this.isBubbleType(t)&&(Be(t.value)&&("z"in t.value||"y"in t.value)||je(t.value)&&t.value.length>=2)},isBarRangeType(t){const e=this,{value:n}=t;return e.isBarType(t)&&je(n)&&n.length>=2&&n.every(a=>he(a))},getDataById(t){var e;const n=this.cache.get(t)||this.api.data(t);return(e=n==null?void 0:n[0])!=null?e:n}};function cc(t,e=!1){const n=this,{api:a}=n;e&&n.api.flush(!0),t==null||t.call(a)}var Lv={load(t,e){const n=this,{axis:a,data:i,org:o,scale:s}=n,{append:l}=e,c={domain:null,currentDomain:null,x:null};let f=t;f&&(e.filter&&(f=f.filter(e.filter)),(e.type||e.types)&&f.forEach(g=>{var v;const m=((v=e.types)==null?void 0:v[g.id])||e.type;n.setTargetType(g.id,m)}),i.targets.forEach(g=>{for(let v=0;v{const a=t.data||n;t.append&&(a.__append__=!0),a&&e.load(e.convertDataToTargets(a),t)}))},unload(t,e){var n;const a=this,{state:i,$el:o,$T:s}=a,l=!!((n=a.hasLegendDefsPoint)!=null&&n.call(a));let c=e,f=t;if(a.cache.reset(),c||(c=()=>{}),f=f.filter(v=>a.hasTarget(a.data.targets,v)),!f||f.length===0){c();return}const g=o.svg.selectAll(f.map(v=>a.selectorTarget(v)));s(g).style("opacity","0").remove().call(Si,c),f.forEach(v=>{var m;const S=a.getTargetSelectorSuffix(v);i.withoutFadeIn[v]=!1,o.legend&&o.legend.selectAll(`.${We.legendItem}${S}`).remove(),a.data.targets=a.data.targets.filter(P=>P.id!==v),l&&((m=o.defs)==null||m.select(`#${a.getDefsPointId(S)}`).remove())}),i.hasFunnel&&a.updateFunnel(a.data.targets),i.hasTreemap&&a.updateTargetsForTreemap(a.data.targets),a.updateTypesElements()}},Ri=t=>()=>t;function Ho(t,{sourceEvent:e,subject:n,target:a,identifier:i,active:o,x:s,y:l,dx:c,dy:f,dispatch:g}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},subject:{value:n,enumerable:!0,configurable:!0},target:{value:a,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:s,enumerable:!0,configurable:!0},y:{value:l,enumerable:!0,configurable:!0},dx:{value:c,enumerable:!0,configurable:!0},dy:{value:f,enumerable:!0,configurable:!0},_:{value:g}})}Ho.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};function Nv(t){return!t.ctrlKey&&!t.button}function Fv(){return this.parentNode}function Bv(t,e){return e==null?{x:t.x,y:t.y}:e}function Uv(){return navigator.maxTouchPoints||"ontouchstart"in this}function uc(){var t=Nv,e=Fv,n=Bv,a=Uv,i={},o=ri("start","drag","end"),s=0,l,c,f,g,v=0;function m(H){H.on("mousedown.drag",S).filter(a).on("touchstart.drag",L).on("touchmove.drag",w,Jd).on("touchend.drag touchcancel.drag",X).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function S(H,k){if(!(g||!t.call(this,H,k))){var K=W(this,e.call(this,H,k),H,k,"mouse");K&&(ot(H.view).on("mousemove.drag",P,Sa).on("mouseup.drag",N,Sa),co(H.view),lo(H),f=!1,l=H.clientX,c=H.clientY,K("start",H))}}function P(H){if(Zr(H),!f){var k=H.clientX-l,K=H.clientY-c;f=k*k+K*K>v}i.mouse("drag",H)}function N(H){ot(H.view).on("mousemove.drag mouseup.drag",null),uo(H.view,f),Zr(H),i.mouse("end",H)}function L(H,k){if(t.call(this,H,k)){var K=H.changedTouches,at=e.call(this,H,k),ht=K.length,$t,dt;for($t=0;$ti.$el[o]).forEach(o=>{a&&i.$el[o].classed(Se.EXPANDED,!1),i.getShapeByIndex(o,e,n).classed(Se.EXPANDED,t)})},setOverOut(t,e){const n=this,{config:a,state:{hasFunnel:i,hasRadar:o,hasTreemap:s},$el:{main:l}}=n,c=Be(e);if(c||e!==-1){const f=a[t?"data_onover":"data_onout"].bind(n.api);if(a.color_onover&&n.setOverColor(t,e,c),c){const g=n.getTargetSelectorSuffix(e.id),v=i||s?`${Se.target+g} .${sn.shape}`:Ve.arc+g;f(e,l.select(`.${v}`).node())}else if(a.tooltip_grouped)t&&(o&&n.isPointFocusOnly()?n.showCircleFocus(n.getAllValuesOnIndex(e,!0)):n.setExpand(e,null,!0)),!n.isMultipleX()&&l.selectAll(`.${sn.shape}-${e}`).each(function(g){f(g,this)});else{const g=n.cache.get(Ln.setOverOut)||[],v=l.selectAll(`.${sn.shape}-${e}`).filter(function(S){return n.isWithinShape(this,S)}),m=v.filter(function(){return g.every(S=>S!==this)});if(!t||v.empty()||g.length===m.size()&&m.nodes().every((S,P)=>S!==g[P]))for(;g.length;){const S=g.pop();a.data_onout.bind(n.api)(ot(S).datum(),S)}m.each(function(){t&&(f(ot(this).datum(),this),g.push(this))}),n.cache.add(Ln.setOverOut,g)}}},callOverOutForTouch(t){const e=this,n=e.cache.get(Ln.callOverOutForTouch);(Be(t)&&n?t.id!==n.id:t!==n)&&((n||he(n))&&e.setOverOut(!1,n),(t||he(t))&&e.setOverOut(!0,t),e.cache.add(Ln.callOverOutForTouch,t))},getDraggableSelection(){const t=this,{config:e,state:n}=t;return e.interaction_enabled&&e.data_selection_draggable&&t.drag?uc().on("drag",function(a){n.event=a,t.drag(Hn(a,this))}).on("start",function(a){n.event=a,t.dragstart(Hn(a,this))}).on("end",a=>{n.event=a,t.dragend()}):()=>{}},dispatchEvent(t,e,n){var a,i,o;const s=this,{config:l,state:{eventReceiver:c,hasAxis:f,hasFunnel:g,hasRadar:v,hasTreemap:m},$el:{eventRect:S,funnel:P,radar:N,svg:L,treemap:w}}=s;let X=(o=(i=(g||m)&&c.rect||v&&N.axes.select(`.${Tn.axis}-${e} text`)||S||((a=s.getArcElementByIdOrIndex)==null?void 0:a.call(s,e)))==null?void 0:i.node)==null?void 0:o.call(i);if(X){const W=s.isMultipleX(),H=l.axis_rotated;let{width:k,left:K,top:at}=X.getBoundingClientRect();if(f&&!v&&!W){const st=c.coords[e];st?(k=st.w,K+=st.x,at+=st.y):(k=0,K=0,at=0)}let ht=K+(n?n[0]:0)+(W||H?0:k/2),$t=at+(n?n[1]:0)+(H?4:0);if(Lo(L)){const st=Ai(s.$el.eventRect.node(),ht,$t,!1);ht=st.x,$t=st.y}const dt={screenX:ht,screenY:$t,clientX:ht,clientY:$t,bubbles:v};(g||m)&&(X=(P!=null?P:w).node()),ev[/^(mouse|click)/.test(t)?"mouse":"touch"](X,t,dt)}},setDragStatus(t){this.state.dragging=t},unbindZoomEvent(){const t=this,{$el:{eventRect:e,zoomResetBtn:n}}=t;e==null||e.on(".zoom wheel.zoom .drag",null),n==null||n.on("click",null).style("display","none")},unbindAllEvents(){var t;const e=this,{$el:{arcs:n,eventRect:a,legend:i,region:o,svg:s,treemap:l},brush:c}=e,f=["wheel","click","mouseover","mousemove","mouseout","touchstart","touchmove","touchend","touchstart.eventRect","touchmove.eventRect","touchend.eventRect",".brush",".drag",".zoom","wheel.zoom","dblclick.zoom"].join(" ");[s,a,o==null?void 0:o.list,c==null?void 0:c.getSelection(),n==null?void 0:n.selectAll("path"),i==null?void 0:i.selectAll("g"),l].forEach(g=>g==null?void 0:g.on(f,null)),(t=e.unbindZoomEvent)==null||t.call(e)}},jv={categoryName(t){var e;const{axis_x_categories:n}=this.config;return(e=n==null?void 0:n[t])!=null?e:t}},Vv={generateClass(t,e){return` ${t} ${t+this.getTargetSelectorSuffix(e)}`},getClass(t,e){const n=/s$/.test(t),a=/^(area|arc|line|funnel|treemap)s?$/.test(t),i=n?"id":"index";return o=>{const s=o.data||o;return((e?this.generateClass(Ue[n?"shapes":"shape"],s[i]):"")+this.generateClass(Ue[t],s[a?"id":i])).trim()}},getChartClass(t){return e=>Ue[`chart${t}`]+this.classTarget((e.data?e.data:e).id)},generateExtraLineClass(){const e=this.config.line_classes||[],n=[];return function(a){var i;const o=a.id||((i=a.data)==null?void 0:i.id)||a;return n.indexOf(o)<0&&n.push(o),e[n.indexOf(o)%e.length]}},classRegion(t,e){return`${this.generateClass(Ue.region,e)} ${"class"in t?t.class:""}`},classTarget(t){const e=this.config.data_classes[t];let n="";return e&&(n=` ${Ue.target}-${e}`),this.generateClass(Ue.target,t)+n},classFocus(t){return this.classFocused(t)+this.classDefocused(t)},classFocused(t){return` ${this.state.focusedTargetIds.indexOf(t.id)>=0?Ue.focused:""}`},classDefocused(t){return` ${this.state.defocusedTargetIds.indexOf(t.id)>=0?Ue.defocused:""}`},getTargetSelectorSuffix(t){return(t||t===0?`-${t}`:"").replace(/[\x00-\x20\x7F-\xA0\s?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\]/g,"-")},selectorTarget(t,e="",n=""){const a=this.getTargetSelectorSuffix(t);return`${e}.${Ue.target+a} ${n}, ${e}.${Ue.circles+a} ${n}`},selectorTargets(t,e){const n=t||[];return n.length?n.map(a=>this.selectorTarget(a,e)):null},selectorLegend(t){return`.${Ue.legendItem+this.getTargetSelectorSuffix(t)}`},selectorLegends(t){return t!=null&&t.length?t.map(e=>this.selectorLegend(e)):null}};class fc extends Map{constructor(e,n=gc){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),e!=null)for(const[a,i]of e)this.set(a,i)}get(e){return super.get(Yo(this,e))}has(e){return super.has(Yo(this,e))}set(e,n){return super.set(dc(this,e),n)}delete(e){return super.delete(hc(this,e))}}class f1 extends Set{constructor(e,n=gc){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),e!=null)for(const a of e)this.add(a)}has(e){return super.has(Yo(this,e))}add(e){return super.add(dc(this,e))}delete(e){return super.delete(hc(this,e))}}function Yo({_intern:t,_key:e},n){const a=e(n);return t.has(a)?t.get(a):n}function dc({_intern:t,_key:e},n){const a=e(n);return t.has(a)?t.get(a):(t.set(a,n),n)}function hc({_intern:t,_key:e},n){const a=e(n);return t.has(a)&&(n=t.get(a),t.delete(a)),n}function gc(t){return t!==null&&typeof t=="object"?t.valueOf():t}function ra(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t);break}return this}function d1(t,e){switch(arguments.length){case 0:break;case 1:{typeof t=="function"?this.interpolator(t):this.range(t);break}default:{this.domain(t),typeof e=="function"?this.interpolator(e):this.range(e);break}}return this}const vc=Symbol("implicit");function pc(){var t=new fc,e=[],n=[],a=vc;function i(o){let s=t.get(o);if(s===void 0){if(a!==vc)return a;t.set(o,s=e.push(o)-1)}return n[s%n.length]}return i.domain=function(o){if(!arguments.length)return e.slice();e=[],t=new fc;for(const s of o)t.has(s)||t.set(s,e.push(s)-1);return i},i.range=function(o){return arguments.length?(n=Array.from(o),i):n.slice()},i.unknown=function(o){return arguments.length?(a=o,i):a},i.copy=function(){return pc(e,n).unknown(a)},ra.apply(i,arguments),i}const Gv=(t,e,n)=>{const a=ot(t.cloneNode(!0));return a.attr("id",n).insert("rect",":first-child").attr("width",a.attr("width")).attr("height",a.attr("height")).style("fill",e),{id:n,node:a.node()}};function Xv(t){const e=Ln.colorPattern,{body:n}=gn;let a=n[e];if(!a){const i=";",o=t.classed(oo.colorPattern,!0).style("background-image");t.classed(oo.colorPattern,!1),o.indexOf(i)>-1&&(a=o.replace(/url[^#]*|["'()]|(\s|%20)/g,"").split(i).map(s=>s.trim().replace(/[\"'\s]/g,"")).filter(Boolean),n[e]=a)}return a}const Hv=["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"];var Yv={generateColor(){const t=this,{$el:e,config:n}=t,a=n.data_colors,i=n.data_color,o=[];let s=cn(n.color_pattern)?n.color_pattern:pc(Xv(e.chart)||Hv).range();const l=s;if(ve(n.color_tiles)){const c=n.color_tiles.bind(t.api)(),f=s.map((g,v)=>{const m=g.replace(/[#\(\)\s,]/g,""),S=`${t.state.datetimeId}-pattern-${m}-${v}`;return Gv(c[v%c.length],g,S)});s=f.map(g=>`url(#${g.id})`),t.patterns=f}return function(c){var f;const g=c.id||((f=c.data)==null?void 0:f.id)||c,v=t.isTypeOf(g,["line","spline","step"])||!n.data_types[g];let m;return ve(a[g])?m=a[g].bind(t.api)(c):a[g]?m=a[g]:(o.indexOf(g)<0&&o.push(g),m=v?l[o.indexOf(g)%l.length]:s[o.indexOf(g)%s.length],a[g]=m),ve(i)?i.bind(t.api)(m,c):m}},generateLevelColor(){const t=this,{config:e}=t,n=e.color_pattern,a=e.color_threshold,i=a.unit==="value",o=a.max||100,s=a.values&&a.values.length?a.values:[];return cn(a)?function(l){const c=i?l:l*100/o;let f=n[n.length-1];for(let g=0,v=s.length;g{const l=`${i.datetimeId}-labels-bg${n.getTargetSelectorSuffix(s)}${ze(t)?n.getTargetSelectorSuffix(t):""}`;a.defs.append("filter").attr("x",e.x).attr("y",e.y).attr("width",e.width).attr("height",e.height).attr("id",l).html(` + `)})}},getGradienColortUrl(t){return`url(#${this.state.datetimeId}-gradient${this.getTargetSelectorSuffix(t)})`},updateLinearGradient(){const t=this,{config:e,data:{targets:n},state:{datetimeId:a},$el:{defs:i}}=t;n.forEach(o=>{const s=`${a}-gradient${t.getTargetSelectorSuffix(o.id)}`,l=t.hasPointType()&&e.point_radialGradient,c=t.isAreaType(o)&&"area"||t.isBarType(o)&&"bar";if((l||c)&&i.select(`#${s}`).empty()){const f=t.color(o),g={defs:null,stops:[]};if(l){const{cx:v=.3,cy:m=.3,r:S=.7,stops:P=[[.1,f,0],[.9,f,1]]}=l;g.stops=P,g.defs=i.append("radialGradient").attr("id",`${s}`).attr("cx",v).attr("cy",m).attr("r",S)}else{const v=e.axis_rotated,{x:m=v?[1,0]:[0,0],y:S=v?[0,0]:[0,1],stops:P=[[0,f,1],[1,f,0]]}=e[`${c}_linearGradient`];g.stops=P,g.defs=i.append("linearGradient").attr("id",`${s}`).attr("x1",m[0]).attr("x2",m[1]).attr("y1",S[0]).attr("y2",S[1])}g.stops.forEach(v=>{const[m,S,P]=v,N=ve(S)?S.bind(t.api)(o.id):S;g.defs&&g.defs.append("stop").attr("offset",m).attr("stop-color",N||f).attr("stop-opacity",P)})}})},setOverColor(t,e){const n=this,{config:a,$el:{main:i}}=n,o=a.color_onover;let s=t?o:n.color;Be(s)?s=({id:l})=>l in o?o[l]:n.color(l):ze(s)?s=()=>o:ve(o)&&(s=s.bind(n.api)),i.selectAll(Be(e)?`.${Ve.arc}${n.getTargetSelectorSuffix(e.id)}`:`.${sn.shape}-${e}`).style("fill",s)}},Wv={getYDomainMinMax(t,e){const n=this,{axis:a,config:i}=n,o=e==="min",s=i.data_groups,l=n.mapToIds(t),c=n.getValuesAsIdKeyed(t);if(s.length>0){const f=n[`has${o?"Negative":"Positive"}ValueInTargets`](t);s.forEach(g=>{const v=g.filter(m=>l.indexOf(m)>=0);if(v.length){const m=v[0],S=a.getId(m);f&&c[m]&&(c[m]=c[m].map(P=>(o?P<0:P>0)?P:0)),v.filter((P,N)=>N>0).forEach(P=>{if(c[P]){const N=a.getId(P);c[P].forEach((L,w)=>{const X=+L,W=o?X>0:X<0;N===S&&!(f&&W)&&(c[m][w]+=X)})}})}})}return _n(e,Object.keys(c).map(f=>_n(e,c[f])))},isHiddenTargetWithYDomain(t){const e=this;return e.state.hiddenTargetIds.some(n=>e.axis.getId(n)===t)},getYDomain(t,e,n){const a=this,{axis:i,config:o,scale:s}=a,l=`axis_${e}`;if(a.isStackNormalized())return[0,100];const c=(s==null?void 0:s[e])&&s[e].type==="log",f=t.filter(dt=>i.getId(dt.id)===e),g=n?a.filterByXDomain(f,n):f;if(g.length===0)return a.isHiddenTargetWithYDomain(e)?s[e].domain():e==="y2"?s.y.domain():a.getYDomain(t,"y2",n);const v=o[`${l}_min`],m=o[`${l}_max`],S=o[`${l}_center`],P=o[`${l}_inverted`],N=a.hasDataLabel()&&o.axis_rotated,L=a.hasDataLabel()&&!o.axis_rotated;let w=a.getYDomainMinMax(g,"min"),X=a.getYDomainMinMax(g,"max"),W=[oe.BAR,oe.BUBBLE,oe.SCATTER,...Sr.Line].some(dt=>{const st=dt.indexOf("area")>-1?"area":dt;return a.hasType(dt,g,!0)&&o[`${st}_zerobased`]});w=De(v)?v:De(m)?w<=m?w:m-10:w,X=De(m)?m:De(v)?v<=X?X:v+10:X,isNaN(w)&&(w=0),isNaN(X)&&(X=w),w===X&&(w<0?X=0:w=0);const H=w>=0&&X>=0,k=w<=0&&X<=0;(De(v)&&H||De(m)&&k)&&(W=!1),W&&(H&&(w=0),k&&(X=0));const K=Math.abs(X-w);let at={top:K*.1,bottom:K*.1};if(Qe(S)){const dt=Math.max(Math.abs(w),Math.abs(X));X=S+dt,w=S-dt}if(N){const dt=Dr(s.y.range()),st=a.getDataLabelLength(w,X,"width").map(Vt=>Vt/dt);["bottom","top"].forEach((Vt,vt)=>{at[Vt]+=K*(st[vt]/(1-st[0]-st[1]))})}else if(L){const dt=a.getDataLabelLength(w,X,"height");["bottom","top"].forEach((st,Vt)=>{at[st]+=a.convertPixelToScale("y",dt[Vt],K)})}at=a.getResettedPadding(at);const ht=o[`${l}_padding`];cn(ht)&&["bottom","top"].forEach(dt=>{at[dt]=i.getPadding(ht,dt,at[dt],K)}),W&&(H&&(at.bottom=w),k&&(at.top=-X));const $t=c?[w,X].map(dt=>dt<0?0:dt):[w-at.bottom,X+at.top];return P?$t.reverse():$t},getXDomainMinMax(t,e){var n;const a=this,i=a.config[`axis_x_${e}`],o=_n(e,t.map(l=>_n(e,l.values.map(c=>c.x))));let s=Be(i)?i.value:i;return s=Qe(s)&&((n=a.axis)!=null&&n.isTimeSeries())?Yn.bind(this)(s):s,Be(i)&&i.fit&&(e==="min"&&so)&&(s=void 0),Qe(s)?s:o},getXDomainPadding(t,e){const n=this,{axis:a,config:i}=n,o=i.axis_x_padding,s=a.isTimeSeries()&&e,l=Dr(t);let c;if(a.isCategorized()||s)c=0;else if(n.hasType("bar")){const v=n.getMaxDataCount();c=v>1?l/(v-1)/2:.5}else c=n.getResettedPadding(l*.01);let{left:f=c,right:g=c}=he(o)?{left:o,right:o}:o;if(o.unit==="px"){const v=Math.abs(l+l*.2);f=a.getPadding(o,"left",c,v),g=a.getPadding(o,"right",c,v)}else{const v=l+f+g;if(s&&v){const m=l/e/v;f=f/v/m,g=g/v/m}}return{left:f,right:g}},getXDomain(t){const e=this,{axis:n,config:a,scale:{x:i}}=e,o=a.axis_x_inverted,s=[e.getXDomainMinMax(t,"min"),e.getXDomainMinMax(t,"max")];let[l=0,c=0]=s;if(i.type!=="log"){const f=n.isCategorized(),g=n.isTimeSeries(),v=e.getXDomainPadding(s);let[m,S]=s;m-S===0&&!f&&(g?(m=new Date(m.getTime()*.5),S=new Date(S.getTime()*1.5)):(m=m===0?1:m*.5,S=S===0?-1:S*1.5)),(m||m===0)&&(l=g?new Date(m.getTime()-v.left):m-v.left),(S||S===0)&&(c=g?new Date(S.getTime()+v.right):S+v.right)}return o?[c,l]:[l,c]},updateXDomain(t,e,n,a,i){var o;const s=this,{config:l,org:c,scale:{x:f,subX:g}}=s,v=l.zoom_enabled;if(n&&(f.domain(i||na(s.getXDomain(t),!l.axis_x_inverted)),c.xDomain=f.domain(),g.domain(f.domain()),(o=s.brush)==null||o.scale(g)),e){const m=i||!s.brush||Kl(s)?c.xDomain:Wl(s).map(g.invert);f.domain(m)}return(n||e)&&v&&s.zoom.updateScaleExtent(),a&&f.domain(s.trimXDomain(f.orgDomain())),f.domain()},trimXDomain(t){const e=this,n=e.config.axis_x_inverted,a=e.getZoomDomain(),[i,o]=a;return(n?t[0]>=i:t[0]<=i)&&(t[1]=+t[1]+(i-t[0]),t[0]=i),(n?t[1]<=o:t[1]>=o)&&(t[0]=+t[0]-(t[1]-o),t[1]=o),t},getZoomDomain(t="zoom",e=!1){const n=this,{config:a,scale:i,org:o}=n;let[s,l]=e&&i[t]?i[t].domain():o.xDomain;return t==="zoom"&&(Qe(a.zoom_x_min)&&(s=_n("min",[s,a.zoom_x_min])),Qe(a.zoom_x_max)&&(l=_n("max",[l,a.zoom_x_max]))),[s,l]},getZoomDomainValue(t){const e=this,{config:n,axis:a}=e;if(a.isCategorized()&&Array.isArray(t)){const i=n.axis_x_inverted;return t.map((s,l)=>Number(s)+(l===0?+i:+!i))}return t},convertPixelToScale(t,e,n){const a=this,{config:i,state:o}=a,s=i.axis_rotated;let l;return t==="x"?l=s?"height":"width":l=s?"width":"height",n*(e/o[l])},withinRange(t,e=[0,0],n){const i=this.config.axis_x_inverted,[o,s]=n;if(Array.isArray(t)){const l=[...t];if(i&&l.reverse(),l[0](f===0?i?+c<=o:+c>=o:i?+c>=s:+c<=s)&&!t.every((g,v)=>g===e[v]))}return!1}};function mc(t,e,n){const{config:a}=t,i=`axis_${e}_tick_format`;return(a[i]?a[i]:t.defaultValueFormat).call(t.api,n)}var Kv={yFormat(t){return mc(this,"y",t)},y2Format(t){return mc(this,"y2",t)},getDefaultValueFormat(){const t=this,{defaultArcValueFormat:e,yFormat:n,y2Format:a}=t,i=t.hasArcType(null,["gauge","polar","radar"]);return function(o,s,l){return(i?e:t.axis&&t.axis.getId(l)==="y2"?a:n).call(t,o,s)}},defaultValueFormat(t){return je(t)?t.join("~"):De(t)?+t:""},defaultArcValueFormat(t,e){return`${(e*100).toFixed(1)}%`},defaultPolarValueFormat(t){return`${t}`},dataLabelFormat(t){const e=this,n=e.config.data_labels,a=o=>{const s="~";let l=o;return je(o)?l=o.join(s):Be(o)&&(l=Object.values(o).join(s)),l};let i=a;return ve(n.format)?i=n.format:nr(n.format)&&(n.format[t]?i=n.format[t]===!0?a:n.format[t]:i=()=>""),i.bind(e.api)}};function Ii(t){const e=this,n=e.getDataById(t);return e.levelColor?e.levelColor(n.values[0].value):e.color(n)}function Wo(t,e=!0){var n;const{config:a}=this;let i=(n=a.data_names[t])!=null?n:t;return e&&ve(a.legend_format)&&(i=a.legend_format(i,t!==i?t:void 0)),i}var Zv={initLegend(){const t=this,{config:e,$el:n}=t;t.legendItemTextBox={},t.state.legendHasRendered=!1,e.legend_show?(e.legend_contents_bindto||(n.legend=t.$el.svg.append("g").classed(We.legend,!0).attr("transform",t.getTranslate("legend"))),t.updateLegend()):t.state.hiddenLegendIds=t.mapToIds(t.data.targets)},updateLegend(t,e,n){var a;const i=this,{config:o,state:s,scale:l,$el:c}=i,f=e||{withTransform:!1,withTransitionForTransform:!1,withTransition:!1};f.withTransition=$r(f,"withTransition",!0),f.withTransitionForTransform=$r(f,"withTransitionForTransform",!0),o.legend_contents_bindto&&o.legend_contents_template?i.updateLegendTemplate():s.hasTreemap||i.updateLegendElement(t||i.mapToIds(i.data.targets),f,n),(a=c.legend)==null||a.selectAll(`.${We.legendItem}`).classed(We.legendItemHidden,function(g){const v=!i.isTargetToShow(g);return v&&(this.style.opacity=null),v}),i.updateScales(!1,!l.zoom),i.updateSvgSize(),i.transformAll(f.withTransitionForTransform,n),s.legendHasRendered=!0},updateLegendTemplate(){const t=this,{config:e,$el:n}=t,a=ot(e.legend_contents_bindto),i=e.legend_contents_template;if(!a.empty()){const o=t.mapToIds(t.data.targets),s=[];let l="";o.forEach(f=>{const g=ve(i)?i.bind(t.api)(f,t.color(f),t.api.data(f)[0].values):bi(i,{COLOR:t.color(f),TITLE:f});g&&(s.push(f),l+=g)});const c=a.html(l).selectAll(function(){return this.childNodes}).data(s);t.setLegendItem(c),n.legend=a}},updateSizeForLegend(t){const e=this,{config:n,state:{isLegendTop:a,isLegendLeft:i,isLegendRight:o,isLegendInset:s,current:l}}=e,{width:c,height:f}=t,g={top:a?e.getCurrentPaddingByDirection("top")+n.legend_inset_y+5.5:l.height-f-e.getCurrentPaddingByDirection("bottom")-n.legend_inset_y,left:i?e.getCurrentPaddingByDirection("left")+n.legend_inset_x+.5:l.width-c-e.getCurrentPaddingByDirection("right")-n.legend_inset_x+.5};e.state.margin3={top:o?0:s?g.top:l.height-f,right:NaN,bottom:0,left:o?l.width-c:s?g.left:0}},transformLegend(t){const e=this,{$el:{legend:n},$T:a}=e;a(n,t).attr("transform",e.getTranslate("legend"))},updateLegendStep(t){this.state.legendStep=t},updateLegendItemWidth(t){this.state.legendItemWidth=t},updateLegendItemHeight(t){this.state.legendItemHeight=t},updateLegendItemColor(t,e){const{legend:n}=this.$el;n&&n.select(`.${We.legendItem}-${t} line`).style("stroke",e)},getLegendWidth(){const t=this,{current:{width:e},isLegendRight:n,isLegendInset:a,legendItemWidth:i,legendStep:o}=t.state;return t.config.legend_show?n||a?i*(o+1):e:0},getLegendHeight(){var t;const e=this,{current:n,isLegendRight:a,legendItemHeight:i,legendStep:o}=e.state,s=((t=e.config.padding)==null?void 0:t.mode)==="fit";return e.config.legend_show?a?n.height:Math.max(s?10:20,i)*(o+1):0},opacityForUnfocusedLegend(t){return t.classed(We.legendItemHidden)?null:"0.3"},toggleFocusLegend(t,e){const n=this,{$el:{legend:a},$T:i}=n,o=n.mapToTargetIds(t);a&&i(a.selectAll(`.${We.legendItem}`).filter(s=>o.indexOf(s)>=0).classed(qe.legendItemFocused,e)).style("opacity",function(){return e?null:n.opacityForUnfocusedLegend.call(n,ot(this))})},revertLegend(){const t=this,{$el:{legend:e},$T:n}=t;e&&n(e.selectAll(`.${We.legendItem}`).classed(qe.legendItemFocused,!1)).style("opacity",null)},showLegend(t){const e=this,{config:n,$el:a,$T:i}=e;n.legend_show||(n.legend_show=!0,a.legend?a.legend.style("visibility",null):e.initLegend(),!e.state.legendHasRendered&&e.updateLegend()),e.removeHiddenLegendIds(t),i(a.legend.selectAll(e.selectorLegends(t)).style("visibility",null)).style("opacity",null)},hideLegend(t){const e=this,{config:n,$el:{legend:a}}=e;n.legend_show&&qn(t)&&(n.legend_show=!1,a.style("visibility","hidden")),e.addHiddenLegendIds(t),a.selectAll(e.selectorLegends(t)).style("opacity","0").style("visibility","hidden")},getLegendItemTextBox(t,e){const n=this,{cache:a,state:i}=n;let o;const s=Ln.legendItemTextBox;return t&&(o=!i.redrawing&&a.get(s)||{},o[t]||(o[t]=n.getTextRect(e,We.legendItem),a.add(s,o)),o=o[t]),o},setLegendItem(t){const e=this,{$el:n,api:a,config:i,state:o}=e,s=o.inputType==="touch",l=e.hasType("gauge"),c=i.boost_useCssRule,f=i.legend_item_interaction;t.attr("class",function(g){const v=ot(this);return(!v.empty()&&v.attr("class")||"")+e.generateClass(We.legendItem,g)}).style("visibility",g=>e.isLegendToShow(g)?null:"hidden"),i.interaction_enabled&&(c&&[[`.${We.legendItem}`,"cursor:pointer"],[`.${We.legendItem} text`,"pointer-events:none"],[`.${We.legendItemPoint} text`,"pointer-events:none"],[`.${We.legendItemTile}`,"pointer-events:none"],[`.${We.legendItemEvent}`,"fill-opacity:0"]].forEach(g=>{const[v,m]=g;e.setCssRule(!1,v,[m])(n.legend)}),t.on(f.dblclick?"dblclick":"click",f||ve(i.legend_item_onclick)?function(g,v){if(!_e(i.legend_item_onclick,a,v,!o.hiddenTargetIds.includes(v))){const{altKey:m,target:S,type:P}=g;P==="dblclick"||m?o.hiddenTargetIds.length&&S.parentNode.getAttribute("class").indexOf(We.legendItemHidden)===-1?a.show():(a.hide(),a.show(v)):(a.toggle(v),ot(this).classed(qe.legendItemFocused,!1))}s&&e.hideTooltip()}:null),!s&&t.on("mouseout",f||ve(i.legend_item_onout)?function(g,v){_e(i.legend_item_onout,a,v,!o.hiddenTargetIds.includes(v))||(ot(this).classed(qe.legendItemFocused,!1),l&&e.undoMarkOverlapped(e,`.${Un.gaugeValue}`),e.api.revert())}:null).on("mouseover",f||ve(i.legend_item_onover)?function(g,v){_e(i.legend_item_onover,a,v,!o.hiddenTargetIds.includes(v))||(ot(this).classed(qe.legendItemFocused,!0),l&&e.markOverlapped(v,e,`.${Un.gaugeValue}`),!o.transiting&&e.isTargetToShow(v)&&a.focus(v))}:null),!t.empty()&&t.on("click mouseout mouseover")&&t.style("cursor",e.getStylePropValue("pointer")))},updateLegendElement(t,e){const n=this,{config:a,state:i,$el:{legend:o},$T:s}=n,c=a.legend_item_tile_type!=="circle",f=a.legend_item_tile_r,g={width:c?a.legend_item_tile_width:f*2,height:c?a.legend_item_tile_height:f*2},v={padding:{top:4,right:10},max:{width:0,height:0},posMin:10,step:0,tileWidth:g.width+5,totalLength:0},m={offsets:{},widths:{},heights:{},margins:[0],steps:{}};let S,P,N;const L=t.filter(K=>!Qe(a.data_names[K])||a.data_names[K]!==null),w=e.withTransition,X=n.getUpdateLegendPositions(L,v,m);i.isLegendInset&&(v.step=a.legend_inset_step?a.legend_inset_step:L.length,n.updateLegendStep(v.step)),i.isLegendRight?(S=K=>v.max.width*m.steps[K],P=K=>m.margins[m.steps[K]]+m.offsets[K]):i.isLegendInset?(S=K=>v.max.width*m.steps[K]+10,P=K=>m.margins[m.steps[K]]+m.offsets[K]):(S=K=>m.margins[m.steps[K]]+m.offsets[K],P=K=>v.max.height*m.steps[K]);const W={xText:(K,at)=>S(K,at)+4+g.width,xRect:(K,at)=>S(K,at),x1Tile:(K,at)=>S(K,at)-2,x2Tile:(K,at)=>S(K,at)-2+g.width,yText:(K,at)=>P(K,at)+9,yRect:(K,at)=>P(K,at)-5,yTile:(K,at)=>P(K,at)+4};n.generateLegendItem(L,g,X,W),N=o.select(`.${We.legendBackground} rect`),i.isLegendInset&&v.max.width>0&&N.size()===0&&(N=o.insert("g",`.${We.legendItem}`).attr("class",We.legendBackground).append("rect")),a.legend_tooltip&&o.selectAll("title").data(L).text(K=>Wo.bind(n)(K,!1));const H=o.selectAll("text").data(L).text(K=>Wo.bind(n)(K)).each(function(K,at){X(this,K,at)});s(H,w).attr("x",W.xText).attr("y",W.yText);const k=o.selectAll(`rect.${We.legendItemEvent}`).data(L);s(k,w).attr("width",K=>m.widths[K]).attr("height",K=>m.heights[K]).attr("x",W.xRect).attr("y",W.yRect),n.updateLegendItemPos(L,w,W),N&&s(N,w).attr("height",n.getLegendHeight()-12).attr("width",v.max.width*(v.step+1)+10),n.updateLegendItemWidth(v.max.width),n.updateLegendItemHeight(v.max.height),n.updateLegendStep(v.step)},getUpdateLegendPositions(t,e,n){const a=this,{config:i,state:o}=a,s=o.isLegendRight||o.isLegendInset;return function(l,c,f){const g=f===0,v=f===t.length-1,m=a.getLegendItemTextBox(c,l),S=m.width+e.tileWidth+(v&&!s?0:e.padding.right)+i.legend_padding,P=m.height+e.padding.top,N=s?P:S,L=s?a.getLegendHeight():a.getLegendWidth();let w;const X=function(H,k){k||(w=(L-e.totalLength-N)/2,w=e.max.width)&&(e.max.width=S),(!e.max.height||P>=e.max.height)&&(e.max.height=P);const W=s?e.max.height:e.max.width;i.legend_equally?(Object.keys(n.widths).forEach(H=>n.widths[H]=e.max.width),Object.keys(n.heights).forEach(H=>n.heights[H]=e.max.height),w=(L-W*t.length)/2,wX(H))):X(c,!0)):X(c)}},generateLegendItem(t,e,n,a){const i=this,{config:o,state:s,$el:{legend:l}}=i,c=o.legend_usePoint,f=o.legend_item_tile_r,g=o.legend_item_tile_type,v=g!=="circle",m=s.isLegendRight||s.isLegendInset,S=-200,P=l.selectAll(`.${We.legendItem}`).data(t).enter().append("g");if(i.setLegendItem(P),o.legend_tooltip&&P.append("title").text(N=>N),P.append("text").text(N=>Wo.bind(i)(N)).each(function(N,L){n(this,N,L)}).style("pointer-events",i.getStylePropValue("none")).attr("x",m?a.xText:S).attr("y",m?S:a.yText),P.append("rect").attr("class",We.legendItemEvent).style("fill-opacity",i.getStylePropValue("0")).attr("x",m?a.xRect:S).attr("y",m?S:a.yRect),c){const N=[];P.append(L=>{const w=cn(o.point_pattern)?o.point_pattern:[o.point_type];N.indexOf(L)===-1&&N.push(L);let X=w[N.indexOf(L)%w.length];return X==="rectangle"&&(X="rect"),gn.createElementNS(ae.svg,"hasValidPointType"in i&&i.hasValidPointType(X)?X:"use")}).attr("class",We.legendItemPoint).style("fill",Ii.bind(i)).style("pointer-events",i.getStylePropValue("none")).attr("href",(L,w,X)=>{const H=X[w].nodeName.toLowerCase(),k=i.getTargetSelectorSuffix(L);return H==="use"?`#${s.datetimeId}-point${k}`:void 0})}else P.append(v?"line":g).attr("class",We.legendItemTile).style("stroke",Ii.bind(i)).style("pointer-events",i.getStylePropValue("none")).call(N=>{g==="circle"?N.attr("r",f).style("fill",Ii.bind(i)).attr("cx",m?a.x2Tile:S).attr("cy",m?S:a.yTile):v&&N.attr("stroke-width",e.height).attr("x1",m?a.x1Tile:S).attr("y1",m?S:a.yTile).attr("x2",m?a.x2Tile:S).attr("y2",m?S:a.yTile)})},updateLegendItemPos(t,e,n){const a=this,{config:i,$el:{legend:o},$T:s}=a,l=i.legend_usePoint,c=i.legend_item_tile_type,f=c!=="circle";if(l){const g=o.selectAll(`.${We.legendItemPoint}`).data(t);s(g,e).each(function(){const v=this.nodeName.toLowerCase(),m=i.point_r;let S="x",P="y",N=2,L=2.5,w=null,X=null,W=null;if(v==="circle"){const H=m*.2;S="cx",P="cy",w=m+H,N=m*2,L=-H}else if(v==="rect"){const H=m*2.5;X=H,W=H,L=3}ot(this).attr(S,H=>n.x1Tile(H)+N).attr(P,H=>n.yTile(H)-L).attr("r",w).attr("width",X).attr("height",W)})}else{const g=o.selectAll(`.${We.legendItemTile}`).data(t);s(g,e).style("stroke",Ii.bind(a)).call(v=>{c==="circle"?v.attr("cx",m=>{const S=n.x2Tile(m);return S-(S-n.x1Tile(m))/2}).attr("cy",n.yTile):f&&v.attr("x1",n.x1Tile).attr("y1",n.yTile).attr("x2",n.x2Tile).attr("y2",n.yTile)})}}},Jv={redraw(t={}){var e,n,a,i;const o=this,{config:s,state:l,$el:c}=o,{main:f,treemap:g}=c;l.redrawing=!0;const v=o.filterTargetsToShow(o.data.targets),{flow:m,initializing:S}=t,P=o.getWithOption(t),N=P.Transition?s.transition_duration:0,L=P.TransitionForExit?N:0,w=P.TransitionForAxis?N:0,X=(e=o.axis)==null?void 0:e.generateTransitions(w);o.updateSizes(S),P.Legend&&s.legend_show?(t.withTransition=!!N,!g&&o.updateLegend(o.mapToIds(o.data.targets),t,X)):P.Dimension&&o.updateDimension(!0),s.data_empty_label_text&&f.select(`text.${On.text}.${Se.empty}`).attr("x",l.width/2).attr("y",l.height/2).text(s.data_empty_label_text).style("display",v.length?"none":null),l.hasAxis?(o.axis.redrawAxis(v,P,X,m,S),o.hasGrid()&&o.updateGrid(),s.regions.length&&o.updateRegion(),["bar","candlestick","line","area"].forEach(W=>{const H=Cn(W);(/^(line|area)$/.test(W)&&o.hasTypeOf(H)||o.hasType(W))&&o[`update${H}`](P.TransitionForExit)}),c.text&&f.selectAll(`.${tn.selectedCircles}`).filter(o.isBarType.bind(o)).selectAll("circle").remove(),s.interaction_enabled&&!m&&P.EventRect&&(o.redrawEventRect(),(n=o.bindZoomEvent)==null||n.call(o))):(c.arcs&&o.redrawArc(N,L,P.Transform),c.radar&&o.redrawRadar(),c.polar&&o.redrawPolar(),c.funnel&&o.redrawFunnel(),g&&o.updateTreemap(L)),!l.resizing&&!g&&(o.hasPointType()||l.hasRadar)?o.updateCircle():(a=o.hasLegendDefsPoint)!=null&&a.call(o)&&o.data.targets.forEach(o.point("create",this)),o.hasDataLabel()&&!o.hasArcType(null,["radar"])&&o.updateText(),(i=o.redrawTitle)==null||i.call(o),S&&o.updateTypesElements(),o.generateRedrawList(v,m,N,P.Subchart),o.updateTooltipOnRedraw(),o.callPluginHook("$redraw",t,N)},generateRedrawList(t,e,n,a){const i=this,{config:o,state:s}=i,l=i.getDrawShape();s.hasAxis&&o.subchart_show&&i.redrawSubchart(a,n,l);const c=e&&i.generateFlow({targets:t,flow:e,duration:e.duration,shape:l,xv:i.xv.bind(i)}),f=(n||c)&&Da(),g=i.getRedrawList(l,e,c,f),v=()=>{c&&c(),s.redrawing=!1,_e(o.onrendered,i.api)};if(v)if(f&&g.length){const m=ec();Ml().duration(n).each(()=>{g.reduce((S,P)=>S.concat(P),[]).forEach(S=>m.add(S))}).call(m,v)}else s.transiting||v();i.mapToIds(i.data.targets).forEach(m=>{s.withoutFadeIn[m]=!0})},getRedrawList(t,e,n,a){const i=this,{config:o,state:{hasAxis:s,hasRadar:l,hasTreemap:c},$el:{grid:f}}=i,{cx:g,cy:v,xForText:m,yForText:S}=t.pos,P=[];return s&&((o.grid_x_lines.length||o.grid_y_lines.length)&&P.push(i.redrawGrid(a)),o.regions.length&&P.push(i.redrawRegion(a)),Object.keys(t.type).forEach(N=>{const L=Cn(N),w=t.type[N];(/^(area|line)$/.test(N)&&i.hasTypeOf(L)||i.hasType(N))&&P.push(i[`redraw${L}`](w,a))}),!e&&f.main&&P.push(i.updateGridFocus())),(!i.hasArcType()||l)&&cn(o.data_labels)&&o.data_labels!==!1&&P.push(i.redrawText(m,S,e,a)),(i.hasPointType()||l)&&!i.isPointFocusOnly()&&i.redrawCircle&&P.push(i.redrawCircle(g,v,a,n)),c&&P.push(i.redrawTreemap(a)),P},updateAndRedraw(t={}){const e=this,{config:n,state:a}=e;let i;t.withTransition=$r(t,"withTransition",!0),t.withTransform=$r(t,"withTransform",!1),t.withLegend=$r(t,"withLegend",!1),t.withUpdateXDomain=!0,t.withUpdateOrgXDomain=!0,t.withTransitionForExit=!1,t.withTransitionForTransform=$r(t,"withTransitionForTransform",t.withTransition),t.withLegend&&n.legend_show||(a.hasAxis&&(i=e.axis.generateTransitions(t.withTransitionForAxis?n.transition_duration:0)),e.updateScales(),e.updateSvgSize(),e.transformAll(t.withTransitionForTransform,i)),e.redraw(t,i)}};const Qv=Math.sqrt(50),kv=Math.sqrt(10),qv=Math.sqrt(2);function Oi(t,e,n){const a=(e-t)/Math.max(0,n),i=Math.floor(Math.log10(a)),o=a/Math.pow(10,i),s=o>=Qv?10:o>=kv?5:o>=qv?2:1;let l,c,f;return i<0?(f=Math.pow(10,-i)/s,l=Math.round(t*f),c=Math.round(e*f),l/fe&&--c,f=-f):(f=Math.pow(10,i)*s,l=Math.round(t/f),c=Math.round(e/f),l*fe&&--c),c0))return[];if(t===e)return[t];const a=e=i))return[];const l=o-i+1,c=new Array(l);if(a)if(s<0)for(let f=0;fe?1:t>=e?0:NaN}function _v(t,e){return t==null||e==null?NaN:et?1:e>=t?0:NaN}function Qo(t){let e,n,a;t.length!==2?(e=Ci,n=(l,c)=>Ci(t(l),c),a=(l,c)=>t(l)-c):(e=t===Ci||t===_v?t:tp,n=t,a=t);function i(l,c,f=0,g=l.length){if(f>>1;n(l[v],c)<0?f=v+1:g=v}while(f>>1;n(l[v],c)<=0?f=v+1:g=v}while(ff&&a(l[v-1],c)>-a(l[v],c)?v-1:v}return{left:i,center:s,right:o}}function tp(){return 0}function ep(t){return t===null?NaN:+t}function*h1(t,e){if(e===void 0)for(let n of t)n!=null&&(n=+n)>=n&&(yield n);else{let n=-1;for(let a of t)(a=e(a,++n,t))!=null&&(a=+a)>=a&&(yield a)}}const yc=Qo(Ci),np=yc.right,g1=yc.left,v1=Qo(ep).center;var rp=np;function ap(t,e){return t=+t,e=+e,function(n){return Math.round(t*(1-n)+e*n)}}function ip(t){return function(){return t}}function op(t){return+t}var xc=[0,1];function aa(t){return t}function ko(t,e){return(e-=t=+t)?function(n){return(n-t)/e}:ip(isNaN(e)?NaN:.5)}function sp(t,e){var n;return t>e&&(n=t,t=e,e=n),function(a){return Math.max(t,Math.min(e,a))}}function lp(t,e,n){var a=t[0],i=t[1],o=e[0],s=e[1];return i2?cp:lp,c=f=null,v}function v(m){return m==null||isNaN(m=+m)?o:(c||(c=l(t.map(a),e,n)))(a(s(m)))}return v.invert=function(m){return s(i((f||(f=l(e,t.map(a),Qn)))(m)))},v.domain=function(m){return arguments.length?(t=Array.from(m,op),g()):t.slice()},v.range=function(m){return arguments.length?(e=Array.from(m),g()):e.slice()},v.rangeRound=function(m){return e=Array.from(m),n=ap,g()},v.clamp=function(m){return arguments.length?(s=m?!0:aa,g()):s!==aa},v.interpolate=function(m){return arguments.length?(n=m,g()):n},v.unknown=function(m){return arguments.length?(o=m,v):o},function(m,S){return a=m,i=S,g()}}function Tc(){return qo()(aa,aa)}var up=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Na(t){if(!(e=up.exec(t)))throw new Error("invalid format: "+t);var e;return new _o({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}Na.prototype=_o.prototype;function _o(t){this.fill=t.fill===void 0?" ":t.fill+"",this.align=t.align===void 0?">":t.align+"",this.sign=t.sign===void 0?"-":t.sign+"",this.symbol=t.symbol===void 0?"":t.symbol+"",this.zero=!!t.zero,this.width=t.width===void 0?void 0:+t.width,this.comma=!!t.comma,this.precision=t.precision===void 0?void 0:+t.precision,this.trim=!!t.trim,this.type=t.type===void 0?"":t.type+""}_o.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function fp(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)}function wi(t,e){if((n=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var n,a=t.slice(0,n);return[a.length>1?a[0]+a.slice(2):a,+t.slice(n+1)]}function ia(t){return t=wi(Math.abs(t)),t?t[1]:NaN}function dp(t,e){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(ia(e)/3)))*3-ia(Math.abs(t)))}function hp(t,e){return function(n,a){for(var i=n.length,o=[],s=0,l=t[0],c=0;i>0&&l>0&&(c+l+1>a&&(l=Math.max(1,a-c)),o.push(n.substring(i-=l,i+l)),!((c+=l+1)>a));)l=t[s=(s+1)%t.length];return o.reverse().join(e)}}function gp(t){return function(e){return e.replace(/[0-9]/g,function(n){return t[+n]})}}function vp(t){t:for(var e=t.length,n=1,a=-1,i;n0&&(a=0);break}return a>0?t.slice(0,a)+t.slice(i+1):t}var $c;function pp(t,e){var n=wi(t,e);if(!n)return t+"";var a=n[0],i=n[1],o=i-($c=Math.max(-8,Math.min(8,Math.floor(i/3)))*3)+1,s=a.length;return o===s?a:o>s?a+new Array(o-s+1).join("0"):o>0?a.slice(0,o)+"."+a.slice(o):"0."+new Array(1-o).join("0")+wi(t,Math.max(0,e+o-1))[0]}function Sc(t,e){var n=wi(t,e);if(!n)return t+"";var a=n[0],i=n[1];return i<0?"0."+new Array(-i).join("0")+a:a.length>i+1?a.slice(0,i+1)+"."+a.slice(i+1):a+new Array(i-a.length+2).join("0")}var Ac={"%":(t,e)=>(t*100).toFixed(e),b:t=>Math.round(t).toString(2),c:t=>t+"",d:fp,e:(t,e)=>t.toExponential(e),f:(t,e)=>t.toFixed(e),g:(t,e)=>t.toPrecision(e),o:t=>Math.round(t).toString(8),p:(t,e)=>Sc(t*100,e),r:Sc,s:pp,X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function Ec(t){return t}var bc=Array.prototype.map,Rc=["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];function mp(t){var e=t.grouping===void 0||t.thousands===void 0?Ec:hp(bc.call(t.grouping,Number),t.thousands+""),n=t.currency===void 0?"":t.currency[0]+"",a=t.currency===void 0?"":t.currency[1]+"",i=t.decimal===void 0?".":t.decimal+"",o=t.numerals===void 0?Ec:gp(bc.call(t.numerals,String)),s=t.percent===void 0?"%":t.percent+"",l=t.minus===void 0?"\u2212":t.minus+"",c=t.nan===void 0?"NaN":t.nan+"";function f(v){v=Na(v);var m=v.fill,S=v.align,P=v.sign,N=v.symbol,L=v.zero,w=v.width,X=v.comma,W=v.precision,H=v.trim,k=v.type;k==="n"?(X=!0,k="g"):Ac[k]||(W===void 0&&(W=12),H=!0,k="g"),(L||m==="0"&&S==="=")&&(L=!0,m="0",S="=");var K=N==="$"?n:N==="#"&&/[boxX]/.test(k)?"0"+k.toLowerCase():"",at=N==="$"?a:/[%p]/.test(k)?s:"",ht=Ac[k],$t=/[defgprs%]/.test(k);W=W===void 0?6:/[gprs]/.test(k)?Math.max(1,Math.min(21,W)):Math.max(0,Math.min(20,W));function dt(st){var Vt=K,vt=at,Q,St,ct;if(k==="c")vt=ht(st)+vt,st="";else{st=+st;var At=st<0||1/st<0;if(st=isNaN(st)?c:ht(Math.abs(st),W),H&&(st=vp(st)),At&&+st==0&&P!=="+"&&(At=!1),Vt=(At?P==="("?P:l:P==="-"||P==="("?"":P)+Vt,vt=(k==="s"?Rc[8+$c/3]:"")+vt+(At&&P==="("?")":""),$t){for(Q=-1,St=st.length;++Qct||ct>57){vt=(ct===46?i+st.slice(Q+1):st.slice(Q))+vt,st=st.slice(0,Q);break}}}X&&!L&&(st=e(st,1/0));var Gt=Vt.length+st.length+vt.length,Bt=Gt>1)+Vt+st+vt+Bt.slice(Gt);break;default:st=Bt+Vt+st+vt;break}return o(st)}return dt.toString=function(){return v+""},dt}function g(v,m){var S=f((v=Na(v),v.type="f",v)),P=Math.max(-8,Math.min(8,Math.floor(ia(m)/3)))*3,N=Math.pow(10,-P),L=Rc[8+P/3];return function(w){return S(N*w)+L}}return{format:f,formatPrefix:g}}var Mi,ts,Ic;yp({thousands:",",grouping:[3],currency:["$",""]});function yp(t){return Mi=mp(t),ts=Mi.format,Ic=Mi.formatPrefix,Mi}function xp(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,ia(e)-ia(t))+1}function Tp(t){return Math.max(0,-ia(Math.abs(t)))}function $p(t,e,n,a){var i=Jo(t,e,n),o;switch(a=Na(a==null?",f":a),a.type){case"s":{var s=Math.max(Math.abs(t),Math.abs(e));return a.precision==null&&!isNaN(o=dp(i,s))&&(a.precision=o),Ic(a,s)}case"":case"e":case"g":case"p":case"r":{a.precision==null&&!isNaN(o=xp(i,Math.max(Math.abs(t),Math.abs(e))))&&(a.precision=o-(a.type==="e"));break}case"f":case"%":{a.precision==null&&!isNaN(o=Tp(i))&&(a.precision=o-(a.type==="%")*2);break}}return ts(a)}function Oc(t){var e=t.domain;return t.ticks=function(n){var a=e();return Ko(a[0],a[a.length-1],n==null?10:n)},t.tickFormat=function(n,a){var i=e();return $p(i[0],i[i.length-1],n==null?10:n,a)},t.nice=function(n){n==null&&(n=10);var a=e(),i=0,o=a.length-1,s=a[i],l=a[o],c,f,g=10;for(l0;){if(f=Zo(s,l,n),f===c)return a[i]=s,a[o]=l,e(a);if(f>0)s=Math.floor(s/f)*f,l=Math.ceil(l/f)*f;else if(f<0)s=Math.ceil(s*f)/f,l=Math.floor(l*f)/f;else break;c=f}return t},t}function Di(){var t=Tc();return t.copy=function(){return Pi(t,Di())},ra.apply(t,arguments),Oc(t)}function Cc(t){return function(e){return Math.sign(e)*Math.log1p(Math.abs(e/t))}}function Pc(t){return function(e){return Math.sign(e)*Math.expm1(Math.abs(e))*t}}function Sp(t){var e=1,n=t(Cc(e),Pc(e));return n.constant=function(a){return arguments.length?t(Cc(e=+a),Pc(e)):e},Oc(n)}function wc(){var t=Sp(qo());return t.copy=function(){return Pi(t,wc()).constant(t.constant())},ra.apply(t,arguments)}function Mc(t,e){t=t.slice();var n=0,a=t.length-1,i=t[n],o=t[a],s;return oMath.pow(t,e)}function Ip(t){return t===Math.E?Math.log:t===10&&Math.log10||t===2&&Math.log2||(t=Math.log(t),e=>Math.log(e)/t)}function Nc(t){return(e,n)=>-t(-e,n)}function Op(t){const e=t(Dc,Lc),n=e.domain;let a=10,i,o;function s(){return i=Ip(a),o=Rp(a),n()[0]<0?(i=Nc(i),o=Nc(o),t(Ap,Ep)):t(Dc,Lc),e}return e.base=function(l){return arguments.length?(a=+l,s()):a},e.domain=function(l){return arguments.length?(n(l),s()):n()},e.ticks=l=>{const c=n();let f=c[0],g=c[c.length-1];const v=g0){for(;m<=S;++m)for(P=1;Pg)break;w.push(N)}}else for(;m<=S;++m)for(P=a-1;P>=1;--P)if(N=m>0?P/o(-m):P*o(m),!(Ng)break;w.push(N)}w.length*2{if(l==null&&(l=10),c==null&&(c=a===10?"s":","),typeof c!="function"&&(!(a%1)&&(c=Na(c)).precision==null&&(c.trim=!0),c=ts(c)),l===1/0)return c;const f=Math.max(1,a*l/e.ticks().length);return g=>{let v=g/o(Math.round(i(g)));return v*an(Mc(n(),{floor:l=>o(Math.floor(i(l))),ceil:l=>o(Math.ceil(i(l)))})),e}function Fc(){const t=Op(qo()).domain([1,10]);return t.copy=()=>Pi(t,Fc()).base(t.base()),ra.apply(t,arguments),t}const Li=en(()=>{},(t,e)=>{t.setTime(+t+e)},(t,e)=>e-t);Li.every=t=>(t=Math.floor(t),!isFinite(t)||!(t>0)?null:t>1?en(e=>{e.setTime(Math.floor(e/t)*t)},(e,n)=>{e.setTime(+e+n*t)},(e,n)=>(n-e)/t):Li);const p1=Li.range,Ur=en(t=>{t.setTime(t-t.getMilliseconds())},(t,e)=>{t.setTime(+t+e*Gn)},(t,e)=>(e-t)/Gn,t=>t.getUTCSeconds()),m1=Ur.range,es=en(t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*Gn)},(t,e)=>{t.setTime(+t+e*In)},(t,e)=>(e-t)/In,t=>t.getMinutes()),y1=es.range,ns=en(t=>{t.setUTCSeconds(0,0)},(t,e)=>{t.setTime(+t+e*In)},(t,e)=>(e-t)/In,t=>t.getUTCMinutes()),x1=ns.range,rs=en(t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*Gn-t.getMinutes()*In)},(t,e)=>{t.setTime(+t+e*Bn)},(t,e)=>(e-t)/Bn,t=>t.getHours()),T1=rs.range,as=en(t=>{t.setUTCMinutes(0,0,0)},(t,e)=>{t.setTime(+t+e*Bn)},(t,e)=>(e-t)/Bn,t=>t.getUTCHours()),$1=as.range,is=en(t=>{t.setDate(1),t.setHours(0,0,0,0)},(t,e)=>{t.setMonth(t.getMonth()+e)},(t,e)=>e.getMonth()-t.getMonth()+(e.getFullYear()-t.getFullYear())*12,t=>t.getMonth()),S1=is.range,os=en(t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCMonth(t.getUTCMonth()+e)},(t,e)=>e.getUTCMonth()-t.getUTCMonth()+(e.getUTCFullYear()-t.getUTCFullYear())*12,t=>t.getUTCMonth()),A1=os.range;function Bc(t,e,n,a,i,o){const s=[[Ur,1,Gn],[Ur,5,5*Gn],[Ur,15,15*Gn],[Ur,30,30*Gn],[o,1,In],[o,5,5*In],[o,15,15*In],[o,30,30*In],[i,1,Bn],[i,3,3*Bn],[i,6,6*Bn],[i,12,12*Bn],[a,1,or],[a,2,2*or],[n,1,to],[e,1,Ps],[e,3,3*Ps],[t,1,eo]];function l(f,g,v){const m=gL).right(s,m);if(S===s.length)return t.every(Jo(f/eo,g/eo,v));if(S===0)return Li.every(Math.max(Jo(f,g,v),1));const[P,N]=s[m/s[S-1][2]n.axis.x.tickOffset()),i=n.config.axis_x_inverted,o=function(s){return t(s)+a()};for(const s in t)o[s]=t[s];return o.orgDomain=()=>t.domain(),o.orgScale=()=>t,n.axis.isCategorized()&&(o.domain=function(s){let l=s;return arguments.length?(t.domain(l),o):(l=this.orgDomain(),i?[l[0]+1,l[1]]:[l[0],l[1]+1])}),o},updateScales(t,e=!0){var n,a;const i=this,{axis:o,config:s,format:l,org:c,scale:f,state:{current:g,width:v,height:m,width2:S,height2:P,hasAxis:N,hasTreemap:L}}=i;if(N){const w=s.axis_rotated,X=i.getResettedPadding(1),W={x:w?X:0,y:w?0:m,subX:w?1:0,subY:w?0:P},H={x:w?m:v,y:w?v:X,subX:w?m:v,subY:w?S:1},k=e&&((n=f.x)==null?void 0:n.orgDomain()),K=e&&c.xDomain;f.x=i.getXScale(W.x,H.x,k,()=>o.x.tickOffset()),f.subX=i.getXScale(W.x,H.x,K,at=>{var ht;return at%1?0:((ht=o.subX)!=null?ht:o.x).tickOffset()}),l.xAxisTick=o.getXAxisTickFormat(),l.subXAxisTick=o.getXAxisTickFormat(!0),o.setAxis("x",f.x,s.axis_x_tick_outer,t),s.subchart_show&&o.setAxis("subX",f.subX,s.axis_x_tick_outer,t),f.y=i.getYScale("y",W.y,H.y,f.y?f.y.domain():s.axis_y_default),f.subY=i.getYScale("y",W.subY,H.subY,f.subY?f.subY.domain():s.axis_y_default),o.setAxis("y",f.y,s.axis_y_tick_outer,t),s.axis_y2_show&&(f.y2=i.getYScale("y2",W.y,H.y,f.y2?f.y2.domain():s.axis_y2_default),f.subY2=i.getYScale("y2",W.subY,H.subY,f.subY2?f.subY2.domain():s.axis_y2_default),o.setAxis("y2",f.y2,s.axis_y2_tick_outer,t))}else if(L){const w=i.getCurrentPadding();f.x=Di().rangeRound([w.left,g.width-w.right]),f.y=Di().rangeRound([w.top,g.height-w.bottom])}else(a=i.updateArc)==null||a.call(i)},xx(t){const e=this,{config:n,scale:{x:a,zoom:i}}=e,o=n.zoom_enabled&&i?i:a;return t?o(De(t.x)?t.x:t):null},xv(t){const e=this,{axis:n,config:a,scale:{x:i,zoom:o}}=e,s=a.zoom_enabled&&o?o:i;let l=e.getBaseValue(t);return n.isTimeSeries()?l=Yn.call(e,l):n.isCategorized()&&ze(l)&&(l=a.axis_x_categories.indexOf(l)),s(l)},yv(t){const e=this,{scale:{y:n,y2:a}}=e;return(t.axis&&t.axis==="y2"?a:n)(e.getBaseValue(t))},subxx(t){return t?this.scale.subX(t.x):null}},Up={setContainerSize(){const t=this,{state:e}=t;e.current.width=t.getCurrentWidth(),e.current.height=t.getCurrentHeight()},getCurrentWidth(){const t=this;return t.config.size_width||t.getParentWidth()},getCurrentHeight(){const t=this,{config:e}=t,n=e.size_height||t.getParentHeight();return n>0?n:320/(t.hasType("gauge")&&!e.gauge_fullCircle?2:1)},getParentRectValue(t){const e=`offset${Cn(t)}`;let n=this.$el.chart.node(),a=0;for(;a<30&&n&&n.tagName!=="BODY";){try{a=n.getBoundingClientRect()[t]}catch(o){e in n&&(a=n[e])}n=n.parentNode}const i=gn.body[e];return a>i&&(a=i),a},getParentWidth(){return this.getParentRectValue("width")},getParentHeight(){const t=this.$el.chart.style("height");let e=0;return t&&(e=/px$/.test(t)?parseInt(t,10):this.getParentRectValue("height")),e},getSvgLeft(t){const e=this,{config:n,state:{hasAxis:a},$el:i}=e,o=n.axis_rotated,s=o||!o&&!n.axis_y_inner,l=o?Tn.axisX:Tn.axisY,c=i.main.select(`.${l}`).node(),f=a&&n[`axis_${o?"x":"y"}_label`];let g=0;if(a&&(ze(f)||ze(f.text)||/^inner-/.test(f==null?void 0:f.position))){const N=i.main.select(`.${l}-label`);N.empty()||(g=N.node().getBoundingClientRect().left)}const v=c&&s?c.getBoundingClientRect():{right:0},m=i.chart.node().getBoundingClientRect().left+g,S=e.hasArcType(),P=v.right-m-(S?0:e.getCurrentPaddingByDirection("left",t));return P>0?P:0},updateDimension(t){var e;const n=this,{config:a,state:{hasAxis:i},$el:o}=n;i&&!t&&n.axis.x&&a.axis_rotated&&((e=n.axis.subX)==null||e.create(o.axis.subX)),n.updateScales(t),n.updateSvgSize(),n.transformAll(!1)},updateSvgSize(){const t=this,{config:e,state:{clip:n,current:a,hasAxis:i,width:o,height:s},$el:{svg:l}}=t;if(e.resize_auto==="viewBox"?l.attr("viewBox",`0 0 ${a.width} ${a.height}`):l.attr("width",a.width).attr("height",a.height),i){const c=l.select(`.${ks.brush} .overlay`),f={width:0,height:0};c.size()&&(f.width=+c.attr("width"),f.height=+c.attr("height")),l.selectAll([`#${n.id}`,`#${n.idGrid}`]).select("rect").attr("width",o).attr("height",s),l.select(`#${n.idXAxis}`).select("rect").call(t.setXAxisClipPath.bind(t)),l.select(`#${n.idYAxis}`).select("rect").call(t.setYAxisClipPath.bind(t)),n.idSubchart&&l.select(`#${n.idSubchart}`).select("rect").attr("width",o).attr("height",f.height)}},getCurrentPaddingByDirection(t,e=!1,n=!1){var a;const i=this,{config:o,$el:s,state:{hasAxis:l}}=i,c=o.axis_rotated,f=((a=o.padding)==null?void 0:a.mode)==="fit",g=he(o[`padding_${t}`])?o[`padding_${t}`]:void 0,v=l?{top:c?"y2":null,bottom:c?"y":"x",left:c?"x":"y",right:c?null:"y2"}[t]:null,m=/^(left|right)$/.test(t),S=v&&o[`axis_${v}_inner`],P=v&&o[`axis_${v}_show`],N=v?o[`axis_${v}_axes`].length:0;let L=v?m?i.getAxisWidthByAxisId(v,e):i.getHorizontalAxisHeight(v):0;const w=20;let X=0;!f&&m&&(L=Jg(L));let W=l&&m&&(S||ln(g)&&!P)?0:f?(P?L:0)+(g!=null?g:0):ln(g)?L:g;return m&&l?(v&&(f||S)&&o[`axis_${v}_label`].text&&(W+=i.axis.getAxisLabelPosition(v).isOuter?w:0),t==="right"?(W+=c?!f&&ln(g)?10:2:!P||S?f?2:1:0,W+=n?i.axis.getXAxisTickTextY2Overflow(w):0):t==="left"&&c&&ln(g)&&(W=o.axis_x_show?f?L:Math.max(L,40):1)):t==="top"?(s.title&&s.title.node()&&(W+=i.getTitlePadding()),X=c&&!S?N:0):t==="bottom"&&l&&c&&!P&&(W+=1),W+L*N-X},getCurrentPadding(t=!1){const e=this,[n,a,i,o]=["top","bottom","left","right"].map(s=>e.getCurrentPaddingByDirection(s,null,t));return{top:n,bottom:a,left:i,right:o}},getResettedPadding(t){const e=this,{config:n}=e,a=he(t);let i=a?0:{};return n.padding===!1?!a&&Object.keys(t).forEach(o=>{i[o]=!qn(n.data_labels)&&n.data_labels!==!1&&o==="top"?t[o]:0}):i=t,i},updateSizes(t){var e,n,a,i,o;const s=this,{config:l,state:c,$el:{legend:f}}=s,g=l.axis_rotated,v=s.hasArcType()||c.hasFunnel||c.hasTreemap,m=((e=l.padding)==null?void 0:e.mode)==="fit";!t&&s.setContainerSize();const S={width:f?s.getLegendWidth():0,height:f?s.getLegendHeight():0};!v&&l.axis_x_show&&l.axis_x_tick_autorotate&&s.updateXAxisTickClip();const P={right:l.legend_show&&c.isLegendRight?s.getLegendWidth()+(m?0:20):0,bottom:!l.legend_show||c.isLegendRight||c.isLegendInset?0:S.height},N=g||v?0:s.getHorizontalAxisHeight("x"),L=l.subchart_axis_x_show&&l.subchart_axis_x_tick_text_show?N:30,w=l.subchart_show&&!v?l.subchart_size_height+L:0,X=s.hasType("gauge")&&l.arc_needle_show&&!l.gauge_fullCircle&&!l.gauge_label_show?10:0,W=s.getCurrentPadding(!0);if(c.margin=!v&&g?{top:W.top,right:v?0:W.right+P.right,bottom:P.bottom+W.bottom,left:w+(v?0:W.left)}:{top:(m?0:4)+W.top,right:v?0:W.right+P.right,bottom:X+w+P.bottom+W.bottom,left:v?0:W.left},c.margin=s.getResettedPadding(c.margin),c.margin2=g?{top:c.margin.top,right:NaN,bottom:20+P.bottom,left:s.state.rotatedPadding.left}:{top:c.current.height-w-P.bottom,right:NaN,bottom:L+P.bottom,left:c.margin.left},c.margin3={top:0,right:NaN,bottom:0,left:0},(n=s.updateSizeForLegend)==null||n.call(s,S),c.width=c.current.width-c.margin.left-c.margin.right,c.height=c.current.height-c.margin.top-c.margin.bottom,c.width<0&&(c.width=0),c.height<0&&(c.height=0),c.width2=g?c.margin.left-c.rotatedPadding.left-c.rotatedPadding.right:c.width,c.height2=g?c.height:c.current.height-c.margin2.top-c.margin2.bottom,c.width2<0&&(c.width2=0),c.height2<0&&(c.height2=0),s.hasArcType()){const H=s.hasType("gauge"),k=l.legend_show&&c.isLegendRight,K=(a=c.hasRadar&&s.cache.get(Ln.radarTextWidth))!=null?a:0;c.arcWidth=c.width-(k?S.width+10:0)-K,c.arcHeight=c.height-(k&&!H?0:10),(i=l.arc_rangeText_values)!=null&&i.length&&(H?(c.arcWidth-=25,c.arcHeight-=10,c.margin.left+=10):(c.arcHeight-=20,c.margin.top+=10)),H&&!l.gauge_fullCircle&&(c.arcHeight+=c.height-s.getPaddingBottomForGauge()),(o=s.updateRadius)==null||o.call(s)}c.isLegendRight&&v&&(c.margin3.left=c.arcWidth/2+c.radiusExpanded*1.1)}},zp={setCssRule(t,e,n,a){const i=this,{config:o,state:{cssRule:s,style:l}}=i;return o.boost_useCssRule?c=>{c.each(f=>{const g=a&&(a==null?void 0:a.call(i,f)),v=`${t?`.${sn.shapes+i.getTargetSelectorSuffix(f.id)}`:""}${e}`;e in s&&l.sheet.deleteRule(s[v]),i.state.cssRule[v]=_g(l,v,n.filter(Boolean).map(m=>ze(g)&&m.indexOf(":")===-1?`${m}: ${g}`:m||""))})}:()=>{}},getStylePropValue(t){const{config:{boost_useCssRule:e}}=this;return e?null:ve(t)?t.bind(this):t}};function Uc(t){return typeof t=="string"?new Ie([document.querySelectorAll(t)],[document.documentElement]):new Ie([T(t)],_t)}function jp(t){let e="middle";return t>0&&t<=170?e="end":t>190&&t<=360&&(e="start"),e}function Vp(t,e,n,a,i){var o;const s=this,{value:l}=t,c=s.isCandlestickType(t),f=he(l)&&l<0||c&&!((o=s.getCandlestickData(t))!=null&&o._isUp);let{x:g,y:v}=e;const m=4,S=m*2;return a?n==="start"?(g+=f?0:S,v+=m):n==="middle"?(g+=S,v-=S):n==="end"&&(f&&(g-=S),v+=m):(n==="start"?(g+=m,f&&(v+=S*2)):n==="middle"?v-=S:n==="end"&&(g-=m,f&&(v+=S*2)),i&&(v+=f?-17:c?13:7)),{x:g,y:v}}function zc(t,e){var n;const a=this.config.data_labels_position,{id:i,index:o,value:s}=t;return(n=ve(a)?a.bind(this.api)(e,s,i,o,this.$el.text):(i in a?a[i]:a)[e])!=null?n:0}var Gp={opacityForText(t){const e=this;return e.isBarType(t)&&!e.meetsLabelThreshold(Math.abs(e.getRatio("bar",t)),"bar")?"0":e.hasDataLabel?null:"0"},initText(){const{$el:t}=this;t.main.select(`.${Se.chart}`).append("g").attr("class",On.chartTexts).style("pointer-events",t.funnel||t.treemap?"none":null)},updateTargetsForText(t){const e=this,n=e.getChartClass("Text"),a=e.getClass("texts","id"),i=e.classFocus.bind(e);e.$el.main.select(`.${On.chartTexts}`).selectAll(`.${On.chartText}`).data(t).attr("class",l=>`${n(l)}${i(l)}`.trim()).enter().append("g").style("opacity","0").attr("class",n).call(e.setCssRule(!0,` .${On.text}`,["fill","pointer-events:none"],e.updateTextColor)).append("g").attr("class",a)},updateText(){const t=this,{$el:e,$T:n,config:a,axis:i}=t,o=t.getClass("text","index"),s=a.data_labels.centered,l=e.main.selectAll(`.${On.texts}`).selectAll(`.${On.text}`).data(t.labelishData.bind(t));n(l.exit()).style("fill-opacity","0").remove(),e.text=l.enter().append("text").merge(l).attr("class",o).attr("text-anchor",c=>{let g=a[`axis_${i==null?void 0:i.getId(c.id)}_inverted`]?c.value>0:c.value<0;if(t.isCandlestickType(c)){const v=t.getCandlestickData(c);g=!(v!=null&&v._isUp)}else if(t.isTreemapType(c))return s?"middle":"start";return a.axis_rotated?g?"end":"start":"middle"}).style("fill",t.getStylePropValue(t.updateTextColor)).style("fill-opacity","0").each(function(c,f,g){const v=ot(this);let{value:m}=c;if(t.isBubbleZType(c))m=t.getBubbleZData(m,"z");else if(t.isCandlestickType(c)){const S=t.getCandlestickData(c);S&&(m=S.close)}m=t.isTreemapType(c)?t.treemapDataLabelFormat(c)(v):t.dataLabelFormat(c.id)(m,c.id,c.index,g),he(m)?this.textContent=m:wa(v,m)})},updateTextColor(t){const e=this,{config:n}=e,a=n.data_labels_colors,i=e.isArcType(t)&&!e.isRadarType(t)||e.isFunnelType(t)||e.isTreemapType(t)?null:e.color(t);let o;if(ze(a))o=a;else if(Be(a)){const{id:s}=t.data||t;o=a[s]}else ve(a)&&(o=a.bind(e.api)(i,t));if(e.isCandlestickType(t)&&!ve(a)){const s=e.getCandlestickData(t);if(!(s!=null&&s._isUp)){const l=n.candlestick_color_down;o=Be(l)?l[t.id]:l}}return o||i},updateTextBGColor(t,e){const n=this,{$el:a}=n;let i="";if(ze(e)||Be(e)){const o=ze(e)?"":n.getTargetSelectorSuffix("id"in t?t.id:t.data.id),s=a.defs.select(["filter[id*='labels-bg","']"].join(o));s.size()&&(i=`url(#${s.attr("id")})`)}return i||null},redrawText(t,e,n,a){const i=this,{$T:o,axis:s,config:l,state:{hasTreemap:c}}=i,f=gr(!0),g=l.axis_rotated,v=l.data_labels.rotate,m=jp(v),S=v?`rotate(${v})`:"";return i.$el.text.style("fill",i.getStylePropValue(i.updateTextColor)).attr("filter",P=>i.updateTextBGColor.bind(i)(P,l.data_labels_backgroundColors)).style("fill-opacity",n?0:i.opacityForText.bind(i)).each(function(P,N){const L=o(c&&this.childElementCount?this.parentNode:this,!!(a&&this.getAttribute("x")),f),w=l[`axis_${s==null?void 0:s.getId(P.id)}_inverted`];let X={x:t.bind(this)(P,N),y:e.bind(this)(P,N)};v&&(X=Vp.bind(i)(P,X,m,g,w),L.attr("text-anchor",m)),this.childElementCount||v?L.attr("transform",`translate(${X.x} ${X.y}) ${S}`):L.attr("x",X.x).attr("y",X.y)}),!0},getTextRect(t,e){const n=this;let a=t.node?t.node():t;/text/i.test(a.tagName)||(a=a.querySelector("text"));const i=a.textContent,o=`${Ln.textRect}-${i.replace(/\W/g,"_")}`;let s=n.cache.get(o);return s||(n.$el.svg.append("text").style("visibility","hidden").style("font",ot(a).style("font")).classed(e,!0).text(i).call(l=>{s=Ma(l.node())}).remove(),n.cache.add(o,s)),s},generateXYForText(t,e){const n=this,{state:{hasRadar:a,hasFunnel:i,hasTreemap:o}}=n,s=Object.keys(t),l={},c=e?n.getXForText:n.getYForText;return i&&s.push("funnel"),a&&s.push("radar"),o&&s.push("treemap"),s.forEach(f=>{l[f]=n[`generateGet${Cn(f)}Points`](t[f],!1)}),function(f,g){const v=n.isAreaType(f)&&"area"||n.isBarType(f)&&"bar"||n.isCandlestickType(f)&&"candlestick"||n.isFunnelType(f)&&"funnel"||n.isRadarType(f)&&"radar"||n.isTreemapType(f)&&"treemap"||"line";return c.call(n,l[v](f,g),f,this)}},getCenteredTextPos(t,e,n,a){const i=this,{config:o}=i,s=o.axis_rotated,l=i.isBarType(t),c=i.isTreemapType(t);if(o.data_labels.centered&&(l||c)){const f=Ma(n);if(l){const g=i.getRangedData(t,null,"bar")>=0;if(s){const v=(g?e[1][1]-e[0][1]:e[0][1]-e[1][1])/2+f.width/2;return g?-v-3:v+2}else{const v=(g?e[0][1]-e[1][1]:e[1][1]-e[0][1])/2+f.height/2;return g?v:-v-2}}else if(c)return a==="x"?(e[1][0]-e[0][0])/2:(e[1][1]-e[0][1])/2+f.height/2}return 0},getXForText(t,e,n){var a;const i=this,{config:o}=i,s=o.axis_rotated,l=i.isFunnelType(e),c=i.isTreemapType(e);let f=t?t[0][0]:0;if(i.isCandlestickType(e))s?f=(a=i.getCandlestickData(e))!=null&&a._isUp?t[2][2]+4:t[2][1]-4:f+=(t[1][0]-f)/2;else if(l)f+=i.state.current.width/2;else if(c)f+=o.data_labels.centered?0:5;else if(s){const g=o[`axis_${i.axis.getId(e.id)}_inverted`],v=i.isBarType(e)?4:6,m=e.value;f=t[2][1],g?f-=v*(m>0?1:-1):f+=v*(m<0?-1:1)}else f=i.hasType("bar")?(t[2][0]+t[0][0])/2:f;return(s||c)&&(f+=i.getCenteredTextPos(e,t,n,"x")),f+zc.call(this,e,"x")},getYForText(t,e,n){const a=this,{axis:i,config:o,state:s}=a,l=o.axis_rotated,c=o[`axis_${i==null?void 0:i.getId(e.id)}_inverted`],f=a.isBarType(e),g=a.isFunnelType(e),v=a.isTreemapType(e),m=o.point_r,S=Ma(n);let{value:P}=e,N=3,L;if(a.isCandlestickType(e))P=a.getCandlestickData(e),l?(L=t[0][0],L+=(t[1][0]-L)/2+N):(L=P&&P._isUp?t[2][2]-N:t[2][1]+N*4,c&&(L+=15*(P._isUp?1:-1)));else if(g)L=t?t[0][1]+(t[1][1]-t[0][1])/2+S.height/2-3:0;else if(v)L=t[0][1]+(o.data_labels.centered?0:S.height+5);else if(l)L=(t[0][0]+t[2][0]+S.height*.6)/2;else if(L=t[2][1],he(m)&&m>5&&(a.isLineType(e)||a.isScatterType(e))&&(N+=o.point_r/2.3),P<0||P===0&&!s.hasPositiveValue&&s.hasNegativeValue)L+=c?f?-3:-5:S.height+(f?-N:N);else{let w=-N*2;f?w=-N:a.isBubbleType(e)&&(w=N),c&&(w=f?10:15),L+=w}return(!l||v)&&(L+=a.getCenteredTextPos(e,t,n,"y")),L+zc.call(this,e,"y")},markOverlapped(t,e,n){const a=e.$el.arcs.selectAll(n),i=a.filter(c=>c.data.id!==t),o=a.filter(c=>c.data.id===t),s=Jl(o.node()),l=(c,f)=>Math.sqrt(Math.pow(c,2)+Math.pow(f,2));o.node()&&i.each(function(){const c=Jl(this),f=ot(this),g=l(s.e,s.f)>l(c.e,c.f)?o:f,v=Math.ceil(Math.abs(s.e-c.e))=i}};function jc(t="left",e){const n=he(e);let a;return t.indexOf("center")>-1?a=n?e/2:"middle":t.indexOf("right")>-1?a=n?e:"end":a=n?0:"start",a}var Xp={initTitle(){const t=this,{config:e,$el:n}=t;if(e.title_text){n.title=n.svg.append("g");const a=n.title.append("text").style("text-anchor",jc(e.title_position)).attr("class",On.title);wa(a,e.title_text,[.3,1.5])}},redrawTitle(){const t=this,{config:e,state:{current:n},$el:{title:a}}=t;if(a){const i=jc(e.title_position,n.width),o=(e.title_padding.top||0)+t.getTextRect(t.$el.title,On.title).height;a.attr("transform",`translate(${i}, ${o})`)}},getTitlePadding(){const t=this,{$el:{title:e},config:n}=t;return(n.title_padding.top||0)+(e?t.getTextRect(e,On.title).height:0)+(n.title_padding.bottom||0)}},Hp={initTooltip(){const t=this,{config:e,$el:n}=t;n.tooltip=ot(e.tooltip_contents.bindto),n.tooltip.empty()&&(n.tooltip=n.chart.append("div").attr("class",ei.tooltipContainer).style("position","absolute").style("pointer-events","none").style("display","none")),t.bindTooltipResizePos()},initShowTooltip(){var t;const e=this,{config:n,$el:a,state:{hasAxis:i,hasRadar:o}}=e;if(n.tooltip_init_show){const s=!(i||o);(t=e.axis)!=null&&t.isTimeSeries()&&ze(n.tooltip_init_x)&&(n.tooltip_init_x=Yn.call(e,n.tooltip_init_x)),e.api.tooltip.show({data:{[s?"index":"x"]:n.tooltip_init_x}});const l=n.tooltip_init_position;if(!n.tooltip_contents.bindto&&!qn(l)){const{top:c=0,left:f=50}=l;a.tooltip.style("top",ze(c)?c:`${c}px`).style("left",ze(f)?f:`${f}px`).style("display",null)}}},getTooltipHTML(...t){const e=this,{api:n,config:a}=e;return ve(a.tooltip_contents)?a.tooltip_contents.bind(n)(...t):e.getTooltipContent(...t)},getTooltipContent(t,e,n,a){var i;const o=this,{api:s,config:l,state:c,$el:f}=o,[g,v,m]=["title","name","value"].map(vt=>{const Q=l[`tooltip_format_${vt}`];return ve(Q)?Q.bind(s):Q}),S=(...vt)=>Po((g||e)(...vt)),P=(...vt)=>Po((v||(Q=>Q))(...vt)),N=(...vt)=>{const Q=m||(c.hasTreemap||o.isStackNormalized()?(St,ct)=>`${(ct*100).toFixed(2)}%`:n);return Po(Q(...vt))},L=l.tooltip_order,w=vt=>o.axis&&o.isBubbleZType(vt)?o.getBubbleZData(vt.value,"z"):o.getBaseValue(vt),X=o.levelColor?vt=>o.levelColor(vt.value):vt=>a(vt),W=l.tooltip_contents,H=W.template,k=o.mapToTargetIds();if(L===null&&l.data_groups.length){const vt=o.orderTargets(o.data.targets).map(Q=>Q.id).reverse();t.sort((Q,St)=>{let ct=Q?Q.value:null,At=St?St.value:null;return ct>0&&At>0&&(ct=Q.id?vt.indexOf(Q.id):null,At=St.id?vt.indexOf(St.id):null),ct-At})}else if(/^(asc|desc)$/.test(L)){const vt=L==="asc";t.sort((Q,St)=>{const ct=Q?w(Q):null,At=St?w(St):null;return vt?ct-At:At-ct})}else ve(L)&&t.sort(L.bind(s));const K=o.getTooltipContentTemplate(H),at=t.length;let ht,$t,dt,st,Vt;for(Vt=0;Vt${vt}`:""})}if(!$t.ratio&&f.arcs&&(dt=["arc",o.$el.arcs.select(`path.${Ve.arc}-${$t.id}`).data()[0]],$t.ratio=o.getRatio(...dt)),dt=[$t.ratio,$t.id,$t.index],o.isAreaRangeType($t)){const[vt,Q]=["high","low"].map(ct=>N(o.getRangedData($t,ct),...dt));st=`Mid: ${N(w($t),...dt)} High: ${vt} Low: ${Q}`}else if(o.isCandlestickType($t)){const[vt,Q,St,ct,At]=["open","high","low","close","volume"].map(Gt=>o.getRangedData($t,Gt,"candlestick")?N(o.getRangedData($t,Gt,"candlestick"),...dt):void 0);st=`Open: ${vt} High: ${Q} Low: ${St} Close: ${ct}${At?` Volume: ${At}`:""}`}else if(o.isBarRangeType($t)){const{value:vt,id:Q,index:St}=$t;st=`${N(vt,void 0,Q,St)}`}else st=N(w($t),...dt);if(st!==void 0){if($t.name===null)continue;const vt=P((i=$t.name)!=null?i:$t.id,...dt),Q=X($t),St={CLASS_TOOLTIP_NAME:ei.tooltipName+o.getTargetSelectorSuffix($t.id),COLOR:H||!o.patterns?Q:``,NAME:vt,VALUE:st};if(H&&Be(W.text)){const ct=k.indexOf($t.id);Object.keys(W.text).forEach(At=>{St[At]=W.text[At][ct]})}ht+=bi(K[1],St)}}return`${ht}`},getTooltipContentTemplate(t){return(t||` + {=TITLE} + {{ + + + }} +
    ${this.patterns?"{=COLOR}":''}{=NAME}{=VALUE}
    `).replace(/(\r?\n|\t)/g,"").split(/{{(.*)}}/)},setTooltipPosition(t,e){var n,a;const i=this,{config:o,scale:s,state:l,$el:{eventRect:c,tooltip:f,svg:g}}=i,{bindto:v}=o.tooltip_contents,m=o.axis_rotated,S=f==null?void 0:f.datum();if(!v&&S){const P=t!=null?t:JSON.parse(S.current),[N,L]=Hn(l.event,e!=null?e:c==null?void 0:c.node()),w={x:N,y:L};if(l.hasAxis&&s.x&&S&&"x"in S){const k=(K=0,at,ht="y")=>{var $t;const dt=s[at?($t=i.axis)==null?void 0:$t.getId(at):ht];return dt?dt(K)+(m?l.margin.left:l.margin.top):0};w.xAxis=s.x(S.x)+(o.tooltip_position?m?l.margin.top:l.margin.left:0),P.length===1?w.yAxis=k(P[0].value,P[0].id):w.yAxis=k}const{width:X=0,height:W=0}=S,H=(a=(n=o.tooltip_position)==null?void 0:n.bind(i.api)(P,X,W,c==null?void 0:c.node(),w))!=null?a:Lo(g)?i.getTooltipPositionViewBox.bind(i)(X,W,w):i.getTooltipPosition.bind(i)(X,W,w);["top","left"].forEach(k=>{const K=H[k];f.style(k,`${K}px`),k==="left"&&!S.xPosInPercent&&(S.xPosInPercent=K/l.current.width*100)})}},getTooltipPositionViewBox(t,e,n){var a,i;const o=this,{$el:{eventRect:s,svg:l},config:c,state:f}=o,g=c.axis_rotated,v=o.hasArcType()||f.hasFunnel||f.hasTreemap,m=(i=(a=v?l:s)==null?void 0:a.node())!=null?i:f.event.target;let{x:S,y:P}=n;f.hasAxis&&(S=g?S:n.xAxis,P=g?n.xAxis:P);const N=Ai(m,S,P,!1),L=m.getBoundingClientRect(),w=Ai(m,20,0,!1).x;let X=N.y,W=N.x+t/2+w;return v&&(f.hasFunnel||f.hasTreemap||f.hasRadar?(W-=t/2+w,X+=e):(X+=L.height/2,W+=L.width/2-(t-w))),W+t>L.width&&(W=L.width-t-w),X+e>L.height&&(X-=e*2),{top:X,left:W}},getTooltipPosition(t,e,n){var a,i,o;const s=this,{config:l,scale:c,state:f}=s,{width:g,height:v,current:m,hasFunnel:S,hasRadar:P,hasTreemap:N,isLegendRight:L,inputType:w}=f,X=s.hasType("gauge")&&!l.gauge_fullCircle,W=l.axis_rotated,H=s.hasArcType(),k=s.getSvgLeft(!0);let K=k+m.width-s.getCurrentPaddingByDirection("right");const at=20;let{x:ht,y:$t}=n;if(P)ht+=ht>=g/2?15:-(t+15),$t+=15;else if(H){if(w!=="touch"){let Vt=(i=(a=s.getTitlePadding)==null?void 0:a.call(s))!=null?i:0;Vt&&X&&((o=l.arc_rangeText_values)!=null&&o.length)&&(Vt+=10),ht+=(g-(L?s.getLegendWidth():0))/2,$t+=(X?v:v/2+e)+Vt}}else if(S||N)$t+=e;else{const st={top:s.getCurrentPaddingByDirection("top",!0),left:s.getCurrentPaddingByDirection("left",!0)};W?(ht+=k+st.left+at,$t=st.top+n.xAxis+at,K-=k):(ht=k+st.left+at+(c.zoom?ht:n.xAxis),$t+=st.top-5)}if(ht+t+15>K&&(ht-=t+(S||N||H?0:W?at*2:38)),$t+e>m.height){const st=N?e+10:30;$t-=X?e*1.5:e+st}const dt={top:$t,left:ht};return Object.keys(dt).forEach(st=>{dt[st]<0&&(dt[st]=0)}),dt},showTooltip(t,e){const n=this,{config:a,$el:{tooltip:i}}=n,o=t.filter(c=>c&&De(n.getBaseValue(c)));if(!i||o.length===0||!a.tooltip_show)return;let s=i.datum();const l=JSON.stringify(t);if(!s||s.current!==l){const{index:c,x:f}=t.concat().sort()[0];_e(a.tooltip_onshow,n.api,t),i.html(n.getTooltipHTML(t,n.axis?n.axis.getXAxisTickFormat():n.categoryName.bind(n),n.getDefaultValueFormat(),n.color)).style("display",null).style("visibility",null).datum(s={index:c,x:f,current:l,width:i.property("offsetWidth"),height:i.property("offsetHeight")}),_e(a.tooltip_onshown,n.api,t),n._handleLinkedCharts(!0,c)}n.setTooltipPosition(o,e)},bindTooltipResizePos(){const t=this,{resizeFunction:e,state:n,$el:{tooltip:a}}=t;e.add(()=>{if(a.style("display")==="block"){const{current:i}=n,{width:o,xPosInPercent:s}=a.datum();let l=i.width/100*s;const c=i.width-(l+o);c<0&&(l+=c),a.style("left",`${l}px`)}})},hideTooltip(t){var e;const n=this,{api:a,config:i,$el:{tooltip:o}}=n;if(o&&o.style("display")!=="none"&&(!i.tooltip_doNotHide||t)){const s=JSON.parse((e=o.datum().current)!=null?e:{});_e(i.tooltip_onhide,a,s),o.style("display","none").style("visibility","hidden").datum(null),_e(i.tooltip_onhidden,a,s)}},_handleLinkedCharts(t,e){const n=this,{charts:a,config:i,state:{event:o}}=n;if(o!=null&&o.isTrusted&&i.tooltip_linked&&a.length>1){const s=i.tooltip_linked_name;a.filter(l=>l!==n.api).forEach(l=>{const{config:c,$el:f}=l.internal,g=c.tooltip_linked,v=c.tooltip_linked_name,m=gn.body.contains(f.chart.node());if(g&&s===v&&m){const S=f.tooltip.data()[0],P=e!==(S==null?void 0:S.index);try{l.tooltip[t&&P?"show":"hide"]({index:e})}catch(N){}}})}},updateTooltipOnRedraw(t,e){var n;const a=this,{config:i,$el:{eventRect:o,svg:s,tooltip:l},state:{event:c,hasAxis:f,hasRadar:g,hasTreemap:v}}=a;if((l==null?void 0:l.style("display"))==="block"&&c){const m=t!=null?t:(n=g?s:o)==null?void 0:n.node();if(f||g)if(a.isMultipleX())a.selectRectForMultipleXs(m,!1);else{const S=e!=null?e:a.getDataIndexFromEvent(c);e===-1?a.api.tooltip.hide():(a.selectRectForSingle(m,S),a.setExpand(S,null,!0))}else{const{clientX:S,clientY:P}=c;setTimeout(()=>{let N=[S,P].every(Number.isFinite)&&gn.elementFromPoint(S,P);const L=N&&ot(N).datum();if(L){const w=a.hasArcType()?a.convertToArcData(a.updateAngle(L)):L==null?void 0:L.data;v&&(N=s.node()),w&&a.showTooltip([w],N)}else a.api.tooltip.hide()},i.transition_duration)}}}},Yp={getTranslate(t,e=0){var n;const a=this,{config:i,state:o}=a,s=i.axis_rotated;let l=0,c,f;if(e&&/^(x|y2?)$/.test(t)&&(l=a.getAxisSize(t)*e),t==="main")c=$i(o.margin.left),f=$i(o.margin.top);else if(t==="context")c=$i(o.margin2.left),f=$i(o.margin2.top);else if(t==="legend")c=o.margin3.left,f=o.margin3.top;else if(t==="x")c=s?-l:0,f=s?0:o.height+l;else if(t==="y")c=s?0:-l,f=s?o.height+l:0;else if(t==="y2")c=s?0:o.width+l,f=s?-l-1:0;else if(t==="subX")c=0,f=s?0:o.height2;else if(t==="arc")c=o.arcWidth/2,f=o.arcHeight/2,(n=i.arc_rangeText_values)!=null&&n.length&&(f+=5+(a.hasType("gauge")&&i.title_text?10:0));else if(t==="polar")c=o.arcWidth/2,f=o.arcHeight/2;else if(t==="radar"){const[g,v]=a.getRadarSize();c=o.width/2-g,f=o.height/2-v}return`translate(${c}, ${f})`},transformMain(t,e){const n=this,{$el:{main:a},$T:i}=n,o=e!=null&&e.axisX?e.axisX:i(a.select(`.${Tn.axisX}`),t),s=e!=null&&e.axisY?e.axisY:i(a.select(`.${Tn.axisY}`),t),l=e!=null&&e.axisY2?e.axisY2:i(a.select(`.${Tn.axisY2}`),t);i(a,t).attr("transform",n.getTranslate("main")),o.attr("transform",n.getTranslate("x")),s.attr("transform",n.getTranslate("y")),l.attr("transform",n.getTranslate("y2")),a.select(`.${Ve.chartArcs}`).attr("transform",n.getTranslate("arc"))},transformAll(t,e){const n=this,{config:a,state:{hasAxis:i,hasFunnel:o,hasTreemap:s},$el:l}=n;!o&&!s&&n.transformMain(t,e),i&&a.subchart_show&&n.transformContext(t,e),l.legend&&n.transformLegend(t)}},Wp={isValidChartType(t){return!!(t&&Object.values(oe).indexOf(t)>-1)},setTargetType(t,e){const n=this,{config:a,state:{withoutFadeIn:i}}=n;n.mapToTargetIds(t).forEach(o=>{i[o]=e===a.data_types[o],a.data_types[o]=e}),t||(a.data_type=e)},updateTypesElements(){const t=this,{state:{current:e}}=t;Object.keys(oe).forEach(n=>{const a=oe[n],i=t.hasType(a,null,!0),o=e.types.indexOf(a);o===-1&&i?e.types.push(a):o>-1&&!i&&e.types.splice(o,1)}),t.setChartElements()},hasType(t,e,n=!1){var a;const i=this,{config:o,state:{current:s}}=i,l=o.data_types,c=e||i.data.targets;let f=!1;return!n&&((a=s.types)==null?void 0:a.indexOf(t))>-1?f=!0:c!=null&&c.length?c.forEach(g=>{const v=l[g.id];(v===t||!v&&t==="line")&&(f=!0)}):Object.keys(l).length?Object.keys(l).forEach(g=>{l[g]===t&&(f=!0)}):f=o.data_type===t,f},hasTypeOf(t,e,n=[]){return t in Sr?!Sr[t].filter(a=>n.indexOf(a)===-1).every(a=>!this.hasType(a,e)):!1},isTypeOf(t,e){var n;const a=ze(t)?t:t.id,i=this.config&&(((n=this.config.data_types)==null?void 0:n[a])||this.config.data_type);return je(e)?e.indexOf(i)>=0:i===e},hasPointType(){const t=this;return t.hasTypeOf("Line")||t.hasType("bubble")||t.hasType("scatter")},hasArcType(t,e){return this.hasTypeOf("Arc",t,e)},hasMultiArcGauge(){return this.hasType("gauge")&&this.config.gauge_type==="multi"},isLineType(t){const e=ze(t)?t:t.id;return!this.config.data_types[e]||this.isTypeOf(e,Sr.Line)},isStepType(t){return this.isTypeOf(t,Sr.Step)},isSplineType(t){return this.isTypeOf(t,Sr.Spline)},isAreaType(t){return this.isTypeOf(t,Sr.Area)},isAreaRangeType(t){return this.isTypeOf(t,Sr.AreaRange)},isBarType(t){return this.isTypeOf(t,"bar")},isBubbleType(t){return this.isTypeOf(t,"bubble")},isCandlestickType(t){return this.isTypeOf(t,"candlestick")},isScatterType(t){return this.isTypeOf(t,"scatter")},isTreemapType(t){return this.isTypeOf(t,"treemap")},isPieType(t){return this.isTypeOf(t,"pie")},isFunnelType(t){return this.isTypeOf(t,"funnel")},isGaugeType(t){return this.isTypeOf(t,"gauge")},isDonutType(t){return this.isTypeOf(t,"donut")},isPolarType(t){return this.isTypeOf(t,"polar")},isRadarType(t){return this.isTypeOf(t,"radar")},isArcType(t){return this.isPieType(t)||this.isDonutType(t)||this.isGaugeType(t)||this.isPolarType(t)||this.isRadarType(t)},isCirclePoint(t){const{config:e}=this,n=e.point_pattern;let a=!1;return(t==null?void 0:t.tagName)==="circle"?a=!0:a=e.point_type==="circle"&&(!n||je(n)&&n.length===0),a},lineData(t){return this.isLineType(t)?[t]:[]},arcData(t){return this.isArcType(t.data)?[t]:[]},labelishData(t){return this.isBarType(t)||this.isLineType(t)||this.isScatterType(t)||this.isBubbleType(t)||this.isCandlestickType(t)||this.isFunnelType(t)||this.isRadarType(t)||this.isTreemapType(t)?t.values.filter(e=>he(e.value)||!!e.value):[]},barLineBubbleData(t){return this.isBarType(t)||this.isLineType(t)||this.isBubbleType(t)?t.values:[]},isInterpolationType(t){return["basis","basis-closed","basis-open","bundle","cardinal","cardinal-closed","cardinal-open","catmull-rom","catmull-rom-closed","catmull-rom-open","linear","linear-closed","monotone-x","monotone-y","natural"].indexOf(t)>=0}};function Ni(t,e,n){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+e)/6,(t._y0+4*t._y1+n)/6)}function Fi(t){this._context=t}Fi.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Ni(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Ni(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function Kp(t){return new Fi(t)}function Ar(){}function Vc(t){this._context=t}Vc.prototype={areaStart:Ar,areaEnd:Ar,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x2,this._y2),this._context.closePath();break}case 2:{this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break}case 3:{this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4);break}}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x2=t,this._y2=e;break;case 1:this._point=2,this._x3=t,this._y3=e;break;case 2:this._point=3,this._x4=t,this._y4=e,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+e)/6);break;default:Ni(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function Zp(t){return new Vc(t)}function Gc(t){this._context=t}Gc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+t)/6,a=(this._y0+4*this._y1+e)/6;this._line?this._context.lineTo(n,a):this._context.moveTo(n,a);break;case 3:this._point=4;default:Ni(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function Jp(t){return new Gc(t)}function Xc(t,e){this._basis=new Fi(t),this._beta=e}Xc.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,e=this._y,n=t.length-1;if(n>0)for(var a=t[0],i=e[0],o=t[n]-a,s=e[n]-i,l=-1,c;++l<=n;)c=l/n,this._basis.point(this._beta*t[l]+(1-this._beta)*(a+c*o),this._beta*e[l]+(1-this._beta)*(i+c*s));this._x=this._y=null,this._basis.lineEnd()},point:function(t,e){this._x.push(+t),this._y.push(+e)}};var Qp=function t(e){function n(a){return e===1?new Fi(a):new Xc(a,e)}return n.beta=function(a){return t(+a)},n}(.85);function Bi(t,e,n){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-e),t._y2+t._k*(t._y1-n),t._x2,t._y2)}function ls(t,e){this._context=t,this._k=(1-e)/6}ls.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Bi(this,this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2,this._x1=t,this._y1=e;break;case 2:this._point=3;default:Bi(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var kp=function t(e){function n(a){return new ls(a,e)}return n.tension=function(a){return t(+a)},n}(0);function cs(t,e){this._context=t,this._k=(1-e)/6}cs.prototype={areaStart:Ar,areaEnd:Ar,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:Bi(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var qp=function t(e){function n(a){return new cs(a,e)}return n.tension=function(a){return t(+a)},n}(0);function us(t,e){this._context=t,this._k=(1-e)/6}us.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Bi(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var _p=function t(e){function n(a){return new us(a,e)}return n.tension=function(a){return t(+a)},n}(0);const Hc=Math.abs,En=Math.atan2,jr=Math.cos,tm=Math.max,fs=Math.min,rr=Math.sin,oa=Math.sqrt,bn=1e-12,Fa=Math.PI,Ui=Fa/2,zi=2*Fa;function em(t){return t>1?0:t<-1?Fa:Math.acos(t)}function Yc(t){return t>=1?Ui:t<=-1?-Ui:Math.asin(t)}function ds(t,e,n){var a=t._x1,i=t._y1,o=t._x2,s=t._y2;if(t._l01_a>bn){var l=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);a=(a*l-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*l-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>bn){var f=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,g=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*f+t._x1*t._l23_2a-e*t._l12_2a)/g,s=(s*f+t._y1*t._l23_2a-n*t._l12_2a)/g}t._context.bezierCurveTo(a,i,o,s,t._x2,t._y2)}function Wc(t,e){this._context=t,this._alpha=e}Wc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,a=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+a*a,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3;default:ds(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var nm=function t(e){function n(a){return e?new Wc(a,e):new ls(a,0)}return n.alpha=function(a){return t(+a)},n}(.5);function Kc(t,e){this._context=t,this._alpha=e}Kc.prototype={areaStart:Ar,areaEnd:Ar,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,a=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+a*a,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:ds(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var rm=function t(e){function n(a){return e?new Kc(a,e):new cs(a,0)}return n.alpha=function(a){return t(+a)},n}(.5);function Zc(t,e){this._context=t,this._alpha=e}Zc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,a=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+a*a,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:ds(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var am=function t(e){function n(a){return e?new Zc(a,e):new us(a,0)}return n.alpha=function(a){return t(+a)},n}(.5);function Jc(t){return t<0?-1:1}function Qc(t,e,n){var a=t._x1-t._x0,i=e-t._x1,o=(t._y1-t._y0)/(a||i<0&&-0),s=(n-t._y1)/(i||a<0&&-0),l=(o*i+s*a)/(a+i);return(Jc(o)+Jc(s))*Math.min(Math.abs(o),Math.abs(s),.5*Math.abs(l))||0}function kc(t,e){var n=t._x1-t._x0;return n?(3*(t._y1-t._y0)/n-e)/2:e}function hs(t,e,n){var a=t._x0,i=t._y0,o=t._x1,s=t._y1,l=(o-a)/3;t._context.bezierCurveTo(a+l,i+l*e,o-l,s-l*n,o,s)}function ji(t){this._context=t}ji.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:hs(this,this._t0,kc(this,this._t0));break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){var n=NaN;if(t=+t,e=+e,!(t===this._x1&&e===this._y1)){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,hs(this,kc(this,n=Qc(this,t,e)),n);break;default:hs(this,this._t0,n=Qc(this,t,e));break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e,this._t0=n}}};function qc(t){this._context=new _c(t)}(qc.prototype=Object.create(ji.prototype)).point=function(t,e){ji.prototype.point.call(this,e,t)};function _c(t){this._context=t}_c.prototype={moveTo:function(t,e){this._context.moveTo(e,t)},closePath:function(){this._context.closePath()},lineTo:function(t,e){this._context.lineTo(e,t)},bezierCurveTo:function(t,e,n,a,i,o){this._context.bezierCurveTo(e,t,a,n,o,i)}};function im(t){return new ji(t)}function om(t){return new qc(t)}function tu(t){this._context=t}tu.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,e=this._y,n=t.length;if(n)if(this._line?this._context.lineTo(t[0],e[0]):this._context.moveTo(t[0],e[0]),n===2)this._context.lineTo(t[1],e[1]);else for(var a=eu(t),i=eu(e),o=0,s=1;s=0;--e)i[e]=(s[e]-i[e+1])/o[e];for(o[n-1]=(t[n]+i[n-1])/2,e=0;e=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:{if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var n=this._x*(1-this._t)+t*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,e)}break}}this._x=t,this._y=e}};function cm(t){return new Vi(t,.5)}function um(t){return new Vi(t,0)}function fm(t){return new Vi(t,1)}function dm(t){const e=this;let n;return e.isLineType(t)?n=e.generateGetLinePoints(e.getShapeIndices(e.isLineType)):e.isBarType(t)&&(n=e.generateGetBarPoints(e.getShapeIndices(e.isBarType))),n}var hm={getDrawShape(){const t=this,e=t.config.axis_rotated,{hasRadar:n,hasTreemap:a}=t.state,i={type:{},indices:{},pos:{}};if(!a&&["bar","candlestick","line","area"].forEach(o=>{const s=Cn(/^(bubble|scatter)$/.test(o)?"line":o);if(t.hasType(o)||t.hasTypeOf(s)||o==="line"&&(t.hasType("bubble")||t.hasType("scatter"))){const l=t.getShapeIndices(t[`is${s}Type`]),c=t[`generateDraw${s}`];i.indices[o]=l,i.type[o]=c?c.bind(t)(l,!1):void 0}}),!t.hasArcType()||n||a){let o,s;a||(o=n?t.radarCircleX:e?t.circleY:t.circleX,s=n?t.radarCircleY:e?t.circleX:t.circleY),i.pos={xForText:t.generateXYForText(i.indices,!0),yForText:t.generateXYForText(i.indices,!1),cx:(o||function(){}).bind(t),cy:(s||function(){}).bind(t)}}return i},getShapeIndices(t){const e=this,{config:n}=e,a=n.data_xs,i=cn(a),o={};let s=i?{}:0;return i&&Mo(Object.keys(a).map(l=>a[l])).forEach(l=>{s[l]=0,o[l]={}}),e.filterTargetsToShow(e.data.targets.filter(t,e)).forEach(l=>{var c;const f=l.id in a?a[l.id]:"",g=f?o[f]:o;for(let v=0,m;m=n.data_groups[v];v++)if(!(m.indexOf(l.id)<0))for(let S=0,P;P=m[S];S++){if(P in g){g[l.id]=g[P];break}l.id!==P&&f&&(g[P]=(c=g[l.id])!=null?c:s[f])}ln(g[l.id])&&(g[l.id]=f?s[f]++:s++,g.__max__=(f?s[f]:s)-1)}),o},getIndices(t,e,n){const a=this,{data_xs:i,bar_indices_removeNull:o}=a.config,{id:s,index:l}=e;if(a.isBarType(s)&&o){const c={};return a.getAllValuesOnIndex(l,!0).forEach((f,g)=>{c[f.id]=g,c.__max__=g}),c}return cn(i)?t[i[s]]:t},getIndicesMax(t){return cn(this.config.data_xs)?Object.keys(t).map(e=>t[e].__max__||0).reduce((e,n)=>e+n):t.__max__},getShapeX(t,e,n){const a=this,{config:i,scale:o}=a,s=n?o.subX:o.zoom||o.x,l=i.bar_overlap,c=i.bar_padding,f=(v,m)=>v+m,g=nr(t)&&(t._$total.length?t._$total.reduce(f)/2:0);return v=>{const m=a.getIndices(e,v,"getShapeX"),S=v.id in m?m[v.id]:0,P=(m.__max__||0)+1;let N=0;if(cn(v.x)){const L=s(v.x,!0);if(g){const w=t[v.id]||t._$width;N=l?L-w/2:L-w+t._$total.slice(0,S+1).reduce(f)-g}else N=L-(he(t)?t:t._$width)*(P/2-(l?1:S))}return t&&N&&P>1&&c&&(S&&(N+=c*S),P>2?N-=(P-1)*c/2:P===2&&(N-=c/2)),N}},getShapeY(t){const e=this,n=e.isStackNormalized();return a=>{let{value:i}=a;return he(a)?i=a:e.isAreaRangeType(a)?i=e.getBaseValue(a,"mid"):n?i=e.getRatio("index",a,!0):e.isBubbleZType(a)?i=e.getBubbleZData(a.value,"y"):e.isBarRangeType(a)&&(i=i[1]),e.getYScaleById(a.id,t)(i)}},getShapeYMin(t){const e=this,n=e.axis.getId(t),a=e.scale[n],[i]=a.domain(),o=e.config[`axis_${n}_inverted`];return!e.isGrouped(t)&&!o&&i>0?i:0},getShapeOffsetData(t){const e=this,n=e.orderTargets(e.filterTargetsToShow(e.data.targets.filter(t,e))),a=e.isStackNormalized(),i=n.map(s=>{let l=s.values;const c={};e.isStepType(s)&&(l=e.convertValuesToStep(l));const f=l.reduce((g,v)=>{const m=Number(v.x);return g[m]=v,c[m]=a?e.getRatio("index",v,!0):v.value,g},{});return{id:s.id,rowValues:l,rowValueMapByXValue:f,values:c}});return{indexMapByTargetId:n.reduce((s,{id:l},c)=>(s[l]=c,s),{}),shapeOffsetTargets:i}},getShapeOffset(t,e,n){const a=this,{shapeOffsetTargets:i,indexMapByTargetId:o}=a.getShapeOffsetData(t),s=a.config.data_groupsZeroAs;return(l,c)=>{const{id:f,value:g,x:v}=l,m=a.getIndices(e,l),S=a.getYScaleById(f,n);if(a.isBarRangeType(l))return S(g[0]);const P=Number(v),N=S(s==="zero"?0:a.getShapeYMin(f));let L=N;return i.filter(w=>w.id!==f&&m[w.id]===m[f]).forEach(w=>{const{id:X,rowValueMapByXValue:W,rowValues:H,values:k}=w;if(o[X]=0&&he(K)&&(g!==0||s==="positive"&&K>0||s==="negative"&&K<0)&&(L+=S(K)-N)}}),L}},circleY(t,e){const n=this,a=t.id;let i;return n.isGrouped(a)&&(i=dm.bind(n)(t)),i?i(t,e)[0][1]:n.getYScaleById(a)(n.getBaseValue(t))},getBarW(t,e,n){var a,i,o,s,l;const c=this,{config:f,org:g,scale:v,state:m}=c,S=c.getMaxDataCount(),P=t==="bar"&&((a=f.data_groups)==null?void 0:a.length),N=`${t}_width`,{k:L}=(o=(i=c.getZoomTransform)==null?void 0:i.call(c))!=null?o:{k:1},w=[(s=f.axis_x_min)!=null?s:g.xDomain[0],(l=f.axis_x_max)!=null?l:g.xDomain[1]].map(c.axis.isTimeSeries()?Yn.bind(c):Number);let X=e.tickInterval(S);if(v.zoom&&!c.axis.isCategorized()&&L>1){const k=w.every((K,at)=>K===g.xDomain[at]);X=g.xDomain.map((K,at)=>{const ht=k?K:K-Math.abs(w[at]);return v.zoom(ht)}).reduce((K,at)=>Math.abs(K)+at)/S}const W=k=>{const K=k?f[N][k]:f[N],at=k?K.ratio:f[`${N}_ratio`],ht=k?K.max:f[`${N}_max`],$t=he(K)?K:ve(K)?K.call(c,m.width,n,S):n?X*at/n:0;return ht&&$t>ht?ht:$t};let H=W();return!P&&nr(f[N])&&(H={_$width:H,_$total:[]},c.filterTargetsToShow(c.data.targets).forEach(k=>{f[N][k.id]&&(H[k.id]=W(k.id),H._$total.push(H[k.id]||H._$width))})),H},getShapeByIndex(t,e,n){const a=this,{$el:i}=a,o=De(e)?`-${e}`:"";let s=i[t];return s&&!s.empty()?s=s.filter(l=>n?l.id===n:!0).filter(l=>De(e)?l.index===e:!0):s=(n?i.main.selectAll(`.${Ue[`${t}s`]}${a.getTargetSelectorSuffix(n)}`):i.main).selectAll(`.${Ue[t]}${o}`),s},isWithinShape(t,e){var n;const a=this,i=ot(t);let o;return a.isTargetToShow(e.id)?(n=a.hasValidPointType)!=null&&n.call(a,t.nodeName)?o=a.isStepType(e)?a.isWithinStep(t,a.getYScaleById(e.id)(a.getBaseValue(e))):a.isWithinCircle(t,a.isBubbleType(e)?a.pointSelectR(e)*1.5:0):t.nodeName==="path"&&(o=i.classed(Ue.bar)?a.isWithinBar(t):!0):o=!1,o},getInterpolate(t){const n=this.getInterpolateType(t);return{basis:Kp,"basis-closed":Zp,"basis-open":Jp,bundle:Qp,cardinal:kp,"cardinal-closed":qp,"cardinal-open":_p,"catmull-rom":nm,"catmull-rom-closed":rm,"catmull-rom-open":am,"monotone-x":im,"monotone-y":om,natural:sm,"linear-closed":lm,linear:gs,step:cm,"step-after":fm,"step-before":um}[n]},getInterpolateType(t){const e=this,{config:n}=e,a=n.spline_interpolation_type,i=e.isInterpolationType(a)?a:"cardinal";return e.isSplineType(t)?i:e.isStepType(t)?n.line_step_type:"linear"},isWithinBar(t){const e=Hn(this.state.event,t),n=Hl(t),[a,i]=n,o=Math.min(a.x,i.x),s=Math.min(a.y,i.y),l=this.config.bar_sensitivity,{width:c,height:f}=t.getBBox(),g=o-l,v=o+c+l,m=s+f+l,S=s-l;return ge in t?gm(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Rn=(t,e,n)=>vm(t,typeof e!="symbol"?e+"":e,n);class Vr{constructor(e){Rn(this,"api"),Rn(this,"config"),Rn(this,"cache"),Rn(this,"$el"),Rn(this,"state"),Rn(this,"charts"),Rn(this,"data",{xs:{},targets:[]}),Rn(this,"axis"),Rn(this,"scale",{x:null,y:null,y2:null,subX:null,subY:null,subY2:null,zoom:null}),Rn(this,"org",{xScale:null,xDomain:null}),Rn(this,"color"),Rn(this,"patterns"),Rn(this,"levelColor"),Rn(this,"point"),Rn(this,"brush"),Rn(this,"format",{extraLineClasses:null,xAxisTick:null,dataTime:null,defaultAxisTime:null,axisTime:null});const n=this;n.api=e,n.config=new Nr,n.cache=new gv;const a=new uv;n.$el=a.getStore("element"),n.state=a.getStore("state"),n.$T=n.$T.bind(n)}$T(e,n,a){const{config:i,state:o}=this,s=i.transition_duration,l=i.subchart_show;let c=e;return c&&("tagName"in c&&(c=ot(c)),c=(n!==!1&&s||n)&&(!o.zooming||o.dragging)&&!o.resizing&&o.rendered&&!l?c.transition(a).duration(s):c),c}beforeInit(){const e=this;e.callPluginHook("$beforeInit"),_e(e.config.onbeforeinit,e.api)}afterInit(){const e=this;e.callPluginHook("$afterInit"),_e(e.config.onafterinit,e.api)}init(){const e=this,{config:n,state:a,$el:i}=e,o=n.boost_useCssRule;if(vv(e),a.hasRadar=!a.hasAxis&&e.hasType("radar"),a.hasFunnel=!a.hasAxis&&e.hasType("funnel"),a.hasTreemap=!a.hasAxis&&e.hasType("treemap"),a.hasAxis=!e.hasArcType()&&!a.hasFunnel&&!a.hasTreemap,a.datetimeId=`bb-${+new Date*gr()}`,o){const l=gn.createElement("style");l.type="text/css",gn.head.appendChild(l),a.style={rootSelctor:`.${a.datetimeId}`,sheet:l.sheet},i.style=l}const s={element:n.bindto,classname:"bb"};Be(n.bindto)&&(s.element=n.bindto.element||"#chart",s.classname=n.bindto.classname||s.classname),i.chart=ve(s.element.node)?n.bindto.element:ot(s.element||[]),i.chart.empty()&&(i.chart=ot(gn.body.appendChild(gn.createElement("div")))),i.chart.html("").classed(s.classname,!0).classed(a.datetimeId,o).style("position","relative"),e.initParams(),e.initToRender()}initToRender(e){const n=this,{config:a,state:i,$el:{chart:o}}=n,s=()=>nv(o,{display:"none",visibility:"hidden"}),l=a.render.lazy===!1?!1:a.render.lazy||s(),c=Ke.MutationObserver;l&&c&&a.render.observe!==!1&&!e&&new c((f,g)=>{s()||(g.disconnect(),!i.rendered&&n.initToRender(!0))}).observe(o.node(),{attributes:!0,attributeFilter:["class","style"]}),(!l||e)&&n.convertData(a,f=>{n.initWithData(f),n.afterInit()})}initParams(){var e;const n=this,{config:a,format:i,state:o}=n,s=a.axis_rotated;if(n.color=n.generateColor(),n.levelColor=n.generateLevelColor(),a.padding===!1&&(a.axis_x_show=!1,a.axis_y_show=!1,a.axis_y2_show=!1,a.subchart_show=!1),(n.hasPointType()||(e=n.hasLegendDefsPoint)!=null&&e.call(n))&&(n.point=n.generatePoint()),o.hasAxis){n.initClip(),i.extraLineClasses=n.generateExtraLineClass(),i.dataTime=a.data_xLocaltime?Ws:Ks,i.axisTime=a.axis_x_localtime?ao:io;const l=n.config.zoom_enabled&&n.config.zoom_type==="drag";i.defaultAxisTime=c=>{const{x:f,zoom:g}=n.scale,v=l?g:g&&f.orgDomain().toString()!==g.domain().toString(),m=c.getMilliseconds()&&".%L"||c.getSeconds()&&".:%S"||c.getMinutes()&&"%I:%M"||c.getHours()&&"%I %p"||c.getDate()!==1&&"%b %d"||v&&c.getDate()===1&&"%b'%y"||c.getMonth()&&"%-m/%-d"||"%Y";return i.axisTime(m)(c)}}o.isLegendRight=a.legend_position==="right",o.isLegendInset=a.legend_position==="inset",o.isLegendTop=a.legend_inset_anchor==="top-left"||a.legend_inset_anchor==="top-right",o.isLegendLeft=a.legend_inset_anchor==="top-left"||a.legend_inset_anchor==="bottom-left",o.rotatedPadding.top=n.getResettedPadding(o.rotatedPadding.top),o.rotatedPadding.right=s&&!a.axis_x_show?0:30,o.inputType=rv(a.interaction_inputType_mouse,a.interaction_inputType_touch)}initWithData(e){var n,a,i;const o=this,{config:s,scale:l,state:c,$el:f,org:g}=o,{hasAxis:v,hasFunnel:m,hasTreemap:S}=c,P=s.interaction_enabled,N=o.hasType("polar"),L=s.data_labels_backgroundColors;if(v&&(o.axis=o.getAxisInstance(),s.zoom_enabled&&o.initZoom()),o.data.xs={},o.data.targets=o.convertDataToTargets(e),s.data_filter&&(o.data.targets=o.data.targets.filter(s.data_filter.bind(o.api))),s.data_hide&&o.addHiddenTargetIds(s.data_hide===!0?o.mapToIds(o.data.targets):s.data_hide),s.legend_hide&&o.addHiddenLegendIds(s.legend_hide===!0?o.mapToIds(o.data.targets):s.legend_hide),o.updateSizes(),o.updateScales(!0),v){const{x:W,y:H,y2:k,subX:K,subY:at,subY2:ht}=l;W&&(W.domain(na(o.getXDomain(o.data.targets),!s.axis_x_inverted)),K.domain(W.domain()),g.xDomain=W.domain()),H&&(H.domain(o.getYDomain(o.data.targets,"y")),at.domain(H.domain())),k&&(k.domain(o.getYDomain(o.data.targets,"y2")),ht&&ht.domain(k.domain()))}if(f.svg=f.chart.append("svg").style("overflow","hidden").style("display","block"),P&&c.inputType){const W=c.inputType==="touch",{onclick:H,onover:k,onout:K}=s;f.svg.on("click",(H==null?void 0:H.bind(o.api))||null).on(W?"touchstart":"mouseenter",(k==null?void 0:k.bind(o.api))||null).on(W?"touchend":"mouseleave",(K==null?void 0:K.bind(o.api))||null)}s.svg_classname&&f.svg.attr("class",s.svg_classname);const w=ve(s.color_tiles)&&o.patterns;(v||w||N||S||L||(n=o.hasLegendDefsPoint)!=null&&n.call(o))&&(f.defs=f.svg.append("defs"),v&&["id","idXAxis","idYAxis","idGrid"].forEach(W=>{o.appendClip(f.defs,c.clip[W])}),o.generateTextBGColorFilter(L),w&&o.patterns.forEach(W=>f.defs.append(()=>W.node))),o.updateSvgSize(),o.bindResize();const X=f.svg.append("g").classed(Se.main,!0).attr("transform",m||S?null:o.getTranslate("main"));if(f.main=X,s.subchart_show&&o.initSubchart(),s.tooltip_show&&o.initTooltip(),s.title_text&&o.initTitle(),!S&&s.legend_show&&o.initLegend(),s.data_empty_label_text&&X.append("text").attr("class",`${On.text} ${Se.empty}`).attr("text-anchor","middle").attr("dominant-baseline","middle"),v&&(s.regions.length&&o.initRegion(),!s.clipPath&&o.axis.init()),X.append("g").classed(Se.chart,!0).attr("clip-path",v?c.clip.path:null),o.callPluginHook("$init"),o.initChartElements(),v&&(P&&((a=o.initEventRect)==null||a.call(o)),o.initGrid(),s.clipPath&&((i=o.axis)==null||i.init())),o.updateTargets(o.data.targets),o.updateDimension(),_e(s.oninit,o.api),o.setBackground(),o.redraw({withTransition:!1,withTransform:!0,withUpdateXDomain:!0,withUpdateOrgXDomain:!0,withTransitionForAxis:!1,initializing:!0}),s.data_onmin||s.data_onmax){const W=o.getMinMaxData();_e(s.data_onmin,o.api,W.min),_e(s.data_onmax,o.api,W.max)}s.tooltip_show&&o.initShowTooltip(),c.rendered=!0}initChartElements(){const e=this,{hasAxis:n,hasRadar:a,hasTreemap:i}=e.state,o=[];if(n){const s=["bar","bubble","candlestick","line"];e.config.bar_front&&s.push(s.shift()),s.forEach(l=>{const c=Cn(l);(l==="line"&&e.hasTypeOf(c)||e.hasType(l))&&o.push(c)})}else if(i)o.push("Treemap");else if(e.hasType("funnel"))o.push("Funnel");else{const s=e.hasType("polar");a||o.push("Arc","Pie"),e.hasType("gauge")?o.push("Gauge"):a?o.push("Radar"):s&&o.push("Polar")}o.forEach(s=>{e[`init${s}`]()}),cn(e.config.data_labels)&&!e.hasArcType(null,["radar"])&&e.initText()}setChartElements(){const e=this,{$el:{chart:n,svg:a,defs:i,main:o,tooltip:s,legend:l,title:c,grid:f,needle:g,arcs:v,circle:m,bar:S,candlestick:P,line:N,area:L,text:w}}=e;e.api.$={chart:n,svg:a,defs:i,main:o,tooltip:s,legend:l,title:c,grid:f,arc:v,circles:m,bar:{bars:S},candlestick:P,line:{lines:N,areas:L},needle:g,text:{texts:w}}}setBackground(){const e=this,{config:{background:n},state:a,$el:{svg:i}}=e;if(cn(n)){const o=i.select("g").insert(n.imgUrl?"image":"rect",":first-child");n.imgUrl?o.attr("href",n.imgUrl):n.color&&o.style("fill",n.color).attr("clip-path",a.clip.path),o.attr("class",n.class||null).attr("width","100%").attr("height","100%")}}updateTargets(e){var n;const a=this,{hasAxis:i,hasFunnel:o,hasRadar:s,hasTreemap:l}=a.state,c=g=>a[`updateTargetsFor${g}`](e.filter(a[`is${g}Type`].bind(a)));if(a.updateTargetsForText(e),i)["bar","candlestick","line"].forEach(g=>{const v=Cn(g);(g==="line"&&a.hasTypeOf(v)||a.hasType(g))&&c(v)}),a.updateTargetsForSubchart&&a.updateTargetsForSubchart(e);else if(a.hasArcType(e)){let g="Arc";s?g="Radar":a.hasType("polar")&&(g="Polar"),c(g)}else o?c("Funnel"):l&&c("Treemap");const f=a.hasType("bubble")||a.hasType("scatter");f&&((n=a.updateTargetForCircle)==null||n.call(a)),a.filterTargetsToShowAtInit(f)}filterTargetsToShowAtInit(e=!1){const n=this,{$el:{svg:a},$T:i}=n;let o=`.${Se.target}`;e&&(o+=`, .${$n.chartCircles} > .${$n.circles}`),i(a.selectAll(o).filter(s=>n.isTargetToShow(s.id))).style("opacity",null)}getWithOption(e){const n={Dimension:!0,EventRect:!0,Legend:!1,Subchart:!0,Transform:!1,Transition:!0,TrimXDomain:!0,UpdateXAxis:"UpdateXDomain",UpdateXDomain:!1,UpdateOrgXDomain:!1,TransitionForExit:"Transition",TransitionForAxis:"Transition",Y:!0};return Object.keys(n).forEach(a=>{let i=n[a];ze(i)&&(i=n[i]),n[a]=$r(e,`with${a}`,i)}),n}initialOpacity(e){const n=this,{withoutFadeIn:a}=n.state;return n.getBaseValue(e)!==null&&a[e.id]?null:"0"}bindResize(){const e=this,{$el:n,config:a,state:i}=e,o=xv(a.resize_timer),s=[];s.push(()=>_e(a.onresize,e.api)),/^(true|parent)$/.test(a.resize_auto)&&s.push(()=>{i.resizing=!0,a.legend_show&&(e.updateSizes(),e.updateLegend()),e.api.flush(!1)}),s.push(()=>{_e(a.onresized,e.api),i.resizing=!1}),s.forEach(l=>o.add(l)),e.resizeFunction=o,a.resize_auto==="parent"?(e.resizeFunction.resizeObserver=new ResizeObserver(e.resizeFunction.bind(e))).observe(n.chart.node().parentNode):Ke.addEventListener("resize",e.resizeFunction)}callPluginHook(e,...n){this.config.plugins.forEach(a=>{e==="$beforeInit"&&(a.$$=this,this.api.plugins.push(a)),a[e](...n)})}}yn(Vr.prototype,[Mv,Dv,Lv,jv,Vv,Yv,Wv,zv,Kv,Zv,Jv,Bp,hm,Up,zp,Gp,Xp,Hp,Yp,Wp]);function pm(t){const e=this.config;let n,a,i;const o=()=>{const s=a.shift();if(s&&n&&nr(n)&&s in n)return n=n[s],o();if(!s)return n};Object.keys(e).forEach(s=>{n=t,a=s.split("_"),i=o(),Qe(i)&&(e[s]=i)}),this.api&&(this.state.orgConfig=t)}var mm={resize(t){const e=this.internal,{config:n,state:a}=e;a.rendered&&(n.size_width=t?t.width:null,n.size_height=t?t.height:null,a.resizing=!0,this.flush(!1),e.resizeFunction())},flush(t){var e,n;const a=this.internal,{state:i,$el:{zoomResetBtn:o}}=a;i.rendered?(i.resizing?(e=a.brush)==null||e.updateResize():(n=a.axis)==null||n.setOrient(),o==null||o.style("display","none"),a.scale.zoom=null,t?a.redraw({withTransform:!0,withUpdateXDomain:!0,withUpdateOrgXDomain:!0,withLegend:!0}):a.updateAndRedraw({withLegend:!0,withTransition:!1,withTransitionForTransform:!1}),!i.resizing&&a.brush&&(a.brush.getSelection().call(a.brush.move),a.unselectRect())):a.initToRender(!0)},destroy(){var t;const e=this.internal,{$el:{chart:n,style:a,svg:i}}=e;if(cn(e)){e.callPluginHook("$willDestroy"),e.charts.splice(e.charts.indexOf(this),1),e.unbindAllEvents(),i.select("*").interrupt(),e.resizeFunction.clear(),(t=e.resizeFunction.resizeObserver)==null||t.disconnect(),Ke.removeEventListener("resize",e.resizeFunction),n.classed("bb",!1).style("position",null).selectChildren().remove(),a&&a.parentNode.removeChild(a),Object.keys(this).forEach(o=>{o==="internal"&&Object.keys(e).forEach(s=>{e[s]=null}),this[o]=null,delete this[o]});for(const o in this)this[o]=()=>{}}return null},config(t,e,n){const a=this.internal,{config:i,state:o}=a,s=t==null?void 0:t.replace(/\./g,"_");let l;return t&&s in i?Qe(e)?(i[s]=e,l=e,n&&this.flush()):l=i[s]:(arguments.length===0||qn(t))&&(l=o.orgConfig),l}},ym={color(t){return this.internal.color(t)}};const au=function(t){const{targets:e}=this.internal.data;if(!ln(t)){const n=je(t)?t:[t];return e.filter(a=>n.some(i=>i===a.id))}return e};yn(au,{shown:function(t){return this.internal.filterTargetsToShow(this.data(t))},values:function(t,e=!0){let n=null;if(t){const a=this.data(t);je(a)&&(n=[],a.forEach(i=>{const o=i.values.map(s=>s.value);e?n=n.concat(o):n.push(o)}))}return n},names:function(t){return this.internal.updateDataAttributes("names",t)},colors:function(t){return this.internal.updateDataAttributes("colors",t)},axes:function(t){return this.internal.updateDataAttributes("axes",t)},min:function(){return this.internal.getMinMaxData().min},max:function(){return this.internal.getMinMaxData().max}});var xm={data:au};const Tm=t=>{var e,n;return(n=(e=Ke).btoa)==null?void 0:n.call(e,encodeURIComponent(t).replace(/%([0-9A-F]{2})/g,(a,i)=>String.fromCharCode(+`0x${i}`)))};function $m(t,e,n){const{width:a,height:i}=e||n,o=new XMLSerializer,s=t.cloneNode(!0),l=tv(Lr(gn.styleSheets)).filter(m=>m.cssText).map(m=>m.cssText);s.setAttribute("xmlns",ae.xhtml),s.style.margin="0",s.style.padding="0",e.preserveFontStyle&&s.querySelectorAll("text").forEach(m=>{m.innerHTML=""});const c=o.serializeToString(s),f=gn.createElement("style");f.appendChild(gn.createTextNode(l.join(` +`)));const g=o.serializeToString(f),v=` + + ${g} + ${c.replace(/(url\()[^#]+/g,"$1")} + `;return`data:image/svg+xml;base64,${Tm(v)}`}function Sm(t,e){const{top:n,left:a}=e,{x:i,y:o}=t.getBBox(),{a:s,b:l,c,d:f,e:g,f:v}=t.getScreenCTM(),{width:m,height:S}=t.getBoundingClientRect();return{x:s*i+c*o+g-a,y:l*i+f*o+v-n+(S-Math.round(S/4)),width:m,height:S}}function Am(t){const{left:e,top:n}=t.getBoundingClientRect(),a=o=>o.textContent||o.childElementCount,i=[];return Lr(t.querySelectorAll("text")).filter(a).forEach(o=>{const s=l=>{const{fill:c,fontFamily:f,fontSize:g,textAnchor:v,transform:m}=Ke.getComputedStyle(l),{x:S,y:P,width:N,height:L}=Sm(l,{left:e,top:n});return{[l.textContent]:{x:S,y:P,width:N,height:L,fill:c,fontFamily:f,fontSize:g,textAnchor:v,transform:m}}};if(o.childElementCount>1){const l=[];return Lr(o.querySelectorAll("tspan")).filter(a).forEach(c=>{i.push(s(c))}),l}else i.push(s(o))}),i}function Em(t,e){e.forEach(n=>{Object.keys(n).forEach(a=>{const{x:i,y:o,width:s,height:l,fill:c,fontFamily:f,fontSize:g,transform:v}=n[a];if(t.save(),t.font=`${g} ${f}`,t.fillStyle=c,v==="none")t.fillText(a,i,o);else{const m=v.replace(/(matrix|\(|\))/g,"").split(",");m.splice(4).every(S=>+S==0)?(m.push(i+s-s/4),m.push(o-l+l/3)):(m.push(i),m.push(o)),t.transform(...m),t.fillText(a,0,0)}t.restore()})})}var bm={export(t,e){const n=this.internal,{state:a,$el:{chart:i,svg:o}}=n,{width:s,height:l}=a.current,c=ea(Object.create(null),{width:s,height:l,preserveAspectRatio:!0,preserveFontStyle:!1,mimeType:"image/png"},t),f=$m(i.node(),c,{width:s,height:l}),g=c.preserveFontStyle?Am(o.node()):[];if(e&&ve(e)){const v=new Image;v.crossOrigin="Anonymous",v.onload=()=>{const m=gn.createElement("canvas"),S=m.getContext("2d");m.width=c.width||s,m.height=c.height||l,S.drawImage(v,0,0),g.length&&(Em(S,g),g.length=0),e.bind(this)(m.toDataURL(c.mimeType))},v.src=f}return f}},Rm={focus(t){const e=this.internal,{state:n}=e,a=e.mapToTargetIds(t),i=e.$el.svg.selectAll(e.selectorTargets(a.filter(e.isTargetToShow,e)));this.revert(),this.defocus(),i.classed(qe.focused,!0).classed(qe.defocused,!1),e.hasArcType()&&!n.hasRadar&&(e.expandArc(a),e.hasType("gauge")&&e.markOverlapped(t,e,`.${Un.gaugeValue}`)),e.toggleFocusLegend(a,!0),n.focusedTargetIds=a,n.defocusedTargetIds=n.defocusedTargetIds.filter(o=>a.indexOf(o)<0)},defocus(t){const e=this.internal,{state:n}=e,a=e.mapToTargetIds(t);e.$el.svg.selectAll(e.selectorTargets(a.filter(e.isTargetToShow,e))).classed(qe.focused,!1).classed(qe.defocused,!0),e.hasArcType(null,["polar"])&&(e.unexpandArc(a),e.hasType("gauge")&&e.undoMarkOverlapped(e,`.${Un.gaugeValue}`)),e.toggleFocusLegend(a,!1),n.focusedTargetIds=n.focusedTargetIds.filter(o=>a.indexOf(o)<0),n.defocusedTargetIds=a},revert(t){const e=this.internal,{config:n,state:a,$el:i}=e,o=e.mapToTargetIds(t);i.svg.selectAll(e.selectorTargets(o)).classed(qe.focused,!1).classed(qe.defocused,!1),e.hasArcType(null,["polar"])&&e.unexpandArc(o),n.legend_show&&(e.showLegend(o.filter(e.isLegendToShow.bind(e))),i.legend.selectAll(e.selectorLegends(o)).filter(function(){return ot(this).classed(qe.legendItemFocused)}).classed(qe.legendItemFocused,!1)),a.focusedTargetIds=[],a.defocusedTargetIds=[]}},Im={legend:{show:function(t){const e=this.internal;e.showLegend(e.mapToTargetIds(t)),e.updateAndRedraw({withLegend:!0})},hide:function(t){const e=this.internal;e.hideLegend(e.mapToTargetIds(t)),e.updateAndRedraw({withLegend:!0})}}},Om={load(t){const e=this.internal,{config:n}=e;t.xs&&e.addXs(t.xs),"names"in t&&this.data.names(t.names),"classes"in t&&Object.keys(t.classes).forEach(a=>{n.data_classes[a]=t.classes[a]}),"categories"in t&&e.axis.isCategorized()&&(n.axis_x_categories=t.categories),"axes"in t&&Object.keys(t.axes).forEach(a=>{n.data_axes[a]=t.axes[a]}),"colors"in t&&Object.keys(t.colors).forEach(a=>{n.data_colors[a]=t.colors[a]}),"unload"in t&&t.unload!==!1?e.unload(e.mapToTargetIds(t.unload===!0?null:t.unload),()=>{jl(()=>e.loadFromArgs(t))}):e.loadFromArgs(t)},unload(t){const e=this.internal;let n=t||{};qn(n)&&this.tooltip.hide(),je(n)?n={ids:n}:ze(n)&&(n={ids:[n]});const a=e.mapToTargetIds(n.ids);e.unload(a,()=>{e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0}),e.cache.remove(a),cc.call(e,n.done,n.resizeAfter)})}};function iu(t,e,n){const a=this.internal,i=a.mapToTargetIds(e),o=a.state.hiddenTargetIds.map(c=>i.indexOf(c)>-1&&c).filter(Boolean);a.state.toggling=!0,a[`${t?"remove":"add"}HiddenTargetIds`](i);const s=a.$el.svg.selectAll(a.selectorTargets(i)),l=t?null:"0";t&&o.length&&(s.style("display",null),_e(a.config.data_onshown,this,o)),a.$T(s).style("opacity",l,"important").call(Si,()=>{var c;!t&&o.length===0&&(s.style("display","none"),_e((c=a.config)==null?void 0:c.data_onhidden,this,i)),s.style("opacity",l)}),n.withLegend&&a[`${t?"show":"hide"}Legend`](i),a.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0}),a.state.toggling=!1}var Cm={show(t,e={}){iu.call(this,!0,t,e)},hide(t,e={}){iu.call(this,!1,t,e)},toggle(t,e={}){const n=this.internal,a={show:[],hide:[]};n.mapToTargetIds(t).forEach(i=>a[n.isTargetToShow(i)?"hide":"show"].push(i)),a.show.length&&this.show(a.show,e),a.hide.length&&setTimeout(()=>this.hide(a.hide,e),0)}},Pm={tooltip:{show:function(t){var e,n,a;const i=this.internal,{$el:o,config:s,state:{eventReceiver:l,hasFunnel:c,hasTreemap:f,inputType:g}}=i;let v,m;if(t.mouse&&(m=t.mouse),t.data){const{data:S}=t,P=(e=i.getYScaleById(S.id))==null?void 0:e(S.value);if((c||f)&&S.id){const N=i.selectorTarget(S.id,void 0,`.${sn.shape}`);l.rect=o.main.select(N)}else i.isMultipleX()?m=[i.xx(S),P]:(s.tooltip_grouped||(m=[0,P]),v=(a=S.index)!=null?a:i.hasArcType()&&S.id?(n=i.getArcElementByIdOrIndex(S.id))==null?void 0:n.datum().index:i.getIndexByX(S.x))}else Qe(t.x)?v=i.getIndexByX(t.x):Qe(t.index)&&(v=t.index);(g==="mouse"?["mouseover","mousemove"]:["touchstart"]).forEach(S=>{i.dispatchEvent(S,v,m)})},hide:function(){var t,e,n;const a=this.internal,{state:{inputType:i},$el:{tooltip:o}}=a,s=o==null?void 0:o.datum();if(s){const{index:l}=JSON.parse(s.current)[0];(i==="mouse"?["mouseout"]:["touchend"]).forEach(c=>{a.dispatchEvent(c,l)})}i==="touch"&&a.callOverOutForTouch(),a.hideTooltip(!0),(t=a.hideGridFocus)==null||t.call(a),(e=a.unexpandCircles)==null||e.call(a),(n=a.expandBarTypeShapes)==null||n.call(a,!1)}}},wm=Object.defineProperty,Mm=(t,e,n)=>e in t?wm(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,ou=(t,e,n)=>Mm(t,typeof e!="symbol"?e+"":e,n);class Er{constructor(e){ou(this,"plugins",[]),ou(this,"internal");const n=new Vr(this);this.internal=n,function a(i,o,s){Object.keys(i).forEach(l=>{const c=ve(i[l]),f=o!==s,g=cn(i[l]),v=g&&Object.keys(i[l]).length>0;c&&(!f&&v||f)?o[l]=i[l].bind(s):g&&!c?o[l]={}:o[l]=i[l],v&&a(i[l],o[l],s)})}(Er.prototype,this,this),pm.call(n,e),n.beforeInit(),n.init()}}yn(Er.prototype,[mm,ym,xm,bm,Rm,Im,Om,Cm,Pm]);function su(t=!1,e,n,a){const i=this,{config:o,$el:{main:s}}=i,l=o.data_selection_grouped,c=o.data_selection_isselectable.bind(i.api);o.data_selection_enabled&&s.selectAll(`.${sn.shapes}`).selectAll(`.${sn.shape}`).each(function(f){const g=ot(this),{id:v,index:m}=f.data?f.data:f,S=i.getToggle(this,f).bind(i),P=l||!e||e.indexOf(v)>=0,N=!n||n.indexOf(m)>=0,L=g.classed(tn.SELECTED);g.classed(ur.line)||g.classed(ti.area)||(t?P&&N&&c(f)&&!L?S(!0,g.classed(tn.SELECTED,!0),f,m):Qe(a)&&a&&L&&S(!1,g.classed(tn.SELECTED,!1),f,m):P&&N&&c(f)&&L&&S(!1,g.classed(tn.SELECTED,!1),f,m))})}var Dm={selected(t){const e=this.internal,n=[];return e.$el.main.selectAll(`.${sn.shapes+e.getTargetSelectorSuffix(t)}`).selectAll(`.${sn.shape}`).filter(function(){return ot(this).classed(tn.SELECTED)}).each(a=>n.push(a)),n},select(t,e,n){const a=this.internal;su.bind(a)(!0,t,e,n)},unselect(t,e){const n=this.internal;su.bind(n)(!1,t,e)}};const lu=function(t){var e;const n=this.internal,{axis:a,brush:i,config:o,scale:{x:s,subX:l},state:c}=n;let f;return o.subchart_show&&(f=t,Array.isArray(f)?(a.isTimeSeries()&&(f=f.map(v=>Yn.bind(n)(v))),n.withinRange(f,n.getZoomDomain("subX",!0),n.getZoomDomain("subX"))&&(c.domain=f,i.move(i.getSelection(),f.map(l)))):f=(e=c.domain)!=null?e:s.orgDomain()),f};yn(lu,{show(){var t,e;const n=this.internal,{$el:{subchart:a},config:i}=n,o=i.subchart_show;if(!o){n.unbindZoomEvent(),i.subchart_show=!o,!a.main&&n.initSubchart();let s=a.main.selectAll(`.${Se.target}`);n.data.targets.length!==s.size()&&(n.updateSizes(),n.updateTargetsForSubchart(n.data.targets),s=(t=a.main)==null?void 0:t.selectAll(`.${Se.target}`)),s==null||s.style("opacity",null),(e=a.main)==null||e.style("display",null),this.resize()}},hide(){const t=this.internal,{$el:{subchart:{main:e}},config:n}=t;n.subchart_show&&(e==null?void 0:e.style("display"))!=="none"&&(n.subchart_show=!1,e.style("display","none"),this.resize())},toggle(){const t=this.internal,{config:e}=t;this.subchart[e.subchart_show?"hide":"show"]()},reset(){const t=this.internal,{brush:e}=t;e.clear(e.getSelection())}});var Lm={subchart:lu},Nm=1e-12;function cu(t){return((t=Math.exp(t))+1/t)/2}function Fm(t){return((t=Math.exp(t))-1/t)/2}function Bm(t){return((t=Math.exp(2*t))-1)/(t+1)}var Um=function t(e,n,a){function i(o,s){var l=o[0],c=o[1],f=o[2],g=s[0],v=s[1],m=s[2],S=g-l,P=v-c,N=S*S+P*P,L,w;if(N()=>t;function zm(t,{sourceEvent:e,target:n,transform:a,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},target:{value:n,enumerable:!0,configurable:!0},transform:{value:a,enumerable:!0,configurable:!0},_:{value:i}})}function vr(t,e,n){this.k=t,this.x=e,this.y=n}vr.prototype={constructor:vr,scale:function(t){return t===1?this:new vr(this.k*t,this.x,this.y)},translate:function(t,e){return t===0&e===0?this:new vr(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var ar=new vr(1,0,0);vs.prototype=vr.prototype;function vs(t){for(;!t.__zoom;)if(!(t=t.parentNode))return ar;return t.__zoom}function ps(t){t.stopImmediatePropagation()}function Ba(t){t.preventDefault(),t.stopImmediatePropagation()}function jm(t){return(!t.ctrlKey||t.type==="wheel")&&!t.button}function Vm(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t,t.hasAttribute("viewBox")?(t=t.viewBox.baseVal,[[t.x,t.y],[t.x+t.width,t.y+t.height]]):[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]):[[0,0],[t.clientWidth,t.clientHeight]]}function uu(){return this.__zoom||ar}function Gm(t){return-t.deltaY*(t.deltaMode===1?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function Xm(){return navigator.maxTouchPoints||"ontouchstart"in this}function Hm(t,e,n){var a=t.invertX(e[0][0])-n[0][0],i=t.invertX(e[1][0])-n[1][0],o=t.invertY(e[0][1])-n[0][1],s=t.invertY(e[1][1])-n[1][1];return t.translate(i>a?(a+i)/2:Math.min(0,a)||Math.max(0,i),s>o?(o+s)/2:Math.min(0,o)||Math.max(0,s))}function Ym(){var t=jm,e=Vm,n=Hm,a=Gm,i=Xm,o=[0,1/0],s=[[-1/0,-1/0],[1/0,1/0]],l=250,c=Um,f=ri("start","zoom","end"),g,v,m,S=500,P=150,N=0,L=10;function w(Q){Q.property("__zoom",uu).on("wheel.zoom",ht,{passive:!1}).on("mousedown.zoom",$t).on("dblclick.zoom",dt).filter(i).on("touchstart.zoom",st).on("touchmove.zoom",Vt).on("touchend.zoom touchcancel.zoom",vt).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}w.transform=function(Q,St,ct,At){var Gt=Q.selection?Q.selection():Q;Gt.property("__zoom",uu),Q!==Gt?k(Q,St,ct,At):Gt.interrupt().each(function(){K(this,arguments).event(At).start().zoom(null,typeof St=="function"?St.apply(this,arguments):St).end()})},w.scaleBy=function(Q,St,ct,At){w.scaleTo(Q,function(){var Gt=this.__zoom.k,Bt=typeof St=="function"?St.apply(this,arguments):St;return Gt*Bt},ct,At)},w.scaleTo=function(Q,St,ct,At){w.transform(Q,function(){var Gt=e.apply(this,arguments),Bt=this.__zoom,Kt=ct==null?H(Gt):typeof ct=="function"?ct.apply(this,arguments):ct,ne=Bt.invert(Kt),le=typeof St=="function"?St.apply(this,arguments):St;return n(W(X(Bt,le),Kt,ne),Gt,s)},ct,At)},w.translateBy=function(Q,St,ct,At){w.transform(Q,function(){return n(this.__zoom.translate(typeof St=="function"?St.apply(this,arguments):St,typeof ct=="function"?ct.apply(this,arguments):ct),e.apply(this,arguments),s)},null,At)},w.translateTo=function(Q,St,ct,At,Gt){w.transform(Q,function(){var Bt=e.apply(this,arguments),Kt=this.__zoom,ne=At==null?H(Bt):typeof At=="function"?At.apply(this,arguments):At;return n(ar.translate(ne[0],ne[1]).scale(Kt.k).translate(typeof St=="function"?-St.apply(this,arguments):-St,typeof ct=="function"?-ct.apply(this,arguments):-ct),Bt,s)},At,Gt)};function X(Q,St){return St=Math.max(o[0],Math.min(o[1],St)),St===Q.k?Q:new vr(St,Q.x,Q.y)}function W(Q,St,ct){var At=St[0]-ct[0]*Q.k,Gt=St[1]-ct[1]*Q.k;return At===Q.x&&Gt===Q.y?Q:new vr(Q.k,At,Gt)}function H(Q){return[(+Q[0][0]+ +Q[1][0])/2,(+Q[0][1]+ +Q[1][1])/2]}function k(Q,St,ct,At){Q.on("start.zoom",function(){K(this,arguments).event(At).start()}).on("interrupt.zoom end.zoom",function(){K(this,arguments).event(At).end()}).tween("zoom",function(){var Gt=this,Bt=arguments,Kt=K(Gt,Bt).event(At),ne=e.apply(Gt,Bt),le=ct==null?H(ne):typeof ct=="function"?ct.apply(Gt,Bt):ct,be=Math.max(ne[1][0]-ne[0][0],ne[1][1]-ne[0][1]),Oe=Gt.__zoom,Ce=typeof St=="function"?St.apply(Gt,Bt):St,He=c(Oe.invert(le).concat(be/Oe.k),Ce.invert(le).concat(be/Ce.k));return function(Fe){if(Fe===1)Fe=Ce;else{var dn=He(Fe),Jt=be/dn[2];Fe=new vr(Jt,le[0]-dn[0]*Jt,le[1]-dn[1]*Jt)}Kt.zoom(null,Fe)}})}function K(Q,St,ct){return!ct&&Q.__zooming||new at(Q,St)}function at(Q,St){this.that=Q,this.args=St,this.active=0,this.sourceEvent=null,this.extent=e.apply(Q,St),this.taps=0}at.prototype={event:function(Q){return Q&&(this.sourceEvent=Q),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(Q,St){return this.mouse&&Q!=="mouse"&&(this.mouse[1]=St.invert(this.mouse[0])),this.touch0&&Q!=="touch"&&(this.touch0[1]=St.invert(this.touch0[0])),this.touch1&&Q!=="touch"&&(this.touch1[1]=St.invert(this.touch1[0])),this.that.__zoom=St,this.emit("zoom"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit("end")),this},emit:function(Q){var St=ot(this.that).datum();f.call(Q,this.that,new zm(Q,{sourceEvent:this.sourceEvent,target:w,type:Q,transform:this.that.__zoom,dispatch:f}),St)}};function ht(Q,...St){if(!t.apply(this,arguments))return;var ct=K(this,St).event(Q),At=this.__zoom,Gt=Math.max(o[0],Math.min(o[1],At.k*Math.pow(2,a.apply(this,arguments)))),Bt=Xn(Q);if(ct.wheel)(ct.mouse[0][0]!==Bt[0]||ct.mouse[0][1]!==Bt[1])&&(ct.mouse[1]=At.invert(ct.mouse[0]=Bt)),clearTimeout(ct.wheel);else{if(At.k===Gt)return;ct.mouse=[Bt,At.invert(Bt)],qr(this),ct.start()}Ba(Q),ct.wheel=setTimeout(Kt,P),ct.zoom("mouse",n(W(X(At,Gt),ct.mouse[0],ct.mouse[1]),ct.extent,s));function Kt(){ct.wheel=null,ct.end()}}function $t(Q,...St){if(m||!t.apply(this,arguments))return;var ct=Q.currentTarget,At=K(this,St,!0).event(Q),Gt=ot(Q.view).on("mousemove.zoom",le,!0).on("mouseup.zoom",be,!0),Bt=Xn(Q,ct),Kt=Q.clientX,ne=Q.clientY;co(Q.view),ps(Q),At.mouse=[Bt,this.__zoom.invert(Bt)],qr(this),At.start();function le(Oe){if(Ba(Oe),!At.moved){var Ce=Oe.clientX-Kt,He=Oe.clientY-ne;At.moved=Ce*Ce+He*He>N}At.event(Oe).zoom("mouse",n(W(At.that.__zoom,At.mouse[0]=Xn(Oe,ct),At.mouse[1]),At.extent,s))}function be(Oe){Gt.on("mousemove.zoom mouseup.zoom",null),uo(Oe.view,At.moved),Ba(Oe),At.event(Oe).end()}}function dt(Q,...St){if(t.apply(this,arguments)){var ct=this.__zoom,At=Xn(Q.changedTouches?Q.changedTouches[0]:Q,this),Gt=ct.invert(At),Bt=ct.k*(Q.shiftKey?.5:2),Kt=n(W(X(ct,Bt),At,Gt),e.apply(this,St),s);Ba(Q),l>0?ot(this).transition().duration(l).call(k,Kt,At,Q):ot(this).call(w.transform,Kt,At,Q)}}function st(Q,...St){if(t.apply(this,arguments)){var ct=Q.touches,At=ct.length,Gt=K(this,St,Q.changedTouches.length===At).event(Q),Bt,Kt,ne,le;for(ps(Q),Kt=0;KtYn.bind(n)(v))),n.withinRange(f,n.getZoomDomain("zoom",!0),n.getZoomDomain("zoom"))){if(l.domain=f,f=n.getZoomDomainValue(f),n.api.tooltip.hide(),i.subchart_show){const v=s.zoom||s.x;n.brush.getSelection().call(n.brush.move,f.map(v))}else{const v=c?s.x.orgScale():o.xScale||s.x;n.updateCurrentZoomTransform(v,f)}n.setZoomResetButton()}}else f=n.zoom.getDomain();return(e=l.domain)!=null?e:f};yn(fu,{enable(t){const e=this.internal,{config:n}=e;/^(drag|wheel)$/.test(t)&&(n.zoom_type=t),n.zoom_enabled=!!t,e.zoom?t===!1&&e.bindZoomEvent(!1):(e.initZoom(),e.bindZoomEvent()),e.updateAndRedraw()},max(t){const e=this.internal,{config:n,org:{xDomain:a}}=e;return(t===0||t)&&(n.zoom_x_max=_n("max",[a[1],t])),n.zoom_x_max},min(t){const e=this.internal,{config:n,org:{xDomain:a}}=e;return(t===0||t)&&(n.zoom_x_min=_n("min",[a[0],t])),n.zoom_x_min},range(t){const e=this.zoom;if(Be(t)){const{min:n,max:a}=t;Qe(n)&&e.min(n),Qe(a)&&e.max(a)}return{min:e.min(),max:e.max()}}});var Wm={zoom:fu,unzoom(){const t=this.internal,{config:e,$el:{eventRect:n,zoomResetBtn:a},scale:{zoom:i},state:o}=t;i&&(e.subchart_show?t.brush.getSelection().call(t.brush.move,null):t.zoom.updateTransformScale(ar),t.updateZoom(!0),a==null||a.style("display","none"),vs(n.node())!==ar&&t.zoom.transform(n,ar),o.domain=void 0)}},Km={initBrush(){const t=this,{config:e,scale:n,$el:{subchart:a},state:i}=t,o=e.axis_rotated,s=e.subchart_size_height;let l,c,f;t.brush=(o?Gg():Vg()).handleSize(5),t.brush.on("start brush end",g=>{const{selection:v,sourceEvent:m,target:S,type:P}=g;P==="start"&&(t.state.inputType==="touch"&&t.hideTooltip(),c=m?v:null),/(start|brush)/.test(P)&&(P==="brush"&&m&&i.domain&&(c==null||c.forEach((N,L)=>{N!==v[L]&&(i.domain[L]=n.x.orgDomain()[L])})),t.redrawForBrush(P!=="start")),P==="end"&&(l=n.x.orgDomain()),S!=null&&S.handle&&(v===null?t.brush.handle.attr("display","none"):t.brush.handle.attr("display",null).attr("transform",(N,L)=>{const w=[v[L],s/2];return`translate(${o?w.reverse():w})`}))}),t.brush.updateResize=function(){f&&clearTimeout(f),f=setTimeout(()=>{const g=this.getSelection();l&&zl(g.node())&&this.move(g,l.map(n.subX.orgScale()))},0)},t.brush.update=function(){var g;return this.extent()()[1].filter(m=>isNaN(m)).length===0&&((g=a.main)==null||g.select(`.${Ue.brush}`).call(this)),this},t.brush.scale=function(g){const v=e.subchart_size_height;let m=t.axis.getExtent();!m&&g.range?m=[[0,0],[g.range()[1],v]]:je(m)&&(m=m.map((S,P)=>[S,P>0?v:P])),o&&m[1].reverse(),this.extent(m),this.update()},t.brush.getSelection=()=>a.main?a.main.select(`.${Ue.brush}`):ot([])},initSubchart(){const t=this,{config:e,state:{clip:n,hasAxis:a},$el:{defs:i,svg:o,subchart:s,axis:l}}=t;if(!a)return;const c=e.subchart_show?null:"hidden",f=`${n.id}-subchart`,g=t.getClipPath(f);n.idSubchart=f,t.appendClip(i,f),t.initBrush(),s.main=o.append("g").classed(Ue.subchart,!0).attr("transform",t.getTranslate("context"));const{main:v}=s;v.style("visibility",c),v.append("g").attr("clip-path",g).attr("class",Ue.chart),["bar","line","bubble","candlestick","scatter"].forEach(S=>{const P=Cn(/^(bubble|scatter)$/.test(S)?"circle":S);if(t.hasType(S)||t.hasTypeOf(P)){const N=v.select(`.${Ue.chart}`),L=Ue[`chart${P}s`];N.select(`.${L}`).empty()&&N.append("g").attr("class",L)}});const m=v.append("g").attr("clip-path",g).attr("class",Ue.brush).call(t.brush);e.subchart_showHandle&&t.addBrushHandle(m),l.subX=v.append("g").attr("class",Ue.axisX).attr("transform",t.getTranslate("subX")).attr("clip-path",e.axis_rotated?"":n.pathXAxis).style("visibility",e.subchart_axis_x_show?c:"hidden")},addBrushHandle(t){const e=this,{config:n}=e,a=n.axis_rotated,i=n.subchart_init_range,o="handle--custom",s=a?["M8.5 0 a6 6 0 0 0 -6 -6.5 H-2.5 a 6 6 0 0 0 -6 6.5 z m-5 -2 H-3.5 m7 -2 H-3.5z","M8.5 0 a6 -6 0 0 1 -6 6.5 H-2.5 a 6 -6 0 0 1 -6 -6.5z m-5 2 H-3.5 m7 2 H-3.5z"]:["M0 -8.5 A6 6 0 0 0 -6.5 -3.5 V2.5 A6 6 0 0 0 0 8.5 Z M-2 -3.5 V3.5 M-4 -3.5 V3.5z","M0 -8.5 A6 6 0 0 1 6.5 -3.5 V2.5 A6 6 0 0 1 0 8.5 Z M2 -3.5 V3.5 M4 -3.5 V3.5z"];e.brush.handle=t.selectAll(`.${o}`).data(a?[{type:"n"},{type:"s"}]:[{type:"w"},{type:"e"}]).enter().append("path").attr("class",o).attr("cursor",`${a?"ns":"ew"}-resize`).attr("d",l=>s[+/[se]/.test(l.type)]).attr("display",i?null:"none")},updateTargetsForSubchart(t){const e=this,{config:n,state:a,$el:{subchart:{main:i}}}=e;n.subchart_show&&(["bar","line","bubble","candlestick","scatter"].filter(o=>e.hasType(o)||e.hasTypeOf(Cn(o))).forEach(o=>{const s=/^(bubble|scatter)$/.test(o),l=Cn(s?"circle":o),c=e.getChartClass(l,!0),f=e.getClass(s?"circles":`${o}s`,!0),g=i.select(`.${Ue[`chart${`${l}s`}`]}`);if(s){const v=g.selectAll(`.${Ue.circles}`).data(t.filter(e[`is${Cn(o)}Type`].bind(e))).attr("class",f);v.exit().remove(),v.enter().append("g").attr("class",f)}else{const v=g.selectAll(`.${Ue[`chart${l}`]}`).attr("class",c).data(t.filter(e[`is${l}Type`].bind(e))),m=v.enter().append("g").style("opacity","0").attr("class",c).append("g").attr("class",f);v.exit().remove(),o==="line"&&e.hasTypeOf("Area")&&m.append("g").attr("class",e.getClass("areas",!0))}}),i.selectAll(`.${Ue.brush} rect`).attr(n.axis_rotated?"width":"height",n.axis_rotated?a.width2:a.height2))},redrawSubchart(t,e,n){var a;const i=this,{config:o,$el:{subchart:{main:s}},state:l}=i,c=!!e;if(s.style("visibility",o.subchart_show?null:"hidden"),o.subchart_show&&(((a=l.event)==null?void 0:a.type)==="zoom"&&i.brush.update(),t)){const f=o.subchart_init_range;if(!Kl(i)&&i.brush.update(),Object.keys(n.type).forEach(g=>{const v=Cn(g),m=i[`generateDraw${v}`](n.indices[g],!0);i[`update${v}`](c,!0),i[`redraw${v}`](m,c,!0)}),i.hasType("bubble")||i.hasType("scatter")){const{cx:g}=n.pos,v=i.updateCircleY(!0);i.updateCircle(!0),i.redrawCircle(g,v,c,void 0,!0)}!l.rendered&&f&&(l.domain=f,i.brush.move(i.brush.getSelection(),f.map(i.scale.x)))}},redrawForBrush(t=!0){var e;const n=this,{config:{subchart_onbrush:a,zoom_rescale:i},scale:o,state:s}=n;n.redraw({withTransition:!1,withY:i,withSubchart:!1,withUpdateXDomain:!0,withDimension:!1}),t&&s.rendered&&a.bind(n.api)((e=s.domain)!=null?e:o.x.orgDomain())},transformContext(t,e){const n=this,{$el:{subchart:a},$T:i}=n,o=e!=null&&e.axisSubX?e.axisSubX:i(a.main.select(`.${Ue.axisX}`),t);a.main.attr("transform",n.getTranslate("context")),o.attr("transform",n.getTranslate("subX"))}},Zm={initZoom(){const t=this;t.scale.zoom=null,t.generateZoom(),t.config.zoom_type==="drag"&&t.initZoomBehaviour()},bindZoomEvent(t=!0){const e=this,{config:n}=e;n.zoom_enabled&&t?!n.subchart_show&&e.bindZoomOnEventRect():t===!1&&(e.api.unzoom(),e.unbindZoomEvent())},generateZoom(){const t=this,{config:e,org:n,scale:a}=t,i=Ym().duration(0).on("start",t.onZoomStart.bind(t)).on("zoom",t.onZoom.bind(t)).on("end",t.onZoomEnd.bind(t));i.orgScaleExtent=()=>{const o=e.zoom_extent||[1,10];return[o[0],Math.max(t.getMaxDataCount()/o[1],o[1])]},i.updateScaleExtent=function(){const o=Dr(t.scale.x.orgDomain())/Dr(t.getZoomDomain()),s=this.orgScaleExtent();return this.scaleExtent([s[0]*o,s[1]*o]),this},i.updateTransformScale=(o,s)=>{var l;const c=e.axis_rotated;(l=n.xScale)==null||l.range(a.x.range());const f=o[c?"rescaleY":"rescaleX"](n.xScale||a.x);if(f.domain().some(m=>/(Invalid Date|NaN)/.test(m.toString())))return;const g=t.trimXDomain(f.domain()),v=e.zoom_rescale;if(f.domain(g,n.xDomain),s){const m=f(a.x.domain()[0]),S=c?o.x:m,P=c?m:o.y;t.$el.eventRect.property("__zoom",ar.translate(S,P).scale(o.k))}t.state.xTickOffset||(t.state.xTickOffset=t.axis.x.tickOffset()),a.zoom=t.getCustomizedXScale(f),t.axis.x.scale(a.zoom),v?(!n.xScale&&(n.xScale=a.x.copy()),a.x.domain(g)):n.xScale&&(a.x.domain(n.xScale.domain()),n.xScale=null)},i.getDomain=()=>{const o=a[a.zoom?"zoom":"subX"].domain();return t.axis.isCategorized()&&(o[1]-=2),o},t.zoom=i},onZoomStart(t){const e=this,{sourceEvent:n}=t;n&&(e.zoom.startEvent=n,e.state.zooming=!0,_e(e.config.zoom_onzoomstart,e.api,t))},onZoom(t){var e;const n=this,{config:a,scale:i,state:o,org:s}=n,{sourceEvent:l}=t,c=(t==null?void 0:t.transform)===ar;if(!a.zoom_enabled||n.filterTargetsToShow(n.data.targets).length===0||!i.zoom&&(l==null?void 0:l.type.indexOf("touch"))>-1&&(l==null?void 0:l.touches.length)===1)return;t.sourceEvent&&(o.zooming=!0,o.domain=void 0);const f=(l==null?void 0:l.type)==="mousemove",g=(l==null?void 0:l.wheelDelta)<0,{transform:v}=t;!f&&g&&i.x.domain().every((S,P)=>S!==s.xDomain[P])&&i.x.domain(s.xDomain),n.zoom.updateTransformScale(v,a.zoom_type==="wheel"&&l);const m=a.transition_duration>0&&!a.subchart_show&&(o.dragging||c||!t.sourceEvent);n.redraw({withTransition:m,withY:a.zoom_rescale,withSubchart:!1,withEventRect:!1,withDimension:!1}),n.state.cancelClick=f,!c&&_e(a.zoom_onzoom,n.api,(e=n.state.domain)!=null?e:n.zoom.getDomain())},onZoomEnd(t){var e,n;const a=this,{config:i,state:o}=a;let{startEvent:s}=a.zoom,l=t==null?void 0:t.sourceEvent;const c=(t==null?void 0:t.transform)===ar;(s==null?void 0:s.type.indexOf("touch"))>-1&&(s=s.changedTouches[0],l=(e=l==null?void 0:l.changedTouches)==null?void 0:e[0]),!(i.zoom_type==="drag"&&l&&s.clientX===l.clientX&&s.clientY===l.clientY)&&(o.zooming=!1,a.redrawEventRect(),a.updateZoom(),!c&&(l||o.dragging)&&_e(i.zoom_onzoomend,a.api,(n=a.state.domain)!=null?n:a.zoom.getDomain()))},updateZoom(t){const e=this,{subX:n,x:a,zoom:i}=e.scale;if(i){const o=i.domain(),s=n.domain(),l=.015,c=e.config.axis_x_inverted?(o[0]>=s[0]||o[0]+l>=s[0])&&(s[1]>=o[1]||s[1]>=o[1]+l):(o[0]<=s[0]||o[0]-l<=s[0])&&(s[1]<=o[1]||s[1]<=o[1]-l);(t||c)&&(e.axis.x.scale(n),a.domain(n.orgDomain()),e.scale.zoom=null)}},updateCurrentZoomTransform(t,e){const n=this,{$el:{eventRect:a},config:i}=n,o=i.axis_rotated,s=[-t(e[0]),0],l=ar.scale(t.range()[1]/(t(e[1])-t(e[0]))).translate(...o?s.reverse():s);a.call(n.zoom.transform,l)},bindZoomOnEventRect(){var t;const e=this,{config:n,$el:{eventRect:a,svg:i}}=e,o=n.zoom_type==="drag"?e.zoomBehaviour:e.zoom;Ke.GestureEvent&&/^((?!chrome|android|mobile).)*safari/i.test((t=Ke.navigator)==null?void 0:t.userAgent)&&i.on("wheel",()=>{}),a==null||a.call(o).on("dblclick.zoom",null)},initZoomBehaviour(){const t=this,{config:e,state:n}=t,a=e.axis_rotated;let i=0,o=0,s,l;const c={axis:a?"y":"x",attr:a?"height":"width",index:a?1:0};t.zoomBehaviour=uc().clickDistance(4).on("start",function(f){l=t.scale.zoom?null:t.axis.getExtent(),n.event=f,t.setDragStatus(!0),t.unselectRect(),s||(s=t.$el.main.append("rect").attr("clip-path",n.clip.path).attr("class",so.zoomBrush).attr("width",a?n.width:0).attr("height",a?0:n.height)),i=Hn(f,this)[c.index],l&&(il[1]&&(i=l[1])),o=i,s.attr(c.axis,i).attr(c.attr,0),t.onZoomStart(f)}).on("drag",function(f){o=Hn(f,this)[c.index],l&&(o>l[1]?o=l[1]:o{const g=t.scale.zoom||t.scale.x;n.event=f,s.attr(c.axis,0).attr(c.attr,0),i>o&&([i,o]=[o,i]),i<0&&(o+=Math.abs(i),i=0),i!==o&&t.api.zoom([i,o].map(v=>g.invert(v))),t.setDragStatus(!1)})},setZoomResetButton(){const t=this,{config:e,$el:n}=t,a=e.zoom_resetButton;a&&e.zoom_type==="drag"&&(n.zoomResetBtn?n.zoomResetBtn.style("display",null):n.zoomResetBtn=t.$el.chart.append("div").classed(Se.button,!0).append("span").on("click",function(){ve(a.onclick)&&a.onclick.bind(t.api)(this),t.api.unzoom()}).classed(so.buttonZoomReset,!0).text(a.text||"Reset Zoom"))},getZoomTransform(){const t=this,{$el:{eventRect:e}}=t;return e!=null&&e.node()?vs(e.node()):{k:1}}},Jm={drag(t){const e=this,{config:n,state:a,$el:{main:i}}=e,o=n.data_selection_grouped,s=n.interaction_enabled&&n.data_selection_isselectable;if(e.hasArcType()||!n.data_selection_enabled||n.zoom_enabled&&!e.zoom.altDomain||!n.data_selection_multiple)return;const[l,c]=a.dragStart||[0,0],[f,g]=t,v=Math.min(l,f),m=Math.max(l,f),S=o?a.margin.top:Math.min(c,g),P=o?a.height:Math.max(c,g);i.select(`.${Or.dragarea}`).attr("x",v).attr("y",S).attr("width",m-v).attr("height",P-S),i.selectAll(`.${sn.shapes}`).selectAll(`.${sn.shape}`).filter(N=>s==null?void 0:s.bind(e.api)(N)).each(function(N,L){const w=ot(this),X=w.classed(tn.SELECTED),W=w.classed(Or.INCLUDED);let H=!1,k;if(w.classed($n.circle)){const K=+w.attr("cx")*1,at=+w.attr("cy")*1;k=e.togglePoint,H=ve in t?Qm(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,ey=(t,e)=>{for(var n in e||(e={}))_m.call(e,n)&&hu(t,n,e[n]);if(du)for(var n of du(e))ty.call(e,n)&&hu(t,n,e[n]);return t},ny=(t,e)=>km(t,qm(e)),ry=ny(ey({},Jm),{selectPoint(t,e,n){const a=this,{config:i,$el:{main:o},$T:s}=a,l=i.axis_rotated,c=(l?a.circleY:a.circleX).bind(a),f=(l?a.circleX:a.circleY).bind(a),g=a.pointSelectR.bind(a);_e(i.data_onselected,a.api,e,t.node()),s(o.select(`.${tn.selectedCircles}${a.getTargetSelectorSuffix(e.id)}`).selectAll(`.${tn.selectedCircle}-${n}`).data([e]).enter().append("circle").attr("class",()=>a.generateClass(tn.selectedCircle,n)).attr("cx",c).attr("cy",f).attr("stroke",a.color).attr("r",v=>a.pointSelectR(v)*1.4)).attr("r",g)},unselectPoint(t,e,n){const a=this,{config:i,$el:{main:o},$T:s}=a;_e(i.data_onunselected,a.api,e,t==null?void 0:t.node()),s(o.select(`.${tn.selectedCircles}${a.getTargetSelectorSuffix(e.id)}`).selectAll(`.${tn.selectedCircle}-${n}`)).attr("r",0).remove()},togglePoint(t,e,n,a){this[`${t?"":"un"}selectPoint`](e,n,a)},selectPath(t,e){const n=this,{config:a}=n;_e(a.data_onselected,n.api,e,t.node()),a.interaction_brighten&&t.style("filter","brightness(1.25)")},unselectPath(t,e){const n=this,{config:a}=n;_e(a.data_onunselected,n.api,e,t.node()),a.interaction_brighten&&t.style("filter",null)},togglePath(t,e,n,a){this[`${t?"":"un"}selectPath`](e,n,a)},getToggle(t,e){const n=this;return t.nodeName==="path"?n.togglePath:n.isStepType(e)?()=>{}:n.togglePoint},toggleShape(t,e,n){var a;const i=this,{config:o,$el:{main:s}}=i;if(o.data_selection_enabled&&o.data_selection_isselectable.bind(i.api)(e)){const l=ot(t),c=l.classed(tn.SELECTED),f=i.getToggle(t,e).bind(i);let g;if(!o.data_selection_multiple){const v=(a=i.isPointFocusOnly)==null?void 0:a.call(i);let m=`.${v?tn.selectedCircles:sn.shapes}`;o.data_selection_grouped&&(m+=i.getTargetSelectorSuffix(e.id)),s.selectAll(m).selectAll(v?`.${tn.selectedCircle}`:`.${sn.shape}.${tn.SELECTED}`).classed(tn.SELECTED,!1).each(function(S){const P=ot(this);g=P,f(!1,P,S,S.index)})}(!g||g.node()!==l.node())&&(l.classed(tn.SELECTED,!c),f(!c,l,e,n))}}}),ay={data_selection_enabled:!1,data_selection_grouped:!1,data_selection_isselectable:()=>!0,data_selection_multiple:!0,data_selection_draggable:!1,data_onselected:()=>{},data_onunselected:()=>{}},iy={subchart_show:!1,subchart_showHandle:!1,subchart_size_height:60,subchart_axis_x_show:!0,subchart_axis_x_tick_show:!0,subchart_axis_x_tick_format:void 0,subchart_axis_x_tick_text_show:!0,subchart_init_range:void 0,subchart_onbrush:()=>{}},oy={zoom_enabled:!1,zoom_type:"wheel",zoom_extent:void 0,zoom_privileged:!1,zoom_rescale:!1,zoom_onzoom:void 0,zoom_onzoomstart:void 0,zoom_onzoomend:void 0,zoom_resetButton:!0,zoom_x_min:void 0,zoom_x_max:void 0};let gu=()=>(yn(Vr.prototype,ry),yn(Er.prototype,Dm),Nr.setOptions([ay]),(gu=()=>!0)()),vu=()=>(yn(Vr.prototype,Km),yn(Er.prototype,Lm),Nr.setOptions([iy]),(vu=()=>!0)()),pu=()=>(yn(Vr.prototype,Zm),yn(Er.prototype,Wm),Nr.setOptions([oy]),(pu=()=>!0)());function mu(t,e,n){const{config:a}=t,i=(o,s)=>{const l=he(s)?s:s===!1?void 0:null;l!==null&&(a[`axis_${o}_${e}`]=l)};Qe(n)&&(nr(n)?Object.keys(n).forEach(o=>{i(o,n[o])}):(he(n)||n===!1)&&["y","y2"].forEach(o=>{i(o,n)}),t.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0}))}function yu(t,e){const{config:n}=t;return{x:n[`axis_x_${e}`],y:n[`axis_y_${e}`],y2:n[`axis_y2_${e}`]}}var sy={axis:{labels:function(t){const e=this.internal;let n;return t&&(Object.keys(t).forEach(a=>{e.axis.setLabelText(a,t[a])}),e.axis.updateLabels()),["x","y","y2"].forEach(a=>{const i=e.axis.getLabelText(a);i&&(!n&&(n={}),n[a]=i)}),n},min:function(t){const e=this.internal;return De(t)||t===!1?mu(e,"min",t):yu(e,"min")},max:function(t){const e=this.internal;return De(t)||t===!1?mu(e,"max",t):yu(e,"max")},range:function(t){const{axis:e}=this;if(arguments.length){const{min:n,max:a}=t;Qe(a)&&e.max(a),Qe(n)&&e.min(n)}else return{max:e.max(),min:e.min()}}}},ly={category(t,e){const n=this.internal,{config:a}=n;return arguments.length>1&&(a.axis_x_categories[t]=e,n.redraw()),a.axis_x_categories[t]},categories(t){const e=this.internal,{config:n}=e;if(!t||!Array.isArray(t)){const a=n.axis_x_categories;return qn(a)?Object.values(e.data.xs)[0]:a}return n.axis_x_categories=t,e.redraw(),n.axis_x_categories}},cy={flow(t){const e=this.internal;let n;(t.json||t.rows||t.columns)&&e.convertData(t,i=>{n=i,a()});function a(){let i,o=0,s=0,l,c;if(e.state.redrawing||!n||!Da())return;const f=[],g=e.getMaxDataCount(),v=e.convertDataToTargets(n,!0),m=e.axis.isTimeSeries();e.data.targets.forEach(N=>{let L=!1;for(let w=0;w{for(let L=0;L{const L=[];for(let w=e.data.targets[0].values[0].index;w{w.index+=s,m||(w.x+=s)}),N.values=L.concat(N.values)}),e.data.targets=e.data.targets.concat(v);const S=e.data.targets[0],P=S.values[0];Qe(t.to)?(o=0,c=m?Yn.call(e,t.to):t.to,S.values.forEach(N=>{N.x1?S.values[S.values.length-1].x-P.x:P.x-e.getXDomain(e.data.targets)[0]:l=1,i=[P.x-l,P.x]),i&&e.updateXDomain(null,!0,!0,!1,i),e.updateTargets(e.data.targets),e.redraw({flow:{index:P.index,length:o,duration:De(t.duration)?t.duration:e.config.transition_duration,done:t.done,orgDataCount:g},withLegend:!0,withTransition:g>1,withTrimXDomain:!1,withUpdateXAxis:!0})}}};function ms(t,e){const n=this.internal,{config:a}=n,i=a.transition_duration&&Da(),o=`grid_${e}_lines`;return t&&(a[o]=t,n.updateGrid(),n.redrawGrid(i)),a[o]}function xu(t,e){const n=`grid_${e}_lines`;return ms.bind(this)(this.internal.config[n].concat(t||[]),e)}function Tu(t,e){this.internal.removeGridLines(t,e)}const $u=function(t){return ms.bind(this)(t,"x")};yn($u,{add(t){return xu.bind(this)(t,"x")},remove(t){return Tu.bind(this)(t,!0)}});const Su=function(t){return ms.bind(this)(t,"y")};yn(Su,{add(t){return xu.bind(this)(t,"y")},remove(t){return Tu.bind(this)(t,!1)}});var uy={xgrids:$u,ygrids:Su},fy={groups(t){const e=this.internal,{config:n}=e;return ln(t)||(n.data_groups=t,e.redraw()),n.data_groups}};function Au(t,e=!1){const n=this.internal,{config:a}=n,i=a.transition_duration&&Da();return t?(a.regions=e?a.regions.concat(t):t,n.updateRegion(),n.redrawRegion(i),e?a.regions:t):a.regions}const Eu=function(t){return Au.bind(this)(t)};yn(Eu,{add:function(t){return Au.bind(this)(t,!0)},remove:function(t){const e=this.internal,{config:n,$T:a}=e,i=t||{},o=$r(i,"classes",[$a.region]);let s=e.$el.main.select(`.${$a.regions}`).selectAll(o.map(l=>`.${l}`));return a(s).style("opacity","0").remove(),s=n.regions,Object.keys(i).length?(s=s.filter(l=>{let c=!1;return l.class?(l.class.split(" ").forEach(f=>{o.indexOf(f)>=0&&(c=!0)}),!c):!0}),n.regions=s):n.regions=[],s}});var dy={regions:Eu},hy={x(t){const e=this.internal,{axis:n,data:a}=e,i=n.isCustomX()&&n.isCategorized();return je(t)&&(i?this.categories(t):(e.updateTargetX(a.targets,t),e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0}))),i?this.categories():a.xs},xs(t){const e=this.internal;return Be(t)&&(e.updateTargetXs(e.data.targets,t),e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})),e.data.xs}};function gy(t){return t}var Xi=1,Hi=2,ys=3,Ua=4,bu=1e-6;function vy(t){return"translate("+t+",0)"}function py(t){return"translate(0,"+t+")"}function my(t){return e=>+t(e)}function yy(t,e){return e=Math.max(0,t.bandwidth()-e*2)/2,t.round()&&(e=Math.round(e)),n=>+t(n)+e}function xy(){return!this.__axis}function Yi(t,e){var n=[],a=null,i=null,o=6,s=6,l=3,c=typeof window!="undefined"&&window.devicePixelRatio>1?0:.5,f=t===Xi||t===Ua?-1:1,g=t===Ua||t===Hi?"x":"y",v=t===Xi||t===ys?vy:py;function m(S){var P=a==null?e.ticks?e.ticks.apply(e,n):e.domain():a,N=i==null?e.tickFormat?e.tickFormat.apply(e,n):gy:i,L=Math.max(o,0)+l,w=e.range(),X=+w[0]+c,W=+w[w.length-1]+c,H=(e.bandwidth?yy:my)(e.copy(),c),k=S.selection?S.selection():S,K=k.selectAll(".domain").data([null]),at=k.selectAll(".tick").data(P,e).order(),ht=at.exit(),$t=at.enter().append("g").attr("class","tick"),dt=at.select("line"),st=at.select("text");K=K.merge(K.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),at=at.merge($t),dt=dt.merge($t.append("line").attr("stroke","currentColor").attr(g+"2",f*o)),st=st.merge($t.append("text").attr("fill","currentColor").attr(g,f*L).attr("dy",t===Xi?"0em":t===ys?"0.71em":"0.32em")),S!==k&&(K=K.transition(S),at=at.transition(S),dt=dt.transition(S),st=st.transition(S),ht=ht.transition(S).attr("opacity",bu).attr("transform",function(Vt){return isFinite(Vt=H(Vt))?v(Vt+c):this.getAttribute("transform")}),$t.attr("opacity",bu).attr("transform",function(Vt){var vt=this.parentNode.__axis;return v((vt&&isFinite(vt=vt(Vt))?vt:H(Vt))+c)})),ht.remove(),K.attr("d",t===Ua||t===Hi?s?"M"+f*s+","+X+"H"+c+"V"+W+"H"+f*s:"M"+c+","+X+"V"+W:s?"M"+X+","+f*s+"V"+c+"H"+W+"V"+f*s:"M"+X+","+c+"H"+W),at.attr("opacity",1).attr("transform",function(Vt){return v(H(Vt)+c)}),dt.attr(g+"2",f*o),st.attr(g,f*L).text(N),k.filter(xy).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",t===Hi?"start":t===Ua?"end":"middle"),k.each(function(){this.__axis=H})}return m.scale=function(S){return arguments.length?(e=S,m):e},m.ticks=function(){return n=Array.from(arguments),m},m.tickArguments=function(S){return arguments.length?(n=S==null?[]:Array.from(S),m):n.slice()},m.tickValues=function(S){return arguments.length?(a=S==null?null:Array.from(S),m):a&&a.slice()},m.tickFormat=function(S){return arguments.length?(i=S,m):i},m.tickSize=function(S){return arguments.length?(o=s=+S,m):o},m.tickSizeInner=function(S){return arguments.length?(o=+S,m):o},m.tickSizeOuter=function(S){return arguments.length?(s=+S,m):s},m.tickPadding=function(S){return arguments.length?(l=+S,m):l},m.offset=function(S){return arguments.length?(c=+S,m):c},m}function Ty(t){return Yi(Xi,t)}function $y(t){return Yi(Hi,t)}function Ru(t){return Yi(ys,t)}function Iu(t){return Yi(Ua,t)}var Sy=Object.defineProperty,Ay=(t,e,n)=>e in t?Sy(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,xs=(t,e,n)=>Ay(t,typeof e!="symbol"?e+"":e,n);class Ou{constructor(e){xs(this,"owner"),xs(this,"config"),xs(this,"scale");const n=zr(),{config:a,params:i}=e;this.owner=e,this.config=a,this.scale=n,(a.noTransition||!i.config.transition_duration)&&(a.withoutTransition=!0),a.range=this.scaleExtent((i.orgXScale||n).range())}static getSizeFor1Char(e,n=!0){const a={w:5.5,h:11.5};return!e.empty()&&e.text("0").call(i=>{try{const{width:o,height:s}=i.node().getBBox();o&&s&&(a.w=o,a.h=s)}finally{i.text("")}}),n&&(this.getSizeFor1Char=()=>a),a}getTickTransformSetter(e){const{config:n}=this,a=e==="x"?i=>`translate(${i+n.tickOffset},0)`:i=>`translate(0,${i})`;return(i,o)=>{i.attr("transform",s=>{const l=o(s);return De(s)?a(l):null})}}scaleExtent(e){const n=e[0],a=e[e.length-1];return n0?i:1,o]).range(e.range());s=c.ticks();for(let f=o.toFixed().length;s.length>15;f--)s=c.ticks(f);s.splice(0,1,i),s.splice(s.length-1,1,o)}else s=e.ticks(...this.config.tickArguments||[]);s=s.map(c=>ze(c)&&he(c)&&!isNaN(c)&&Math.round(c*10)/10||c)}return s}copyScale(){const e=this.scale.copy();return e.domain().length||e.domain(this.scale.domain()),e.type=this.scale.type,e}textFormatted(e){const n=this.config.tickFormat,a=/\d+\.\d+0{5,}\d$/.test(e)?+String(e).replace(/0+\d$/,""):e,i=n?n(a):a;return Qe(i)?i:""}transitionise(e){const{config:n}=this;let a=e;if(n.withoutTransition)a=e.interrupt();else if(n.transition||!this.owner.params.noTransition)try{a=e.transition(n.transition)}catch(i){}return a}}var Ey=Object.defineProperty,by=(t,e,n)=>e in t?Ey(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,za=(t,e,n)=>by(t,typeof e!="symbol"?e+"":e,n);class Ry{constructor(e={}){za(this,"helper"),za(this,"config"),za(this,"params"),za(this,"g"),za(this,"generatedTicks");const n={innerTickSize:6,outerTickSize:e.outerTick?6:0,orient:"bottom",range:[],tickArguments:null,tickCentered:null,tickCulling:!0,tickFormat:null,tickLength:9,tickOffset:0,tickPadding:3,tickValues:null,transition:null,noTransition:e.noTransition};n.tickLength=Math.max(n.innerTickSize,0)+n.tickPadding,this.config=n,this.params=e,this.helper=new Ou(this)}create(e){const n=this,{config:a,helper:i,params:o}=n,{scale:s}=i,{orient:l}=a,c=this.splitTickText.bind(n),f=/^(left|right)$/.test(l),g=/^(top|bottom)$/.test(l),v=i.getTickTransformSetter(g?"x":"y"),m=v===i.axisX?"y":"x",S=/^(top|left)$/.test(l)?-1:1,P=o.tickTextRotate;this.config.range=s.rangeExtent?s.rangeExtent():i.scaleExtent((o.orgXScale||s).range());const{innerTickSize:N,tickLength:L,range:w}=a,X=o.id,W=X&&/^(x|y|y2)$/.test(X)?o.config[`axis_${X}_tick_text_position`]:{x:0,y:0},H=X==="subX"?"subchart_axis_x":`axis_${X}`,k=o.config[`${H}_show`],K={tick:k?o.config[`${H}_tick_show`]:!1,text:k?o.config[`${H}_tick_text_show`]:!1},at=o.config.axis_evalTextSize;let ht;e.each(function(){const $t=ot(this);let dt=this.__chart__||s,st=i.copyScale();ht=$t,this.__chart__=st,a.tickOffset=o.isCategory?(st(1)-st(0))/2:0;const Vt=$t.selectAll(".domain").data([0]);if(Vt.enter().append("path").attr("class","domain").merge(Vt).attr("d",()=>{const vt=a.outerTickSize*S;return g?`M${w[0]},${vt}V0H${w[1]}V${vt}`:`M${vt},${w[0]}H0V${w[1]}H${vt}`}),K.tick||K.text){const vt=a.tickValues||i.generateTicks(st,f);n.generatedTicks=vt;let Q=$t.selectAll(".tick").data(vt,st);const St=Q.enter().insert("g",".domain").attr("class","tick"),ct=Q.exit().remove();Q=St.merge(Q),K.tick&&St.append("line"),K.text&&St.append("text");const At=Q.select("text"),Gt=ve(at)?at.bind(n.params.owner.api)(At.node()):Ou.getSizeFor1Char(At,at),Bt=[];let Kt=At.selectAll("tspan").data((be,Oe)=>{const Ce=o.tickMultiline?c(be,st,vt,f,Gt.w):je(i.textFormatted(be))?i.textFormatted(be).concat():[i.textFormatted(be)];return Bt[Oe]=Ce.length,Ce.map(He=>({index:Oe,splitted:He}))});Kt.exit().remove(),Kt=Kt.enter().append("tspan").merge(Kt).text(be=>be.splitted),Kt.attr("x",g?0:L*S).attr("dx",(()=>{let be=0;return/(top|bottom)/.test(l)&&P&&(be=8*Math.sin(Math.PI*(P/180))*(l==="top"?-1:1)),be+(W.x||0)})()).attr("dy",(be,Oe)=>{const Ce=".71em";let He=0;return l!=="top"&&(He=Gt.h,Oe===0&&(He=f?-((Bt[be.index]-1)*(Gt.h/2)-3):W.y===0?Ce:0)),he(He)&&W.y?He+W.y:He||Ce});const ne=Q.select("line"),le=Q.select("text");if(St.select("line").attr(`${m}2`,N*S),St.select("text").attr(m,L*S),n.setTickLineTextPosition(ne,le),o.tickTitle){const be=le.select("title");(be.empty()?le.append("title"):be).text(Oe=>o.tickTitle[Oe])}if(st.bandwidth){const be=st,Oe=be.bandwidth()/2;dt=Ce=>be(Ce)+Oe,st=dt}else dt.bandwidth?dt=st:v(ct,st);Q=o.owner.state.flowing?i.transitionise(Q):o.owner.$T(Q),v(St,dt),v(Q.style("opacity",null),st)}}),this.g=ht}getGeneratedTicks(e){var n;const a=((n=this.generatedTicks)==null?void 0:n.length)-1;let i=this.generatedTicks;if(a>e){const o=Math.round(a/e+.1);i=this.generatedTicks.map((s,l)=>l%o===0?s:null).filter(s=>s!==null).splice(0,e)}return i}getTickXY(){const{config:e}=this,n={x:0,y:0};return this.params.isCategory&&(n.x=e.tickCentered?0:e.tickOffset,n.y=e.tickCentered?e.tickOffset:0),n}getTickSize(e){const{scale:n}=this.helper,{config:a}=this,{innerTickSize:i,range:o}=a,s=n(e)+(a.tickCentered?0:a.tickOffset);return o[0]{const N=["start","end"];return o==="top"&&N.reverse(),P?N[P>0?0:1]:"middle"},g=P=>P?`rotate(${P})`:null,v=P=>{const N=P/(o==="bottom"?15:23);return P?11.5-2.5*N*(P>0?1:-1):s},{config:{axis_rotated:m,axis_x_tick_text_inner:S}}=this.params.owner;switch(o){case"bottom":e.attr("x1",a.x).attr("x2",a.x).attr("y2",this.getTickSize.bind(this)),n.attr("x",0).attr("y",v(c)).style("text-anchor",f(c)).style("text-anchor",(P,N,{length:L})=>!m&&N===0&&(S===!0||S.first)?"start":!m&&N===L-1&&(S===!0||S.last)?"end":f(c)).attr("transform",g(c));break;case"top":e.attr("x2",0).attr("y2",-i),n.attr("x",0).attr("y",-v(c)*2).style("text-anchor",f(c)).attr("transform",g(c));break;case"left":e.attr("x2",-i).attr("y1",a.y).attr("y2",a.y),n.attr("x",-s).attr("y",l).style("text-anchor","end");break;case"right":e.attr("x2",i).attr("y2",0),n.attr("x",s).attr("y",0).style("text-anchor","start")}}splitTickText(e,n,a,i,o){const{params:s}=this,l=this.helper.textFormatted(e),c=ze(l)&&l.indexOf(` +`)>-1?l.split(` +`):[];if(c.length)return c;if(je(l))return l;let f=s.tickWidth;(!f||f<=0)&&(f=i?95:s.isCategory?(s.isInverted?n(a[0])-n(a[1]):n(a[1])-n(a[0]))-12:110);function g(v,m){let S,P,N;for(let L=1;L{const S=v+1;return Se(this.helper.scale.domain());else{if(!arguments.length)return n.tickValues;n.tickValues=e}return this}setTransition(e){return this.config.transition=e,this}}var Iy=Object.defineProperty,Oy=(t,e,n)=>e in t?Iy(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,pr=(t,e,n)=>Oy(t,typeof e!="symbol"?e+"":e,n),Cy={getAxisInstance:function(){return this.axis||new Py(this)}};class Py{constructor(e){pr(this,"owner"),pr(this,"x"),pr(this,"subX"),pr(this,"y"),pr(this,"y2"),pr(this,"axesList",{}),pr(this,"tick",{x:null,y:null,y2:null}),pr(this,"xs",[]),pr(this,"orient",{x:"bottom",y:"left",y2:"right",subX:"bottom"}),this.owner=e,this.setOrient()}getAxisClassName(e){return`${Tn.axis} ${Tn[`axis${Cn(e)}`]}`}isHorizontal(e,n){const a=e.config.axis_rotated;return n?a:!a}isCategorized(){const{config:e,state:n}=this.owner;return e.axis_x_type.indexOf("category")>=0||n.hasRadar}isCustomX(){const{config:e}=this.owner;return!this.isTimeSeries()&&(e.data_x||cn(e.data_xs))}isTimeSeries(e="x"){return this.owner.config[`axis_${e}_type`]==="timeseries"}isLog(e="x"){return this.owner.config[`axis_${e}_type`]==="log"}isTimeSeriesY(){return this.isTimeSeries("y")}getAxisType(e="x"){let n="linear";return this.isTimeSeries(e)?n=this.owner.config.axis_x_localtime?"time":"utc":this.isLog(e)&&(n="log"),n}getExtent(){const e=this.owner,{config:n,scale:a}=e;let i=n.axis_x_extent;if(i){if(ve(i))i=i.bind(e.api)(e.getXDomain(e.data.targets),a.subX);else if(this.isTimeSeries()&&i.every(isNaN)){const o=Yn.bind(e);i=i.map(s=>a.subX(o(s)))}}return i}init(){const e=this.owner,{config:n,$el:{main:a,axis:i},state:{clip:o}}=e,s=["x","y"];n.axis_y2_show&&s.push("y2"),s.forEach(l=>{const c=this.getAxisClassName(l);i[l]=a.append("g").attr("class",c).attr("clip-path",()=>{let f=null;return l==="x"?f=o.pathXAxis:l==="y"&&(f=o.pathYAxis),f}).attr("transform",e.getTranslate(l)).style("visibility",n[`axis_${l}_show`]?null:"hidden"),this.generateAxes(l)})}setOrient(){const e=this.owner,{axis_rotated:n,axis_y_inner:a,axis_y2_inner:i}=e.config;this.orient={x:n?"left":"bottom",y:n?a?"top":"bottom":a?"right":"left",y2:n?i?"bottom":"top":i?"left":"right",subX:n?"left":"bottom"}}generateAxes(e){const n=this.owner,{config:a}=n,i=[],o=a[`axis_${e}_axes`],s=a.axis_rotated;let l;e==="x"?l=s?Iu:Ru:e==="y"?l=s?Ru:Iu:e==="y2"&&(l=s?Ty:$y),o.length&&o.forEach(c=>{const f=c.tick||{},g=n.scale[e].copy();c.domain&&g.domain(c.domain),i.push(l(g).ticks(f.count).tickFormat(ve(f.format)?f.format.bind(n.api):v=>v).tickValues(f.values).tickSizeOuter(f.outer===!1?0:6))}),this.axesList[e]=i}updateAxes(){const e=this.owner,{config:n,$el:{main:a},$T:i}=e;Object.keys(this.axesList).forEach(o=>{const s=n[`axis_${o}_axes`],l=e.scale[o].copy(),c=l.range();this.axesList[o].forEach((f,g)=>{const v=f.scale().range();c.every((P,N)=>P===v[N])||f.scale().range(c);const m=`${this.getAxisClassName(o)}-${g+1}`;let S=a.select(`.${m.replace(/\s/,".")}`);S.empty()?S=a.append("g").attr("class",m).style("visibility",n[`axis_${o}_show`]?null:"hidden").call(f):(s[g].domain&&l.domain(s[g].domain),i(S).call(f.scale(l))),S.attr("transform",e.getTranslate(o,g+1))})})}setAxis(e,n,a,i){const o=this.owner;e!=="subX"&&(this.tick[e]=this.getTickValues(e)),this[e]=this.getAxis(e,n,a,e==="x"&&(o.scale.zoom||o.config.subchart_show||o.state.resizing)?!0:i)}getAxis(e,n,a,i,o){const s=this.owner,{config:l}=s,c=/^(x|subX)$/.test(e),f=c?"x":e,g=c&&this.isCategorized(),v=this.orient[e],m=o?0:s.getAxisTickRotate(f);let S;if(c)S=e==="subX"?s.format.subXAxisTick:s.format.xAxisTick;else{const X=l[`axis_${e}_tick_format`];ve(X)&&(S=X.bind(s.api))}let P=this.tick[f];const N=ea({outerTick:a,noTransition:i,config:l,id:e,tickTextRotate:m,owner:s},c&&{isCategory:g,isInverted:l.axis_x_inverted,tickMultiline:l.axis_x_tick_multiline,tickWidth:l.axis_x_tick_width,tickTitle:g&&l.axis_x_tick_tooltip&&s.api.categories(),orgXScale:s.scale.x});c||(N.tickStepSize=l[`axis_${f}_tick_stepSize`]);const L=new Ry(N).scale(c&&s.scale.zoom||n).orient(v);if(c&&this.isTimeSeries()&&P&&!ve(P)){const X=Yn.bind(s);P=P.map(W=>X(W))}else!c&&this.isTimeSeriesY()&&(L.ticks(l.axis_y_tick_time_value),P=null);P&&L.tickValues(P),L.tickFormat(S||!c&&s.isStackNormalized()&&(X=>`${X}%`)),g&&(L.tickCentered(l.axis_x_tick_centered),qn(l.axis_x_tick_culling)&&(l.axis_x_tick_culling=!1));const w=l[`axis_${f}_tick_count`];return w&&L.ticks(w),L}updateXAxisTickValues(e,n){var a;const i=this.owner,{config:o}=i,s=o.axis_x_tick_fit;let l=o.axis_x_tick_count,c;return(s||l&&s)&&(c=i.mapTargetsToUniqueXs(e),this.isCategorized()&&l>c.length&&(l=c.length),c=this.generateTickValues(c,l,this.isTimeSeries())),n?n.tickValues(c):this.x&&(this.x.tickValues(c),(a=this.subX)==null||a.tickValues(c)),c}getId(e){const{config:n,scale:a}=this.owner;let i=n.data_axes[e];return(!i||!a[i])&&(i="y"),i}getXAxisTickFormat(e){const n=this.owner,{config:a,format:i}=n,o=e&&a.subchart_axis_x_tick_format||a.axis_x_tick_format,s=this.isTimeSeries(),l=this.isCategorized();let c;return o?ve(o)?c=o.bind(n.api):s&&(c=f=>f?i.axisTime(o)(f):""):c=s?i.defaultAxisTime:l?n.categoryName:f=>f<0?f.toFixed(0):f,ve(c)?f=>c.apply(n,l?[f,n.categoryName(f)]:[f]):c}getTickValues(e){const n=this.owner,a=n.config[`axis_${e}_tick_values`],i=n[`${e}Axis`];return(ve(a)?a.call(n.api):a)||(i?i.tickValues():void 0)}getLabelOptionByAxisId(e){return this.owner.config[`axis_${e}_label`]}getLabelText(e){const n=this.getLabelOptionByAxisId(e);return ze(n)?n:n?n.text:null}setLabelText(e,n){const a=this.owner,{config:i}=a,o=this.getLabelOptionByAxisId(e);ze(o)?i[`axis_${e}_label`]=n:o&&(o.text=n)}getLabelPosition(e,n){const a=this.owner.config.axis_rotated,i=this.getLabelOptionByAxisId(e),o=nr(i)&&i.position?i.position:n[+!a],s=l=>!!~o.indexOf(l);return{isInner:s("inner"),isOuter:s("outer"),isLeft:s("left"),isCenter:s("center"),isRight:s("right"),isTop:s("top"),isMiddle:s("middle"),isBottom:s("bottom")}}getAxisLabelPosition(e){return this.getLabelPosition(e,e==="x"?["inner-top","inner-right"]:["inner-right","inner-top"])}getLabelPositionById(e){return this.getAxisLabelPosition(e)}xForAxisLabel(e){const n=this.owner,{state:{width:a,height:i}}=n,o=this.getAxisLabelPosition(e);let s=o.isMiddle?-i/2:0;return this.isHorizontal(n,e!=="x")?s=o.isLeft?0:o.isCenter?a/2:a:o.isBottom&&(s=-i),s}textAnchorForAxisLabel(e){const n=this.owner,a=this.getAxisLabelPosition(e);let i=a.isMiddle?"middle":"end";return this.isHorizontal(n,e!=="x")?i=a.isLeft?"start":a.isCenter?"middle":"end":a.isBottom&&(i="start"),i}dxForAxisLabel(e){const n=this.owner,a=this.getAxisLabelPosition(e);let i=a.isBottom?"0.5em":"0";return this.isHorizontal(n,e!=="x")?i=a.isLeft?"0.5em":a.isRight?"-0.5em":"0":a.isTop&&(i="-0.5em"),i}dyForAxisLabel(e){const n=this.owner,{config:a}=n,i=a.axis_rotated,o=this.getAxisLabelPosition(e).isInner,s=a[`axis_${e}_tick_rotate`]?n.getHorizontalAxisHeight(e):0,{width:l}=this.getMaxTickSize(e);let c;if(e==="x"){const f=a.axis_x_height;i?c=o?"1.2em":-25-l:o?c="-0.5em":f?c=f-10:s?c=s-10:c="3em"}else c={y:["-0.5em",10,"3em","1.2em",10],y2:["1.2em",-20,"-2.2em","-0.5em",15]}[e],i?o?c=c[0]:s?c=s*(e==="y2"?-1:1)-c[1]:c=c[2]:c=o?c[3]:(c[4]+(a[`axis_${e}_inner`]?0:l+c[4]))*(e==="y"?-1:1);return c}getMaxTickSize(e,n){const a=this.owner,{config:i,state:{current:o},$el:{svg:s,chart:l}}=a,c=o.maxTickSize[e],f=`axis_${e}`,g={width:0,height:0};if(n||!i[`${f}_show`]||c.width>0&&a.filterTargetsToShow().length===0)return c;if(s){const v=/^y2?$/.test(e),m=a.filterTargetsToShow(a.data.targets),S=a.scale[e].copy().domain(a[`get${v?"Y":"X"}Domain`](m,e)),P=S.domain(),N=P[0]===P[1]&&P.every(K=>K>0),L=je(c.domain)&&c.domain[0]===c.domain[1]&&c.domain.every(K=>K>0);if(N||L)return c.size;c.domain=P,v||c.ticks.splice(0);const w=this.getAxis(e,S,!1,!1,!0),X=i[`${f}_tick_rotate`],W=i[`${f}_tick_count`];!i[`${f}_tick_values`]&&W&&w.tickValues(this.generateTickValues(P,W,v?this.isTimeSeriesY():this.isTimeSeries())),!v&&this.updateXAxisTickValues(m,w);const k=l.append("svg").style("visibility","hidden").style("position","fixed").style("top","0").style("left","0");w.create(k),k.selectAll("text").attr("transform",he(X)?`rotate(${X})`:null).each(function(K,at){const{width:ht,height:$t}=this.getBoundingClientRect();g.width=Math.max(g.width,ht),g.height=Math.max(g.height,$t),v||(c.ticks[at]=ht)}),k.remove()}return Object.keys(g).forEach(v=>{g[v]>0&&(c[v]=g[v])}),c}getXAxisTickTextY2Overflow(e){const n=this.owner,{axis:a,config:i,state:{current:o,isLegendRight:s,legendItemWidth:l}}=n,c=n.getAxisTickRotate("x"),f=c>0&&c<90;if((a.isCategorized()||a.isTimeSeries())&&i.axis_x_tick_fit&&(!i.axis_x_tick_culling||qn(i.axis_x_tick_culling))&&!i.axis_x_tick_multiline&&f){const g=i.axis_y2_show&&o.maxTickSize.y2.width||0,v=s&&l||0,m=o.width-n.getCurrentPaddingByDirection("left"),S=this.getXAxisTickMaxOverflow(c,m-e)-g-v,P=Math.max(0,S)+e;return Math.min(P,m/2)}return 0}getXAxisTickMaxOverflow(e,n){const a=this.owner,{axis:i,config:o,state:s}=a,l=i.isTimeSeries(),c=s.current.maxTickSize.x.ticks,f=c.length,{left:g,right:v}=s.axis.x.padding;let m=0;const S=f-(l&&o.axis_x_tick_fit?.5:0);for(let L=0;L{const c=this.getLabelText(l),f=`axis${Cn(l)}`,g=Tn[`${f}Label`];if(c){let v=i.select(`text.${g}`);v.empty()&&(v=i.select(`g.${Tn[f]}`).insert("text",":first-child").attr("class",g).attr("transform",["rotate(-90)",null][l==="x"?+!s:+s]).style("text-anchor",()=>this.textAnchorForAxisLabel(l))),o(v,e).attr("x",()=>this.xForAxisLabel(l)).attr("dx",()=>this.dxForAxisLabel(l)).attr("dy",()=>this.dyForAxisLabel(l)).text(c)}})}getPadding(e,n,a,i){const o=he(e)?e:e[n];return De(o)?this.owner.convertPixelToScale(/(bottom|top)/.test(n)?"y":"x",o,i):a}generateTickValues(e,n,a){let i=e;if(n){const o=ve(n)?n():n;if(o===1)i=[e[0]];else if(o===2)i=[e[0],e[e.length-1]];else if(o>2){const s=this.isCategorized(),l=o-2,c=e[0],f=e[e.length-1],g=(f-c)/(l+1);let v;i=[c];for(let m=0;mo-s)),i}generateTransitions(e){const n=this.owner,{$el:{axis:a},$T:i}=n,[o,s,l,c]=["x","y","y2","subX"].map(f=>i(a[f],e));return{axisX:o,axisY:s,axisY2:l,axisSubX:c}}redraw(e,n,a){const i=this.owner,{config:o,state:s,$el:l}=i,c=n?"0":null;["x","y","y2","subX"].forEach(f=>{const g=this[f],v=l.axis[f];g&&v&&(!a&&!o.transition_duration&&(g.config.withoutTransition=!0),v.style("opacity",c),g.create(e[`axis${Cn(f)}`]))}),this.updateAxes(),!s.rendered&&o.axis_tooltip&&this.setAxisTooltip()}redrawAxis(e,n,a,i,o){var s,l,c;const f=this.owner,{config:g,scale:v,$el:m}=f,S=!!v.zoom;let P;!S&&this.isCategorized()&&e.length===0&&v.x.domain([0,m.axis.x.selectAll(".tick").size()]),v.x&&e.length?(!S&&f.updateXDomain(e,n.UpdateXDomain,n.UpdateOrgXDomain,n.TrimXDomain),g.axis_x_tick_values||this.updateXAxisTickValues(e)):this.x&&(this.x.tickValues([]),(s=this.subX)==null||s.tickValues([])),g.zoom_rescale&&!i&&(P=v.x.orgDomain()),["y","y2"].forEach(N=>{const L=`axis_${N}_`,w=v[N];if(w){const X=g[`${L}tick_values`],W=g[`${L}tick_count`];if(w.domain(f.getYDomain(e,N,P)),!X&&W){const H=f.axis[N],k=w.domain();H.tickValues(this.generateTickValues(k,k.every(K=>K===0)?1:W,this.isTimeSeriesY()))}}}),this.redraw(a,f.hasArcType(),o),this.updateLabels(n.Transition),(n.UpdateXDomain||n.UpdateXAxis||n.Y)&&e.length&&this.setCulling(),n.Y&&((l=v.subY)==null||l.domain(f.getYDomain(e,"y")),(c=v.subY2)==null||c.domain(f.getYDomain(e,"y2")))}setCulling(){const e=this.owner,{config:n,state:{clip:a,current:i},$el:o}=e;["subX","x","y","y2"].forEach(s=>{const l=o.axis[s],f=`axis_${s==="subX"?"x":s}_tick_culling`,g=n[f];if(l&&g){const v=l.selectAll(".tick"),m=na(v.data()),S=m.length,P=n[`${f}_max`],N=n[`${f}_lines`];let L;if(S){for(let w=1;w{var f,g,v;if(ze(l)||l[c])if(s[c]=(f=o[c])==null?void 0:f.append("text").classed(Tn[`axis${c.toUpperCase()}Tooltip`],!0).attr("filter",n.updateTextBGColor({id:c},l)),a){const m=c==="x"?"x":"y",S=c==="y"?"1.15em":c==="x"?"-0.3em":"-0.4em";(g=s[c])==null||g.attr(m,S).attr(`d${c==="x"?"y":"x"}`,c==="x"?"0.4em":"-1.3em").style("text-anchor",c==="x"?"end":null)}else{const m=c==="x"?"y":"x",S=c==="x"?"1.15em":`${c==="y"?"-":""}0.4em`;(v=s[c])==null||v.attr(m,S).attr(`d${c==="x"?"x":"y"}`,c==="x"?"-1em":"0.3em").style("text-anchor",c==="y"?"end":null)}})}}var wy={initEventRect(){this.$el.main.select(`.${Se.chart}`).append("g").attr("class",Zn.eventRects).style("fill-opacity","0")},redrawEventRect(){var t;const e=this,{config:n,state:a,$el:i}=e,o=e.isMultipleX(),s=n.axis_x_inverted;if(i.eventRect)e.updateEventRect(i.eventRect,!0);else if(e.data.targets.length){const c=e.$el.main.select(`.${Zn.eventRects}`).style("cursor",n.zoom_enabled&&n.zoom_type!=="drag"?n.axis_rotated?"ns-resize":"ew-resize":null).classed(Zn.eventRectsMultiple,o).classed(Zn.eventRectsSingle,!o).selectAll(`.${Zn.eventRect}`).data([0]).enter().append("rect");e.updateEventRect(c),e.updateEventType(c),c.call(e.getDraggableSelection()),i.eventRect=c,e.state.inputType==="touch"&&!i.svg.on("touchstart.eventRect")&&!e.hasArcType()&&e.bindTouchOnEventRect(),a.rendered&&e.updateEventRect(i.eventRect,!0)}if(!o){const l=e.getMaxDataCountTarget();(!n.data_xSort||s)&&l.sort((c,f)=>s?f.x-c.x:c.x-f.x),e.updateDataIndexByX(l),e.updateXs(l),(t=e.updatePointClass)==null||t.call(e,!0),a.eventReceiver.data=l}e.updateEventRectData()},bindTouchOnEventRect(){const t=this,{config:e,state:n,$el:{eventRect:a,svg:i}}=t,o=m=>{if(t.isMultipleX())t.selectRectForMultipleXs(m);else{const S=t.getDataIndexFromEvent(n.event);t.callOverOutForTouch(S),S===-1?t.unselectRect():t.selectRectForSingle(m,S)}},s=()=>{t.unselectRect(),t.callOverOutForTouch()},l=e.interaction_inputType_touch.preventDefault,c=Co(l)&&l||!1,f=!isNaN(l)&&l||null;let g;const v=m=>{const S=m.type,N=m.changedTouches[0][`client${e.axis_rotated?"Y":"X"}`];S==="touchstart"?c?m.preventDefault():f!==null&&(g=N):S==="touchmove"&&(c||g===!0||f!==null&&Math.abs(g-N)>=f)&&(g=!0,m.preventDefault())};a.on("touchstart",m=>{n.event=m,t.updateEventRect()}).on("touchstart.eventRect touchmove.eventRect",m=>{if(n.event=m,!a.empty()&&a.classed(Zn.eventRect)){if(n.dragging||n.flowing||t.hasArcType()||m.touches.length>1)return;v(m),o(a.node())}else s()},!0).on("touchend.eventRect",m=>{n.event=m,!a.empty()&&a.classed(Zn.eventRect)&&(t.hasArcType()||!t.toggleShape||n.cancelClick)&&n.cancelClick&&(n.cancelClick=!1)},!0),i.on("touchstart",m=>{n.event=m;const{target:S}=m;S&&S!==a.node()&&s()})},updateEventRect(t,e=!1){const n=this,{state:a,$el:i}=n,{eventReceiver:o,width:s,height:l,rendered:c,resizing:f}=a,g=t||i.eventRect,v=()=>{if(o){const m=Zl(i.chart.node());o.rect=g.node().getBoundingClientRect().toJSON(),o.rect.top+=m.y,o.rect.left+=m.x}};(!c||f||e)&&(g.attr("x",0).attr("y",0).attr("width",s).attr("height",l),(!c||e)&&g.classed(Zn.eventRect,!0)),v()},updateEventType(t){const e=this,n=Co(t),a=n?e.$el.eventRect:t,i=n?t!==(a==null?void 0:a.datum().multipleX):!1;a&&(i&&(a==null||a.on("mouseover mousemove mouseout click",null)),e.isMultipleX()?e.generateEventRectsForMultipleXs(a):e.generateEventRectsForSingleX(a))},updateEventRectData(){const t=this,{config:e,scale:n,state:a}=t,i=n.zoom||n.x,o=e.axis_rotated,s=t.isMultipleX();let l,c,f,g;if(t.updateEventType(s),s)l=0,c=0,f=a.width,g=a.height;else{let S,P;if(t.axis.isCategorized())S=t.getEventRectWidth(),P=N=>i(N.x)-S/2;else{const N=({index:L})=>({prev:t.getPrevX(L),next:t.getNextX(L)});S=L=>{const w=N(L),X=i.domain();let W;return w.prev===null&&w.next===null?W=o?a.height:a.width:w.prev===null?W=(i(w.next)+i(L.x))/2:w.next===null?W=i(X[1])-(i(w.prev)+i(L.x))/2:(Object.keys(w).forEach((H,k)=>{var K;w[H]=(K=w[H])!=null?K:X[k]}),W=Math.max(0,(i(w.next)-i(w.prev))/2)),W},P=L=>{const w=N(L);let X;return w.prev===null&&w.next===null?X=0:w.prev===null?X=i(i.domain()[0]):X=(i(L.x)+i(w.prev))/2,X}}l=o?0:P,c=o?P:0,f=o?a.width:S,g=o?S:a.height}const{eventReceiver:v}=a,m=(S,P)=>ve(S)?S(P):S;v.coords.splice(v.data.length),v.data.forEach((S,P)=>{v.coords[P]={x:m(l,S),y:m(c,S),w:m(f,S),h:m(g,S)}})},selectRectForSingle(t,e){var n,a;const i=this,{config:o,$el:{main:s,circle:l}}=i,c=o.data_selection_enabled,f=o.data_selection_grouped,g=o.data_selection_isselectable,v=o.tooltip_grouped,m=i.getAllValuesOnIndex(e);if(v&&(i.showTooltip(m,t),(n=i.showGridFocus)==null||n.call(i,m),!c||f))return;!l&&s.selectAll(`.${Se.EXPANDED}:not(.${sn.shape}-${e})`).classed(Se.EXPANDED,!1);const S=s.selectAll(`.${sn.shape}-${e}`).classed(Se.EXPANDED,!0).style("cursor",g?"pointer":null).filter(function(P){return i.isWithinShape(this,P)});S.empty()&&!v&&o.interaction_onout&&((a=i.hideGridFocus)==null||a.call(i),i.hideTooltip(),!f&&i.setExpand(e)),S.call(P=>{var N,L;const w=P.data();c&&(f||g!=null&&g.bind(i.api)(w))&&(t.style.cursor="pointer"),v||(i.showTooltip(w,t),(N=i.showGridFocus)==null||N.call(i,w),(L=i.unexpandCircles)==null||L.call(i),P.each(X=>i.setExpand(e,X.id)))})},selectRectForMultipleXs(t,e=!0){const n=this,{config:a,state:i}=n,o=n.filterTargetsToShow(n.data.targets);if(i.dragging||n.hasArcType(o))return;const s=Hn(i.event,t),l=n.findClosestFromTargets(o,s);if(e&&i.mouseover&&(!l||l.id!==i.mouseover.id)&&(a.data_onout.call(n.api,i.mouseover),i.mouseover=void 0),!l){n.unselectRect();return}const f=(n.isBubbleType(l)||n.isScatterType(l)||!a.tooltip_grouped?[l]:n.filterByX(o,l.x)).map(v=>n.addName(v));n.showTooltip(f,t),n.setExpand(l.index,l.id,!0),n.showGridFocus(f);const g=n.dist(l,s);(n.isBarType(l.id)||g{const c=l?e.getDataIndexFromEvent(l):i.currentIdx;return c>-1?i.data[c]:null};o.on("mouseover",l=>{a.event=l,e.updateEventRect(),Object.values(e.$el.axisTooltip).forEach(c=>c==null?void 0:c.style("display",null))}).on("mousemove",function(l){const c=s(l);if(a.event=l,!c)return;let{index:f}=c;const g=n.line_step_type;if(n.line_step_tooltipMatch&&e.hasType("step")&&/^step\-(before|after)$/.test(g)){const m=e.scale.zoom||e.scale.x,S=e.axis.xs[f],P=m.invert(Hn(l,this)[0]);g==="step-after"&&PS&&(f+=1)}e.showAxisGridFocus();const v=n.tooltip_grouped&&f===i.currentIdx;if(a.dragging||a.flowing||e.hasArcType()||v){n.tooltip_show&&v&&e.setTooltipPosition();return}f!==i.currentIdx&&(e.setOverOut(!1,i.currentIdx),i.currentIdx=f),f===-1?e.unselectRect():e.selectRectForSingle(this,f),e.setOverOut(f!==-1,f)}).on("mouseout",l=>{a.event=l,!(!n||e.hasArcType()||i.currentIdx===-1||!n.interaction_onout)&&(e.hideAxisGridFocus(),e.unselectRect(),e.setOverOut(!1,i.currentIdx),i.currentIdx=-1)})}return o},clickHandlerForSingleX(t,e){const n=e,{config:a,state:i,$el:{main:o}}=n;if(!t||n.hasArcType()||i.cancelClick){i.cancelClick&&(i.cancelClick=!1);return}const{index:s}=t;o.selectAll(`.${sn.shape}-${s}`).each(function(l){var c;(a.data_selection_grouped||n.isWithinShape(this,l))&&((c=n.toggleShape)==null||c.call(n,this,l,s),a.data_onclick.bind(n.api)(l,this))})},generateEventRectsForMultipleXs(t){const e=this,{config:n,state:a}=e;t.on("click",function(i){a.event=i,e.clickHandlerForMultipleXS.bind(this)(e)}).datum({multipleX:!0}),a.inputType==="mouse"&&t.on("mouseover mousemove",function(i){a.event=i,e.selectRectForMultipleXs(this)}).on("mouseout",i=>{a.event=i,!(!e.config||e.hasArcType()||!n.interaction_onout)&&e.unselectRect()})},clickHandlerForMultipleXS(t){const e=t,{config:n,state:a}=e,i=e.filterTargetsToShow(e.data.targets);if(e.hasArcType(i))return;const o=Hn(a.event,this),s=e.findClosestFromTargets(i,o),l=e.getPointSensitivity(s);s&&(e.isBarType(s.id)||e.dist(s,o)+t;var Dy={generateFlow(t){const e=this,{data:n,state:a,$el:i}=e;return function(){const o=t.flow.length;a.flowing=!0,n.targets.forEach(l=>{l.values.splice(0,o)}),e.updateXGrid&&e.updateXGrid(!0);const s={};["axis.x","grid.x","gridLines.x","region.list","text","bar","line","area","circle"].forEach(l=>{const c=l.split(".");let f=i[c[0]];f&&c.length>1&&(f=f[c[1]]),f!=null&&f.size()&&(s[l]=f)}),e.hideGridFocus(),e.setFlowList(s,t)}},setFlowList(t,e){const n=this,{flow:a,targets:i}=e,{duration:o=e.duration,index:s,length:l,orgDataCount:c}=a,f=n.getFlowTransform(i,c,s,l),g=ec();let v;g.add(Object.keys(t).map(m=>(v=t[m].transition().ease(My).duration(o),m==="axis.x"?v=v.call(S=>{n.axis.x.setTransition(S).create(S)}):m==="region.list"?v=v.filter(n.isRegionOnX).attr("transform",f):v=v.attr("transform",f),v))),v.call(g,()=>{n.cleanUpFlow(t,e)})},cleanUpFlow(t,e){const n=this,{config:a,state:i,$el:{svg:o}}=n,s=a.axis_rotated,{flow:l,shape:c,xv:f}=e,{cx:g,cy:v,xForText:m,yForText:S}=c.pos,{done:P=()=>{},length:N}=l;N&&(["circle","text","shape","eventRect"].forEach(L=>{const w=[];for(let X=0;X{const w=t[L];if(L!=="axis.x"&&w.attr("transform",null),L==="grid.x")w.attr(i.xgridAttr);else if(L==="gridLines.x")w.attr("x1",s?0:f).attr("x2",s?i.width:f),w.select("text").attr("x",s?i.width:0).attr("y",f);else if(/^(area|bar|line)$/.test(L))w.attr("d",c.type[L]);else if(L==="text")w.attr("x",m).attr("y",S).style("fill-opacity",n.opacityForText.bind(n));else if(L==="circle")if(n.isCirclePoint())w.attr("cx",g).attr("cy",v);else{const X=H=>g(H)-a.point_r,W=H=>v(H)-a.point_r;w.attr("x",X).attr("y",W)}else L==="region.list"&&w.select("rect").filter(n.isRegionOnX).attr("x",n.regionX.bind(n)).attr("width",n.regionWidth.bind(n))}),a.interaction_enabled&&n.redrawEventRect(),P.call(n.api),i.flowing=!1},getFlowTransform(t,e,n,a){const i=this,{data:o,scale:{x:s}}=i,l=o.targets[0].values;let c=i.getValueOnIndex(l,n),f=i.getValueOnIndex(l,n+a),g;const v=s.domain(),m=i.updateXDomain(t,!0,!0);e?e===1||(c==null?void 0:c.x)===(f==null?void 0:f.x)?g=s(v[0])-s(m[0]):g=i.axis.isTimeSeries()?s(v[0])-s(m[0]):s((c==null?void 0:c.x)||0)-s(f.x):l.length!==1?g=s(v[0])-s(m[0]):i.axis.isTimeSeries()?(c=i.getValueOnIndex(l,0),f=i.getValueOnIndex(l,l.length-1),g=s(c.x)-s(f.x)):g=Dr(m)/2;const S=Dr(v)/Dr(m);return`translate(${g},0) scale(${S},1)`}},Ly={initClip(){const t=this,{clip:e,datetimeId:n}=t.state;e.id=`${n}-clip`,e.idXAxis=`${e.id}-xaxis`,e.idYAxis=`${e.id}-yaxis`,e.idGrid=`${e.id}-grid`,e.path=t.getClipPath(e.id),e.pathXAxis=t.getClipPath(e.idXAxis),e.pathYAxis=t.getClipPath(e.idYAxis),e.pathGrid=t.getClipPath(e.idGrid)},getClipPath(t){const e=this,{config:n}=e;return!n.clipPath&&/-clip$/.test(t)||!n.axis_x_clipPath&&/-clip-xaxis$/.test(t)||!n.axis_y_clipPath&&/-clip-yaxis$/.test(t)?null:`url(#${t})`},appendClip(t,e){e&&t.append("clipPath").attr("id",e).append("rect")},setXAxisClipPath(t){const e=this,{config:n,state:{margin:a,width:i,height:o}}=e,s=n.axis_rotated,l=Math.max(30,a.left)-(s?0:20),c=(s?a.top+o+10:a.bottom)+20,f=s?-(1+l):-(l-1),g=-15,v=s?a.left+20:i+10+l;t.attr("x",f).attr("y",g).attr("width",v).attr("height",c)},setYAxisClipPath(t){const e=this,{config:n,state:{margin:a,width:i,height:o}}=e,s=n.axis_rotated,l=Math.max(30,a.left)-(s?20:0),c=n.axis_y_inner,f=c&&!s?n.axis_y_label.text?-20:-1:s?-(1+l):-(l-1),g=-(s?20:a.top),v=(s?i+15+l:a.left+20)+(c?20:0),m=(s?a.bottom+10:a.top+o)+10;t.attr("x",f).attr("y",g).attr("width",v).attr("height",m)},updateXAxisTickClip(){const t=this,{config:e,state:{clip:n,xAxisHeight:a},$el:{defs:i}}=t,o=t.getHorizontalAxisHeight("x");if(i&&!n.idXAxisTickTexts){const s=`${n.id}-xaxisticktexts`;t.appendClip(i,s),n.pathXAxisTickTexts=t.getClipPath(n.idXAxisTickTexts),n.idXAxisTickTexts=s}!e.axis_x_tick_multiline&&t.getAxisTickRotate("x")&&o!==a&&(t.setXAxisTickClipWidth(),t.setXAxisTickTextClipPathWidth()),t.state.xAxisHeight=o},setXAxisTickClipWidth(){const t=this,{config:e,state:{current:{maxTickSize:n}}}=t,a=t.getAxisTickRotate("x");if(!e.axis_x_tick_multiline&&a){const i=Math.sin(Math.PI/180*Math.abs(a));n.x.clipPath=(t.getHorizontalAxisHeight("x")-20)/i}else n.x.clipPath=null},setXAxisTickTextClipPathWidth(){const t=this,{state:{clip:e,current:n},$el:{svg:a}}=t;a&&a.select(`#${e.idXAxisTickTexts} rect`).attr("width",n.maxTickSize.x.clipPath).attr("height",30)}};const Ny=t=>De(t.position)||"end",Fy=t=>t.position==="start"?4:t.position==="middle"?0:-4;function Cu(t,e,n){return a=>{let i=t?0:e;return a.position==="start"?i=t?-n:0:a.position==="middle"&&(i=(t?-n:e)/2),i}}function Pu(t,e){e==="grid"&&t.each(function(){const n=ot(this);["x1","x2","y1","y2"].forEach(a=>n.attr(a,+n.attr(a)))})}var By={hasGrid(){const{config:t}=this;return["x","y"].some(e=>t[`grid_${e}_show`]||t[`grid_${e}_lines`].length)},initGrid(){const t=this;t.hasGrid()&&t.initGridLines(),t.initFocusGrid()},initGridLines(){const t=this,{config:e,state:{clip:n},$el:a}=t;(e.grid_x_lines.length||e.grid_y_lines.length)&&(a.gridLines.main=a.main.insert("g",`.${Se.chart}${e.grid_lines_front?" + *":""}`).attr("clip-path",n.pathGrid).attr("class",`${on.grid} ${on.gridLines}`),a.gridLines.main.append("g").attr("class",on.xgridLines),a.gridLines.main.append("g").attr("class",on.ygridLines),a.gridLines.x=Uc([]))},updateXGrid(t){const e=this,{config:n,scale:a,state:i,$el:{main:o,grid:s}}=e,l=n.axis_rotated,c=e.generateGridData(n.grid_x_type,a.x),f=e.axis.isCategorized()?e.axis.x.tickOffset():0,g=v=>(a.zoom||a.x)(v)+f*(l?-1:1);i.xgridAttr=l?{x1:0,x2:i.width,y1:g,y2:g}:{x1:g,x2:g,y1:0,y2:i.height},s.x=o.select(`.${on.xgrids}`).selectAll(`.${on.xgrid}`).data(c),s.x.exit().remove(),s.x=s.x.enter().append("line").attr("class",on.xgrid).merge(s.x),t||s.x.each(function(){const v=ot(this);Object.keys(i.xgridAttr).forEach(m=>{v.attr(m,i.xgridAttr[m]).style("opacity",()=>v.attr(l?"y1":"x1")===(l?i.height:0)?"0":null)})})},updateYGrid(){const t=this,{axis:e,config:n,scale:a,state:i,$el:{grid:o,main:s}}=t,l=n.axis_rotated,c=g=>a.y(g),f=e.y.getGeneratedTicks(n.grid_y_ticks)||t.scale.y.ticks(n.grid_y_ticks);o.y=s.select(`.${on.ygrids}`).selectAll(`.${on.ygrid}`).data(f),o.y.exit().remove(),o.y=o.y.enter().append("line").attr("class",on.ygrid).merge(o.y),o.y.attr("x1",l?c:0).attr("x2",l?c:i.width).attr("y1",l?0:c).attr("y2",l?i.height:c),Pu(o.y,"grid")},updateGrid(){const t=this,{$el:{grid:e,gridLines:n}}=t;!n.main&&t.initGridLines(),e.main.style("visibility",t.hasArcType()?"hidden":null),t.hideGridFocus(),t.updateGridLines("x"),t.updateGridLines("y")},updateGridLines(t){const e=this,{config:n,$el:{gridLines:a,main:i},$T:o}=e,s=n.axis_rotated,l=t==="x";n[`grid_${t}_show`]&&e[`update${t.toUpperCase()}Grid`]();let c=i.select(`.${on[`${t}gridLines`]}`).selectAll(`.${on[`${t}gridLine`]}`).data(n[`grid_${t}_lines`]);o(c.exit()).style("opacity","0").remove();const f=c.enter().append("g");f.append("line").style("opacity","0"),c=f.merge(c),c.each(function(g){const v=ot(this);v.select("text").empty()&&g.text&&v.append("text").style("opacity","0")}),o(c.attr("class",g=>`${on[`${t}gridLine`]} ${g.class||""}`.trim()).select("text").attr("text-anchor",Ny).attr("transform",()=>l?s?null:"rotate(-90)":s?"rotate(-90)":null).attr("dx",Fy).attr("dy",-5)).text(function(g){var v;return(v=g.text)!=null?v:this.remove()}),a[t]=c},redrawGrid(t){const e=this,{config:{axis_rotated:n},state:{width:a,height:i},$el:{gridLines:o},$T:s}=e,l=e.xv.bind(e),c=e.yv.bind(e);let f=o.x.select("line"),g=o.x.select("text"),v=o.y.select("line"),m=o.y.select("text");return f=s(f,t).attr("x1",n?0:l).attr("x2",n?a:l).attr("y1",n?l:0).attr("y2",n?l:i),g=s(g,t).attr("x",Cu(!n,a,i)).attr("y",l),v=s(v,t).attr("x1",n?c:0).attr("x2",n?c:a).attr("y1",n?0:c).attr("y2",n?i:c),m=s(m,t).attr("x",Cu(n,a,i)).attr("y",c),[f.style("opacity",null),g.style("opacity",null),v.style("opacity",null),m.style("opacity",null)]},initFocusGrid(){const t=this,{config:e,state:{clip:n},$el:a}=t,i=e.grid_front,o=`.${i&&a.gridLines.main?on.gridLines:Se.chart}${i?" + *":""}`,s=a.main.insert("g",o).attr("clip-path",n.pathGrid).attr("class",on.grid);if(a.grid.main=s,e.grid_x_show&&s.append("g").attr("class",on.xgrids),e.grid_y_show&&s.append("g").attr("class",on.ygrids),e.axis_tooltip){const l=s.append("g").attr("class","bb-axis-tooltip");l.append("line").attr("class","bb-axis-tooltip-x"),l.append("line").attr("class","bb-axis-tooltip-y")}e.interaction_enabled&&e.grid_focus_show&&!e.axis_tooltip&&(s.append("g").attr("class",qe.xgridFocus).append("line").attr("class",qe.xgridFocus),e.grid_focus_y&&!e.tooltip_grouped&&s.append("g").attr("class",qe.ygridFocus).append("line").attr("class",qe.ygridFocus))},showAxisGridFocus(){var t,e;const n=this,{config:a,format:i,state:{event:o,width:s,height:l}}=n,c=a.axis_rotated,[f,g]=Hn(o,(t=n.$el.eventRect)==null?void 0:t.node()),v={x:f,y:g};for(const[m,S]of Object.entries(n.$el.axisTooltip)){const P=m==="x"&&!c||m!=="x"&&c?"x":"y",N=v[P];let L=(e=n.scale[m])==null?void 0:e.invert(N);L&&(L=m==="x"&&n.axis.isTimeSeries()?i.xAxisTick(L):L==null?void 0:L.toFixed(2),S==null||S.attr(P,N).text(L))}n.$el.main.selectAll("line.bb-axis-tooltip-x, line.bb-axis-tooltip-y").style("visibility",null).each(function(m,S){const P=ot(this);S===0?P.attr("x1",f).attr("x2",f).attr("y1",S?0:l).attr("y2",S?l:0):P.attr("x1",S?0:s).attr("x2",S?s:0).attr("y1",g).attr("y2",g)})},hideAxisGridFocus(){const t=this;t.$el.main.selectAll("line.bb-axis-tooltip-x, line.bb-axis-tooltip-y").style("visibility","hidden"),Object.values(t.$el.axisTooltip).forEach(e=>e==null?void 0:e.style("display","none"))},showGridFocus(t){var e;const n=this,{config:a,state:{width:i,height:o}}=n,s=a.axis_rotated,l=n.$el.main.selectAll(`line.${qe.xgridFocus}, line.${qe.ygridFocus}`),c=(t||[l.datum()]).filter(v=>v&&De(n.getBaseValue(v)));if(!a.tooltip_show||c.length===0||!a.axis_x_forceAsSingle&&n.hasType("bubble")||n.hasArcType())return;const f=a.grid_focus_edge&&!a.tooltip_grouped,g=n.xx.bind(n);l.style("visibility",null).data(c.concat(c)).each(function(v){const m=ot(this),S={x:g(v),y:n.getYScaleById(v.id)(v.value)};let P;if(m.classed(qe.xgridFocus))P=s?[null,S.x,f?S.y:i,S.x]:[S.x,f?S.y:null,S.x,o];else{const N=n.axis.getId(v.id)==="y2";P=s?[S.y,f&&!N?S.x:null,S.y,f&&N?S.x:o]:[f&&N?S.x:null,S.y,f&&!N?S.x:i,S.y]}["x1","y1","x2","y2"].forEach((N,L)=>m.attr(N,P[L]))}),Pu(l,"grid"),(e=n.showCircleFocus)==null||e.call(n,t)},hideGridFocus(){var t;const e=this,{state:{inputType:n,resizing:a},$el:{main:i}}=e;(n==="mouse"||!a)&&(i.selectAll(`line.${qe.xgridFocus}, line.${qe.ygridFocus}`).style("visibility","hidden"),(t=e.hideCircleFocus)==null||t.call(e))},updateGridFocus(){var t;const e=this,{state:{inputType:n,width:a,height:i,resizing:o},$el:{grid:s}}=e,l=s.main.select(`line.${qe.xgridFocus}`);if(n==="touch")l.empty()?o&&((t=e.showCircleFocus)==null||t.call(e)):e.showGridFocus();else{const c=e.config.axis_rotated;l.attr("x1",c?0:-10).attr("x2",c?a:-10).attr("y1",c?-10:0).attr("y2",c?-10:i)}return!0},generateGridData(t,e){const n=this,a=n.$el.main.select(`.${Tn.axisX}`).selectAll(".tick").size();let i=[];if(t==="year"){const o=n.getXDomain(),[s,l]=o.map(c=>c.getFullYear());for(let c=s;c<=l;c++)i.push(new Date(`${c}-01-01 00:00:00`))}else i=e.ticks(10),i.length>a&&(i=i.filter(o=>String(o).indexOf(".")<0));return i},getGridFilterToRemove(t){return t?e=>{let n=!1;return(je(t)?t.concat():[t]).forEach(a=>{("value"in a&&e.value===a.value||"class"in a&&e.class===a.class)&&(n=!0)}),n}:()=>!0},removeGridLines(t,e){const n=this,{config:a,$T:i}=n,o=n.getGridFilterToRemove(t),s=g=>!o(g),l=e?on.xgridLines:on.ygridLines,c=e?on.xgridLine:on.ygridLine;i(n.$el.main.select(`.${l}`).selectAll(`.${c}`).filter(o)).style("opacity","0").remove();const f=`grid_${e?"x":"y"}_lines`;a[f]=a[f].filter(s)}},Uy={initRegion(){const t=this,{$el:e}=t;e.region.main=e.main.insert("g",":first-child").attr("clip-path",t.state.clip.path).attr("class",$a.regions)},updateRegion(){const t=this,{config:e,$el:{region:n},$T:a}=t;n.main||t.initRegion(),n.main.style("visibility",t.hasArcType()?"hidden":null);const i=n.main.selectAll(`.${$a.region}`).data(e.regions);a(i.exit()).style("opacity","0").remove();const o=i.enter().append("g");o.append("rect").style("fill-opacity","0"),n.list=o.merge(i).attr("class",t.classRegion.bind(t)),n.list.each(function(s){var l;ot(this).select("text").empty()&&((l=s.label)!=null&&l.text)&&ot(this).append("text").style("opacity","0")})},redrawRegion(t){const e=this,{$el:{region:n},$T:a}=e,i=e.regionX.bind(e),o=e.regionY.bind(e),s=["width","height"];let l=n.list.select("rect"),c=n.list.selectAll("text");return l=a(l,t).attr("x",i).attr("y",o).attr("width",e.regionWidth.bind(e)).attr("height",e.regionHeight.bind(e)),c=a(c,t).text(f=>{var g;return(g=f.label)==null?void 0:g.text}).attr("transform",({label:f})=>f.rotated?" rotate(-90)":null).attr("transform",function(f){var g;const{x:v=0,y:m=0,center:S=!1,rotated:P=!1}=(g=f.label)!=null?g:{},N=this.previousElementSibling,L={x:0,y:0};return ze(S)&&["x","y"].forEach((w,X)=>{S.indexOf(w)>-1&&(L[w]=(+N.getAttribute(s[X])-Ma(this)[s[X]])/2)}),`translate(${i(f)+L.x+v}, ${o(f)+L.y+m})${P?" rotate(-90)":""}`}).attr("text-anchor",({label:f})=>f!=null&&f.rotated?"end":null).attr("dy","1em").style("fill",({label:f})=>{var g;return(g=f==null?void 0:f.color)!=null?g:null}),[l.style("fill-opacity",f=>De(f.opacity)?f.opacity:null).on("end",function(){ot(this.parentNode).selectAll("rect:not([x])").remove()}),c.style("opacity",null)]},regionX(t){return this.getRegionSize("x",t)},regionY(t){return this.getRegionSize("y",t)},regionWidth(t){return this.getRegionSize("width",t)},regionHeight(t){return this.getRegionSize("height",t)},getRegionSize(t,e){const n=this,{config:a,scale:i,state:o}=n,s=a.axis_rotated,l=/(x|y|y2)/.test(t),c=l?t==="x":t==="width",f=!l&&n[c?"regionX":"regionY"](e);let g=l?"start":"end",v=l?0:o[t],m;if(e.axis==="y"||e.axis==="y2"?(!l&&!c?g="start":l&&!c&&(g="end"),(c?s:!s)&&g in e&&(m=i[e.axis])):(c?!s:s)&&g in e&&(m=i.zoom||i.x),m){let S=0;v=e[g],n.axis.isTimeSeries(e.axis)?v=Yn.call(n,v):/(x|width)/.test(t)&&n.axis.isCategorized()&&isNaN(v)&&(v=a.axis_x_categories.indexOf(v),S=n.axis.x.tickOffset()*(g==="start"?-1:1)),v=m(v)+S}return l?v:v0&&(!i.axis_x_tick_autorotate||a.needToRotateXAxisTickTexts());return(i.axis_x_tick_multiline||L)&&N.height>S&&(P+=N.height-S),P+(a.axis.getLabelPositionById(t).isInner?0:10)+(t==="y2"&&!f?-10:0)},getEventRectWidth(){const t=this,{config:e,axis:n}=t,a=e.axis_x_inverted,i=n.x.tickInterval();return Math.max(0,a?Math.abs(i):i)},getAxisTickRotate(t){const e=this,{axis:n,config:a,state:i,$el:o}=e;let s=a[`axis_${t}_tick_rotate`];if(t==="x"){const l=n.isCategorized()||n.isTimeSeries();if(a.axis_x_tick_fit&&l){const c=a.axis_x_tick_count,f=i.current.maxTickSize.x.ticks.length;let g=0;if(c?g=c>f?f:c:f&&(g=f),g!==i.axis.x.tickCount){const{targets:v}=e.data;i.axis.x.padding=e.getXDomainPadding([e.getXDomainMinMax(v,"min"),e.getXDomainMinMax(v,"max")],g)}i.axis.x.tickCount=g}o.svg&&a.axis_x_tick_autorotate&&a.axis_x_tick_fit&&!a.axis_x_tick_multiline&&!a.axis_x_tick_culling&&l&&(s=e.needToRotateXAxisTickTexts()?a.axis_x_tick_rotate:0)}return s},needToRotateXAxisTickTexts(){const t=this,{state:{axis:e,current:n,isLegendRight:a,legendItemWidth:i}}=t,o=a&&i,s=n.width-o-t.getCurrentPaddingByDirection("left")-t.getCurrentPaddingByDirection("right"),l=e.x.tickCount+e.x.padding.left+e.x.padding.right,{width:c}=t.axis.getMaxTickSize("x"),f=l?s/l:0;return c>f}},jy={axis_x_clipPath:!0,axis_x_show:!0,axis_x_forceAsSingle:!1,axis_x_type:"indexed",axis_x_localtime:!0,axis_x_categories:[],axis_x_tick_centered:!1,axis_x_tick_format:void 0,axis_x_tick_culling:{},axis_x_tick_culling_max:10,axis_x_tick_culling_lines:!0,axis_x_tick_count:void 0,axis_x_tick_show:!0,axis_x_tick_text_show:!0,axis_x_tick_text_inner:!1,axis_x_tick_text_position:{x:0,y:0},axis_x_tick_fit:!0,axis_x_tick_values:null,axis_x_tick_autorotate:!1,axis_x_tick_rotate:0,axis_x_tick_outer:!0,axis_x_tick_multiline:!0,axis_x_tick_width:null,axis_x_tick_tooltip:!1,axis_x_max:void 0,axis_x_min:void 0,axis_x_inverted:!1,axis_x_padding:{},axis_x_height:void 0,axis_x_extent:void 0,axis_x_label:{},axis_x_axes:[]},Vy={axis_y_clipPath:!0,axis_y_show:!0,axis_y_type:"indexed",axis_y_max:void 0,axis_y_min:void 0,axis_y_inverted:!1,axis_y_center:void 0,axis_y_inner:!1,axis_y_label:{},axis_y_tick_format:void 0,axis_y_tick_culling:!1,axis_y_tick_culling_max:5,axis_y_tick_culling_lines:!0,axis_y_tick_outer:!0,axis_y_tick_values:null,axis_y_tick_rotate:0,axis_y_tick_count:void 0,axis_y_tick_show:!0,axis_y_tick_stepSize:null,axis_y_tick_text_show:!0,axis_y_tick_text_position:{x:0,y:0},axis_y_tick_time_value:void 0,axis_y_padding:{},axis_y_default:void 0,axis_y_axes:[]},Gy={axis_y2_show:!1,axis_y2_type:"indexed",axis_y2_max:void 0,axis_y2_min:void 0,axis_y2_inverted:!1,axis_y2_center:void 0,axis_y2_inner:!1,axis_y2_label:{},axis_y2_tick_format:void 0,axis_y2_tick_culling:!1,axis_y2_tick_culling_max:5,axis_y2_tick_culling_lines:!0,axis_y2_tick_outer:!0,axis_y2_tick_values:null,axis_y2_tick_rotate:0,axis_y2_tick_count:void 0,axis_y2_tick_show:!0,axis_y2_tick_stepSize:null,axis_y2_tick_text_show:!0,axis_y2_tick_text_position:{x:0,y:0},axis_y2_padding:{},axis_y2_default:void 0,axis_y2_axes:[]},Xy=Object.defineProperty,wu=Object.getOwnPropertySymbols,Hy=Object.prototype.hasOwnProperty,Yy=Object.prototype.propertyIsEnumerable,Mu=(t,e,n)=>e in t?Xy(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Ts=(t,e)=>{for(var n in e||(e={}))Hy.call(e,n)&&Mu(t,n,e[n]);if(wu)for(var n of wu(e))Yy.call(e,n)&&Mu(t,n,e[n]);return t},Wy=Ts(Ts(Ts({axis_evalTextSize:!0,axis_rotated:!1,axis_tooltip:!1},jy),Vy),Gy),Ky={grid_x_show:!1,grid_x_type:"tick",grid_x_lines:[],grid_y_show:!1,grid_y_lines:[],grid_y_ticks:void 0,grid_focus_edge:!1,grid_focus_show:!0,grid_focus_y:!1,grid_front:!1,grid_lines_front:!0},Zy={data_xs:{},data_xFormat:"%Y-%m-%d",data_xLocaltime:!0,data_xSort:!0,data_axes:{},data_regions:{},data_stack_normalize:!1};const Jy=[sy,ly,cy,uy,fy,dy,hy],Du={axis:Cy,clip:Ly,eventrect:wy,flow:Dy,grid:By,region:Uy,sizeAxis:zy},Lu={optDataAxis:Zy,optAxis:Wy,optGrid:Ky};var I1=Array.prototype.slice;function $s(t){return typeof t=="object"&&"length"in t?t:Array.from(t)}function Le(t){return function(){return t}}function Qy(t,e){return et?1:e>=t?0:NaN}function ky(t){return t}function qy(){var t=ky,e=Qy,n=null,a=Le(0),i=Le(zi),o=Le(0);function s(l){var c,f=(l=$s(l)).length,g,v,m=0,S=new Array(f),P=new Array(f),N=+a.apply(this,arguments),L=Math.min(zi,Math.max(-zi,i.apply(this,arguments)-N)),w,X=Math.min(Math.abs(L)/f,o.apply(this,arguments)),W=X*(L<0?-1:1),H;for(c=0;c0&&(m+=H);for(e!=null?S.sort(function(k,K){return e(P[k],P[K])}):n!=null&&S.sort(function(k,K){return n(l[k],l[K])}),c=0,v=m?(L-f*W)/m:0;c0?H*v:0)+W,P[g]={data:l[g],index:c,value:H,startAngle:N,endAngle:w,padAngle:X};return P}return s.value=function(l){return arguments.length?(t=typeof l=="function"?l:Le(+l),s):t},s.sortValues=function(l){return arguments.length?(e=l,n=null,s):e},s.sort=function(l){return arguments.length?(n=l,e=null,s):n},s.startAngle=function(l){return arguments.length?(a=typeof l=="function"?l:Le(+l),s):a},s.endAngle=function(l){return arguments.length?(i=typeof l=="function"?l:Le(+l),s):i},s.padAngle=function(l){return arguments.length?(o=typeof l=="function"?l:Le(+l),s):o},s}var _y=Math.pow;const Ss=Math.PI,As=2*Ss,Gr=1e-6,tx=As-Gr;function Nu(t){this._+=t[0];for(let e=1,n=t.length;e=0))throw new Error(`invalid digits: ${t}`);if(e>15)return Nu;const n=_y(10,e);return function(a){this._+=a[0];for(let i=1,o=a.length;iGr)if(!(Math.abs(v*c-f*g)>Gr)||!o)this._append`L${this._x1=e},${this._y1=n}`;else{let S=a-s,P=i-l,N=c*c+f*f,L=S*S+P*P,w=Math.sqrt(N),X=Math.sqrt(m),W=o*Math.tan((Ss-Math.acos((N+m-L)/(2*w*X)))/2),H=W/X,k=W/w;Math.abs(H-1)>Gr&&this._append`L${e+H*g},${n+H*v}`,this._append`A${o},${o},0,0,${+(v*S>g*P)},${this._x1=e+k*c},${this._y1=n+k*f}`}}arc(e,n,a,i,o,s){if(e=+e,n=+n,a=+a,s=!!s,a<0)throw new Error(`negative radius: ${a}`);let l=a*Math.cos(i),c=a*Math.sin(i),f=e+l,g=n+c,v=1^s,m=s?i-o:o-i;this._x1===null?this._append`M${f},${g}`:(Math.abs(this._x1-f)>Gr||Math.abs(this._y1-g)>Gr)&&this._append`L${f},${g}`,a&&(m<0&&(m=m%As+As),m>tx?this._append`A${a},${a},0,1,${v},${e-l},${n-c}A${a},${a},0,1,${v},${this._x1=f},${this._y1=g}`:m>Gr&&this._append`A${a},${a},0,${+(m>=Ss)},${v},${this._x1=e+a*Math.cos(o)},${this._y1=n+a*Math.sin(o)}`)}rect(e,n,a,i){this._append`M${this._x0=this._x1=+e},${this._y0=this._y1=+n}h${a=+a}v${+i}h${-a}Z`}toString(){return this._}}function nx(){return new Wi}nx.prototype=Wi.prototype;function O1(t=3){return new Wi(+t)}function Es(t){let e=3;return t.digits=function(n){if(!arguments.length)return e;if(n==null)e=null;else{const a=Math.floor(n);if(!(a>=0))throw new RangeError(`invalid digits: ${n}`);e=a}return t},()=>new Wi(e)}function rx(t){return t.innerRadius}function ax(t){return t.outerRadius}function ix(t){return t.startAngle}function ox(t){return t.endAngle}function sx(t){return t&&t.padAngle}function lx(t,e,n,a,i,o,s,l){var c=n-t,f=a-e,g=s-i,v=l-o,m=v*c-g*f;if(!(m*mQ*Q+St*St&&(ht=dt,$t=st),{cx:ht,cy:$t,x01:-g,y01:-v,x11:ht*(i/k-1),y11:$t*(i/k-1)}}function Fu(){var t=rx,e=ax,n=Le(0),a=null,i=ix,o=ox,s=sx,l=null,c=Es(f);function f(){var g,v,m=+t.apply(this,arguments),S=+e.apply(this,arguments),P=i.apply(this,arguments)-Ui,N=o.apply(this,arguments)-Ui,L=Hc(N-P),w=N>P;if(l||(l=g=c()),Sbn))l.moveTo(0,0);else if(L>zi-bn)l.moveTo(S*jr(P),S*rr(P)),l.arc(0,0,S,P,N,!w),m>bn&&(l.moveTo(m*jr(N),m*rr(N)),l.arc(0,0,m,N,P,w));else{var X=P,W=N,H=P,k=N,K=L,at=L,ht=s.apply(this,arguments)/2,$t=ht>bn&&(a?+a.apply(this,arguments):oa(m*m+S*S)),dt=fs(Hc(S-m)/2,+n.apply(this,arguments)),st=dt,Vt=dt,vt,Q;if($t>bn){var St=Yc($t/m*rr(ht)),ct=Yc($t/S*rr(ht));(K-=St*2)>bn?(St*=w?1:-1,H+=St,k-=St):(K=0,H=k=(P+N)/2),(at-=ct*2)>bn?(ct*=w?1:-1,X+=ct,W-=ct):(at=0,X=W=(P+N)/2)}var At=S*jr(X),Gt=S*rr(X),Bt=m*jr(k),Kt=m*rr(k);if(dt>bn){var ne=S*jr(W),le=S*rr(W),be=m*jr(H),Oe=m*rr(H),Ce;if(Lbn?Vt>bn?(vt=Ki(be,Oe,At,Gt,S,Vt,w),Q=Ki(ne,le,Bt,Kt,S,Vt,w),l.moveTo(vt.cx+vt.x01,vt.cy+vt.y01),Vtbn)||!(K>bn)?l.lineTo(Bt,Kt):st>bn?(vt=Ki(Bt,Kt,ne,le,m,-st,w),Q=Ki(At,Gt,be,Oe,m,-st,w),l.lineTo(vt.cx+vt.x01,vt.cy+vt.y01),ste in t?cx(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,gx=(t,e)=>{for(var n in e||(e={}))dx.call(e,n)&&Uu(t,n,e[n]);if(Bu)for(var n of Bu(e))hx.call(e,n)&&Uu(t,n,e[n]);return t},vx=(t,e)=>ux(t,fx(e));function zu(t=0){const e=this,{config:n,state:a}=e,i=e.hasMultiArcGauge(),o=a.gaugeArcWidth/e.filterTargetsToShow(e.data.targets).length,s=t?Math.min(a.radiusExpanded*t-a.radius,o*.8-(1-t)*100):0;return{inner(l){const{innerRadius:c}=e.getRadius(l);return i?a.radius-o*(l.index+1):he(c)?c:0},outer(l){const{outerRadius:c}=e.getRadius(l);let f;if(i)f=a.radius-o*l.index+s;else if(e.hasType("polar")&&!t)f=e.getPolarOuterRadius(l,c);else if(f=c,t){let{radiusExpanded:g}=a;a.radius!==c&&(g-=Math.abs(a.radius-c)),f=g*t}return f},corner(l,c){const{arc_cornerRadius_ratio:f=0,arc_cornerRadius:g=0}=n,{data:{id:v},value:m}=l;let S=0;return f?S=f*c:S=he(g)?g:g.call(e.api,v,m,c),S}}}function bs(t){return function(e){const n=({startAngle:i=0,endAngle:o=0,padAngle:s=0})=>({startAngle:i,endAngle:o,padAngle:s}),a=Qr(n(this._current),n(e));return this._current=e,function(i){const o=a(i),{data:s,index:l,value:c}=e;return t(vx(gx({},o),{data:s,index:l,value:c}))}}}var px={initPie(){const t=this,{config:e}=t,n=e.data_type,a=e[`${n}_padding`],i=e[`${n}_startingAngle`]||0,o=(a?a*.01:e[`${n}_padAngle`])||0;t.pie=qy().startAngle(i).endAngle(i+2*Math.PI).padAngle(o).value(s=>{var l,c;return(c=(l=s.values)==null?void 0:l.reduce((f,g)=>f+g.value,0))!=null?c:s}).sort(t.getSortCompareFn.bind(t)(!0))},updateRadius(){const t=this,{config:e,state:n}=t,a=e.data_type,i=e[`${a}_padding`],o=e.gauge_width||e.donut_width,s=t.filterTargetsToShow(t.data.targets).length*e.gauge_arcs_minWidth;n.radiusExpanded=Math.min(n.arcWidth,n.arcHeight)/2*(t.hasMultiArcGauge()&&e.gauge_label_show?.85:1),n.radius=n.radiusExpanded*.95,n.innerRadiusRatio=o?(n.radius-o)/n.radius:.6,n.gaugeArcWidth=o||(s<=n.radius-n.innerRadius?n.radius-n.innerRadius:s<=n.radius?s:n.radius);const l=e.pie_innerRadius||(i?i*(n.innerRadiusRatio+.1):0);n.outerRadius=e.pie_outerRadius,n.innerRadius=t.hasType("donut")||t.hasType("gauge")?n.radius*n.innerRadiusRatio:l},getRadius(t){const e=this,n=t==null?void 0:t.data;let{innerRadius:a,outerRadius:i}=e.state;return!he(a)&&n&&(a=a[n.id]||0),Be(i)&&n&&n.id in i?i=i[n.id]:he(i)||(i=e.state.radius),{innerRadius:a,outerRadius:i}},updateArc(){const t=this;t.updateRadius(),t.svgArc=t.getSvgArc(),t.svgArcExpanded=t.getSvgArcExpanded()},getArcLength(){const t=this,{config:e}=t,n=e.gauge_arcLength*3.6;let a=2*(n/360);return n<-360?a=-2:n>360&&(a=2),a*Math.PI},getStartingAngle(){const t=this,{config:e}=t,n=e.data_type,a=t.hasType("gauge")?e.gauge_fullCircle:!1,i=-1*Math.PI/2,o=Math.PI/2;let s=e[`${n}_startingAngle`]||0;return!a&&s<=i?s=i:!a&&s>=o?s=o:(s>Math.PI||s<-1*Math.PI)&&(s=Math.PI),s},updateAngle(t,e=!1){var n;const a=this,{config:i,state:o}=a,s=e&&a.hasType("gauge");let{pie:l}=a,c=t,f=!1;if(!i)return null;const g=a.getStartingAngle(),v=i.gauge_fullCircle||e&&!s?a.getArcLength():g*-2;if(c.data&&a.isGaugeType(c.data)&&!a.hasMultiArcGauge()){const{gauge_min:m,gauge_max:S}=i,P=a.getTotalDataSum(o.rendered),N=v*((P-m)/(S-m));l=l.startAngle(g).endAngle(N+g)}if(e===!1&&l(a.filterTargetsToShow()).forEach((m,S)=>{var P;!f&&m.data.id===((P=c.data)==null?void 0:P.id)&&(f=!0,c=m,c.index=S)}),isNaN(c.startAngle)&&(c.startAngle=0),isNaN(c.endAngle)&&(c.endAngle=c.startAngle),e||c.data&&(i.gauge_enforceMinMax||a.hasMultiArcGauge())){const{gauge_min:m,gauge_max:S}=i,P=e&&!s?a.getTotalDataSum(o.rendered):S,N=v/(P-m),L=(n=c.value)!=null?n:0,w=L{const l=e.updateAngle(s),c=a(l);let f=0;return l&&(f=i(l,c)),l?o.cornerRadius(f)(l):"M 0 0"}},getArc(t,e,n){return n||this.isArcType(t.data)?this.svgArc(t,e):"M 0 0"},redrawArcRangeText(){const t=this,{config:e,$el:{arcs:n},state:a,$T:i}=t,o=e.arc_rangeText_format,s=t.hasType("gauge")&&e.arc_rangeText_fixed;let l=e.arc_rangeText_values;if(l!=null&&l.length){const c=e.arc_rangeText_unit==="%",f=t.getTotalDataSum(a.rendered);c&&(l=l.map(m=>f/100*m));const g=t.pie(l).map((m,S)=>(m.index=S,m));let v=n.selectAll(`.${Ve.arcRange}`).data(l);v.exit(),v=i(v.enter().append("text").attr("class",Ve.arcRange).style("text-anchor","middle").style("pointer-events","none").style("opacity","0").text(m=>{const S=c?m/f*100:m;return ve(o)?o(S):`${S}${c?"%":""}`}).merge(v)),(!a.rendered||a.rendered&&!s)&&f>0&&v.attr("transform",(m,S)=>t.transformForArcLabel(g[S],!0)),v.style("opacity",m=>!s&&(m>f||f===0)?"0":null)}},transformForArcLabel(t,e=!1){var n,a,i;const o=this,{config:s,state:{radiusExpanded:l}}=o,c=o.updateAngle(t,e);let f="";if(c){if(e||o.hasMultiArcGauge()){const g=Math.sin(c.endAngle-Math.PI/2),v=s.arc_rangeText_position;let m=Math.cos(c.endAngle-Math.PI/2)*(l+(e?5:25)),S=g*(l+15-Math.abs(g*10))+3;if(e&&v){const P=s.arc_rangeText_values,N=ve(v)?v(P[t.index]):v;m+=(n=N==null?void 0:N.x)!=null?n:0,S+=(a=N==null?void 0:N.y)!=null?a:0}f=`translate(${m},${S})`}else if(!o.hasType("gauge")||o.data.targets.length>1){let{outerRadius:g}=o.getRadius(t);o.hasType("polar")&&(g=o.getPolarOuterRadius(t,g));const v=this.svgArc.centroid(c),[m,S]=v.map(L=>isNaN(L)?0:L),P=Math.sqrt(m*m+S*S);let N=(i=["donut","gauge","pie","polar"].filter(o.hasType.bind(o)).map(L=>s[`${L}_label_ratio`]))==null?void 0:i[0];N?N=ve(N)?N.bind(o.api)(t,g,P):N:N=g&&(P?(36/g>.375?1.175-36/g:.8)*g/P:0),f=`translate(${m*N},${S*N})`}}return f},convertToArcData(t){return this.addName({id:"data"in t?t.data.id:t.id,value:t.value,ratio:this.getRatio("arc",t),index:t.index})},textForArcLabel(t){const e=this,n=e.hasType("gauge");e.shouldShowArcLabel()&&t.style("fill",e.updateTextColor.bind(e)).attr("filter",a=>e.updateTextBGColor.bind(e)(a,e.config.data_labels_backgroundColors)).each(function(a){var i;const o=ot(this),s=e.updateAngle(a),l=e.getRatio("arc",s);if(e.meetsLabelThreshold(l,(i=["donut","gauge","pie","polar"].filter(e.hasType.bind(e)))==null?void 0:i[0])){const{value:f}=s||a,g=(e.getArcLabelFormat()||e.defaultArcValueFormat)(f,l,a.data.id).toString();wa(o,g,[-1,1],n)}else o.text("")})},expandArc(t){const e=this,{state:{transiting:n},$el:a}=e;if(n){const o=setInterval(()=>{n||(clearInterval(o),a.legend.selectAll(`.${qe.legendItemFocused}`).size()>0&&e.expandArc(t))},10);return}const i=e.mapToTargetIds(t);a.svg.selectAll(e.selectorTargets(i,`.${Ve.chartArc}`)).each(function(o){if(!e.shouldExpand(o.data.id))return;const s=e.getExpandConfig(o.data.id,"duration"),l=e.getSvgArcExpanded(e.getExpandConfig(o.data.id,"rate"));ot(this).selectAll("path").transition().duration(s).attrTween("d",bs(e.svgArcExpanded.bind(e))).transition().duration(s*2).attrTween("d",bs(l.bind(e)))})},unexpandArc(t){const e=this,{state:{transiting:n},$el:{svg:a}}=e;if(n)return;const i=e.mapToTargetIds(t);a.selectAll(e.selectorTargets(i,`.${Ve.chartArc}`)).selectAll("path").transition().duration(o=>e.getExpandConfig(o.data.id,"duration")).attrTween("d",bs(e.svgArc.bind(e))),a.selectAll(`${Ve.arc}`).style("opacity",null)},getExpandConfig(t,e){const n=this,{config:a}=n,i={duration:50,rate:.98};let o;return n.isDonutType(t)?o="donut":n.isGaugeType(t)?o="gauge":n.isPieType(t)&&(o="pie"),o?a[`${o}_expand_${e}`]:i[e]},shouldExpand(t){const e=this,{config:n}=e;return e.isDonutType(t)&&n.donut_expand||e.isGaugeType(t)&&n.gauge_expand||e.isPieType(t)&&n.pie_expand},shouldShowArcLabel(){const t=this,{config:e}=t;return["donut","gauge","pie","polar"].some(n=>t.hasType(n)&&e[`${n}_label_show`])},getArcLabelFormat(){const t=this,{config:e}=t;let n=a=>a;return["donut","gauge","pie","polar"].filter(t.hasType.bind(t)).forEach(a=>{n=e[`${a}_label_format`]}),ve(n)?n.bind(t.api):n},updateTargetsForArc(t){const e=this,{$el:n}=e,a=e.hasType("gauge"),i=e.getChartClass("Arc"),o=e.getClass("arcs",!0),s=e.classFocus.bind(e),l=n.main.select(`.${Ve.chartArcs}`),c=l.selectAll(`.${Ve.chartArc}`).data(e.pie(t)).attr("class",g=>i(g)+s(g.data)),f=c.enter().append("g").attr("class",i).call(this.setCssRule(!1,`.${Ve.chartArcs} text`,["pointer-events:none","text-anchor:middle"]));f.append("g").attr("class",o).merge(c),f.append("text").attr("dy",a&&!e.hasMultiTargets()?"-.1em":".35em").style("opacity","0").style("text-anchor",e.getStylePropValue("middle")).style("pointer-events",e.getStylePropValue("none")),n.text=l.selectAll(`.${Se.target} text`)},initArc(){const t=this,{$el:e}=t;e.arcs=e.main.select(`.${Se.chart}`).append("g").attr("class",Ve.chartArcs).attr("transform",t.getTranslate("arc")),t.setArcTitle()},setArcTitle(t){const e=this,n=t||e.getArcTitle(),a=e.hasType("gauge");if(n){const i=a?Un.chartArcsGaugeTitle:Ve.chartArcsTitle;let o=e.$el.arcs.select(`.${i}`);o.empty()&&(o=e.$el.arcs.append("text").attr("class",i).style("text-anchor","middle")),a&&o.attr("dy","-0.3em"),wa(o,n,a?void 0:[-.6,1.35],!0)}},getArcTitle(){const t=this,e=t.hasType("donut")&&"donut"||t.hasType("gauge")&&"gauge";return e?t.config[`${e}_title`]:""},getArcTitleWithNeedleValue(){const t=this,{config:e,state:n}=t,a=t.getArcTitle();if(a&&t.config.arc_needle_show&&/{=[A-Z_]+}/.test(a)){let i=n.current.needle;return he(i)||(i=e.arc_needle_value),bi(a,{NEEDLE_VALUE:he(i)?i:0})}return!1},redrawArc(t,e,n){const a=this,{config:i,state:o,$el:{main:s}}=a,l=i.interaction_enabled,c=l&&i.data_selection_isselectable;let f=s.selectAll(`.${Ve.arcs}`).selectAll(`.${Ve.arc}`).data(a.arcData.bind(a));f.exit().transition().duration(e).style("opacity","0").remove(),f=f.enter().append("path").attr("class",a.getClass("arc",!0)).style("fill",g=>a.color(g.data)).style("cursor",g=>{var v;return(v=c==null?void 0:c.bind)!=null&&v.call(c,a.api)(g)?"pointer":null}).style("opacity","0").each(function(g){a.isGaugeType(g.data)&&(g.startAngle=i.gauge_startingAngle,g.endAngle=i.gauge_startingAngle),this._current=g}).merge(f),a.hasType("gauge")&&(a.updateGaugeMax(),a.hasMultiArcGauge()&&a.redrawArcGaugeLine()),f.attr("transform",g=>!a.isGaugeType(g.data)&&n?"scale(0)":"").style("opacity",function(g){return g===this._current?"0":null}).each(()=>{o.transiting=!0}).transition().duration(t).attrTween("d",function(g){const v=a.updateAngle(g);if(!v)return()=>"M 0 0";isNaN(this._current.startAngle)&&(this._current.startAngle=0),isNaN(this._current.endAngle)&&(this._current.endAngle=this._current.startAngle);const m=Qr(this._current,v);return this._current=m(0),function(S){const P=m(S);return P.data=g.data,a.getArc(P,!0)}}).attr("transform",n?"scale(1)":"").style("fill",g=>{let v;return a.levelColor?(v=a.levelColor(g.data.values[0].value),i.data_colors[g.data.id]=v):v=a.color(g.data),v}).style("opacity",null).call(Si,function(){if(a.levelColor){const g=ot(this),v=g.datum(this._current);a.updateLegendItemColor(v.data.id,g.style("fill"))}o.transiting=!1,_e(i.onrendered,a.api)}),l&&a.bindArcEvent(f),a.hasType("polar")&&a.redrawPolar(),a.hasType("gauge")&&a.redrawBackgroundArcs(),i.arc_needle_show&&a.redrawNeedle(),a.redrawArcText(t),a.redrawArcRangeText()},redrawNeedle(){const t=this,{$el:e,config:n,state:{hiddenTargetIds:a,radius:i}}=t,o=(i-1)/100*n.arc_needle_length,s=a.length!==t.data.targets.length;let l=t.$el.arcs.select(`.${Ve.needle}`);const c=n.arc_needle_path,f=n.arc_needle_bottom_width/2,g=n.arc_needle_top_width/2,v=n.arc_needle_top_rx,m=n.arc_needle_top_ry,S=n.arc_needle_bottom_len,P=n.arc_needle_bottom_rx,N=n.arc_needle_bottom_ry,L=t.getNeedleAngle(),w=()=>{const X=t.getArcTitleWithNeedleValue();X&&t.setArcTitle(X)};if(w(),l.empty()&&(l=e.arcs.append("path").classed(Ve.needle,!0),e.needle=l,e.needle.updateHelper=(X,W=!1)=>{e.needle.style("display")!=="none"&&t.$T(e.needle).style("transform",`rotate(${t.getNeedleAngle(X)}deg)`).call(Si,()=>{W&&(n.arc_needle_value=X),w()})}),s){const X=ve(c)?c.call(t,o):`M-${f} ${S} A${P} ${N} 0 0 0 ${f} ${S} L${g} -${o} A${v} ${m} 0 0 0 -${g} -${o} L-${f} ${S} Z`;t.$T(l).attr("d",X).style("fill",n.arc_needle_color).style("display",null).style("transform",`rotate(${L}deg)`)}else l.style("display","none")},getNeedleAngle(t){const e=this,{config:n,state:a}=e,i=e.getArcLength(),o=e.hasType("gauge"),s=e.getTotalDataSum(!0);let l=Qe(t)?t:n.arc_needle_value,c=n[`${n.data_type}_startingAngle`]||0,f=0;if(he(l)||(l=o&&e.data.targets.length===1?s:0),a.current.needle=l,o){c=e.getStartingAngle();const g=n.gauge_fullCircle?i:c*-2,{gauge_min:v,gauge_max:m}=n;f=g*((l-v)/(m-v))}else f=i*(l/s);return(c+f)*(180/Math.PI)},redrawBackgroundArcs(){const t=this,{config:e,state:n}=t,a=t.hasMultiArcGauge(),i=e.gauge_fullCircle,o=t.filterTargetsToShow(t.data.targets).length===0&&!!e.data_empty_label_text,s=t.getStartingAngle(),l=i?s+t.getArcLength():s*-1;let c=t.$el.arcs.select(`${a?"g":""}.${Ve.chartArcsBackground}`);if(a){let f=0;c=c.selectAll(`path.${Ve.chartArcsBackground}`).data(t.data.targets),c.enter().append("path").attr("class",(g,v)=>`${Ve.chartArcsBackground} ${Ve.chartArcsBackground}-${v}`).merge(c).style("fill",e.gauge_background||null).attr("d",({id:g})=>{if(o||n.hiddenTargetIds.indexOf(g)>=0)return"M 0 0";const v={data:[{value:e.gauge_max}],startAngle:s,endAngle:l,index:f++};return t.getArc(v,!0,!0)}),c.exit().remove()}else c.attr("d",o?"M 0 0":()=>{const f={data:[{value:e.gauge_max}],startAngle:s,endAngle:l};return t.getArc(f,!0,!0)})},bindArcEvent(t){const e=this,{config:n,state:a}=e,i=a.inputType==="touch",o=a.inputType==="mouse";function s(c,f,g){e.expandArc(g),e.api.focus(g),e.toggleFocusLegend(g,!0),e.showTooltip([f],c)}function l(c){const f=(c==null?void 0:c.id)||void 0;e.unexpandArc(f),e.api.revert(),e.revertLegend(),e.hideTooltip()}if(t.on("click",function(c,f,g){var v;const m=e.updateAngle(f);let S;m&&(S=e.convertToArcData(m),(v=e.toggleShape)==null||v.call(e,this,S,g),n.data_onclick.bind(e.api)(S,this))}),o&&t.on("mouseover",function(c,f){if(a.transiting)return;a.event=c;const g=e.updateAngle(f),v=g?e.convertToArcData(g):null,m=(v==null?void 0:v.id)||void 0;s(this,v,m),e.setOverOut(!0,v)}).on("mouseout",(c,f)=>{if(a.transiting||!n.interaction_onout)return;a.event=c;const g=e.updateAngle(f),v=g?e.convertToArcData(g):null;l(),e.setOverOut(!1,v)}).on("mousemove",function(c,f){const g=e.updateAngle(f),v=g?e.convertToArcData(g):null;a.event=c,e.showTooltip([v],this)}),i&&e.hasArcType()&&!e.radars){const c=f=>{var g,v;const{clientX:m,clientY:S}=(v=(g=f.changedTouches)==null?void 0:g[0])!=null?v:{clientX:0,clientY:0};return ot(gn.elementFromPoint(m,S))};e.$el.svg.on("touchstart touchmove",function(f){if(a.transiting)return;a.event=f;const v=c(f).datum(),m=v!=null&&v.data&&v.data.id?e.updateAngle(v):null,S=m?e.convertToArcData(m):null,P=(S==null?void 0:S.id)||void 0;e.callOverOutForTouch(S),ln(P)?l():s(this,S,P)})}},redrawArcText(t){const e=this,{config:n,state:a,$el:{main:i,arcs:o}}=e,s=e.hasType("gauge"),l=e.hasMultiArcGauge();let c;if(s&&e.data.targets.length===1&&n.gauge_title||(c=i.selectAll(`.${Ve.chartArc}`).select("text").style("opacity","0").attr("class",f=>e.isGaugeType(f.data)?Un.gaugeValue:null).call(e.textForArcLabel.bind(e)).attr("transform",f=>e.transformForArcLabel.bind(e)(f)).style("font-size",f=>e.isGaugeType(f.data)&&e.data.targets.length===1&&!l?`${Math.round(a.radius/5)}px`:null).transition().duration(t).style("opacity",f=>e.isTargetToShow(f.data.id)&&e.isArcType(f.data)?null:"0"),l&&c.attr("dy","-.1em")),i.select(`.${Ve.chartArcsTitle}`).style("opacity",e.hasType("donut")||s?null:"0"),s){const f=n.gauge_fullCircle;f&&(c==null||c.attr("dy",`${l?0:Math.round(a.radius/14)}`)),n.gauge_label_show&&(o.select(`.${Un.chartArcsGaugeUnit}`).attr("dy",`${f?1.5:.75}em`).text(n.gauge_units),o.select(`.${Un.chartArcsGaugeMin}`).attr("dx",`${-1*(a.innerRadius+(a.radius-a.innerRadius)/(f?1:2))}px`).attr("dy","1.2em").text(e.textForGaugeMinMax(n.gauge_min,!1)),!f&&o.select(`.${Un.chartArcsGaugeMax}`).attr("dx",`${a.innerRadius+(a.radius-a.innerRadius)/2}px`).attr("dy","1.2em").text(e.textForGaugeMinMax(n.gauge_max,!0)))}},getArcElementByIdOrIndex(t){const e=this,{$el:{arcs:n}}=e,a=he(t)?i=>i.index===t:i=>i.data.id===t;return n==null?void 0:n.selectAll(`.${Se.target} path`).filter(a)}};function ju(t){return t[0]}function Vu(t){return t[1]}function Gu(t,e){var n=Le(!0),a=null,i=gs,o=null,s=Es(l);t=typeof t=="function"?t:t===void 0?ju:Le(t),e=typeof e=="function"?e:e===void 0?Vu:Le(e);function l(c){var f,g=(c=$s(c)).length,v,m=!1,S;for(a==null&&(o=i(S=s())),f=0;f<=g;++f)!(f=S;--P)l.point(W[P],H[P]);l.lineEnd(),l.areaEnd()}w&&(W[m]=+t(L,m,v),H[m]=+e(L,m,v),l.point(a?+a(L,m,v):W[m],n?+n(L,m,v):H[m]))}if(X)return l=null,X+""||null}function g(){return Gu().defined(i).curve(s).context(o)}return f.x=function(v){return arguments.length?(t=typeof v=="function"?v:Le(+v),a=null,f):t},f.x0=function(v){return arguments.length?(t=typeof v=="function"?v:Le(+v),f):t},f.x1=function(v){return arguments.length?(a=v==null?null:typeof v=="function"?v:Le(+v),f):a},f.y=function(v){return arguments.length?(e=typeof v=="function"?v:Le(+v),n=null,f):e},f.y0=function(v){return arguments.length?(e=typeof v=="function"?v:Le(+v),f):e},f.y1=function(v){return arguments.length?(n=v==null?null:typeof v=="function"?v:Le(+v),f):n},f.lineX0=f.lineY0=function(){return g().x(t).y(e)},f.lineY1=function(){return g().x(t).y(n)},f.lineX1=function(){return g().x(a).y(e)},f.defined=function(v){return arguments.length?(i=typeof v=="function"?v:Le(!!v),f):i},f.curve=function(v){return arguments.length?(s=v,o!=null&&(l=s(o)),f):s},f.context=function(v){return arguments.length?(v==null?o=l=null:l=s(o=v),f):o},f}var sa={initArea(t){const e=this,{config:n}=e;t.insert("g",`.${n.area_front?$n.circles:ur.lines}`).attr("class",e.getClass("areas",!0))},updateAreaColor(t){const e=this;return e.config.area_linearGradient?e.getGradienColortUrl(t.id):e.color(t)},updateArea(t,e=!1){const n=this,{config:a,state:i,$el:o,$T:s}=n,l=e?o.subchart:o;a.area_linearGradient&&n.updateLinearGradient();const c=l.main.selectAll(`.${ti.areas}`).selectAll(`.${ti.area}`).data(n.lineData.bind(n));s(c.exit(),t).style("opacity","0").remove(),l.area=c.enter().append("path").attr("class",n.getClass("area",!0)).style("fill",n.updateAreaColor.bind(n)).style("opacity",function(){return i.orgAreaOpacity=ot(this).style("opacity"),"0"}).merge(c),c.style("opacity",i.orgAreaOpacity),n.setRatioForGroupedData(l.area.data())},redrawArea(t,e,n=!1){const a=this,{area:i}=n?this.$el.subchart:this.$el,{orgAreaOpacity:o}=a.state;return[a.$T(i,e,gr()).attr("d",t).style("fill",a.updateAreaColor.bind(a)).style("opacity",s=>String(a.isAreaRangeType(s)?o/1.75:o))]},generateDrawArea(t,e){const n=this,{config:a}=n,i=a.line_connectNull,o=a.axis_rotated,s=n.generateGetAreaPoints(t,e),l=n.getYScaleById.bind(n),c=v=>(e?n.subxx:n.xx).call(n,v),f=(v,m)=>n.isGrouped(v.id)?s(v,m)[0][1]:l(v.id,e)(n.isAreaRangeType(v)?n.getRangedData(v,"high"):n.getShapeYMin(v.id)),g=(v,m)=>n.isGrouped(v.id)?s(v,m)[1][1]:l(v.id,e)(n.isAreaRangeType(v)?n.getRangedData(v,"low"):v.value);return v=>{let m=i?n.filterRemoveNull(v.values):v.values,S=0,P=0,N;if(n.isAreaType(v)){let L=mx();L=o?L.y(c).x0(f).x1(g):L.x(c).y0(a.area_above?0:a.area_below?n.state.height:f).y1(g),i||(L=L.defined(w=>n.getBaseValue(w)!==null)),n.isStepType(v)&&(m=n.convertValuesToStep(m)),N=L.curve(n.getCurve(v))(m)}else m[0]&&(S=n.scale.x(m[0].x),P=n.getYScaleById(v.id)(m[0].value)),N=o?`M ${P} ${S}`:`M ${S} ${P}`;return N||"M 0 0"}},generateGetAreaPoints(t,e){const n=this,{config:a}=n,i=n.getShapeX(0,t,e),o=n.getShapeY(!!e),s=n.getShapeOffset(n.isAreaType,t,e),l=n.getYScaleById.bind(n);return function(c,f){const g=l.call(n,c.id,e)(n.getShapeYMin(c.id)),v=s(c,f)||g,m=i(c),S=c.value;let P=o(c);return a.axis_rotated&&(S>0&&Pg.values.some(v=>he(v.value)||e.isBarRangeType(v)))).attr("class",g=>i(g)+s(g)).enter().append("g").attr("class",i).style("opacity","0").style("pointer-events",e.getStylePropValue("none")).append("g").attr("class",o).style("cursor",g=>{var v;return(v=l==null?void 0:l.bind)!=null&&v.call(l,e.api)(g)?"pointer":null}).call(e.setCssRule(!0,` .${Kn.bar}`,["fill"],e.color))},updateBar(t,e=!1){const n=this,{config:a,$el:i,$T:o}=n,s=e?i.subchart:i,l=n.getClass("bar",!0),c=n.initialOpacity.bind(n);a.bar_linearGradient&&n.updateLinearGradient();const f=s.main.selectAll(`.${Kn.bars}`).selectAll(`.${Kn.bar}`).data(n.labelishData.bind(n));o(f.exit(),t).style("opacity","0").remove(),s.bar=f.enter().append("path").attr("class",l).style("fill",n.updateBarColor.bind(n)).merge(f).style("opacity",c),n.setRatioForGroupedData(s.bar.data())},updateBarColor(t){const e=this,n=e.getStylePropValue(e.color);return e.config.bar_linearGradient?e.getGradienColortUrl(t.id):n?n(t):null},redrawBar(t,e,n=!1){const a=this,{bar:i}=n?a.$el.subchart:a.$el;return[a.$T(i,e,gr()).attr("d",o=>(he(o.value)||a.isBarRangeType(o))&&t(o)).style("fill",a.updateBarColor.bind(a)).style("clip-path",o=>o.clipPath).style("opacity",null)]},generateDrawBar(t,e){const n=this,{config:a}=n,i=n.generateGetBarPoints(t,e),o=a.axis_rotated,s=a.bar_radius,l=a.bar_radius_ratio,c=he(s)&&s>0?()=>s:he(l)?f=>f*l:null;return(f,g)=>{const v=i(f,g),m=+o,S=+!m,P=f.value<0,N=a[`axis_${n.axis.getId(f.id)}_inverted`],L=!N&&P||N&&!P,w=["",""],X=n.isGrouped(f.id),W=c&&X?n.isStackingRadiusData(f):!1,H=[v[0][m],v[0][S]];let k=0;if(f.clipPath=null,c){const ht=o?S:m,$t=v[2][ht]-v[0][ht];k=!X||W?c($t):0;const dt=`a${k} ${k} ${L?"1 0 0":"0 0 1"} `;w[+!o]=`${dt}${k},${k}`,w[+o]=`${dt}${[-k,k][o?"sort":"reverse"]()}`,L&&w.reverse()}const K=o?v[1][m]+(L?k:-k):v[1][S]+(L?-k:k);if(k){let ht="";o?L&&H[0]K&&(ht=`0 0 0 ${H[0]-K}px`):L&&H[1]>K?ht=`${H[1]-K}px 0 0 0`:!L&&H[1]-1){const m=n.bar.filter(S=>S.id===s&&S.value===c);return!m.empty()&&/a\d+/i.test(m.attr("d"))}const f=a.data_groups.find(m=>m.indexOf(s)>-1),v=e.orderTargets(e.filterTargetsToShow(i.targets.filter(e.isBarType,e))).filter(m=>f.indexOf(m.id)>-1).map(m=>m.values.filter(S=>S.index===l&&(he(c)&&c>0?S.value>0:S.value<0))[0]).filter(Boolean).map(m=>m.id);return c!==0&&v.indexOf(s)===v.length-1},generateGetBarPoints(t,e){const n=this,{config:a}=n,i=e?n.axis.subX:n.axis.x,o=n.getIndicesMax(t)+1,s=n.getBarW("bar",i,o),l=n.getShapeX(s,t,!!e),c=n.getShapeY(!!e),f=n.getShapeOffset(n.isBarType,t,!!e),g=n.getYScaleById.bind(n);return(v,m)=>{const{id:S}=v,P=g.call(n,S,e)(n.getShapeYMin(S)),N=f(v,m)||P,L=he(s)?s:s[v.id]||s._$width,w=a[`axis_${n.axis.getId(S)}_inverted`],X=v.value,W=l(v);let H=c(v);a.axis_rotated&&!w&&(X>0&&He.isBubbleZType(s)?e.getBubbleZData(s.value,"y"):Be(s.value)?s.value.mid:s.value)),i=n*n*Math.PI,o=(e.isBubbleZType(t)?e.getBubbleZData(t.value,"z"):t.value)*(i/a);return Math.sqrt(o/Math.PI)},getBubbleZData(t,e){return Be(t)?t[e]:t[e==="y"?0:1]}},Tx=Object.defineProperty,Xu=Object.getOwnPropertySymbols,$x=Object.prototype.hasOwnProperty,Sx=Object.prototype.propertyIsEnumerable,Hu=(t,e,n)=>e in t?Tx(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Ax=(t,e)=>{for(var n in e||(e={}))$x.call(e,n)&&Hu(t,n,e[n]);if(Xu)for(var n of Xu(e))Sx.call(e,n)&&Hu(t,n,e[n]);return t},Ex={initCandlestick(){const{$el:t}=this;t.candlestick=t.main.select(`.${Se.chart}`).append("g").attr("class",cr.chartCandlesticks)},updateTargetsForCandlestick(t){const e=this,{$el:n}=e,a=e.getChartClass("Candlestick");n.candlestick||e.initCandlestick(),e.$el.main.select(`.${cr.chartCandlesticks}`).selectAll(`.${cr.chartCandlestick}`).data(t).enter().append("g").attr("class",a).style("pointer-events","none")},updateCandlestick(t,e=!1){const n=this,{$el:a,$T:i}=n,o=e?a.subchart:a,s=n.getClass("candlestick",!0),l=n.initialOpacity.bind(n),c=o.main.selectAll(`.${cr.chartCandlestick}`).selectAll(`.${cr.candlestick}`).data(n.labelishData.bind(n));i(c.exit(),t).style("opacity","0").remove();const f=c.enter().filter(g=>g.value).append("g").attr("class",s);f.append("line"),f.append("path"),o.candlestick=c.merge(f).style("opacity",l)},generateDrawCandlestick(t,e){const n=this,{config:a}=n,i=n.generateGetCandlestickPoints(t,e),o=a.axis_rotated,s=a.candlestick_color_down;return(l,c,f)=>{const g=i(l,c),v=n.getCandlestickData(l),m=v==null?void 0:v._isUp,S=+o,P=+!S;f.classed&&f.classed(cr[m?"valueUp":"valueDown"],!0);const N=o?`H${g[1][1]} V${g[1][0]} H${g[0][1]}`:`V${g[1][1]} H${g[1][0]} V${g[0][1]}`;f.select("path").attr("d",`M${g[0][S]},${g[0][P]}${N}z`).style("fill",X=>(m?n.color(X):Be(s)?s[X.id]:s)||n.color(X));const L=f.select("line"),w=o?{x1:g[2][1],x2:g[2][2],y1:g[2][0],y2:g[2][0]}:{x1:g[2][0],x2:g[2][0],y1:g[2][1],y2:g[2][2]};for(const X in w)L.attr(X,w[X])}},generateGetCandlestickPoints(t,e=!1){const n=this,a=e?n.axis.subX:n.axis.x,i=n.getIndicesMax(t)+1,o=n.getBarW("candlestick",a,i),s=n.getShapeX(o,t,!!e),l=n.getShapeY(!!e),c=n.getShapeOffset(n.isBarType,t,!!e),f=n.getYScaleById.bind(n);return(g,v)=>{const m=f.call(n,g.id,e)(n.getShapeYMin(g.id)),S=c(g,v)||m,P=he(o)?o:o[g.id]||o._$width,N=n.getCandlestickData(g);let L;if(N&&he(N.open)&&he(N.close)){const w={start:s(g),end:0};w.end=w.start+P;const X={start:l(N.open),end:l(N.close)},W={x:w.start+P/2,high:l(N.high),low:l(N.low)};X.start-=m-S,L=[[w.start,X.start],[w.end,X.end],[W.x,W.low,W.high]]}else L=[[0,0],[0,0],[0,0,0]];return L}},redrawCandlestick(t,e,n=!1){const a=this,{$el:i,$T:o}=a,{candlestick:s}=n?i.subchart:i,l=gr(!0);return[s.each(function(c,f){const g=o(ot(this),e,l);t(c,f,g)}).style("opacity",null)]},getCandlestickData({value:t}){let e;if(je(t)){const[n,a,i,o,s=!1]=t;e={open:n,high:a,low:i,close:o},s!==!1&&(e.volume=s)}else Be(t)&&(e=Ax({},t));return e&&(e._isUp=e.close>=e.open),e||null}},bx=Object.defineProperty,Yu=Object.getOwnPropertySymbols,Rx=Object.prototype.hasOwnProperty,Ix=Object.prototype.propertyIsEnumerable,Wu=(t,e,n)=>e in t?bx(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Ox=(t,e)=>{for(var n in e||(e={}))Rx.call(e,n)&&Wu(t,n,e[n]);if(Yu)for(var n of Yu(e))Ix.call(e,n)&&Wu(t,n,e[n]);return t};function Zi(t=!1){const e=this,{config:n,state:{current:{width:a,height:i}}}=e,o=e.getCurrentPadding(),s=Ox({width:a-(o.left+o.right),height:i-(n.legend_show?e.getLegendHeight()+10:0)-(o.top+o.bottom)},o);if(t){const{width:l,height:c}=Ku.call(e,{width:s.width,height:s.height});s.width{let l=o;return Be(o)&&(l=t[s?"height":"width"]*o.ratio),l}),{width:a,height:i}}function Cx(t){const e=this,{top:n,left:a,width:i}=Zi.call(e,!0),o=[];return t.forEach((s,l)=>{const{ratio:c}=s,f=l>0?o[l-1][2][1]:n;o.push(s.coords=[[a,f],[a+i,f],[a+i,l>0?c+f:c+n],[a,l>0?c+f:c+n],[a,f]])}),o}function Zu(t=!1){const e=this,{width:n,height:a,top:i,left:o}=Zi.call(e,!0),s=Ku.call(e,{width:n,height:a}),l=(n-s.width)/2,c=(n+s.width)/2,f=a-s.height,g=[[0,0],[n,0],[c,f],[c,a],[l,a],[l,f],[0,0]];return t&&g.forEach(v=>{v[0]+=o,v[1]+=i}),`M${g.join("L")}z`}function Px(t){const e=this,{config:n}=e,a=t.map(i=>({id:i.id,value:i.values.reduce((o,s)=>o+s.value,0)}));return n.data_order&&a.sort(e.getSortCompareFn.bind(e)(!0)),Ju.call(e,a)}function Ju(t){const e=this,{height:n}=Zi.call(e),a=e.getTotalDataSum(!0);return t.forEach(i=>{i.ratio=i.value/a*n}),t}var wx={initFunnel(){const t=this,{$el:e}=t;e.funnel=e.main.select(`.${Se.chart}`).append("g").classed(Ta.chartFunnels,!0),e.funnel.background=e.funnel.append("path").classed(Ta.funnelBackground,!0),t.bindFunnelEvent()},bindFunnelEvent(){const t=this,{$el:{funnel:e},config:n,state:a}=t,i=o=>{var s;const l=o.isTrusted?o.target:(s=a.eventReceiver.rect)==null?void 0:s.node();let c;return/^path$/i.test(l.tagName)&&(a.event=o,c=ot(l).datum()),c};if(n.interaction_enabled){const o=a.inputType==="touch";e.on(o?"touchstart":"mouseover mousemove",s=>{const l=i(s);l&&(t.showTooltip([l],s.target),/^(touchstart|mouseover)$/.test(s.type)&&t.setOverOut(!0,l))}).on(o?"touchend":"mouseout",s=>{const l=i(s);n.interaction_onout&&(t.hideTooltip(),t.setOverOut(!1,l))})}},updateTargetsForFunnel(t){const e=this,{$el:{funnel:n}}=e,a=e.getChartClass("Funnel"),i=e.getClass("funnel",!0);n||e.initFunnel();const o=Px.call(e,t.filter(e.isFunnelType.bind(e))),s=n.selectAll(`.${Ta.chartFunnel}`).data(o);s.exit().remove();const l=s.enter().insert("g",`.${Ta.funnelBackground}`);l.append("path"),n.path=l.merge(s).attr("class",c=>a(c)).select("path").attr("class",i).style("opacity","0").style("fill",e.color)},updateFunnel(t){const e=this,{$el:{funnel:n}}=e,a=t.map(({id:i})=>i);n.path=n.path.filter(i=>a.indexOf(i.id)>=0)},generateGetFunnelPoints(){const t=this,{$el:{funnel:e}}=t,n=t.filterTargetsToShow(e.path),{top:a,left:i,right:o}=Zi.call(t),s=(i-o)/2,l={};let c=a!=null?a:0;return n.each((f,g)=>{var v;l[f.id]=[[s,c],[s,c+=((v=n==null?void 0:n[g])!=null?v:f).ratio]]}),f=>l[f.id]},redrawFunnel(){const t=this,{$T:e,$el:{funnel:n}}=t,a=t.filterTargetsToShow(n.path),i=Cx.call(t,Ju.call(t,a.data()));n.attr("clip-path",`path('${Zu.bind(t)()}')`),n.background.attr("d",Zu.call(t,!0)),e(a).attr("d",(o,s)=>`M${i[s].join("L")}z`).style("opacity","1"),n.selectAll("g").style("opacity",null)}},Mx={initGauge(){const t=this,{config:e,$el:{arcs:n}}=t,a=(i=null,o="")=>{n.append("text").attr("class",i).style("text-anchor","middle").style("pointer-events","none").text(o)};if(t.hasType("gauge")){const i=t.hasMultiArcGauge();n.append(i?"g":"path").attr("class",Ve.chartArcsBackground).style("fill",!i&&e.gauge_background||null),e.gauge_units&&a(Un.chartArcsGaugeUnit),e.gauge_label_show&&(a(Un.chartArcsGaugeMin),!e.gauge_fullCircle&&a(Un.chartArcsGaugeMax))}},updateGaugeMax(){const t=this,{config:e,state:n}=t,i=t.hasMultiArcGauge()?t.getMinMaxData().max[0].value:t.getTotalDataSum(n.rendered);!e.gauge_enforceMinMax&&i+e.gauge_min*(e.gauge_min>0?-1:1)>e.gauge_max&&(e.gauge_max=i-e.gauge_min)},redrawArcGaugeLine(){const t=this,{config:e,state:n,$el:a}=t,{hiddenTargetIds:i}=t.state,o=a.main.selectAll(`.${Ve.arcs}`).selectAll(`.${Ve.arcLabelLine}`).data(t.arcData.bind(t));o.enter().append("rect").attr("class",l=>`${Ve.arcLabelLine} ${Se.target} ${Se.target}-${l.data.id}`).merge(o).style("fill",l=>t.levelColor?t.levelColor(l.data.values[0].value):t.color(l.data)).style("display",e.gauge_label_show?null:"none").each(function(l){let c=0;const f=2;let g=0,v=0,m="";if(i.indexOf(l.data.id)<0){const S=t.updateAngle(l),P=n.gaugeArcWidth/t.filterTargetsToShow(t.data.targets).length*(S.index+1),N=S.endAngle-Math.PI/2,L=n.radius-P,w=N-(L===0?0:1/L);c=n.radiusExpanded-n.radius+P,g=Math.cos(w)*L,v=Math.sin(w)*L,m=`rotate(${N*180/Math.PI}, ${g}, ${v})`}ot(this).attr("x",g).attr("y",v).attr("width",c).attr("height",f).attr("transform",m).style("stroke-dasharray",`0, ${c+f}, 0`)})},textForGaugeMinMax(t,e){const n=this,{config:a}=n,i=a.gauge_label_extents;return ve(i)?i.bind(n.api)(t,e):t},getGaugeLabelHeight(){const{config:t}=this;return this.config.gauge_label_show&&!t.gauge_fullCircle?20:0},getPaddingBottomForGauge(){const t=this;return t.getGaugeLabelHeight()*(t.config.gauge_label_show?2:2.5)}};function Dx(t,e,n,a=!1){const i=t?[t,0]:n;for(let o=t||n.reduce((s,l)=>s+l);o<=e;)n.forEach(s=>{o+s<=e&&i.push(s),o+=s});return i.length%2!==0&&i.push(a?n[1]:0),{dash:i.join(" "),length:i.reduce((o,s)=>o+s,0)}}function Lx(t,e,n){const a=this,i=[],o="2 2";if(Qe(e)){const s=(l,c)=>ln(l)?c:n?Yn.call(a,l):l;for(let l=0,c;c=e[l];l++){const f=s(c.start,t[0].x),g=s(c.end,t[t.length-1].x),v=c.style||{dasharray:o};i[l]={start:f,end:g,style:v}}}return i}var Nx={initLine(){const{$el:t}=this;t.line=t.main.select(`.${Se.chart}`).append("g").attr("class",ur.chartLines).call(this.setCssRule(!1,`.${ur.chartLines}`,["pointer-events:none"]))},updateTargetsForLine(t){const e=this,{$el:{area:n,line:a,main:i}}=e,o=e.getChartClass("Line"),s=e.getClass("lines",!0),l=e.classFocus.bind(e);a||e.initLine();const c=t.filter(v=>!(e.isScatterType(v)||e.isBubbleType(v))),f=i.select(`.${ur.chartLines}`).selectAll(`.${ur.chartLine}`).data(c).attr("class",v=>o(v)+l(v)),g=f.enter().append("g").attr("class",o).style("opacity","0").style("pointer-events",e.getStylePropValue("none"));if(g.append("g").attr("class",s),e.hasTypeOf("Area")){const v=(!n&&g.empty()?f:g).filter(e.isAreaType.bind(e));e.initArea(v)}e.updateTargetForCircle(c,g)},updateLine(t,e=!1){const n=this,{format:{extraLineClasses:a},$el:i,$T:o}=n,s=e?i.subchart:i,l=s.main.selectAll(`.${ur.lines}`).selectAll(`.${ur.line}`).data(n.lineData.bind(n));o(l.exit(),t).style("opacity","0").remove(),s.line=l.enter().append("path").attr("class",c=>`${n.getClass("line",!0)(c)} ${a(c)||""}`).style("stroke",n.color).merge(l).style("opacity",n.initialOpacity.bind(n)).attr("transform",null)},redrawLine(t,e,n=!1){const a=this,{$el:i,$T:o}=a,{line:s}=n?i.subchart:i;return[o(s,e,gr()).attr("d",t).style("stroke",this.color).style("opacity",null)]},getCurve(t){const e=this;return e.config.axis_rotated&&e.isStepType(t)?a=>{const i=e.getInterpolate(t)(a);return i.orgPoint=i.point,i.pointRotated=function(o,s){this._point===1&&(this._point=2);const l=this._y*(1-this._t)+s*this._t;this._context.lineTo(this._x,l),this._context.lineTo(o,l),this._x=o,this._y=s},i.point=function(o,s){this._point===0?this.orgPoint(o,s):this.pointRotated(o,s)},i}:e.getInterpolate(t)},generateDrawLine(t,e){const n=this,{config:a,scale:i}=n,o=a.line_connectNull,s=a.axis_rotated,l=n.generateGetLinePoints(t,e),c=n.getYScaleById.bind(n),f=S=>(e?n.subxx:n.xx).call(n,S),g=(S,P)=>n.isGrouped(S.id)?l(S,P)[0][1]:c(S.id,e)(n.getBaseValue(S));let v=Gu();v=s?v.x(g).y(f):v.x(f).y(g),o||(v=v.defined(S=>n.getBaseValue(S)!==null));const m=e?i.subX:i.x;return S=>{const P=c(S.id,e);let N=o?n.filterRemoveNull(S.values):S.values,L=0,w=0,X;if(n.isLineType(S)){const W=a.data_regions[S.id];W?X=n.lineWithRegions(N,i.zoom||m,P,W):(n.isStepType(S)&&(N=n.convertValuesToStep(N)),X=v.curve(n.getCurve(S))(N))}else N[0]&&(L=m(N[0].x),w=P(N[0].value)),X=s?`M ${w} ${L}`:`M ${L} ${w}`;return X||"M 0 0"}},lineWithRegions(t,e,n,a){const i=this,{config:o}=i,s=o.axis_rotated,l=i.axis.isTimeSeries(),c="2 2",f=Lx.bind(i)(t,a,l),g=i.hasNullDataValue(t);let v,m,S,P;const N=s?dt=>n(dt.value):dt=>e(dt.x),L=s?dt=>e(dt.x):dt=>n(dt.value),w=dt=>`M${dt[0][0]},${dt[0][1]}L${dt[1][0]},${dt[1][1]}`,X=l?(dt,st,Vt,vt)=>{const Q=dt.x.getTime(),St=st.x-dt.x,ct=new Date(Q+St*Vt),At=new Date(Q+St*(Vt+vt)),Gt=s?[[n(m(Vt)),e(ct)],[n(m(Vt+S)),e(At)]]:[[e(ct),n(m(Vt))],[e(At),n(m(Vt+S))]];return w(Gt)}:(dt,st,Vt,vt)=>{const Q=e(st.x,!s),St=n(st.value,s),ct=Vt+vt,At=e(v(Vt),!s),Gt=n(m(Vt),s);let Bt=e(v(ct),!s),Kt=n(m(ct),s);Bt>Q&&(Bt=Q),dt.value>st.value&&(s?KtSt)&&(Kt=St);const ne=[[At,Gt],[Bt,Kt]];return s&&ne.forEach(le=>le.reverse()),w(ne)},W={x:i.axis.getAxisType("x"),y:i.axis.getAxisType("y")};let H="";const k=i.$el.line.filter(({id:dt})=>dt===t[0].id),K=k.clone().style("display","none"),at=(dt,st)=>dt.attr("d",st).node().getTotalLength(),ht={dash:[],lastLength:0};let $t=!1;for(let dt=0,st;st=t[dt];dt++){const Vt=t[dt-1],vt=Vt&&De(Vt.value);let Q=i.isWithinRegions(st.x,f);if(De(st.value)){if(ln(f)||!Q||!vt)H+=`${dt&&vt?"L":"M"}${N(st)},${L(st)}`;else if(vt)if(Q=((Q==null?void 0:Q.dasharray)||c).split(" ").map(Number),v=zr(W.x,Vt.x,st.x),m=zr(W.y,Vt.value,st.value),g){const St=e(st.x)-e(Vt.x),ct=n(st.value)-n(Vt.value),At=Math.sqrt(Math.pow(St,2)+Math.pow(ct,2));S=Q[0]/At,P=S*Q[1];for(let Gt=S;Gt<=1;Gt+=P)H+=X(Vt,st,Gt,S),Gt+P>=1&&(H+=X(Vt,st,1,0))}else{let St=[];if($t=st.x===t[t.length-1].x,l){const Bt=+Vt.x,Kt=new Date(Bt),ne=new Date(Bt+(+st.x-Bt));St=[[e(Kt),n(m(0))],[e(ne),n(m(1))]]}else St=[[e(v(0)),n(m(0))],[e(v(1)),n(m(1))]];s&&St.forEach(Bt=>Bt.reverse());const ct=at(K,H),At=at(K,H+=`L${St[1].join(",")}`),Gt=Dx(ct-ht.lastLength,At-ht.lastLength,Q,$t);ht.lastLength+=Gt.length,ht.dash.push(Gt.dash)}}}return ht.dash.length&&(!$t&&ht.dash.push(at(K,H)),K.remove(),k.attr("stroke-dasharray",ht.dash.join(" "))),H},isWithinRegions(t,e){for(let n=0,a;a=e[n];n++)if(a.startgr();var Ji={initialOpacityForCircle(t){const{config:e,state:{withoutFadeIn:n}}=this;let a=e.point_opacity;return ln(a)&&(a=this.getBaseValue(t)!==null&&n[t.id]?this.opacityForCircle(t):"0"),a},opacityForCircle(t){var e;const{config:n}=this;let a=n.point_opacity;return ln(a)&&(a=n.point_show&&!((e=this.isPointFocusOnly)!=null&&e.call(this))?null:"0",a=De(this.getBaseValue(t))?this.isBubbleType(t)||this.isScatterType(t)?"0.5":a:"0"),a},initCircle(){const t=this,{$el:{main:e}}=t;!t.point&&(t.point=t.generatePoint()),(t.hasType("bubble")||t.hasType("scatter"))&&e.select(`.${Se.chart} > .${$n.chartCircles}`).empty()&&e.select(`.${Se.chart}`).append("g").attr("class",$n.chartCircles)},updateTargetForCircle(t,e){const n=this,{config:a,data:i,$el:o}=n,s=a.interaction_enabled&&a.data_selection_enabled,l=s&&a.data_selection_isselectable,c=n.getClass("circles",!0);if(!a.point_show)return;n.initCircle();let f=t,g=e;if(!f){f=i.targets.filter(m=>this.isScatterType(m)||this.isBubbleType(m));const v=o.main.select(`.${$n.chartCircles}`).style("pointer-events","none").selectAll(`.${$n.circles}`).data(f);v.exit().remove(),g=v.enter()}s&&g.append("g").attr("class",v=>n.generateClass(tn.selectedCircles,v.id)),g.append("g").attr("class",c).call(v=>{n.setCssRule(!0,`.${$n.circles}`,["cursor:pointer"],l)(v),n.setCssRule(!0,` .${$n.circle}`,["fill","stroke"],n.color)(v)}).style("opacity",function(){return ot(this.parentNode).attr("class").indexOf($n.chartCircles)>-1?"0":null}),s&&f.forEach(v=>{o.main.selectAll(`.${tn.selectedCircles}${n.getTargetSelectorSuffix(v.id)}`).selectAll(`${tn.selectedCircle}`).each(m=>{m.value=v.values[m.index].value})})},updateCircle(t=!1){const e=this,{config:n,state:a,$el:i}=e,o=e.isPointFocusOnly(),s=t?i.subchart:i;if(n.point_show&&!a.toggling){n.point_radialGradient&&e.updateLinearGradient();const l=s.main.selectAll(`.${$n.circles}`).selectAll(`.${$n.circle}`).data(c=>e.isLineType(c)&&e.shouldDrawPointsForLine(c)||e.isBubbleType(c)||e.isRadarType(c)||e.isScatterType(c)?o?[c.values[0]]:c.values:[]);l.exit().remove(),l.enter().filter(Boolean).append(e.point("create",this,e.pointR.bind(e),e.updateCircleColor.bind(e))),s.circle=s.main.selectAll(`.${$n.circles} .${$n.circle}`).style("stroke",e.getStylePropValue(e.color)).style("opacity",e.initialOpacityForCircle.bind(e))}},updateCircleColor(t){const e=this,n=e.getStylePropValue(e.color);return e.config.point_radialGradient?e.getGradienColortUrl(t.id):n?n(t):null},redrawCircle(t,e,n,a,i=!1){const o=this,{state:{rendered:s},$el:l,$T:c}=o,f=i?l.subchart:l,g=f.main.selectAll(`.${tn.selectedCircle}`);if(!o.config.point_show)return[];const v=o.point("update",o,t,e,o.updateCircleColor.bind(o),n,a,g),m=o.isCirclePoint()?"c":"",S=gr(),P=o.opacityForCircle.bind(o),N=[];return f.circle.each(function(L){let w=v.bind(this)(L);w=c(w,n||!s,S).style("opacity",P),N.push(w)}),[N,c(g,n).attr(`${m}x`,t).attr(`${m}y`,e)]},showCircleFocus(t){const e=this,{state:{hasRadar:n,resizing:a,toggling:i,transiting:o},$el:s}=e;let{circle:l}=s;if(o===!1&&l&&e.isPointFocusOnly()){const c=(n?e.radarCircleX:e.circleX).bind(e),f=(n?e.radarCircleY:e.circleY).bind(e),g=i||ln(t),v=e.point("update",e,c,f,e.getStylePropValue(e.color),a?!1:g);t&&(l=l.filter(function(m){var S;const P=(S=t.filter)==null?void 0:S.call(t,N=>N.id===m.id);return P.length?ot(this).datum(P[0]):!1})),l.attr("class",this.updatePointClass.bind(this)).style("opacity",null).each(function(m){const{id:S,index:P,value:N}=m;let L="hidden";De(N)&&(v.bind(this)(m),e.expandCircles(P,S),L=""),this.style.visibility=L})}},hideCircleFocus(){const t=this,{$el:{circle:e}}=t;t.isPointFocusOnly()&&e&&(t.unexpandCircles(),e.style("visibility","hidden"))},circleX(t){return this.xx(t)},updateCircleY(t=!1){const e=this,n=e.generateGetLinePoints(e.getShapeIndices(e.isLineType),t);return(a,i)=>{const o=a.id;return e.isGrouped(o)?n(a,i)[0][1]:e.getYScaleById(o,t)(e.getBaseValue(a))}},expandCircles(t,e,n){const a=this,i=a.pointExpandedR.bind(a);n&&a.unexpandCircles();const o=a.getShapeByIndex("circle",t,e).classed(Se.EXPANDED,!0),s=i(o)/a.config.point_r,l=1-s;a.isCirclePoint()?o.attr("r",i):o.each(function(){const c=ot(this);if(this.tagName==="circle")c.attr("r",i);else{const{width:f,height:g}=this.getBBox(),v=l*(+c.attr("x")+f/2),m=l*(+c.attr("y")+g/2);c.attr("transform",`translate(${v} ${m}) scale(${s})`)}})},unexpandCircles(t){const e=this,n=e.pointR.bind(e),a=e.getShapeByIndex("circle",t).filter(function(){return ot(this).classed(Se.EXPANDED)}).classed(Se.EXPANDED,!1);if(a.attr("r",n),!e.isCirclePoint()){const i=n(a)/e.config.point_r;a.attr("transform",i!==1?`scale(${i})`:null)}},pointR(t){const e=this,{config:n}=e,a=n.point_r;let i=a;return e.isBubbleType(t)?i=e.getBubbleR(t):ve(a)&&(i=a.bind(e.api)(t)),t.r=i,i},pointExpandedR(t){const e=this,{config:n}=e,a=e.isBubbleType(t)?1.15:1.75;return n.point_focus_expand_enabled?n.point_focus_expand_r||e.pointR(t)*a:e.pointR(t)},pointSelectR(t){const e=this,n=e.config.point_select_r;return ve(n)?n(t):n||e.pointR(t)*4},isPointFocusOnly(){const t=this;return t.config.point_focus_only&&!t.hasType("bubble")&&!t.hasType("scatter")&&!t.hasArcType(null,["radar"])},isWithinCircle(t,e){const{state:n}=this,a=Hn(n.event,t),i=ot(t),o=this.isCirclePoint(t)?"c":"",s=this.getPointSensitivity(i==null?void 0:i.datum());let l=+i.attr(`${o}x`),c=+i.attr(`${o}y`);if(!(l||c)&&t.nodeType===1){const{x:f,y:g}=Ma(t);l=f,c=g}return Math.sqrt(Math.pow(l-a[0],2)+Math.pow(c-a[1],2))<(e||s)},getPointSensitivity(t){const e=this;let n=e.config.point_sensitivity;if(t)ve(n)?n=n.call(e.api,t):n==="radius"&&(n=t.r);else return n;return n},updatePointClass(t){const e=this,{circle:n}=e.$el;let a=!1;return(Be(t)||n)&&(a=t===!0?n.each(function(i){let o=e.getClass("circle",!0)(i);this.getAttribute("class").indexOf(Se.EXPANDED)>-1&&(o+=` ${Se.EXPANDED}`),this.setAttribute("class",o)}):e.getClass("circle",!0)(t)),a},generateGetLinePoints(t,e){const n=this,{config:a}=n,i=n.getShapeX(0,t,e),o=n.getShapeY(e),s=n.getShapeOffset(n.isLineType,t,e),l=n.getYScaleById.bind(n);return(c,f)=>{const g=l.call(n,c.id,e)(n.getShapeYMin(c.id)),v=s(c,f)||g,m=i(c);let S=o(c);a.axis_rotated&&(c.value>0&&SDe(S.value)?e(S)-c/2:0,v=S=>De(S.value)?n(S)-f/2:0;let m=t;return i&&(o&&m.attr("x",g),m=l.$T(m,i,la()),s&&l.$T(s,i,la())),m.attr("x",g).attr("y",v).style("fill",a)}},circle:{create(t,e,n){return t.append("circle").attr("class",this.updatePointClass.bind(this)).attr("r",e).style("fill",n).node()},update(t,e,n,a,i,o,s){const l=this;let c=t;return l.hasType("bubble")&&c.attr("r",l.pointR.bind(l)),i&&(o&&c.attr("cx",e),c.attr("cx")&&(c=l.$T(c,i,la())),s&&l.$T(c,i,la())),c.attr("cx",e).attr("cy",n).style("fill",a)}},rectangle:{create(t,e,n){const a=i=>e(i)*2;return t.append("rect").attr("class",this.updatePointClass.bind(this)).attr("width",a).attr("height",a).style("fill",n).node()},update(t,e,n,a,i,o,s){const l=this,c=l.config.point_r,f=m=>e(m)-c,g=m=>n(m)-c;let v=t;return i&&(o&&v.attr("x",f),v=l.$T(v,i,la()),s&&l.$T(s,i,la())),v.attr("x",f).attr("y",g).style("fill",a)}}};function Fx(t){return nr(t)&&ve(t.create)&&ve(t.update)}function Bx(t,e){var n;const a=this,i=(c,f)=>{const g=c.attributes;for(let v=0,m;m=g[v];v++)m=m.name,f.setAttribute(m,c.getAttribute(m))},s=new DOMParser().parseFromString(t,"image/svg+xml").documentElement,l=gn.createElementNS(ae.svg,s.nodeName.toLowerCase());if(l.id=e,l.style.fill="inherit",l.style.stroke="inherit",i(s,l),(n=s.childNodes)!=null&&n.length){const c=ot(l);"innerHTML"in l?c.html(s.innerHTML):Lr(s.childNodes).forEach(f=>{i(f,c.append(f.tagName).node())})}a.$el.defs.node().appendChild(l)}var ca={hasValidPointType(t){return/^(circle|rect(angle)?|polygon|ellipse|use)$/i.test(t||this.config.point_type)},hasLegendDefsPoint(){var t;const{config:e}=this;return e.legend_show&&((t=e.point_pattern)==null?void 0:t.length)&&e.legend_usePoint},getDefsPointId(t){const{state:{datetimeId:e}}=this;return`${e}-point${t}`},generatePoint(){const t=this,{$el:e,config:n}=t,a=[],i=cn(n.point_pattern)?n.point_pattern:[n.point_type];return function(o,s,...l){return function(c){var f,g,v,m;const S=t.getTargetSelectorSuffix(c.id||((f=c.data)==null?void 0:f.id)||c),P=ot(this);a.indexOf(S)<0&&a.push(S);let N=i[a.indexOf(S)%i.length];if(t.hasValidPointType(N))N=t[N];else if(!Fx(N||n.point_type)){const L=t.getDefsPointId(S);if(e.defs.select(`#${L}`).size()<1&&Bx.bind(t)(N,L),o==="create")return(g=t.custom)==null?void 0:g.create.bind(s)(P,L,...l);if(o==="update")return(v=t.custom)==null?void 0:v.update.bind(s)(P,...l)}return(m=N[o])==null?void 0:m.bind(s)(P,...l)}}}};function Qu(t){const e=t.config.polar_level_max;let n=t.getMinMaxData().max[0].value;return e&&e>n&&(n=e),n}var Ux={initPolar(){const t=this,{$el:{arcs:e},config:n}=t,a=n.polar_level_text_show,i=n.polar_level_text_backgroundColor;e.levels=e.append("g").attr("class",Tr.levels),a&&i&&t.generateTextBGColorFilter(i)},getPolarOuterRadius(t,e){var n;const a=Qu(this);return((n=t==null?void 0:t.data.values[0].value)!=null?n:0)/a*e},updateTargetsForPolar(t){this.updateTargetsForArc(t)},redrawPolar(){const t=this,{config:e}=t;e.polar_level_show&&t.updatePolarLevel()},updatePolarLevel(){const t=this,{config:e,state:n,$el:{arcs:{levels:a}}}=t,i=e.polar_level_depth,o=Qu(t),s=Ei(0,i),l=n.radius,c=s.map(m=>l*((m+1)/i)),f=(e.polar_level_text_format||function(){}).bind(t.api),g=a.selectAll(`.${Tr.level}`).data(s);g.exit().remove();const v=g.enter().append("g").attr("class",(m,S)=>`${Tr.level} ${Tr.level}-${S}`);if(v.append("circle"),v.merge(g).selectAll("circle").style("visibility",e.polar_level_show?null:"hidden").attr("cx",0).attr("cy",0).attr("r",m=>c[m]),e.polar_level_text_show){const m=e.polar_level_text_backgroundColor,S=`#${n.datetimeId}-labels-bg${t.getTargetSelectorSuffix(m)}`;v.append("text").style("text-anchor","middle"),v.merge(g).selectAll("text").attr("dy",P=>-c[P]+5).attr("filter",m?`url(${S})`:null).text(P=>f(o/s.length*(P+1)))}}};function zx(t,e,n,a,i,o){const s=t&&a>0?n-a:a,l=2*Math.PI;return i*(1-o*(e==="x"?Math.sin:Math.cos)(s*l/n))}const ua=Ln.radarPoints,ku=Ln.radarTextWidth;var jx={initRadar(){const t=this,{config:e,state:{current:n},$el:a}=t;t.hasType("radar")&&(a.radar=a.main.select(`.${Se.chart}`).append("g").attr("class",Qs.chartRadars),a.radar.levels=a.radar.append("g").attr("class",Tr.levels),a.radar.axes=a.radar.append("g").attr("class",Tn.axis),a.radar.shapes=a.radar.append("g").attr("class",sn.shapes),n.dataMax=e.radar_axis_max||t.getMinMaxData().max[0].value,e.radar_axis_text_show&&(e.interaction_enabled&&t.bindRadarEvent(),t.updateRadarLevel(),t.updateRadarAxes()))},getRadarSize(){const t=this,{config:e,state:{arcWidth:n,arcHeight:a}}=t,i=e.axis_x_categories.length<4?-20:10,o=(Math.min(n,a)-i)/2;return[o,o]},updateTargetsForRadar(t){const e=this,{config:n}=e;qn(n.axis_x_categories)&&(n.axis_x_categories=Ei(0,_n("max",t.map(a=>a.values.length)))),e.generateRadarPoints()},getRadarPosition(t,e,n,a){const i=this,{config:o}=i,[s,l]=i.getRadarSize(),c=o.axis_x_categories.length,f=o.radar_direction_clockwise,g=Lr(t).map(v=>zx(f,v,c,e,Qe(n)?n:t==="x"?s:l,he(a)?a:o.radar_size_ratio));return g.length===1?g[0]:g},generateRadarPoints(){const t=this,e=t.data.targets,[n,a]=t.getRadarSize(),i=t.cache.get(ua)||{},o=i._size;(!o||o.width!==n&&o.height!==a)&&(e.forEach(s=>{i[s.id]=s.values.map((l,c)=>t.getRadarPosition(["x","y"],c,void 0,t.getRatio("radar",l)))}),i._size={width:n,height:a},t.cache.add(ua,i))},redrawRadar(){const t=this,{radar:e,main:n}=t.$el,a=t.getTranslate("radar");a&&(e.attr("transform",a),n.select(`.${On.chartTexts}`).attr("transform",a),t.generateRadarPoints(),t.updateRadarLevel(),t.updateRadarAxes(),t.updateRadarShape())},generateGetRadarPoints(){const t=this.cache.get(ua);return(e,n)=>{const a=t[e.id][n];return[a,a,a,a]}},updateRadarLevel(){const t=this,{config:e,state:n,$el:{radar:a}}=t,[i,o]=t.getRadarSize(),s=e.radar_level_depth,l=e.axis_x_categories.length,c=e.radar_level_text_show,f=a.levels,g=Ei(0,s),v=e.radar_size_ratio*Math.min(i,o),m=g.map(w=>v*((w+1)/s)),S=(e.radar_level_text_format||function(){}).bind(t.api),P=g.map(w=>{const X=m[w];return Ei(0,l).map(H=>t.getRadarPosition(["x","y"],H,X,1).join(",")).join(" ")}),N=f.selectAll(`.${Tr.level}`).data(g);N.exit().remove();const L=N.enter().append("g").attr("class",(w,X)=>`${Tr.level} ${Tr.level}-${X}`);L.append("polygon").style("visibility",e.radar_level_show?null:"hidden"),c&&(f.select("text").empty()&&f.append("text").attr("dx","-.5em").attr("dy","-.7em").style("text-anchor","end").text(()=>S(0)),L.append("text").attr("dx","-.5em").style("text-anchor","end").text(w=>S(n.current.dataMax/g.length*(w+1)))),L.merge(N).attr("transform",w=>`translate(${i-m[w]}, ${o-m[w]})`).selectAll("polygon").attr("points",w=>P[w]),c&&f.selectAll("text").attr("x",w=>ln(w)?i:P[w].split(",")[0]).attr("y",w=>ln(w)?o:0)},updateRadarAxes(){const t=this,{config:e,$el:{radar:n}}=t,[a,i]=t.getRadarSize(),o=e.axis_x_categories;let s=n.axes.selectAll("g").data(o);s.exit().remove();const l=s.enter().append("g").attr("class",(c,f)=>`${Tn.axis}-${f}`);if(e.radar_axis_line_show&&l.append("line"),e.radar_axis_text_show&&l.append("text"),s=l.merge(s),e.radar_axis_line_show&&s.select("line").attr("x1",a).attr("y1",i).attr("x2",(c,f)=>t.getRadarPosition("x",f)).attr("y2",(c,f)=>t.getRadarPosition("y",f)),e.radar_axis_text_show){const{x:c=0,y:f=0}=e.radar_axis_text_position,g=t.cache.get(ku)||0;if(s.select("text").style("text-anchor","middle").attr("dy",".5em").call(v=>{v.each(function(m){wa(ot(this),String(m),[-.6,1.2])})}).datum((v,m)=>({index:m})).attr("transform",function(v){ln(this.width)&&(this.width=this.getBoundingClientRect().width/2);let m=t.getRadarPosition("x",v.index,void 0,1),S=Math.round(t.getRadarPosition("y",v.index,void 0,1));return m>a?m+=this.width+c:Math.round(m)i?(S/2===i&&this.firstChild.tagName==="tspan"&&this.firstChild.setAttribute("dy","0em"),S+=f):SYl(m.node()).width);v.every(m=>m>0)&&t.cache.add(ku,v[0]-v[1])}}},bindRadarEvent(){const t=this,{config:e,state:n,$el:{radar:a,svg:i}}=t,o=t.isPointFocusOnly(),{inputType:s,transiting:l}=n,c=s==="mouse",f=g=>{if(n.event=g,!e.interaction_onout)return;const v=t.getDataIndexFromEvent(g),m=ln(v);(c||m)&&(t.hideTooltip(),o?t.hideCircleFocus():t.unexpandCircles(),c?t.setOverOut(!1,v):m&&t.callOverOutForTouch())};a.axes.on(c?"mouseover ":"touchstart",g=>{if(l)return;n.event=g;const v=t.getDataIndexFromEvent(g);t.selectRectForSingle(i.node(),v),c?t.setOverOut(!0,v):t.callOverOutForTouch(v)}).on("mouseout",c?f:null),c||i.on("touchstart",f)},updateRadarShape(){const t=this,e=t.data.targets.filter(o=>t.isRadarType(o)),n=t.cache.get(ua),a=t.$el.radar.shapes.selectAll("polygon").data(e),i=a.enter().append("g").attr("class",t.getChartClass("Radar"));t.$T(a.exit()).remove(),i.append("polygon").merge(a).style("fill",t.color).style("stroke",t.color).attr("points",o=>n[o.id].join(" ")),t.updateTargetForCircle(e,i)},radarCircleX(t){return this.cache.get(ua)[t.id][t.index][0]},radarCircleY(t){return this.cache.get(ua)[t.id][t.index][1]}};function Vx(t){var e=0,n=t.children,a=n&&n.length;if(!a)e=1;else for(;--a>=0;)e+=n[a].value;t.value=e}function Gx(){return this.eachAfter(Vx)}function Xx(t,e){let n=-1;for(const a of this)t.call(e,a,++n,this);return this}function Hx(t,e){for(var n=this,a=[n],i,o,s=-1;n=a.pop();)if(t.call(e,n,++s,this),i=n.children)for(o=i.length-1;o>=0;--o)a.push(i[o]);return this}function Yx(t,e){for(var n=this,a=[n],i=[],o,s,l,c=-1;n=a.pop();)if(i.push(n),o=n.children)for(s=0,l=o.length;s=0;)n+=a[i].value;e.value=n})}function Zx(t){return this.eachBefore(function(e){e.children&&e.children.sort(t)})}function Jx(t){for(var e=this,n=Qx(e,t),a=[e];e!==n;)e=e.parent,a.push(e);for(var i=a.length;t!==n;)a.splice(i,0,t),t=t.parent;return a}function Qx(t,e){if(t===e)return t;var n=t.ancestors(),a=e.ancestors(),i=null;for(t=n.pop(),e=a.pop();t===e;)i=t,t=n.pop(),e=a.pop();return i}function kx(){for(var t=this,e=[t];t=t.parent;)e.push(t);return e}function qx(){return Array.from(this)}function _x(){var t=[];return this.eachBefore(function(e){e.children||t.push(e)}),t}function t0(){var t=this,e=[];return t.each(function(n){n!==t&&e.push({source:n.parent,target:n})}),e}function*e0(){var t=this,e,n=[t],a,i,o;do for(e=n.reverse(),n=[];t=e.pop();)if(yield t,a=t.children)for(i=0,o=a.length;i=0;--l)i.push(o=s[l]=new Qi(s[l])),o.parent=a,o.depth=a.depth+1;return n.eachBefore(o0)}function n0(){return Rs(this).eachBefore(i0)}function r0(t){return t.children}function a0(t){return Array.isArray(t)?t[1]:null}function i0(t){t.data.value!==void 0&&(t.value=t.data.value),t.data=t.data.data}function o0(t){var e=0;do t.height=e;while((t=t.parent)&&t.height<++e)}function Qi(t){this.data=t,this.depth=this.height=0,this.parent=null}Qi.prototype=Rs.prototype={constructor:Qi,count:Gx,each:Xx,eachAfter:Yx,eachBefore:Hx,find:Wx,sum:Kx,sort:Zx,path:Jx,ancestors:kx,descendants:qx,leaves:_x,links:t0,copy:n0,[Symbol.iterator]:e0};function s0(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)}function ki(t,e,n,a,i){for(var o=t.children,s,l=-1,c=o.length,f=t.value&&(a-e)/t.value;++lX&&(X=f),K=L*L*k,W=Math.max(X/K,K/w),W>H){L-=f;break}H=W}s.push(c={value:L,dice:S1?a:1)},n}(qu);function C1(t){return t==null?null:ef(t)}function ef(t){if(typeof t!="function")throw new Error;return t}function ja(){return 0}function Va(t){return function(){return t}}function l0(){var t=tf,e=!1,n=1,a=1,i=[0],o=ja,s=ja,l=ja,c=ja,f=ja;function g(m){return m.x0=m.y0=0,m.x1=n,m.y1=a,m.eachBefore(v),i=[0],e&&m.eachBefore(s0),m}function v(m){var S=i[m.depth],P=m.x0+S,N=m.y0+S,L=m.x1-S,w=m.y1-S;L=m-1){var X=o[v];X.x0=P,X.y0=N,X.x1=L,X.y1=w;return}for(var W=f[v],H=S/2+W,k=v+1,K=m-1;k>>1;f[at]w-N){var dt=S?(P*$t+L*ht)/S:L;g(v,k,ht,P,N,dt,w),g(k,m,$t,dt,N,L,w)}else{var st=S?(N*$t+w*ht)/S:w;g(v,k,ht,P,N,L,st),g(k,m,$t,P,st,L,w)}}}function c0(t,e,n,a,i){(t.depth&1?qi:ki)(t,e,n,a,i)}var u0=function t(e){function n(a,i,o,s,l){if((c=a._squarify)&&c.ratio===e)for(var c,f,g,v,m=-1,S,P=c.length,N=a.value;++m1?a:1)},n}(qu);function f0(t,e){const n=this,{scale:{x:a,y:i},state:{width:o}}=n;t.selectAll("g").attr("transform",s=>`translate(${s===e?"0,0":`${a(s.x0)},${i(s.y0)}`})`).select("rect").attr("width",s=>s===e?o:a(s.x1)-a(s.x0)).attr("height",s=>s===e?0:i(s.y1)-i(s.y0))}function d0(t){const e=this;return t.map(n=>{const{id:a,values:i}=n,{value:o}=i[0];return{name:a,id:a,value:o,ratio:e.getRatio("treemap",i[0])}})}function h0(t){const e=this,n=Rs(t).sum(i=>i.value),a=e.getSortCompareFn(!0);return[e.treemap(a?n.sort(a):n)]}var g0={initTreemap(){const t=this,{$el:e,state:{current:{width:n,height:a},clip:i,datetimeId:o}}=t;i.id=`${o}-clip`,t.treemap=l0().tile(t.getTreemapTile()),e.defs.append("clipPath").attr("id",i.id).append("rect").attr("width",n).attr("height",a),e.treemap=e.main.select(`.${Se.chart}`).attr("clip-path",`url(#${i.id})`).append("g").classed(qs.chartTreemaps,!0),t.bindTreemapEvent()},bindTreemapEvent(){const t=this,{$el:e,config:n,state:a}=t,i=o=>{var s;const l=o.isTrusted?o.target:(s=a.eventReceiver.rect)==null?void 0:s.node();let c;return/^rect$/i.test(l.tagName)&&(a.event=o,c=ot(l).datum()),c==null?void 0:c.data};if(n.interaction_enabled){const o=a.inputType==="touch";e.treemap.on(o?"touchstart":"mouseover mousemove",s=>{const l=i(s);l&&(t.showTooltip([l],s.currentTarget),/^(touchstart|mouseover)$/.test(s.type)&&t.setOverOut(!0,l))}).on(o?"touchend":"mouseout",s=>{const l=i(s);n.interaction_onout&&(t.hideTooltip(),t.setOverOut(!1,l))})}},getTreemapTile(){var t,e;const n=this,{config:a,state:{current:{width:i,height:o}}}=n,s=(e={binary:nf,dice:ki,slice:qi,sliceDice:c0,squarify:tf,resquarify:u0}[(t=a.treemap_tile)!=null?t:"binary"])!=null?e:nf;return(l,c,f,g,v)=>{s(l,0,0,i,o);for(const m of l.children)m.x0=c+m.x0/i*(g-c),m.x1=c+m.x1/i*(g-c),m.y0=f+m.y0/o*(v-f),m.y1=f+m.y1/o*(v-f)}},getTreemapData(t){const e=this;return{name:"root",children:d0.bind(e)(e.filterTargetsToShow(t.filter(e.isTreemapType,e)))}},updateTargetsForTreemap(t){const e=this,{$el:{treemap:n}}=e,a=h0.call(e,e.getTreemapData(t!=null?t:e.data.targets));n.data(a)},updateTreemap(t){const e=this,{$el:n,$T:a}=e,i=n.treemap.datum(),o=e.getChartClass("Treemap"),s=e.getClass("treemap",!0),l=n.treemap.selectAll("g").data(i.children);a(l.exit(),t).style("opacity","0").remove(),l.enter().append("g").append("rect"),n.treemap.selectAll("g").attr("class",o).select("rect").attr("class",s).attr("fill",c=>e.color(c.data.name))},generateGetTreemapPoints(){const t=this,{$el:e,scale:{x:n,y:a}}=t,i={};return e.treemap.selectAll("g").each(o=>{i[o.data.name]=[[n(o.x0),a(o.y0)],[n(o.x1),a(o.y1)]]}),o=>i[o.id]},redrawTreemap(t){const e=this,{$el:n,state:{current:{width:a,height:i}}}=e;return n.defs.select("rect").attr("width",a).attr("height",i),[e.$T(n.treemap,t,gr()).call(f0.bind(e),n.treemap.datum())]},treemapDataLabelFormat(t){const e=this,{config:n}=e,{id:a,value:i}=t,o=n.treemap_label_format,s=e.getRatio("treemap",t),l=(s*100).toFixed(2),c=n.treemap_label_show&&e.meetsLabelThreshold(s,"treemap")?null:"0";return function(f){return f.style("opacity",c),ve(o)?o.bind(e.api)(i,s,a):`${a} +${l}%`}}},Xr={point_show:!0,point_r:2.5,point_radialGradient:!1,point_sensitivity:10,point_focus_expand_enabled:!0,point_focus_expand_r:void 0,point_focus_only:!1,point_opacity:void 0,point_pattern:[],point_select_r:void 0,point_type:"circle"},fa={area_above:!1,area_below:!1,area_front:!0,area_linearGradient:!1,area_zerobased:!0},v0={bar_front:!1,bar_indices_removeNull:!1,bar_label_threshold:0,bar_linearGradient:!1,bar_overlap:!1,bar_padding:0,bar_radius:void 0,bar_radius_ratio:void 0,bar_sensitivity:2,bar_width:void 0,bar_width_ratio:.6,bar_width_max:void 0,bar_zerobased:!0},p0={bubble_maxR:35,bubble_zerobased:!1},m0={candlestick_width:void 0,candlestick_width_ratio:.6,candlestick_width_max:void 0,candlestick_color_down:"red"},y0={line_connectNull:!1,line_step_type:"step",line_step_tooltipMatch:!1,line_zerobased:!1,line_classes:void 0,line_point:!0},x0={scatter_zerobased:!1},Is={spline_interpolation_type:"cardinal"},_i={arc_cornerRadius:0,arc_cornerRadius_ratio:0,arc_needle_show:!1,arc_needle_color:void 0,arc_needle_value:void 0,arc_needle_path:void 0,arc_needle_length:100,arc_needle_top_rx:0,arc_needle_top_ry:0,arc_needle_top_width:0,arc_needle_bottom_rx:1,arc_needle_bottom_ry:1,arc_needle_bottom_width:15,arc_needle_bottom_len:0,arc_rangeText_values:void 0,arc_rangeText_unit:"absolute",arc_rangeText_fixed:!1,arc_rangeText_format:void 0,arc_rangeText_position:void 0},T0={donut_label_show:!0,donut_label_format:void 0,donut_label_threshold:.05,donut_label_ratio:void 0,donut_width:void 0,donut_title:"",donut_expand:{},donut_expand_rate:.98,donut_expand_duration:50,donut_padAngle:0,donut_startingAngle:0},$0={funnel_neck_width:0,funnel_neck_height:0},S0={gauge_background:"",gauge_fullCircle:!1,gauge_label_show:!0,gauge_label_extents:void 0,gauge_label_format:void 0,gauge_label_ratio:void 0,gauge_label_threshold:0,gauge_enforceMinMax:!1,gauge_min:0,gauge_max:100,gauge_type:"single",gauge_startingAngle:-1*Math.PI/2,gauge_arcLength:100,gauge_title:"",gauge_units:void 0,gauge_width:void 0,gauge_arcs_minWidth:5,gauge_expand:{},gauge_expand_rate:.98,gauge_expand_duration:50},A0={pie_label_show:!0,pie_label_format:void 0,pie_label_ratio:void 0,pie_label_threshold:.05,pie_expand:{},pie_expand_rate:.98,pie_expand_duration:50,pie_innerRadius:0,pie_outerRadius:void 0,pie_padAngle:0,pie_padding:0,pie_startingAngle:0},E0={polar_label_show:!0,polar_label_format:void 0,polar_label_threshold:.05,polar_label_ratio:void 0,polar_level_depth:3,polar_level_max:void 0,polar_level_show:!0,polar_level_text_backgroundColor:"#fff",polar_level_text_format:t=>t%1===0?t:t.toFixed(2),polar_level_text_show:!0,polar_padAngle:0,polar_padding:0,polar_startingAngle:0},b0={radar_axis_max:void 0,radar_axis_line_show:!0,radar_axis_text_show:!0,radar_axis_text_position:{},radar_level_depth:3,radar_level_show:!0,radar_level_text_format:t=>t%1===0?t:t.toFixed(2),radar_level_text_show:!0,radar_size_ratio:.87,radar_direction_clockwise:!1},R0={treemap_tile:"binary",treemap_label_format:void 0,treemap_label_threshold:.05,treemap_label_show:!0};function da(t,e){yn(Vr.prototype,Object.values(Du).concat(t)),yn(Er.prototype,Jy),Nr.setOptions(Object.values(Lu).concat(e||[]))}function mr(t,e){da([ca,Ji,Nx].concat(t||[])),Nr.setOptions([Xr,y0].concat(e||[]))}function ha(t,e){yn(Vr.prototype,[px,ca].concat(t||[])),Nr.setOptions([Xr].concat(e||[]))}let rf=()=>(mr(sa,[fa]),(rf=()=>oe.AREA)()),af=()=>(mr(sa,[fa]),(af=()=>oe.AREA_LINE_RANGE)()),of=()=>(mr(sa,[fa]),(of=()=>oe.AREA_STEP_RANGE)()),sf=()=>(mr(sa,[fa,Is]),(sf=()=>oe.AREA_SPLINE)()),lf=()=>(mr(sa,[fa,Is]),(lf=()=>oe.AREA_SPLINE_RANGE)()),cf=()=>(mr(sa,[fa]),(cf=()=>oe.AREA_STEP)()),uf=()=>(mr(),(uf=()=>oe.LINE)()),ff=()=>(mr(void 0,[Is]),(ff=()=>oe.SPLINE)()),df=()=>(mr(),(df=()=>oe.STEP)()),hf=()=>(ha(void 0,[_i,T0]),(hf=()=>oe.DONUT)()),gf=()=>(ha([Mx],[_i,S0]),(gf=()=>oe.GAUGE)()),vf=()=>(ha(void 0,[_i,A0]),(vf=()=>oe.PIE)()),pf=()=>(ha([Ux],[_i,E0]),(pf=()=>oe.POLAR)()),mf=()=>(ha([Du.eventrect,Ji,jx],[Xr,b0,{axis_x_categories:Lu.optAxis.axis_x_categories}]),(mf=()=>oe.RADAR)()),yf=()=>(da([yx,ca],[v0,Xr]),(yf=()=>oe.BAR)()),xf=()=>(da([ca,Ji,xx],[p0,Xr]),(xf=()=>oe.BUBBLE)()),Tf=()=>(da([Ex,ca],[m0,Xr]),(Tf=()=>oe.CANDLESTICK)()),$f=()=>(da([ca,Ji],[Xr,x0]),($f=()=>oe.SCATTER)()),Sf=()=>(ha([wx],[$0]),(Sf=()=>oe.FUNNEL)()),Af=()=>(da([g0],[R0]),(Af=()=>oe.TREEMAP)()),Os=Object.create(null);const Ef={version:"3.15.1",generate(t){const e=ea(Object.create(null),Os,t),n=new Er(e);return n.internal.charts=this.instance,this.instance.push(n),n},defaults(t){return Be(t)&&(Os=t),Os},instance:[],plugin:{}};Object.keys(d).forEach(t=>d[t]()),Object.keys(u).forEach(t=>u[t]())}],Xa={};function zn(x){var b=Xa[x];if(b!==void 0)return b.exports;var r=Xa[x]={exports:{}};return Cs[x].call(r.exports,r,r.exports,zn),r.exports}(function(){zn.d=function(x,b){for(var r in b)zn.o(b,r)&&!zn.o(x,r)&&Object.defineProperty(x,r,{enumerable:!0,get:b[r]})}})(),function(){zn.o=function(x,b){return Object.prototype.hasOwnProperty.call(x,b)}}(),function(){zn.r=function(x){typeof Symbol!="undefined"&&Symbol.toStringTag&&Object.defineProperty(x,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(x,"__esModule",{value:!0})}}(),zn(0);var Ha=zn(584);return Ha}()}); diff --git a/src/Report/Html/Renderer/Template/js/bootstrap.bundle.min.js b/src/Report/Html/Renderer/Template/js/bootstrap.bundle.min.js new file mode 100644 index 000000000..8739c91c5 --- /dev/null +++ b/src/Report/Html/Renderer/Template/js/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v5.3.6 (https://getbootstrap.com/) + * Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),s=t=>{t.dispatchEvent(new Event(i))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,a=t=>{if(!o(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},l=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),c=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?c(t.parentNode):null},h=()=>{},d=t=>{t.offsetHeight},u=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,f=[],p=()=>"rtl"===document.documentElement.dir,m=t=>{var e;e=()=>{const e=u();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(f.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of f)t()})),f.push(e)):e()},g=(t,e=[],i=t)=>"function"==typeof t?t.call(...e):i,_=(t,e,n=!0)=>{if(!n)return void g(t);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),g(t))};e.addEventListener(i,a),setTimeout((()=>{r||s(e)}),o)},b=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,A={};let E=1;const T={mouseenter:"mouseover",mouseleave:"mouseout"},C=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function O(t,e){return e&&`${e}::${E++}`||t.uidEvent||E++}function x(t){const e=O(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function k(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function L(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=I(t);return C.has(o)||(o=t),[n,s,o]}function S(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=L(e,i,n);if(e in T){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=x(t),c=l[a]||(l[a]={}),h=k(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=O(r,e.replace(v,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return P(s,{delegateTarget:r}),n.oneOff&&N.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return P(n,{delegateTarget:t}),i.oneOff&&N.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function D(t,e,i,n,s){const o=k(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function $(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&D(t,e,i,r.callable,r.delegationSelector)}function I(t){return t=t.replace(y,""),T[t]||t}const N={on(t,e,i,n){S(t,e,i,n,!1)},one(t,e,i,n){S(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=L(e,i,n),a=r!==e,l=x(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))$(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(w,"");a&&!e.includes(s)||D(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;D(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=u();let s=null,o=!0,r=!0,a=!1;e!==I(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=P(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function P(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function j(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function M(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const F={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${M(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${M(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1),e[i]=j(t.dataset[n])}return e},getDataAttribute:(t,e)=>j(t.getAttribute(`data-bs-${M(e)}`))};class H{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=o(e)?F.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...o(e)?F.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],r=o(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${r}" but expected type "${s}".`)}var i}}class W extends H{constructor(t,i){super(),(t=r(t))&&(this._element=t,this._config=this._getConfig(i),e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){e.remove(this._element,this.constructor.DATA_KEY),N.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){_(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return e.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.6"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const B=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e?e.split(",").map((t=>n(t))).join(","):null},z={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!l(t)&&a(t)))},getSelectorFromElement(t){const e=B(t);return e&&z.findOne(e)?e:null},getElementFromSelector(t){const e=B(t);return e?z.findOne(e):null},getMultipleElementsFromSelector(t){const e=B(t);return e?z.find(e):[]}},R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;N.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),l(this))return;const s=z.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},q=".bs.alert",V=`close${q}`,K=`closed${q}`;class Q extends W{static get NAME(){return"alert"}close(){if(N.trigger(this._element,V).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),N.trigger(this._element,K),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Q.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(Q,"close"),m(Q);const X='[data-bs-toggle="button"]';class Y extends W{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=Y.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}N.on(document,"click.bs.button.data-api",X,(t=>{t.preventDefault();const e=t.target.closest(X);Y.getOrCreateInstance(e).toggle()})),m(Y);const U=".bs.swipe",G=`touchstart${U}`,J=`touchmove${U}`,Z=`touchend${U}`,tt=`pointerdown${U}`,et=`pointerup${U}`,it={endCallback:null,leftCallback:null,rightCallback:null},nt={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class st extends H{constructor(t,e){super(),this._element=t,t&&st.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return it}static get DefaultType(){return nt}static get NAME(){return"swipe"}dispose(){N.off(this._element,U)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),g(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&g(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(N.on(this._element,tt,(t=>this._start(t))),N.on(this._element,et,(t=>this._end(t))),this._element.classList.add("pointer-event")):(N.on(this._element,G,(t=>this._start(t))),N.on(this._element,J,(t=>this._move(t))),N.on(this._element,Z,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const ot=".bs.carousel",rt=".data-api",at="ArrowLeft",lt="ArrowRight",ct="next",ht="prev",dt="left",ut="right",ft=`slide${ot}`,pt=`slid${ot}`,mt=`keydown${ot}`,gt=`mouseenter${ot}`,_t=`mouseleave${ot}`,bt=`dragstart${ot}`,vt=`load${ot}${rt}`,yt=`click${ot}${rt}`,wt="carousel",At="active",Et=".active",Tt=".carousel-item",Ct=Et+Tt,Ot={[at]:ut,[lt]:dt},xt={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},kt={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class Lt extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===wt&&this.cycle()}static get Default(){return xt}static get DefaultType(){return kt}static get NAME(){return"carousel"}next(){this._slide(ct)}nextWhenVisible(){!document.hidden&&a(this._element)&&this.next()}prev(){this._slide(ht)}pause(){this._isSliding&&s(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?N.one(this._element,pt,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void N.one(this._element,pt,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?ct:ht;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&N.on(this._element,mt,(t=>this._keydown(t))),"hover"===this._config.pause&&(N.on(this._element,gt,(()=>this.pause())),N.on(this._element,_t,(()=>this._maybeEnableCycle()))),this._config.touch&&st.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of z.find(".carousel-item img",this._element))N.on(t,bt,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(dt)),rightCallback:()=>this._slide(this._directionToOrder(ut)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new st(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=Ot[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=z.findOne(Et,this._indicatorsElement);e.classList.remove(At),e.removeAttribute("aria-current");const i=z.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(At),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===ct,s=e||b(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>N.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(ft).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),d(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(At),i.classList.remove(At,c,l),this._isSliding=!1,r(pt)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return z.findOne(Ct,this._element)}_getItems(){return z.find(Tt,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return p()?t===dt?ht:ct:t===dt?ct:ht}_orderToDirection(t){return p()?t===ht?dt:ut:t===ht?ut:dt}static jQueryInterface(t){return this.each((function(){const e=Lt.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}N.on(document,yt,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=z.getElementFromSelector(this);if(!e||!e.classList.contains(wt))return;t.preventDefault();const i=Lt.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===F.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),N.on(window,vt,(()=>{const t=z.find('[data-bs-ride="carousel"]');for(const e of t)Lt.getOrCreateInstance(e)})),m(Lt);const St=".bs.collapse",Dt=`show${St}`,$t=`shown${St}`,It=`hide${St}`,Nt=`hidden${St}`,Pt=`click${St}.data-api`,jt="show",Mt="collapse",Ft="collapsing",Ht=`:scope .${Mt} .${Mt}`,Wt='[data-bs-toggle="collapse"]',Bt={parent:null,toggle:!0},zt={parent:"(null|element)",toggle:"boolean"};class Rt extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=z.find(Wt);for(const t of i){const e=z.getSelectorFromElement(t),i=z.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Bt}static get DefaultType(){return zt}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Rt.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(N.trigger(this._element,Dt).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(Mt),this._element.classList.add(Ft),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(Ft),this._element.classList.add(Mt,jt),this._element.style[e]="",N.trigger(this._element,$t)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(N.trigger(this._element,It).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,d(this._element),this._element.classList.add(Ft),this._element.classList.remove(Mt,jt);for(const t of this._triggerArray){const e=z.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(Ft),this._element.classList.add(Mt),N.trigger(this._element,Nt)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(jt)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=r(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(Wt);for(const e of t){const t=z.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=z.find(Ht,this._config.parent);return z.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Rt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}N.on(document,Pt,Wt,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of z.getMultipleElementsFromSelector(this))Rt.getOrCreateInstance(t,{toggle:!1}).toggle()})),m(Rt);var qt="top",Vt="bottom",Kt="right",Qt="left",Xt="auto",Yt=[qt,Vt,Kt,Qt],Ut="start",Gt="end",Jt="clippingParents",Zt="viewport",te="popper",ee="reference",ie=Yt.reduce((function(t,e){return t.concat([e+"-"+Ut,e+"-"+Gt])}),[]),ne=[].concat(Yt,[Xt]).reduce((function(t,e){return t.concat([e,e+"-"+Ut,e+"-"+Gt])}),[]),se="beforeRead",oe="read",re="afterRead",ae="beforeMain",le="main",ce="afterMain",he="beforeWrite",de="write",ue="afterWrite",fe=[se,oe,re,ae,le,ce,he,de,ue];function pe(t){return t?(t.nodeName||"").toLowerCase():null}function me(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function ge(t){return t instanceof me(t).Element||t instanceof Element}function _e(t){return t instanceof me(t).HTMLElement||t instanceof HTMLElement}function be(t){return"undefined"!=typeof ShadowRoot&&(t instanceof me(t).ShadowRoot||t instanceof ShadowRoot)}const ve={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];_e(s)&&pe(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});_e(n)&&pe(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function ye(t){return t.split("-")[0]}var we=Math.max,Ae=Math.min,Ee=Math.round;function Te(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function Ce(){return!/^((?!chrome|android).)*safari/i.test(Te())}function Oe(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&_e(t)&&(s=t.offsetWidth>0&&Ee(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&Ee(n.height)/t.offsetHeight||1);var r=(ge(t)?me(t):window).visualViewport,a=!Ce()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function xe(t){var e=Oe(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function ke(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&be(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function Le(t){return me(t).getComputedStyle(t)}function Se(t){return["table","td","th"].indexOf(pe(t))>=0}function De(t){return((ge(t)?t.ownerDocument:t.document)||window.document).documentElement}function $e(t){return"html"===pe(t)?t:t.assignedSlot||t.parentNode||(be(t)?t.host:null)||De(t)}function Ie(t){return _e(t)&&"fixed"!==Le(t).position?t.offsetParent:null}function Ne(t){for(var e=me(t),i=Ie(t);i&&Se(i)&&"static"===Le(i).position;)i=Ie(i);return i&&("html"===pe(i)||"body"===pe(i)&&"static"===Le(i).position)?e:i||function(t){var e=/firefox/i.test(Te());if(/Trident/i.test(Te())&&_e(t)&&"fixed"===Le(t).position)return null;var i=$e(t);for(be(i)&&(i=i.host);_e(i)&&["html","body"].indexOf(pe(i))<0;){var n=Le(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function Pe(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function je(t,e,i){return we(t,Ae(e,i))}function Me(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function Fe(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const He={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=ye(i.placement),l=Pe(a),c=[Qt,Kt].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return Me("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:Fe(t,Yt))}(s.padding,i),d=xe(o),u="y"===l?qt:Qt,f="y"===l?Vt:Kt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=Ne(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,A=je(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&ke(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function We(t){return t.split("-")[1]}var Be={top:"auto",right:"auto",bottom:"auto",left:"auto"};function ze(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=t.isFixed,u=r.x,f=void 0===u?0:u,p=r.y,m=void 0===p?0:p,g="function"==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty("x"),b=r.hasOwnProperty("y"),v=Qt,y=qt,w=window;if(c){var A=Ne(i),E="clientHeight",T="clientWidth";A===me(i)&&"static"!==Le(A=De(i)).position&&"absolute"===a&&(E="scrollHeight",T="scrollWidth"),(s===qt||(s===Qt||s===Kt)&&o===Gt)&&(y=Vt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==Qt&&(s!==qt&&s!==Vt||o!==Gt)||(v=Kt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&Be),x=!0===h?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:Ee(i*s)/s||0,y:Ee(n*s)/s||0}}({x:f,y:m},me(i)):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?"0":"",C[v]=_?"0":"",C.transform=(w.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",C)):Object.assign({},O,((e={})[y]=b?m+"px":"",e[v]=_?f+"px":"",e.transform="",e))}const Re={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:ye(e.placement),variation:We(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,ze(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,ze(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var qe={passive:!0};const Ve={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=me(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,qe)})),a&&l.addEventListener("resize",i.update,qe),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,qe)})),a&&l.removeEventListener("resize",i.update,qe)}},data:{}};var Ke={left:"right",right:"left",bottom:"top",top:"bottom"};function Qe(t){return t.replace(/left|right|bottom|top/g,(function(t){return Ke[t]}))}var Xe={start:"end",end:"start"};function Ye(t){return t.replace(/start|end/g,(function(t){return Xe[t]}))}function Ue(t){var e=me(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Ge(t){return Oe(De(t)).left+Ue(t).scrollLeft}function Je(t){var e=Le(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ze(t){return["html","body","#document"].indexOf(pe(t))>=0?t.ownerDocument.body:_e(t)&&Je(t)?t:Ze($e(t))}function ti(t,e){var i;void 0===e&&(e=[]);var n=Ze(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=me(n),r=s?[o].concat(o.visualViewport||[],Je(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(ti($e(r)))}function ei(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function ii(t,e,i){return e===Zt?ei(function(t,e){var i=me(t),n=De(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Ce();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+Ge(t),y:l}}(t,i)):ge(e)?function(t,e){var i=Oe(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):ei(function(t){var e,i=De(t),n=Ue(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=we(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=we(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+Ge(t),l=-n.scrollTop;return"rtl"===Le(s||i).direction&&(a+=we(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(De(t)))}function ni(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?ye(s):null,r=s?We(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case qt:e={x:a,y:i.y-n.height};break;case Vt:e={x:a,y:i.y+i.height};break;case Kt:e={x:i.x+i.width,y:l};break;case Qt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?Pe(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case Ut:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Gt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function si(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?Jt:a,c=i.rootBoundary,h=void 0===c?Zt:c,d=i.elementContext,u=void 0===d?te:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=Me("number"!=typeof g?g:Fe(g,Yt)),b=u===te?ee:te,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=ti($e(t)),i=["absolute","fixed"].indexOf(Le(t).position)>=0&&_e(t)?Ne(t):t;return ge(i)?e.filter((function(t){return ge(t)&&ke(t,i)&&"body"!==pe(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=ii(t,i,n);return e.top=we(s.top,e.top),e.right=Ae(s.right,e.right),e.bottom=Ae(s.bottom,e.bottom),e.left=we(s.left,e.left),e}),ii(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(ge(y)?y:y.contextElement||De(t.elements.popper),l,h,r),A=Oe(t.elements.reference),E=ni({reference:A,element:v,placement:s}),T=ei(Object.assign({},v,E)),C=u===te?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===te&&x){var k=x[s];Object.keys(O).forEach((function(t){var e=[Kt,Vt].indexOf(t)>=0?1:-1,i=[qt,Vt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e}))}return O}function oi(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?ne:l,h=We(n),d=h?a?ie:ie.filter((function(t){return We(t)===h})):Yt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=si(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[ye(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const ri={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=ye(g),b=l||(_!==g&&p?function(t){if(ye(t)===Xt)return[];var e=Qe(t);return[Ye(t),e,Ye(e)]}(g):[Qe(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(ye(i)===Xt?oi(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,A=new Map,E=!0,T=v[0],C=0;C=0,S=L?"width":"height",D=si(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),$=L?k?Kt:Qt:k?Vt:qt;y[S]>w[S]&&($=Qe($));var I=Qe($),N=[];if(o&&N.push(D[x]<=0),a&&N.push(D[$]<=0,D[I]<=0),N.every((function(t){return t}))){T=O,E=!1;break}A.set(O,N)}if(E)for(var P=function(t){var e=v.find((function(e){var i=A.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},j=p?3:1;j>0&&"break"!==P(j);j--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function ai(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function li(t){return[qt,Kt,Vt,Qt].some((function(e){return t[e]>=0}))}const ci={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=si(e,{elementContext:"reference"}),a=si(e,{altBoundary:!0}),l=ai(r,n),c=ai(a,s,o),h=li(l),d=li(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},hi={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=ne.reduce((function(t,i){return t[i]=function(t,e,i){var n=ye(t),s=[Qt,qt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[Qt,Kt].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},di={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=ni({reference:e.rects.reference,element:e.rects.popper,placement:e.placement})},data:{}},ui={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=si(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=ye(e.placement),b=We(e.placement),v=!b,y=Pe(_),w="x"===y?"y":"x",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O="number"==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S="y"===y?qt:Qt,D="y"===y?Vt:Kt,$="y"===y?"height":"width",I=A[y],N=I+g[S],P=I-g[D],j=f?-T[$]/2:0,M=b===Ut?E[$]:T[$],F=b===Ut?-T[$]:-E[$],H=e.elements.arrow,W=f&&H?xe(H):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=je(0,E[$],W[$]),V=v?E[$]/2-j-q-z-O.mainAxis:M-q-z-O.mainAxis,K=v?-E[$]/2+j+q+R+O.mainAxis:F+q+R+O.mainAxis,Q=e.elements.arrow&&Ne(e.elements.arrow),X=Q?"y"===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=I+K-Y,G=je(f?Ae(N,I+V-Y-X):N,I,f?we(P,U):P);A[y]=G,k[y]=G-I}if(a){var J,Z="x"===y?qt:Qt,tt="x"===y?Vt:Kt,et=A[w],it="y"===w?"height":"width",nt=et+g[Z],st=et-g[tt],ot=-1!==[qt,Qt].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=je(t,e,i);return n>i?i:n}(at,et,lt):je(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:["offset"]};function fi(t,e,i){void 0===i&&(i=!1);var n,s,o=_e(e),r=_e(e)&&function(t){var e=t.getBoundingClientRect(),i=Ee(e.width)/t.offsetWidth||1,n=Ee(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=De(e),l=Oe(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==pe(e)||Je(a))&&(c=(n=e)!==me(n)&&_e(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:Ue(n)),_e(e)?((h=Oe(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=Ge(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function pi(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var mi={placement:"bottom",modifiers:[],strategy:"absolute"};function gi(){for(var t=arguments.length,e=new Array(t),i=0;iNumber.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(F.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...g(this._config.popperConfig,[void 0,t])}}_selectMenuItem({key:t,target:e}){const i=z.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>a(t)));i.length&&b(i,e,t===Oi,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=Ki.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=z.find(ji);for(const i of e){const e=Ki.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Ci,Oi].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Pi)?this:z.prev(this,Pi)[0]||z.next(this,Pi)[0]||z.findOne(Pi,t.delegateTarget.parentNode),o=Ki.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}N.on(document,$i,Pi,Ki.dataApiKeydownHandler),N.on(document,$i,Mi,Ki.dataApiKeydownHandler),N.on(document,Di,Ki.clearMenus),N.on(document,Ii,Ki.clearMenus),N.on(document,Di,Pi,(function(t){t.preventDefault(),Ki.getOrCreateInstance(this).toggle()})),m(Ki);const Qi="backdrop",Xi="show",Yi=`mousedown.bs.${Qi}`,Ui={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Gi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Ji extends H{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Ui}static get DefaultType(){return Gi}static get NAME(){return Qi}show(t){if(!this._config.isVisible)return void g(t);this._append();const e=this._getElement();this._config.isAnimated&&d(e),e.classList.add(Xi),this._emulateAnimation((()=>{g(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Xi),this._emulateAnimation((()=>{this.dispose(),g(t)}))):g(t)}dispose(){this._isAppended&&(N.off(this._element,Yi),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=r(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),N.on(t,Yi,(()=>{g(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){_(t,this._getElement(),this._config.isAnimated)}}const Zi=".bs.focustrap",tn=`focusin${Zi}`,en=`keydown.tab${Zi}`,nn="backward",sn={autofocus:!0,trapElement:null},on={autofocus:"boolean",trapElement:"element"};class rn extends H{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return sn}static get DefaultType(){return on}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),N.off(document,Zi),N.on(document,tn,(t=>this._handleFocusin(t))),N.on(document,en,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,N.off(document,Zi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=z.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===nn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?nn:"forward")}}const an=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",ln=".sticky-top",cn="padding-right",hn="margin-right";class dn{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,cn,(e=>e+t)),this._setElementAttributes(an,cn,(e=>e+t)),this._setElementAttributes(ln,hn,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,cn),this._resetElementAttributes(an,cn),this._resetElementAttributes(ln,hn)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&F.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=F.getDataAttribute(t,e);null!==i?(F.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(o(t))e(t);else for(const i of z.find(t,this._element))e(i)}}const un=".bs.modal",fn=`hide${un}`,pn=`hidePrevented${un}`,mn=`hidden${un}`,gn=`show${un}`,_n=`shown${un}`,bn=`resize${un}`,vn=`click.dismiss${un}`,yn=`mousedown.dismiss${un}`,wn=`keydown.dismiss${un}`,An=`click${un}.data-api`,En="modal-open",Tn="show",Cn="modal-static",On={backdrop:!0,focus:!0,keyboard:!0},xn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class kn extends W{constructor(t,e){super(t,e),this._dialog=z.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new dn,this._addEventListeners()}static get Default(){return On}static get DefaultType(){return xn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||N.trigger(this._element,gn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(En),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(N.trigger(this._element,fn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Tn),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){N.off(window,un),N.off(this._dialog,un),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ji({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new rn({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=z.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),d(this._element),this._element.classList.add(Tn),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,N.trigger(this._element,_n,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){N.on(this._element,wn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),N.on(window,bn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),N.on(this._element,yn,(t=>{N.one(this._element,vn,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(En),this._resetAdjustments(),this._scrollBar.reset(),N.trigger(this._element,mn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(N.trigger(this._element,pn).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(Cn)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(Cn),this._queueCallback((()=>{this._element.classList.remove(Cn),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=p()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=p()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=kn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}N.on(document,An,'[data-bs-toggle="modal"]',(function(t){const e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),N.one(e,gn,(t=>{t.defaultPrevented||N.one(e,mn,(()=>{a(this)&&this.focus()}))}));const i=z.findOne(".modal.show");i&&kn.getInstance(i).hide(),kn.getOrCreateInstance(e).toggle(this)})),R(kn),m(kn);const Ln=".bs.offcanvas",Sn=".data-api",Dn=`load${Ln}${Sn}`,$n="show",In="showing",Nn="hiding",Pn=".offcanvas.show",jn=`show${Ln}`,Mn=`shown${Ln}`,Fn=`hide${Ln}`,Hn=`hidePrevented${Ln}`,Wn=`hidden${Ln}`,Bn=`resize${Ln}`,zn=`click${Ln}${Sn}`,Rn=`keydown.dismiss${Ln}`,qn={backdrop:!0,keyboard:!0,scroll:!1},Vn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class Kn extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return qn}static get DefaultType(){return Vn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||N.trigger(this._element,jn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new dn).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(In),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add($n),this._element.classList.remove(In),N.trigger(this._element,Mn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(N.trigger(this._element,Fn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(Nn),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove($n,Nn),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new dn).reset(),N.trigger(this._element,Wn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Ji({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():N.trigger(this._element,Hn)}:null})}_initializeFocusTrap(){return new rn({trapElement:this._element})}_addEventListeners(){N.on(this._element,Rn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():N.trigger(this._element,Hn))}))}static jQueryInterface(t){return this.each((function(){const e=Kn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}N.on(document,zn,'[data-bs-toggle="offcanvas"]',(function(t){const e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this))return;N.one(e,Wn,(()=>{a(this)&&this.focus()}));const i=z.findOne(Pn);i&&i!==e&&Kn.getInstance(i).hide(),Kn.getOrCreateInstance(e).toggle(this)})),N.on(window,Dn,(()=>{for(const t of z.find(Pn))Kn.getOrCreateInstance(t).show()})),N.on(window,Bn,(()=>{for(const t of z.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&Kn.getOrCreateInstance(t).hide()})),R(Kn),m(Kn);const Qn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Xn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Yn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Un=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Xn.has(i)||Boolean(Yn.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Gn={allowList:Qn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
    "},Jn={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Zn={entry:"(string|element|function|null)",selector:"(string|element)"};class ts extends H{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Gn}static get DefaultType(){return Jn}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Zn)}_setContent(t,e,i){const n=z.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?o(e)?this._putElementInTemplate(r(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Un(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return g(t,[void 0,this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const es=new Set(["sanitize","allowList","sanitizeFn"]),is="fade",ns="show",ss=".tooltip-inner",os=".modal",rs="hide.bs.modal",as="hover",ls="focus",cs={AUTO:"auto",TOP:"top",RIGHT:p()?"left":"right",BOTTOM:"bottom",LEFT:p()?"right":"left"},hs={allowList:Qn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ds={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class us extends W{constructor(t,e){if(void 0===wi)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org/docs/v2/)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return hs}static get DefaultType(){return ds}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),N.off(this._element.closest(os),rs,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=N.trigger(this._element,this.constructor.eventName("show")),e=(c(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),N.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(ns),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.on(t,"mouseover",h);this._queueCallback((()=>{N.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!N.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(ns),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.off(t,"mouseover",h);this._activeTrigger.click=!1,this._activeTrigger[ls]=!1,this._activeTrigger[as]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),N.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(is,ns),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(is),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new ts({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{[ss]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(is)}_isShown(){return this.tip&&this.tip.classList.contains(ns)}_createPopper(t){const e=g(this._config.placement,[this,t,this._element]),i=cs[e.toUpperCase()];return yi(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return g(t,[this._element,this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...g(this._config.popperConfig,[void 0,e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)N.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===as?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===as?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");N.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?ls:as]=!0,e._enter()})),N.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?ls:as]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},N.on(this._element.closest(os),rs,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=F.getDataAttributes(this._element);for(const t of Object.keys(e))es.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:r(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=us.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(us);const fs=".popover-header",ps=".popover-body",ms={...us.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},gs={...us.DefaultType,content:"(null|string|element|function)"};class _s extends us{static get Default(){return ms}static get DefaultType(){return gs}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[fs]:this._getTitle(),[ps]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=_s.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(_s);const bs=".bs.scrollspy",vs=`activate${bs}`,ys=`click${bs}`,ws=`load${bs}.data-api`,As="active",Es="[href]",Ts=".nav-link",Cs=`${Ts}, .nav-item > ${Ts}, .list-group-item`,Os={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},xs={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class ks extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return Os}static get DefaultType(){return xs}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=r(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(N.off(this._config.target,ys),N.on(this._config.target,ys,Es,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=z.find(Es,this._config.target);for(const e of t){if(!e.hash||l(e))continue;const t=z.findOne(decodeURI(e.hash),this._element);a(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(As),this._activateParents(t),N.trigger(this._element,vs,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))z.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(As);else for(const e of z.parents(t,".nav, .list-group"))for(const t of z.prev(e,Cs))t.classList.add(As)}_clearActiveClass(t){t.classList.remove(As);const e=z.find(`${Es}.${As}`,t);for(const t of e)t.classList.remove(As)}static jQueryInterface(t){return this.each((function(){const e=ks.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(window,ws,(()=>{for(const t of z.find('[data-bs-spy="scroll"]'))ks.getOrCreateInstance(t)})),m(ks);const Ls=".bs.tab",Ss=`hide${Ls}`,Ds=`hidden${Ls}`,$s=`show${Ls}`,Is=`shown${Ls}`,Ns=`click${Ls}`,Ps=`keydown${Ls}`,js=`load${Ls}`,Ms="ArrowLeft",Fs="ArrowRight",Hs="ArrowUp",Ws="ArrowDown",Bs="Home",zs="End",Rs="active",qs="fade",Vs="show",Ks=".dropdown-toggle",Qs=`:not(${Ks})`,Xs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Ys=`.nav-link${Qs}, .list-group-item${Qs}, [role="tab"]${Qs}, ${Xs}`,Us=`.${Rs}[data-bs-toggle="tab"], .${Rs}[data-bs-toggle="pill"], .${Rs}[data-bs-toggle="list"]`;class Gs extends W{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),N.on(this._element,Ps,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?N.trigger(e,Ss,{relatedTarget:t}):null;N.trigger(t,$s,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Rs),this._activate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),N.trigger(t,Is,{relatedTarget:e})):t.classList.add(Vs)}),t,t.classList.contains(qs)))}_deactivate(t,e){t&&(t.classList.remove(Rs),t.blur(),this._deactivate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),N.trigger(t,Ds,{relatedTarget:e})):t.classList.remove(Vs)}),t,t.classList.contains(qs)))}_keydown(t){if(![Ms,Fs,Hs,Ws,Bs,zs].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!l(t)));let i;if([Bs,zs].includes(t.key))i=e[t.key===Bs?0:e.length-1];else{const n=[Fs,Ws].includes(t.key);i=b(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Gs.getOrCreateInstance(i).show())}_getChildren(){return z.find(Ys,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=z.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=z.findOne(t,i);s&&s.classList.toggle(n,e)};n(Ks,Rs),n(".dropdown-menu",Vs),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Rs)}_getInnerElement(t){return t.matches(Ys)?t:z.findOne(Ys,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Gs.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(document,Ns,Xs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this)||Gs.getOrCreateInstance(this).show()})),N.on(window,js,(()=>{for(const t of z.find(Us))Gs.getOrCreateInstance(t)})),m(Gs);const Js=".bs.toast",Zs=`mouseover${Js}`,to=`mouseout${Js}`,eo=`focusin${Js}`,io=`focusout${Js}`,no=`hide${Js}`,so=`hidden${Js}`,oo=`show${Js}`,ro=`shown${Js}`,ao="hide",lo="show",co="showing",ho={animation:"boolean",autohide:"boolean",delay:"number"},uo={animation:!0,autohide:!0,delay:5e3};class fo extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return uo}static get DefaultType(){return ho}static get NAME(){return"toast"}show(){N.trigger(this._element,oo).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(ao),d(this._element),this._element.classList.add(lo,co),this._queueCallback((()=>{this._element.classList.remove(co),N.trigger(this._element,ro),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(N.trigger(this._element,no).defaultPrevented||(this._element.classList.add(co),this._queueCallback((()=>{this._element.classList.add(ao),this._element.classList.remove(co,lo),N.trigger(this._element,so)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(lo),super.dispose()}isShown(){return this._element.classList.contains(lo)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){N.on(this._element,Zs,(t=>this._onInteraction(t,!0))),N.on(this._element,to,(t=>this._onInteraction(t,!1))),N.on(this._element,eo,(t=>this._onInteraction(t,!0))),N.on(this._element,io,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=fo.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(fo),m(fo),{Alert:Q,Button:Y,Carousel:Lt,Collapse:Rt,Dropdown:Ki,Modal:kn,Offcanvas:Kn,Popover:_s,ScrollSpy:ks,Tab:Gs,Toast:fo,Tooltip:us}})); +//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/src/Report/Html/Renderer/Template/js/file.js b/src/Report/Html/Renderer/Template/js/file.js new file mode 100644 index 000000000..124a8a18f --- /dev/null +++ b/src/Report/Html/Renderer/Template/js/file.js @@ -0,0 +1,53 @@ +$(function () { + var $window = $(window) + , $top_link = $('#toplink') + , $body = $('body, html') + , offset = $('#code').offset().top; + + $top_link.hide().click(function (event) { + event.preventDefault(); + $body.animate({scrollTop: 0}, 800); + }); + + $window.scroll(function () { + if ($window.scrollTop() > offset) { + $top_link.fadeIn(); + } else { + $top_link.fadeOut(); + } + }); + + var $popovers = $('.popin > :first-child'); + $('.popin').on({ + 'click.popover': function (event) { + event.stopPropagation(); + + var $container = $(this).children().first(); + + //Close all other popovers: + $popovers.each(function () { + var $current = $(this); + if (!$current.is($container)) { + $current.popover('hide'); + } + }); + + // Toggle this popover: + $container.popover('toggle'); + }, + }); + + //Hide all popovers on outside click: + $(document).click(function (event) { + if ($(event.target).closest($('.popover')).length === 0) { + $popovers.popover('hide'); + } + }); + + //Hide all popovers on escape: + $(document).keyup(function (event) { + if (event.key === 'Escape') { + $popovers.popover('hide'); + } + }); +}); diff --git a/src/Report/Html/Renderer/Template/js/jquery.min.js b/src/Report/Html/Renderer/Template/js/jquery.min.js new file mode 100644 index 000000000..798cc8bf7 --- /dev/null +++ b/src/Report/Html/Renderer/Template/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="
    ",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0{{lineNumber}}{{lineContent}} diff --git a/src/Report/Html/Renderer/Template/lines.html.dist b/src/Report/Html/Renderer/Template/lines.html.dist new file mode 100644 index 000000000..add40e4e9 --- /dev/null +++ b/src/Report/Html/Renderer/Template/lines.html.dist @@ -0,0 +1,5 @@ + + +{{lines}} + +
    diff --git a/src/Report/Html/Renderer/Template/method_item.html.dist b/src/Report/Html/Renderer/Template/method_item.html.dist new file mode 100644 index 000000000..2311d4564 --- /dev/null +++ b/src/Report/Html/Renderer/Template/method_item.html.dist @@ -0,0 +1,12 @@ + + {{name}} + {{lines_bar}} +
    {{lines_executed_percent}}
    +
    {{lines_number}}
    + {{methods_bar}} +
    {{methods_tested_percent}}
    +
    {{methods_number}}
    + {{crap}} + + + diff --git a/src/Report/Html/Renderer/Template/method_item_branch.html.dist b/src/Report/Html/Renderer/Template/method_item_branch.html.dist new file mode 100644 index 000000000..36d6cb741 --- /dev/null +++ b/src/Report/Html/Renderer/Template/method_item_branch.html.dist @@ -0,0 +1,18 @@ + + {{name}} + {{lines_bar}} +
    {{lines_executed_percent}}
    +
    {{lines_number}}
    + {{branches_bar}} +
    {{branches_executed_percent}}
    +
    {{branches_number}}
    + {{paths_bar}} +
    {{paths_executed_percent}}
    +
    {{paths_number}}
    + {{methods_bar}} +
    {{methods_tested_percent}}
    +
    {{methods_number}}
    + {{crap}} + + + diff --git a/src/Report/Html/Renderer/Template/paths.html.dist b/src/Report/Html/Renderer/Template/paths.html.dist new file mode 100644 index 000000000..d14b8ad9d --- /dev/null +++ b/src/Report/Html/Renderer/Template/paths.html.dist @@ -0,0 +1,9 @@ +
    +

    Paths

    +

    + Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not + necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. + Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement + always has an else as part of its logical flow even if you didn't write one. +

    +{{paths}} diff --git a/src/Report/OpenClover.php b/src/Report/OpenClover.php new file mode 100644 index 000000000..042132b33 --- /dev/null +++ b/src/Report/OpenClover.php @@ -0,0 +1,257 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use function assert; +use function basename; +use function count; +use function dirname; +use function file_put_contents; +use function is_string; +use function ksort; +use function max; +use function range; +use function str_contains; +use function str_replace; +use function time; +use DOMDocument; +use DOMElement; +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Node\File; +use SebastianBergmann\CodeCoverage\Util\Filesystem; +use SebastianBergmann\CodeCoverage\Version; +use SebastianBergmann\CodeCoverage\WriteOperationFailedException; + +final class OpenClover +{ + /** + * @throws WriteOperationFailedException + */ + public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string + { + $time = (string) time(); + + $xmlDocument = new DOMDocument('1.0', 'UTF-8'); + $xmlDocument->formatOutput = true; + + $xmlCoverage = $xmlDocument->createElement('coverage'); + $xmlCoverage->setAttribute('clover', Version::id()); + $xmlCoverage->setAttribute('generated', $time); + $xmlDocument->appendChild($xmlCoverage); + + $xmlProject = $xmlDocument->createElement('project'); + $xmlProject->setAttribute('timestamp', $time); + + if (is_string($name)) { + $xmlProject->setAttribute('name', $name); + } + + $xmlCoverage->appendChild($xmlProject); + + /** @var array $packages */ + $packages = []; + $report = $coverage->getReport(); + + foreach ($report as $item) { + if (!$item instanceof File) { + continue; + } + + $xmlFile = $xmlDocument->createElement('file'); + $xmlFile->setAttribute('name', basename($item->pathAsString())); + $xmlFile->setAttribute('path', $item->pathAsString()); + + $classes = $item->classesAndTraits(); + $coverageData = $item->lineCoverageData(); + $lines = []; + $namespace = 'global'; + + foreach ($classes as $className => $class) { + $classStatements = 0; + $coveredClassStatements = 0; + $coveredMethods = 0; + $classMethods = 0; + + // Assumption: one namespace per file + if ($class['namespace'] !== '') { + $namespace = $class['namespace']; + } + + foreach ($class['methods'] as $methodName => $method) { + /** @phpstan-ignore equal.notAllowed */ + if ($method['executableLines'] == 0) { + continue; + } + + $classMethods++; + $classStatements += $method['executableLines']; + $coveredClassStatements += $method['executedLines']; + + /** @phpstan-ignore equal.notAllowed */ + if ($method['coverage'] == 100) { + $coveredMethods++; + } + + $methodCount = 0; + + foreach (range($method['startLine'], $method['endLine']) as $line) { + if (isset($coverageData[$line])) { + $methodCount = max($methodCount, count($coverageData[$line])); + } + } + + $lines[$method['startLine']] = [ + 'ccn' => $method['ccn'], + 'count' => $methodCount, + 'type' => 'method', + 'signature' => $method['signature'], + 'visibility' => $method['visibility'], + ]; + } + + $xmlClass = $xmlDocument->createElement('class'); + $xmlClass->setAttribute('name', str_replace($class['namespace'] . '\\', '', $className)); + + $xmlFile->appendChild($xmlClass); + + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('complexity', (string) $class['ccn']); + $xmlMetrics->setAttribute('elements', (string) ($classMethods + $classStatements + $class['executableBranches'])); + $xmlMetrics->setAttribute('coveredelements', (string) ($coveredMethods + $coveredClassStatements + $class['executedBranches'])); + $xmlMetrics->setAttribute('conditionals', (string) $class['executableBranches']); + $xmlMetrics->setAttribute('coveredconditionals', (string) $class['executedBranches']); + $xmlMetrics->setAttribute('statements', (string) $classStatements); + $xmlMetrics->setAttribute('coveredstatements', (string) $coveredClassStatements); + $xmlMetrics->setAttribute('methods', (string) $classMethods); + $xmlMetrics->setAttribute('coveredmethods', (string) $coveredMethods); + $xmlClass->insertBefore($xmlMetrics, $xmlClass->firstChild); + } + + foreach ($coverageData as $line => $data) { + if ($data === null || isset($lines[$line])) { + continue; + } + + $lines[$line] = [ + 'count' => count($data), + 'type' => 'stmt', + ]; + } + + ksort($lines); + + foreach ($lines as $line => $data) { + $xmlLine = $xmlDocument->createElement('line'); + $xmlLine->setAttribute('num', (string) $line); + $xmlLine->setAttribute('type', $data['type']); + + if (isset($data['ccn'])) { + $xmlLine->setAttribute('complexity', (string) $data['ccn']); + } + + $xmlLine->setAttribute('count', (string) $data['count']); + + if (isset($data['signature'])) { + $xmlLine->setAttribute('signature', $data['signature']); + } + + if (isset($data['visibility'])) { + $xmlLine->setAttribute('visibility', $data['visibility']); + } + + $xmlFile->appendChild($xmlLine); + } + + $linesOfCode = $item->linesOfCode(); + + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode()); + $xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode()); + $xmlMetrics->setAttribute('classes', (string) $item->numberOfClassesAndTraits()); + $xmlMetrics->setAttribute('complexity', (string) $item->cyclomaticComplexity()); + $xmlMetrics->setAttribute('elements', (string) ($item->numberOfMethods() + $item->numberOfExecutableLines() + $item->numberOfExecutableBranches())); + $xmlMetrics->setAttribute('coveredelements', (string) ($item->numberOfTestedMethods() + $item->numberOfExecutedLines() + $item->numberOfExecutedBranches())); + $xmlMetrics->setAttribute('conditionals', (string) $item->numberOfExecutableBranches()); + $xmlMetrics->setAttribute('coveredconditionals', (string) $item->numberOfExecutedBranches()); + $xmlMetrics->setAttribute('statements', (string) $item->numberOfExecutableLines()); + $xmlMetrics->setAttribute('coveredstatements', (string) $item->numberOfExecutedLines()); + $xmlMetrics->setAttribute('methods', (string) $item->numberOfMethods()); + $xmlMetrics->setAttribute('coveredmethods', (string) $item->numberOfTestedMethods()); + $xmlFile->insertBefore($xmlMetrics, $xmlFile->firstChild); + + if (!isset($packages[$namespace])) { + $packages[$namespace] = $xmlDocument->createElement('package'); + $packages[$namespace]->setAttribute('name', $namespace); + + $xmlPackageMetrics = $xmlDocument->createElement('metrics'); + $xmlPackageMetrics->setAttribute('complexity', '0'); + $xmlPackageMetrics->setAttribute('elements', '0'); + $xmlPackageMetrics->setAttribute('coveredelements', '0'); + $xmlPackageMetrics->setAttribute('conditionals', '0'); + $xmlPackageMetrics->setAttribute('coveredconditionals', '0'); + $xmlPackageMetrics->setAttribute('statements', '0'); + $xmlPackageMetrics->setAttribute('coveredstatements', '0'); + $xmlPackageMetrics->setAttribute('methods', '0'); + $xmlPackageMetrics->setAttribute('coveredmethods', '0'); + $packages[$namespace]->appendChild($xmlPackageMetrics); + + $xmlProject->appendChild($packages[$namespace]); + } + + $xmlPackageMetrics = $packages[$namespace]->firstChild; + + assert($xmlPackageMetrics instanceof DOMElement); + + $xmlPackageMetrics->setAttribute('complexity', (string) ((int) $xmlPackageMetrics->getAttribute('complexity') + $item->cyclomaticComplexity())); + $xmlPackageMetrics->setAttribute('elements', (string) ((int) $xmlPackageMetrics->getAttribute('elements') + $item->numberOfMethods() + $item->numberOfExecutableLines() + $item->numberOfExecutableBranches())); + $xmlPackageMetrics->setAttribute('coveredelements', (string) ((int) $xmlPackageMetrics->getAttribute('coveredelements') + $item->numberOfTestedMethods() + $item->numberOfExecutedLines() + $item->numberOfExecutedBranches())); + $xmlPackageMetrics->setAttribute('conditionals', (string) ((int) $xmlPackageMetrics->getAttribute('conditionals') + $item->numberOfExecutableBranches())); + $xmlPackageMetrics->setAttribute('coveredconditionals', (string) ((int) $xmlPackageMetrics->getAttribute('coveredconditionals') + $item->numberOfExecutedBranches())); + $xmlPackageMetrics->setAttribute('statements', (string) ((int) $xmlPackageMetrics->getAttribute('statements') + $item->numberOfExecutableLines())); + $xmlPackageMetrics->setAttribute('coveredstatements', (string) ((int) $xmlPackageMetrics->getAttribute('coveredstatements') + $item->numberOfExecutedLines())); + $xmlPackageMetrics->setAttribute('methods', (string) ((int) $xmlPackageMetrics->getAttribute('methods') + $item->numberOfMethods())); + $xmlPackageMetrics->setAttribute('coveredmethods', (string) ((int) $xmlPackageMetrics->getAttribute('coveredmethods') + $item->numberOfTestedMethods())); + + $packages[$namespace]->appendChild($xmlFile); + } + + $linesOfCode = $report->linesOfCode(); + + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('files', (string) count($report)); + $xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode()); + $xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode()); + $xmlMetrics->setAttribute('classes', (string) $report->numberOfClassesAndTraits()); + $xmlMetrics->setAttribute('complexity', (string) $report->cyclomaticComplexity()); + $xmlMetrics->setAttribute('elements', (string) ($report->numberOfMethods() + $report->numberOfExecutableLines() + $report->numberOfExecutableBranches())); + $xmlMetrics->setAttribute('coveredelements', (string) ($report->numberOfTestedMethods() + $report->numberOfExecutedLines() + $report->numberOfExecutedBranches())); + $xmlMetrics->setAttribute('conditionals', (string) $report->numberOfExecutableBranches()); + $xmlMetrics->setAttribute('coveredconditionals', (string) $report->numberOfExecutedBranches()); + $xmlMetrics->setAttribute('statements', (string) $report->numberOfExecutableLines()); + $xmlMetrics->setAttribute('coveredstatements', (string) $report->numberOfExecutedLines()); + $xmlMetrics->setAttribute('methods', (string) $report->numberOfMethods()); + $xmlMetrics->setAttribute('coveredmethods', (string) $report->numberOfTestedMethods()); + $xmlProject->insertBefore($xmlMetrics, $xmlProject->firstChild); + + $buffer = $xmlDocument->saveXML(); + + if ($target !== null) { + if (!str_contains($target, '://')) { + Filesystem::createDirectory(dirname($target)); + } + + if (@file_put_contents($target, $buffer) === false) { + throw new WriteOperationFailedException($target); + } + } + + return $buffer; + } +} diff --git a/src/Report/PHP.php b/src/Report/PHP.php new file mode 100644 index 000000000..051f9154e --- /dev/null +++ b/src/Report/PHP.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use const PHP_EOL; +use function dirname; +use function file_put_contents; +use function serialize; +use function str_contains; +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Util\Filesystem; +use SebastianBergmann\CodeCoverage\WriteOperationFailedException; + +final class PHP +{ + public function process(CodeCoverage $coverage, ?string $target = null): string + { + $coverage->clearCache(); + + $buffer = " + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use const PHP_EOL; +use function array_map; +use function date; +use function ksort; +use function max; +use function sprintf; +use function str_pad; +use function strlen; +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Node\File; +use SebastianBergmann\CodeCoverage\Util\Percentage; + +final class Text +{ + private const string COLOR_GREEN = "\x1b[30;42m"; + private const string COLOR_YELLOW = "\x1b[30;43m"; + private const string COLOR_RED = "\x1b[37;41m"; + private const string COLOR_HEADER = "\x1b[1;37;40m"; + private const string COLOR_RESET = "\x1b[0m"; + private readonly Thresholds $thresholds; + private readonly bool $showUncoveredFiles; + private readonly bool $showOnlySummary; + + public function __construct(Thresholds $thresholds, bool $showUncoveredFiles = false, bool $showOnlySummary = false) + { + $this->thresholds = $thresholds; + $this->showUncoveredFiles = $showUncoveredFiles; + $this->showOnlySummary = $showOnlySummary; + } + + public function process(CodeCoverage $coverage, bool $showColors = false): string + { + $hasBranchCoverage = $coverage->getData(true)->functionCoverage() !== []; + + $output = PHP_EOL . PHP_EOL; + $report = $coverage->getReport(); + + $colors = [ + 'header' => '', + 'classes' => '', + 'methods' => '', + 'lines' => '', + 'branches' => '', + 'paths' => '', + 'reset' => '', + ]; + + if ($showColors) { + $colors['classes'] = $this->coverageColor( + $report->numberOfTestedClassesAndTraits(), + $report->numberOfClassesAndTraits(), + ); + + $colors['methods'] = $this->coverageColor( + $report->numberOfTestedMethods(), + $report->numberOfMethods(), + ); + + $colors['lines'] = $this->coverageColor( + $report->numberOfExecutedLines(), + $report->numberOfExecutableLines(), + ); + + $colors['branches'] = $this->coverageColor( + $report->numberOfExecutedBranches(), + $report->numberOfExecutableBranches(), + ); + + $colors['paths'] = $this->coverageColor( + $report->numberOfExecutedPaths(), + $report->numberOfExecutablePaths(), + ); + + $colors['reset'] = self::COLOR_RESET; + $colors['header'] = self::COLOR_HEADER; + } + + $classes = sprintf( + ' Classes: %6s (%d/%d)', + Percentage::fromFractionAndTotal( + $report->numberOfTestedClassesAndTraits(), + $report->numberOfClassesAndTraits(), + )->asString(), + $report->numberOfTestedClassesAndTraits(), + $report->numberOfClassesAndTraits(), + ); + + $methods = sprintf( + ' Methods: %6s (%d/%d)', + Percentage::fromFractionAndTotal( + $report->numberOfTestedMethods(), + $report->numberOfMethods(), + )->asString(), + $report->numberOfTestedMethods(), + $report->numberOfMethods(), + ); + + $paths = ''; + $branches = ''; + + if ($hasBranchCoverage) { + $paths = sprintf( + ' Paths: %6s (%d/%d)', + Percentage::fromFractionAndTotal( + $report->numberOfExecutedPaths(), + $report->numberOfExecutablePaths(), + )->asString(), + $report->numberOfExecutedPaths(), + $report->numberOfExecutablePaths(), + ); + + $branches = sprintf( + ' Branches: %6s (%d/%d)', + Percentage::fromFractionAndTotal( + $report->numberOfExecutedBranches(), + $report->numberOfExecutableBranches(), + )->asString(), + $report->numberOfExecutedBranches(), + $report->numberOfExecutableBranches(), + ); + } + + $lines = sprintf( + ' Lines: %6s (%d/%d)', + Percentage::fromFractionAndTotal( + $report->numberOfExecutedLines(), + $report->numberOfExecutableLines(), + )->asString(), + $report->numberOfExecutedLines(), + $report->numberOfExecutableLines(), + ); + + $padding = max(array_map('strlen', [$classes, $methods, $lines])); + + if ($this->showOnlySummary) { + $title = 'Code Coverage Report Summary:'; + $padding = max($padding, strlen($title)); + + $output .= $this->format($colors['header'], $padding, $title); + } else { + $date = date(' Y-m-d H:i:s'); + $title = 'Code Coverage Report:'; + + $output .= $this->format($colors['header'], $padding, $title); + $output .= $this->format($colors['header'], $padding, $date); + $output .= $this->format($colors['header'], $padding, ''); + $output .= $this->format($colors['header'], $padding, ' Summary:'); + } + + $output .= $this->format($colors['classes'], $padding, $classes); + $output .= $this->format($colors['methods'], $padding, $methods); + + if ($hasBranchCoverage) { + $output .= $this->format($colors['paths'], $padding, $paths); + $output .= $this->format($colors['branches'], $padding, $branches); + } + $output .= $this->format($colors['lines'], $padding, $lines); + + if ($this->showOnlySummary) { + return $output . PHP_EOL; + } + + $classCoverage = []; + + foreach ($report as $item) { + if (!$item instanceof File) { + continue; + } + + $classes = $item->classesAndTraits(); + + foreach ($classes as $className => $class) { + $classExecutableLines = 0; + $classExecutedLines = 0; + $classExecutableBranches = 0; + $classExecutedBranches = 0; + $classExecutablePaths = 0; + $classExecutedPaths = 0; + $coveredMethods = 0; + $classMethods = 0; + + foreach ($class['methods'] as $method) { + /** @phpstan-ignore equal.notAllowed */ + if ($method['executableLines'] == 0) { + continue; + } + + $classMethods++; + $classExecutableLines += $method['executableLines']; + $classExecutedLines += $method['executedLines']; + $classExecutableBranches += $method['executableBranches']; + $classExecutedBranches += $method['executedBranches']; + $classExecutablePaths += $method['executablePaths']; + $classExecutedPaths += $method['executedPaths']; + + /** @phpstan-ignore equal.notAllowed */ + if ($method['coverage'] == 100) { + $coveredMethods++; + } + } + + $classCoverage[$className] = [ + 'namespace' => $class['namespace'], + 'className' => $className, + 'methodsCovered' => $coveredMethods, + 'methodCount' => $classMethods, + 'statementsCovered' => $classExecutedLines, + 'statementCount' => $classExecutableLines, + 'branchesCovered' => $classExecutedBranches, + 'branchesCount' => $classExecutableBranches, + 'pathsCovered' => $classExecutedPaths, + 'pathsCount' => $classExecutablePaths, + ]; + } + } + + ksort($classCoverage); + + $methodColor = ''; + $pathsColor = ''; + $branchesColor = ''; + $linesColor = ''; + $resetColor = ''; + + foreach ($classCoverage as $fullQualifiedPath => $classInfo) { + /** @phpstan-ignore notEqual.notAllowed */ + if ($this->showUncoveredFiles || $classInfo['statementsCovered'] != 0) { + if ($showColors) { + $methodColor = $this->coverageColor($classInfo['methodsCovered'], $classInfo['methodCount']); + $pathsColor = $this->coverageColor($classInfo['pathsCovered'], $classInfo['pathsCount']); + $branchesColor = $this->coverageColor($classInfo['branchesCovered'], $classInfo['branchesCount']); + $linesColor = $this->coverageColor($classInfo['statementsCovered'], $classInfo['statementCount']); + $resetColor = $colors['reset']; + } + + $output .= PHP_EOL . $fullQualifiedPath . PHP_EOL + . ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' '; + + if ($hasBranchCoverage) { + $output .= ' ' . $pathsColor . 'Paths: ' . $this->printCoverageCounts($classInfo['pathsCovered'], $classInfo['pathsCount'], 3) . $resetColor . ' ' + . ' ' . $branchesColor . 'Branches: ' . $this->printCoverageCounts($classInfo['branchesCovered'], $classInfo['branchesCount'], 3) . $resetColor . ' '; + } + $output .= ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor; + } + } + + return $output . PHP_EOL; + } + + private function coverageColor(int $numberOfCoveredElements, int $totalNumberOfElements): string + { + $coverage = Percentage::fromFractionAndTotal( + $numberOfCoveredElements, + $totalNumberOfElements, + ); + + if ($coverage->asFloat() >= $this->thresholds->highLowerBound()) { + return self::COLOR_GREEN; + } + + if ($coverage->asFloat() > $this->thresholds->lowUpperBound()) { + return self::COLOR_YELLOW; + } + + return self::COLOR_RED; + } + + private function printCoverageCounts(int $numberOfCoveredElements, int $totalNumberOfElements, int $precision): string + { + $format = '%' . $precision . 's'; + + return Percentage::fromFractionAndTotal( + $numberOfCoveredElements, + $totalNumberOfElements, + )->asFixedWidthString() . + ' (' . sprintf($format, $numberOfCoveredElements) . '/' . + sprintf($format, $totalNumberOfElements) . ')'; + } + + private function format(string $color, int $padding, false|string $string): string + { + if ($color === '') { + return (string) $string . PHP_EOL; + } + + return $color . str_pad((string) $string, $padding) . self::COLOR_RESET . PHP_EOL; + } +} diff --git a/src/Report/Thresholds.php b/src/Report/Thresholds.php new file mode 100644 index 000000000..d1a81a260 --- /dev/null +++ b/src/Report/Thresholds.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use SebastianBergmann\CodeCoverage\InvalidArgumentException; + +/** + * @immutable + */ +final readonly class Thresholds +{ + private int $lowUpperBound; + private int $highLowerBound; + + public static function default(): self + { + return new self(50, 90); + } + + /** + * @throws InvalidArgumentException + */ + public static function from(int $lowUpperBound, int $highLowerBound): self + { + if ($lowUpperBound > $highLowerBound) { + throw new InvalidArgumentException( + '$lowUpperBound must not be larger than $highLowerBound', + ); + } + + return new self($lowUpperBound, $highLowerBound); + } + + private function __construct(int $lowUpperBound, int $highLowerBound) + { + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + } + + public function lowUpperBound(): int + { + return $this->lowUpperBound; + } + + public function highLowerBound(): int + { + return $this->highLowerBound; + } +} diff --git a/src/Report/Xml/BuildInformation.php b/src/Report/Xml/BuildInformation.php new file mode 100644 index 000000000..dba230123 --- /dev/null +++ b/src/Report/Xml/BuildInformation.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use function assert; +use function phpversion; +use DateTimeImmutable; +use DOMElement; +use SebastianBergmann\Environment\Runtime; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class BuildInformation +{ + private DOMElement $contextNode; + + public function __construct(DOMElement $contextNode) + { + $this->contextNode = $contextNode; + } + + public function setRuntimeInformation(Runtime $runtime): void + { + $runtimeNode = $this->nodeByName('runtime'); + + $runtimeNode->setAttribute('name', $runtime->getName()); + $runtimeNode->setAttribute('version', $runtime->getVersion()); + $runtimeNode->setAttribute('url', $runtime->getVendorUrl()); + + $driverNode = $this->nodeByName('driver'); + + if ($runtime->hasXdebug()) { + $driverNode->setAttribute('name', 'xdebug'); + $driverNode->setAttribute('version', phpversion('xdebug')); + } + + if ($runtime->hasPCOV()) { + $driverNode->setAttribute('name', 'pcov'); + $driverNode->setAttribute('version', phpversion('pcov')); + } + } + + public function setBuildTime(DateTimeImmutable $date): void + { + $this->contextNode->setAttribute('time', $date->format('D M j G:i:s T Y')); + } + + public function setGeneratorVersions(string $phpUnitVersion, string $coverageVersion): void + { + $this->contextNode->setAttribute('phpunit', $phpUnitVersion); + $this->contextNode->setAttribute('coverage', $coverageVersion); + } + + private function nodeByName(string $name): DOMElement + { + $node = $this->contextNode->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + $name, + )->item(0); + + if ($node === null) { + $node = $this->contextNode->appendChild( + $this->contextNode->ownerDocument->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + $name, + ), + ); + } + + assert($node instanceof DOMElement); + + return $node; + } +} diff --git a/src/Report/Xml/Coverage.php b/src/Report/Xml/Coverage.php new file mode 100644 index 000000000..afb70a069 --- /dev/null +++ b/src/Report/Xml/Coverage.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use DOMElement; +use SebastianBergmann\CodeCoverage\ReportAlreadyFinalizedException; +use XMLWriter; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Coverage +{ + private readonly XMLWriter $writer; + private readonly DOMElement $contextNode; + private bool $finalized = false; + + public function __construct(DOMElement $context, string $line) + { + $this->contextNode = $context; + + $this->writer = new XMLWriter; + $this->writer->openMemory(); + $this->writer->startElementNs(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0'); + $this->writer->writeAttribute('nr', $line); + } + + /** + * @throws ReportAlreadyFinalizedException + */ + public function addTest(string $test): void + { + if ($this->finalized) { + // @codeCoverageIgnoreStart + throw new ReportAlreadyFinalizedException; + // @codeCoverageIgnoreEnd + } + + $this->writer->startElement('covered'); + $this->writer->writeAttribute('by', $test); + $this->writer->endElement(); + } + + public function finalize(): void + { + $this->writer->endElement(); + + $fragment = $this->contextNode->ownerDocument->createDocumentFragment(); + $fragment->appendXML($this->writer->outputMemory()); + + $this->contextNode->parentNode->replaceChild( + $fragment, + $this->contextNode, + ); + + $this->finalized = true; + } +} diff --git a/src/Report/Xml/Directory.php b/src/Report/Xml/Directory.php new file mode 100644 index 000000000..b712953ab --- /dev/null +++ b/src/Report/Xml/Directory.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Directory extends Node +{ +} diff --git a/src/Report/Xml/Facade.php b/src/Report/Xml/Facade.php new file mode 100644 index 000000000..ee2e8aa01 --- /dev/null +++ b/src/Report/Xml/Facade.php @@ -0,0 +1,324 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use const DIRECTORY_SEPARATOR; +use const PHP_EOL; +use function count; +use function dirname; +use function file_get_contents; +use function file_put_contents; +use function is_array; +use function is_dir; +use function is_file; +use function is_writable; +use function libxml_clear_errors; +use function libxml_get_errors; +use function libxml_use_internal_errors; +use function sprintf; +use function strlen; +use function substr; +use DateTimeImmutable; +use DOMDocument; +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Node\AbstractNode; +use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use SebastianBergmann\CodeCoverage\Node\File; +use SebastianBergmann\CodeCoverage\Node\File as FileNode; +use SebastianBergmann\CodeCoverage\PathExistsButIsNotDirectoryException; +use SebastianBergmann\CodeCoverage\Util\Filesystem as DirectoryUtil; +use SebastianBergmann\CodeCoverage\Version; +use SebastianBergmann\CodeCoverage\WriteOperationFailedException; +use SebastianBergmann\CodeCoverage\XmlException; +use SebastianBergmann\Environment\Runtime; + +/** + * @phpstan-import-type ProcessedClassType from File + * @phpstan-import-type ProcessedTraitType from File + * @phpstan-import-type ProcessedFunctionType from File + * @phpstan-import-type TestType from CodeCoverage + */ +final class Facade +{ + private string $target; + private Project $project; + private readonly string $phpUnitVersion; + + public function __construct(string $version) + { + $this->phpUnitVersion = $version; + } + + /** + * @throws XmlException + */ + public function process(CodeCoverage $coverage, string $target): void + { + if (substr($target, -1, 1) !== DIRECTORY_SEPARATOR) { + $target .= DIRECTORY_SEPARATOR; + } + + $this->target = $target; + $this->initTargetDirectory($target); + + $report = $coverage->getReport(); + + $this->project = new Project( + $coverage->getReport()->name(), + ); + + $this->setBuildInformation(); + $this->processTests($coverage->getTests()); + $this->processDirectory($report, $this->project); + + $this->saveDocument($this->project->asDom(), 'index'); + } + + private function setBuildInformation(): void + { + $buildNode = $this->project->buildInformation(); + $buildNode->setRuntimeInformation(new Runtime); + $buildNode->setBuildTime(new DateTimeImmutable); + $buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id()); + } + + /** + * @throws PathExistsButIsNotDirectoryException + * @throws WriteOperationFailedException + */ + private function initTargetDirectory(string $directory): void + { + if (is_file($directory)) { + // @codeCoverageIgnoreStart + if (!is_dir($directory)) { + throw new PathExistsButIsNotDirectoryException($directory); + } + + if (!is_writable($directory)) { + throw new WriteOperationFailedException($directory); + } + // @codeCoverageIgnoreEnd + } + + DirectoryUtil::createDirectory($directory); + } + + /** + * @throws XmlException + */ + private function processDirectory(DirectoryNode $directory, Node $context): void + { + $directoryName = $directory->name(); + + if ($this->project->projectSourceDirectory() === $directoryName) { + $directoryName = '/'; + } + + $directoryObject = $context->addDirectory($directoryName); + + $this->setTotals($directory, $directoryObject->totals()); + + foreach ($directory->directories() as $node) { + $this->processDirectory($node, $directoryObject); + } + + foreach ($directory->files() as $node) { + $this->processFile($node, $directoryObject); + } + } + + /** + * @throws XmlException + */ + private function processFile(FileNode $file, Directory $context): void + { + $fileObject = $context->addFile( + $file->name(), + $file->id() . '.xml', + ); + + $this->setTotals($file, $fileObject->totals()); + + $path = substr( + $file->pathAsString(), + strlen($this->project->projectSourceDirectory()), + ); + + $fileReport = new Report($path); + + $this->setTotals($file, $fileReport->totals()); + + foreach ($file->classesAndTraits() as $unit) { + $this->processUnit($unit, $fileReport); + } + + foreach ($file->functions() as $function) { + $this->processFunction($function, $fileReport); + } + + foreach ($file->lineCoverageData() as $line => $tests) { + if (!is_array($tests) || count($tests) === 0) { + continue; + } + + $coverage = $fileReport->lineCoverage((string) $line); + + foreach ($tests as $test) { + $coverage->addTest($test); + } + + $coverage->finalize(); + } + + $fileReport->source()->setSourceCode( + file_get_contents($file->pathAsString()), + ); + + $this->saveDocument($fileReport->asDom(), $file->id()); + } + + /** + * @param ProcessedClassType|ProcessedTraitType $unit + */ + private function processUnit(array $unit, Report $report): void + { + if (isset($unit['className'])) { + $unitObject = $report->classObject($unit['className']); + } else { + $unitObject = $report->traitObject($unit['traitName']); + } + + $unitObject->setLines( + $unit['startLine'], + $unit['executableLines'], + $unit['executedLines'], + ); + + $unitObject->setCrap((float) $unit['crap']); + $unitObject->setNamespace($unit['namespace']); + + foreach ($unit['methods'] as $method) { + $methodObject = $unitObject->addMethod($method['methodName']); + $methodObject->setSignature($method['signature']); + $methodObject->setLines((string) $method['startLine'], (string) $method['endLine']); + $methodObject->setCrap($method['crap']); + $methodObject->setTotals( + (string) $method['executableLines'], + (string) $method['executedLines'], + (string) $method['coverage'], + ); + } + } + + /** + * @param ProcessedFunctionType $function + */ + private function processFunction(array $function, Report $report): void + { + $functionObject = $report->functionObject($function['functionName']); + + $functionObject->setSignature($function['signature']); + $functionObject->setLines((string) $function['startLine']); + $functionObject->setCrap($function['crap']); + $functionObject->setTotals((string) $function['executableLines'], (string) $function['executedLines'], (string) $function['coverage']); + } + + /** + * @param array $tests + */ + private function processTests(array $tests): void + { + $testsObject = $this->project->tests(); + + foreach ($tests as $test => $result) { + $testsObject->addTest($test, $result); + } + } + + private function setTotals(AbstractNode $node, Totals $totals): void + { + $loc = $node->linesOfCode(); + + $totals->setNumLines( + $loc->linesOfCode(), + $loc->commentLinesOfCode(), + $loc->nonCommentLinesOfCode(), + $node->numberOfExecutableLines(), + $node->numberOfExecutedLines(), + ); + + $totals->setNumClasses( + $node->numberOfClasses(), + $node->numberOfTestedClasses(), + ); + + $totals->setNumTraits( + $node->numberOfTraits(), + $node->numberOfTestedTraits(), + ); + + $totals->setNumMethods( + $node->numberOfMethods(), + $node->numberOfTestedMethods(), + ); + + $totals->setNumFunctions( + $node->numberOfFunctions(), + $node->numberOfTestedFunctions(), + ); + } + + private function targetDirectory(): string + { + return $this->target; + } + + /** + * @throws XmlException + */ + private function saveDocument(DOMDocument $document, string $name): void + { + $filename = sprintf('%s/%s.xml', $this->targetDirectory(), $name); + + $document->formatOutput = true; + $document->preserveWhiteSpace = false; + $this->initTargetDirectory(dirname($filename)); + + file_put_contents($filename, $this->documentAsString($document)); + } + + /** + * @throws XmlException + * + * @see https://bugs.php.net/bug.php?id=79191 + */ + private function documentAsString(DOMDocument $document): string + { + $xmlErrorHandling = libxml_use_internal_errors(true); + $xml = $document->saveXML(); + + if ($xml === false) { + // @codeCoverageIgnoreStart + $message = 'Unable to generate the XML'; + + foreach (libxml_get_errors() as $error) { + $message .= PHP_EOL . $error->message; + } + + throw new XmlException($message); + // @codeCoverageIgnoreEnd + } + + libxml_clear_errors(); + libxml_use_internal_errors($xmlErrorHandling); + + return $xml; + } +} diff --git a/src/Report/Xml/File.php b/src/Report/Xml/File.php new file mode 100644 index 000000000..4a3fea008 --- /dev/null +++ b/src/Report/Xml/File.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use function assert; +use DOMDocument; +use DOMElement; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +class File +{ + private readonly DOMDocument $dom; + private readonly DOMElement $contextNode; + + public function __construct(DOMElement $context) + { + $this->dom = $context->ownerDocument; + $this->contextNode = $context; + } + + public function totals(): Totals + { + $totalsContainer = $this->contextNode->firstChild; + + if ($totalsContainer === null) { + $totalsContainer = $this->contextNode->appendChild( + $this->dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'totals', + ), + ); + } + + assert($totalsContainer instanceof DOMElement); + + return new Totals($totalsContainer); + } + + public function lineCoverage(string $line): Coverage + { + $coverage = $this->contextNode->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'coverage', + )->item(0); + + if ($coverage === null) { + $coverage = $this->contextNode->appendChild( + $this->dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'coverage', + ), + ); + } + + $lineNode = $coverage->appendChild( + $this->dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'line', + ), + ); + + assert($lineNode instanceof DOMElement); + + return new Coverage($lineNode, $line); + } + + protected function contextNode(): DOMElement + { + return $this->contextNode; + } + + protected function dom(): DOMDocument + { + return $this->dom; + } +} diff --git a/src/Report/Xml/Method.php b/src/Report/Xml/Method.php new file mode 100644 index 000000000..1994d0f79 --- /dev/null +++ b/src/Report/Xml/Method.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use DOMElement; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class Method +{ + private DOMElement $contextNode; + + public function __construct(DOMElement $context, string $name) + { + $this->contextNode = $context; + + $this->setName($name); + } + + public function setSignature(string $signature): void + { + $this->contextNode->setAttribute('signature', $signature); + } + + public function setLines(string $start, ?string $end = null): void + { + $this->contextNode->setAttribute('start', $start); + + if ($end !== null) { + $this->contextNode->setAttribute('end', $end); + } + } + + public function setTotals(string $executable, string $executed, string $coverage): void + { + $this->contextNode->setAttribute('executable', $executable); + $this->contextNode->setAttribute('executed', $executed); + $this->contextNode->setAttribute('coverage', $coverage); + } + + public function setCrap(string $crap): void + { + $this->contextNode->setAttribute('crap', $crap); + } + + private function setName(string $name): void + { + $this->contextNode->setAttribute('name', $name); + } +} diff --git a/src/Report/Xml/Node.php b/src/Report/Xml/Node.php new file mode 100644 index 000000000..e41197a08 --- /dev/null +++ b/src/Report/Xml/Node.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use function assert; +use DOMDocument; +use DOMElement; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +abstract class Node +{ + private DOMDocument $dom; + private DOMElement $contextNode; + + public function __construct(DOMElement $context) + { + $this->setContextNode($context); + } + + public function dom(): DOMDocument + { + return $this->dom; + } + + public function totals(): Totals + { + $totalsContainer = $this->contextNode()->firstChild; + + if ($totalsContainer === null) { + $totalsContainer = $this->contextNode()->appendChild( + $this->dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'totals', + ), + ); + } + + assert($totalsContainer instanceof DOMElement); + + return new Totals($totalsContainer); + } + + public function addDirectory(string $name): Directory + { + $dirNode = $this->dom()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'directory', + ); + + $dirNode->setAttribute('name', $name); + $this->contextNode()->appendChild($dirNode); + + return new Directory($dirNode); + } + + public function addFile(string $name, string $href): File + { + $fileNode = $this->dom()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'file', + ); + + $fileNode->setAttribute('name', $name); + $fileNode->setAttribute('href', $href); + $this->contextNode()->appendChild($fileNode); + + return new File($fileNode); + } + + protected function setContextNode(DOMElement $context): void + { + $this->dom = $context->ownerDocument; + $this->contextNode = $context; + } + + protected function contextNode(): DOMElement + { + return $this->contextNode; + } +} diff --git a/src/Report/Xml/Project.php b/src/Report/Xml/Project.php new file mode 100644 index 000000000..21b5a2ce1 --- /dev/null +++ b/src/Report/Xml/Project.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use function assert; +use DOMDocument; +use DOMElement; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Project extends Node +{ + /** + * @phpstan-ignore constructor.missingParentCall + */ + public function __construct(string $directory) + { + $this->init(); + $this->setProjectSourceDirectory($directory); + } + + public function projectSourceDirectory(): string + { + return $this->contextNode()->getAttribute('source'); + } + + public function buildInformation(): BuildInformation + { + $buildNode = $this->dom()->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'build', + )->item(0); + + if ($buildNode === null) { + $buildNode = $this->dom()->documentElement->appendChild( + $this->dom()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'build', + ), + ); + } + + assert($buildNode instanceof DOMElement); + + return new BuildInformation($buildNode); + } + + public function tests(): Tests + { + $testsNode = $this->contextNode()->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'tests', + )->item(0); + + if ($testsNode === null) { + $testsNode = $this->contextNode()->appendChild( + $this->dom()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'tests', + ), + ); + } + + assert($testsNode instanceof DOMElement); + + return new Tests($testsNode); + } + + public function asDom(): DOMDocument + { + return $this->dom(); + } + + private function init(): void + { + $dom = new DOMDocument; + $dom->loadXML(''); + + $this->setContextNode( + $dom->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'project', + )->item(0), + ); + } + + private function setProjectSourceDirectory(string $name): void + { + $this->contextNode()->setAttribute('source', $name); + } +} diff --git a/src/Report/Xml/Report.php b/src/Report/Xml/Report.php new file mode 100644 index 000000000..f39ab860c --- /dev/null +++ b/src/Report/Xml/Report.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use function assert; +use function basename; +use function dirname; +use DOMDocument; +use DOMElement; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Report extends File +{ + public function __construct(string $name) + { + $dom = new DOMDocument; + $dom->loadXML(''); + + $contextNode = $dom->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'file', + )->item(0); + + parent::__construct($contextNode); + + $this->setName($name); + } + + public function asDom(): DOMDocument + { + return $this->dom(); + } + + public function functionObject(string $name): Method + { + $node = $this->contextNode()->appendChild( + $this->dom()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'function', + ), + ); + + assert($node instanceof DOMElement); + + return new Method($node, $name); + } + + public function classObject(string $name): Unit + { + return $this->unitObject('class', $name); + } + + public function traitObject(string $name): Unit + { + return $this->unitObject('trait', $name); + } + + public function source(): Source + { + $source = $this->contextNode()->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'source', + )->item(0); + + if ($source === null) { + $source = $this->contextNode()->appendChild( + $this->dom()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'source', + ), + ); + } + + assert($source instanceof DOMElement); + + return new Source($source); + } + + private function setName(string $name): void + { + $this->contextNode()->setAttribute('name', basename($name)); + $this->contextNode()->setAttribute('path', dirname($name)); + } + + private function unitObject(string $tagName, string $name): Unit + { + $node = $this->contextNode()->appendChild( + $this->dom()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + $tagName, + ), + ); + + assert($node instanceof DOMElement); + + return new Unit($node, $name); + } +} diff --git a/src/Report/Xml/Source.php b/src/Report/Xml/Source.php new file mode 100644 index 000000000..448fe72d6 --- /dev/null +++ b/src/Report/Xml/Source.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use DOMElement; +use TheSeer\Tokenizer\NamespaceUri; +use TheSeer\Tokenizer\Tokenizer; +use TheSeer\Tokenizer\XMLSerializer; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class Source +{ + private DOMElement $context; + + public function __construct(DOMElement $context) + { + $this->context = $context; + } + + public function setSourceCode(string $source): void + { + $context = $this->context; + + $tokens = (new Tokenizer)->parse($source); + $srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens); + + $context->parentNode->replaceChild( + $context->ownerDocument->importNode($srcDom->documentElement, true), + $context, + ); + } +} diff --git a/src/Report/Xml/Tests.php b/src/Report/Xml/Tests.php new file mode 100644 index 000000000..c9e9c48ef --- /dev/null +++ b/src/Report/Xml/Tests.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use function assert; +use DOMElement; +use SebastianBergmann\CodeCoverage\CodeCoverage; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @phpstan-import-type TestType from CodeCoverage + */ +final readonly class Tests +{ + private DOMElement $contextNode; + + public function __construct(DOMElement $context) + { + $this->contextNode = $context; + } + + /** + * @param TestType $result + */ + public function addTest(string $test, array $result): void + { + $node = $this->contextNode->appendChild( + $this->contextNode->ownerDocument->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'test', + ), + ); + + assert($node instanceof DOMElement); + + $node->setAttribute('name', $test); + $node->setAttribute('size', $result['size']); + $node->setAttribute('status', $result['status']); + } +} diff --git a/src/Report/Xml/Totals.php b/src/Report/Xml/Totals.php new file mode 100644 index 000000000..8e285a78e --- /dev/null +++ b/src/Report/Xml/Totals.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use function sprintf; +use DOMElement; +use SebastianBergmann\CodeCoverage\Util\Percentage; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class Totals +{ + private DOMElement $linesNode; + private DOMElement $methodsNode; + private DOMElement $functionsNode; + private DOMElement $classesNode; + private DOMElement $traitsNode; + + public function __construct(DOMElement $container) + { + $dom = $container->ownerDocument; + + $this->linesNode = $dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'lines', + ); + + $this->methodsNode = $dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'methods', + ); + + $this->functionsNode = $dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'functions', + ); + + $this->classesNode = $dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'classes', + ); + + $this->traitsNode = $dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'traits', + ); + + $container->appendChild($this->linesNode); + $container->appendChild($this->methodsNode); + $container->appendChild($this->functionsNode); + $container->appendChild($this->classesNode); + $container->appendChild($this->traitsNode); + } + + public function setNumLines(int $loc, int $cloc, int $ncloc, int $executable, int $executed): void + { + $this->linesNode->setAttribute('total', (string) $loc); + $this->linesNode->setAttribute('comments', (string) $cloc); + $this->linesNode->setAttribute('code', (string) $ncloc); + $this->linesNode->setAttribute('executable', (string) $executable); + $this->linesNode->setAttribute('executed', (string) $executed); + $this->linesNode->setAttribute( + 'percent', + $executable === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($executed, $executable)->asFloat()), + ); + } + + public function setNumClasses(int $count, int $tested): void + { + $this->classesNode->setAttribute('count', (string) $count); + $this->classesNode->setAttribute('tested', (string) $tested); + $this->classesNode->setAttribute( + 'percent', + $count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()), + ); + } + + public function setNumTraits(int $count, int $tested): void + { + $this->traitsNode->setAttribute('count', (string) $count); + $this->traitsNode->setAttribute('tested', (string) $tested); + $this->traitsNode->setAttribute( + 'percent', + $count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()), + ); + } + + public function setNumMethods(int $count, int $tested): void + { + $this->methodsNode->setAttribute('count', (string) $count); + $this->methodsNode->setAttribute('tested', (string) $tested); + $this->methodsNode->setAttribute( + 'percent', + $count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()), + ); + } + + public function setNumFunctions(int $count, int $tested): void + { + $this->functionsNode->setAttribute('count', (string) $count); + $this->functionsNode->setAttribute('tested', (string) $tested); + $this->functionsNode->setAttribute( + 'percent', + $count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()), + ); + } +} diff --git a/src/Report/Xml/Unit.php b/src/Report/Xml/Unit.php new file mode 100644 index 000000000..a00f85d39 --- /dev/null +++ b/src/Report/Xml/Unit.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use function assert; +use DOMElement; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class Unit +{ + private DOMElement $contextNode; + + public function __construct(DOMElement $context, string $name) + { + $this->contextNode = $context; + + $this->setName($name); + } + + public function setLines(int $start, int $executable, int $executed): void + { + $this->contextNode->setAttribute('start', (string) $start); + $this->contextNode->setAttribute('executable', (string) $executable); + $this->contextNode->setAttribute('executed', (string) $executed); + } + + public function setCrap(float $crap): void + { + $this->contextNode->setAttribute('crap', (string) $crap); + } + + public function setNamespace(string $namespace): void + { + $node = $this->contextNode->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'namespace', + )->item(0); + + if ($node === null) { + $node = $this->contextNode->appendChild( + $this->contextNode->ownerDocument->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'namespace', + ), + ); + } + + assert($node instanceof DOMElement); + + $node->setAttribute('name', $namespace); + } + + public function addMethod(string $name): Method + { + $node = $this->contextNode->appendChild( + $this->contextNode->ownerDocument->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'method', + ), + ); + + assert($node instanceof DOMElement); + + return new Method($node, $name); + } + + private function setName(string $name): void + { + $this->contextNode->setAttribute('name', $name); + } +} diff --git a/src/StaticAnalysis/CacheWarmer.php b/src/StaticAnalysis/CacheWarmer.php new file mode 100644 index 000000000..dcd0c61ec --- /dev/null +++ b/src/StaticAnalysis/CacheWarmer.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function file_get_contents; +use SebastianBergmann\CodeCoverage\Filter; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class CacheWarmer +{ + /** + * @return array{cacheHits: non-negative-int, cacheMisses: non-negative-int} + */ + public function warmCache(string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, Filter $filter): array + { + $analyser = new CachingSourceAnalyser( + $cacheDirectory, + new ParsingSourceAnalyser, + ); + + foreach ($filter->files() as $file) { + $analyser->analyse( + $file, + file_get_contents($file), + $useAnnotationsForIgnoringCode, + $ignoreDeprecatedCode, + ); + } + + return [ + 'cacheHits' => $analyser->cacheHits(), + 'cacheMisses' => $analyser->cacheMisses(), + ]; + } +} diff --git a/src/StaticAnalysis/CachingSourceAnalyser.php b/src/StaticAnalysis/CachingSourceAnalyser.php new file mode 100644 index 000000000..6a7b8001b --- /dev/null +++ b/src/StaticAnalysis/CachingSourceAnalyser.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use const DIRECTORY_SEPARATOR; +use function file_get_contents; +use function file_put_contents; +use function hash; +use function implode; +use function is_file; +use function serialize; +use function unserialize; +use SebastianBergmann\CodeCoverage\Util\Filesystem; +use SebastianBergmann\CodeCoverage\Version; + +/** + * @internal This interface is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class CachingSourceAnalyser implements SourceAnalyser +{ + /** + * @var non-empty-string + */ + private readonly string $directory; + private readonly SourceAnalyser $sourceAnalyser; + + /** + * @var non-negative-int + */ + private int $cacheHits = 0; + + /** + * @var non-negative-int + */ + private int $cacheMisses = 0; + + public function __construct(string $directory, SourceAnalyser $sourceAnalyser) + { + Filesystem::createDirectory($directory); + + $this->directory = $directory; + $this->sourceAnalyser = $sourceAnalyser; + } + + /** + * @param non-empty-string $sourceCodeFile + */ + public function analyse(string $sourceCodeFile, string $sourceCode, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode): AnalysisResult + { + $cacheFile = $this->cacheFile( + $sourceCode, + $useAnnotationsForIgnoringCode, + $ignoreDeprecatedCode, + ); + + $cachedAnalysisResult = $this->read($cacheFile); + + if ($cachedAnalysisResult !== false) { + $this->cacheHits++; + + return $cachedAnalysisResult; + } + + $this->cacheMisses++; + + $analysisResult = $this->sourceAnalyser->analyse( + $sourceCodeFile, + $sourceCode, + $useAnnotationsForIgnoringCode, + $ignoreDeprecatedCode, + ); + + $this->write($cacheFile, $analysisResult); + + return $analysisResult; + } + + /** + * @return non-negative-int + */ + public function cacheHits(): int + { + return $this->cacheHits; + } + + /** + * @return non-negative-int + */ + public function cacheMisses(): int + { + return $this->cacheMisses; + } + + /** + * @param non-empty-string $cacheFile + */ + private function read(string $cacheFile): AnalysisResult|false + { + if (!is_file($cacheFile)) { + return false; + } + + return unserialize( + file_get_contents($cacheFile), + [ + 'allowed_classes' => [ + AnalysisResult::class, + Class_::class, + Function_::class, + Interface_::class, + LinesOfCode::class, + Method::class, + Trait_::class, + ], + ], + ); + } + + /** + * @param non-empty-string $cacheFile + */ + private function write(string $cacheFile, AnalysisResult $result): void + { + file_put_contents( + $cacheFile, + serialize($result), + ); + } + + private function cacheFile(string $source, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode): string + { + $cacheKey = hash( + 'sha256', + implode( + "\0", + [ + $source, + Version::id(), + $useAnnotationsForIgnoringCode, + $ignoreDeprecatedCode, + ], + ), + ); + + return $this->directory . DIRECTORY_SEPARATOR . $cacheKey; + } +} diff --git a/src/StaticAnalysis/FileAnalyser.php b/src/StaticAnalysis/FileAnalyser.php new file mode 100644 index 000000000..3b1c5a42b --- /dev/null +++ b/src/StaticAnalysis/FileAnalyser.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function file_get_contents; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class FileAnalyser +{ + private readonly SourceAnalyser $sourceAnalyser; + private readonly bool $useAnnotationsForIgnoringCode; + private readonly bool $ignoreDeprecatedCode; + + /** + * @var array + */ + private array $cache = []; + + public function __construct(SourceAnalyser $sourceAnalyser, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode) + { + $this->sourceAnalyser = $sourceAnalyser; + $this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode; + $this->ignoreDeprecatedCode = $ignoreDeprecatedCode; + } + + /** + * @param non-empty-string $sourceCodeFile + */ + public function analyse(string $sourceCodeFile): AnalysisResult + { + if (isset($this->cache[$sourceCodeFile])) { + return $this->cache[$sourceCodeFile]; + } + + $this->cache[$sourceCodeFile] = $this->sourceAnalyser->analyse( + $sourceCodeFile, + file_get_contents($sourceCodeFile), + $this->useAnnotationsForIgnoringCode, + $this->ignoreDeprecatedCode, + ); + + return $this->cache[$sourceCodeFile]; + } +} diff --git a/src/StaticAnalysis/ParsingSourceAnalyser.php b/src/StaticAnalysis/ParsingSourceAnalyser.php new file mode 100644 index 000000000..7f46eeca6 --- /dev/null +++ b/src/StaticAnalysis/ParsingSourceAnalyser.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use const T_COMMENT; +use const T_DOC_COMMENT; +use function array_merge; +use function array_unique; +use function assert; +use function is_array; +use function max; +use function range; +use function sort; +use function sprintf; +use function substr_count; +use function token_get_all; +use function trim; +use PhpParser\Error; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitor\NameResolver; +use PhpParser\ParserFactory; +use SebastianBergmann\CodeCoverage\ParserException; +use SebastianBergmann\LinesOfCode\LineCountingVisitor; + +/** + * @internal This interface is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class ParsingSourceAnalyser implements SourceAnalyser +{ + /** + * @param non-empty-string $sourceCodeFile + */ + public function analyse(string $sourceCodeFile, string $sourceCode, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode): AnalysisResult + { + $linesOfCode = max(substr_count($sourceCode, "\n") + 1, substr_count($sourceCode, "\r") + 1); + + if ($linesOfCode === 0 && $sourceCode !== '') { + $linesOfCode = 1; + } + + assert($linesOfCode > 0); + + $parser = (new ParserFactory)->createForHostVersion(); + + try { + $nodes = $parser->parse($sourceCode); + + assert($nodes !== null); + + $traverser = new NodeTraverser; + $codeUnitFindingVisitor = new CodeUnitFindingVisitor($sourceCodeFile); + $lineCountingVisitor = new LineCountingVisitor($linesOfCode); + $ignoredLinesFindingVisitor = new IgnoredLinesFindingVisitor($useAnnotationsForIgnoringCode, $ignoreDeprecatedCode); + $executableLinesFindingVisitor = new ExecutableLinesFindingVisitor($sourceCode); + + $traverser->addVisitor(new NameResolver); + $traverser->addVisitor(new AttributeParentConnectingVisitor); + $traverser->addVisitor($codeUnitFindingVisitor); + $traverser->addVisitor($lineCountingVisitor); + $traverser->addVisitor($ignoredLinesFindingVisitor); + $traverser->addVisitor($executableLinesFindingVisitor); + + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($nodes); + // @codeCoverageIgnoreStart + } catch (Error $error) { + throw new ParserException( + sprintf( + 'Cannot parse %s: %s', + $sourceCodeFile, + $error->getMessage(), + ), + $error->getCode(), + $error, + ); + } + // @codeCoverageIgnoreEnd + + $ignoredLines = array_unique( + array_merge( + $this->findLinesIgnoredByLineBasedAnnotations( + $sourceCodeFile, + $sourceCode, + $useAnnotationsForIgnoringCode, + ), + $ignoredLinesFindingVisitor->ignoredLines(), + ), + ); + + sort($ignoredLines); + + return new AnalysisResult( + $codeUnitFindingVisitor->interfaces(), + $codeUnitFindingVisitor->classes(), + $codeUnitFindingVisitor->traits(), + $codeUnitFindingVisitor->functions(), + new LinesOfCode( + $lineCountingVisitor->result()->linesOfCode(), + $lineCountingVisitor->result()->commentLinesOfCode(), + $lineCountingVisitor->result()->nonCommentLinesOfCode(), + ), + $executableLinesFindingVisitor->executableLinesGroupedByBranch(), + $ignoredLines, + ); + } + + /** + * @return array + */ + private function findLinesIgnoredByLineBasedAnnotations(string $filename, string $source, bool $useAnnotationsForIgnoringCode): array + { + if (!$useAnnotationsForIgnoringCode) { + return []; + } + + $result = []; + $start = false; + + foreach (token_get_all($source) as $token) { + if (!is_array($token) || + !(T_COMMENT === $token[0] || T_DOC_COMMENT === $token[0])) { + continue; + } + + $comment = trim($token[1]); + + if ($comment === '// @codeCoverageIgnore' || + $comment === '//@codeCoverageIgnore') { + $result[] = $token[2]; + + continue; + } + + if ($comment === '// @codeCoverageIgnoreStart' || + $comment === '//@codeCoverageIgnoreStart') { + $start = $token[2]; + + continue; + } + + if ($comment === '// @codeCoverageIgnoreEnd' || + $comment === '//@codeCoverageIgnoreEnd') { + if (false === $start) { + $start = $token[2]; + } + + $result = array_merge( + $result, + range($start, $token[2]), + ); + } + } + + return $result; + } +} diff --git a/src/StaticAnalysis/SourceAnalyser.php b/src/StaticAnalysis/SourceAnalyser.php new file mode 100644 index 000000000..bc18996ff --- /dev/null +++ b/src/StaticAnalysis/SourceAnalyser.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +/** + * @internal This interface is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +interface SourceAnalyser +{ + /** + * @param non-empty-string $sourceCodeFile + */ + public function analyse(string $sourceCodeFile, string $sourceCode, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode): AnalysisResult; +} diff --git a/src/StaticAnalysis/Value/AnalysisResult.php b/src/StaticAnalysis/Value/AnalysisResult.php new file mode 100644 index 000000000..88fff2c46 --- /dev/null +++ b/src/StaticAnalysis/Value/AnalysisResult.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +/** + * @phpstan-type LinesType array + * + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class AnalysisResult +{ + /** + * @var array + */ + private array $interfaces; + + /** + * @var array + */ + private array $classes; + + /** + * @var array + */ + private array $traits; + + /** + * @var array + */ + private array $functions; + private LinesOfCode $linesOfCode; + + /** + * @var LinesType + */ + private array $executableLines; + + /** + * @var LinesType + */ + private array $ignoredLines; + + /** + * @param array $interfaces + * @param array $classes + * @param array $traits + * @param array $functions + * @param LinesType $executableLines + * @param LinesType $ignoredLines + */ + public function __construct(array $interfaces, array $classes, array $traits, array $functions, LinesOfCode $linesOfCode, array $executableLines, array $ignoredLines) + { + $this->interfaces = $interfaces; + $this->classes = $classes; + $this->traits = $traits; + $this->functions = $functions; + $this->linesOfCode = $linesOfCode; + $this->executableLines = $executableLines; + $this->ignoredLines = $ignoredLines; + } + + /** + * @return array + */ + public function interfaces(): array + { + return $this->interfaces; + } + + /** + * @return array + */ + public function classes(): array + { + return $this->classes; + } + + /** + * @return array + */ + public function traits(): array + { + return $this->traits; + } + + /** + * @return array + */ + public function functions(): array + { + return $this->functions; + } + + public function linesOfCode(): LinesOfCode + { + return $this->linesOfCode; + } + + /** + * @return LinesType + */ + public function executableLines(): array + { + return $this->executableLines; + } + + /** + * @return LinesType + */ + public function ignoredLines(): array + { + return $this->ignoredLines; + } +} diff --git a/src/StaticAnalysis/Value/Class_.php b/src/StaticAnalysis/Value/Class_.php new file mode 100644 index 000000000..ee87b246e --- /dev/null +++ b/src/StaticAnalysis/Value/Class_.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class Class_ +{ + /** + * @var non-empty-string + */ + private string $name; + + /** + * @var non-empty-string + */ + private string $namespacedName; + private string $namespace; + + /** + * @var non-empty-string + */ + private string $file; + + /** + * @var non-negative-int + */ + private int $startLine; + + /** + * @var non-negative-int + */ + private int $endLine; + + /** + * @var ?non-empty-string + */ + private ?string $parentClass; + + /** + * @var list + */ + private array $interfaces; + + /** + * @var list + */ + private array $traits; + + /** + * @var array + */ + private array $methods; + + /** + * @param non-empty-string $name + * @param non-empty-string $namespacedName + * @param non-empty-string $file + * @param non-negative-int $startLine + * @param non-negative-int $endLine + * @param ?non-empty-string $parentClass + * @param list $interfaces + * @param list $traits + * @param array $methods + */ + public function __construct(string $name, string $namespacedName, string $namespace, string $file, int $startLine, int $endLine, ?string $parentClass, array $interfaces, array $traits, array $methods) + { + $this->name = $name; + $this->namespacedName = $namespacedName; + $this->namespace = $namespace; + $this->file = $file; + $this->startLine = $startLine; + $this->endLine = $endLine; + $this->parentClass = $parentClass; + $this->interfaces = $interfaces; + $this->traits = $traits; + $this->methods = $methods; + } + + /** + * @return non-empty-string + */ + public function name(): string + { + return $this->name; + } + + /** + * @return non-empty-string + */ + public function namespacedName(): string + { + return $this->namespacedName; + } + + public function isNamespaced(): bool + { + return $this->namespace !== ''; + } + + public function namespace(): string + { + return $this->namespace; + } + + /** + * @return non-empty-string + */ + public function file(): string + { + return $this->file; + } + + /** + * @return non-negative-int + */ + public function startLine(): int + { + return $this->startLine; + } + + /** + * @return non-negative-int + */ + public function endLine(): int + { + return $this->endLine; + } + + public function hasParent(): bool + { + return $this->parentClass !== null; + } + + /** + * @return ?non-empty-string + */ + public function parentClass(): ?string + { + return $this->parentClass; + } + + /** + * @return list + */ + public function interfaces(): array + { + return $this->interfaces; + } + + /** + * @return list + */ + public function traits(): array + { + return $this->traits; + } + + /** + * @return array + */ + public function methods(): array + { + return $this->methods; + } +} diff --git a/src/StaticAnalysis/Value/Function_.php b/src/StaticAnalysis/Value/Function_.php new file mode 100644 index 000000000..7069dec99 --- /dev/null +++ b/src/StaticAnalysis/Value/Function_.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class Function_ +{ + /** + * @var non-empty-string + */ + private string $name; + + /** + * @var non-empty-string + */ + private string $namespacedName; + private string $namespace; + + /** + * @var non-negative-int + */ + private int $startLine; + + /** + * @var non-negative-int + */ + private int $endLine; + + /** + * @var non-empty-string + */ + private string $signature; + + /** + * @var positive-int + */ + private int $cyclomaticComplexity; + + /** + * @param non-empty-string $name + * @param non-empty-string $namespacedName + * @param non-negative-int $startLine + * @param non-negative-int $endLine + * @param non-empty-string $signature + * @param positive-int $cyclomaticComplexity + */ + public function __construct(string $name, string $namespacedName, string $namespace, int $startLine, int $endLine, string $signature, int $cyclomaticComplexity) + { + $this->name = $name; + $this->namespacedName = $namespacedName; + $this->namespace = $namespace; + $this->startLine = $startLine; + $this->endLine = $endLine; + $this->signature = $signature; + $this->cyclomaticComplexity = $cyclomaticComplexity; + } + + /** + * @return non-empty-string + */ + public function name(): string + { + return $this->name; + } + + /** + * @return non-empty-string + */ + public function namespacedName(): string + { + return $this->namespacedName; + } + + public function isNamespaced(): bool + { + return $this->namespace !== ''; + } + + public function namespace(): string + { + return $this->namespace; + } + + /** + * @return non-negative-int + */ + public function startLine(): int + { + return $this->startLine; + } + + /** + * @return non-negative-int + */ + public function endLine(): int + { + return $this->endLine; + } + + /** + * @return non-empty-string + */ + public function signature(): string + { + return $this->signature; + } + + /** + * @return positive-int + */ + public function cyclomaticComplexity(): int + { + return $this->cyclomaticComplexity; + } +} diff --git a/src/StaticAnalysis/Value/Interface_.php b/src/StaticAnalysis/Value/Interface_.php new file mode 100644 index 000000000..0b2579280 --- /dev/null +++ b/src/StaticAnalysis/Value/Interface_.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class Interface_ +{ + /** + * @var non-empty-string + */ + private string $name; + + /** + * @var non-empty-string + */ + private string $namespacedName; + private string $namespace; + + /** + * @var non-negative-int + */ + private int $startLine; + + /** + * @var non-negative-int + */ + private int $endLine; + + /** + * @var list + */ + private array $parentInterfaces; + + /** + * @param non-empty-string $name + * @param non-empty-string $namespacedName + * @param non-negative-int $startLine + * @param non-negative-int $endLine + * @param list $parentInterfaces + */ + public function __construct(string $name, string $namespacedName, string $namespace, int $startLine, int $endLine, array $parentInterfaces) + { + $this->name = $name; + $this->namespacedName = $namespacedName; + $this->namespace = $namespace; + $this->startLine = $startLine; + $this->endLine = $endLine; + $this->parentInterfaces = $parentInterfaces; + } + + /** + * @return non-empty-string + */ + public function name(): string + { + return $this->name; + } + + /** + * @return non-empty-string + */ + public function namespacedName(): string + { + return $this->namespacedName; + } + + public function isNamespaced(): bool + { + return $this->namespace !== ''; + } + + public function namespace(): string + { + return $this->namespace; + } + + /** + * @return non-negative-int + */ + public function startLine(): int + { + return $this->startLine; + } + + /** + * @return non-negative-int + */ + public function endLine(): int + { + return $this->endLine; + } + + /** + * @return list + */ + public function parentInterfaces(): array + { + return $this->parentInterfaces; + } +} diff --git a/src/StaticAnalysis/Value/LinesOfCode.php b/src/StaticAnalysis/Value/LinesOfCode.php new file mode 100644 index 000000000..f8720e1a7 --- /dev/null +++ b/src/StaticAnalysis/Value/LinesOfCode.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class LinesOfCode +{ + /** + * @var non-negative-int + */ + private int $linesOfCode; + + /** + * @var non-negative-int + */ + private int $commentLinesOfCode; + + /** + * @var non-negative-int + */ + private int $nonCommentLinesOfCode; + + /** + * @param non-negative-int $linesOfCode + * @param non-negative-int $commentLinesOfCode + * @param non-negative-int $nonCommentLinesOfCode + */ + public function __construct(int $linesOfCode, int $commentLinesOfCode, int $nonCommentLinesOfCode) + { + $this->linesOfCode = $linesOfCode; + $this->commentLinesOfCode = $commentLinesOfCode; + $this->nonCommentLinesOfCode = $nonCommentLinesOfCode; + } + + /** + * @return non-negative-int + */ + public function linesOfCode(): int + { + return $this->linesOfCode; + } + + /** + * @return non-negative-int + */ + public function commentLinesOfCode(): int + { + return $this->commentLinesOfCode; + } + + /** + * @return non-negative-int + */ + public function nonCommentLinesOfCode(): int + { + return $this->nonCommentLinesOfCode; + } +} diff --git a/src/StaticAnalysis/Value/Method.php b/src/StaticAnalysis/Value/Method.php new file mode 100644 index 000000000..12e3438eb --- /dev/null +++ b/src/StaticAnalysis/Value/Method.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class Method +{ + /** + * @var non-empty-string + */ + private string $name; + + /** + * @var non-negative-int + */ + private int $startLine; + + /** + * @var non-negative-int + */ + private int $endLine; + private Visibility $visibility; + + /** + * @var non-empty-string + */ + private string $signature; + + /** + * @var positive-int + */ + private int $cyclomaticComplexity; + + /** + * @param non-empty-string $name + * @param non-negative-int $startLine + * @param non-negative-int $endLine + * @param non-empty-string $signature + * @param positive-int $cyclomaticComplexity + */ + public function __construct(string $name, int $startLine, int $endLine, string $signature, Visibility $visibility, int $cyclomaticComplexity) + { + $this->name = $name; + $this->startLine = $startLine; + $this->endLine = $endLine; + $this->signature = $signature; + $this->visibility = $visibility; + $this->cyclomaticComplexity = $cyclomaticComplexity; + } + + /** + * @return non-empty-string + */ + public function name(): string + { + return $this->name; + } + + /** + * @return non-negative-int + */ + public function startLine(): int + { + return $this->startLine; + } + + /** + * @return non-negative-int + */ + public function endLine(): int + { + return $this->endLine; + } + + /** + * @return non-empty-string + */ + public function signature(): string + { + return $this->signature; + } + + public function visibility(): Visibility + { + return $this->visibility; + } + + /** + * @return positive-int + */ + public function cyclomaticComplexity(): int + { + return $this->cyclomaticComplexity; + } +} diff --git a/src/StaticAnalysis/Value/Trait_.php b/src/StaticAnalysis/Value/Trait_.php new file mode 100644 index 000000000..7bf1084cd --- /dev/null +++ b/src/StaticAnalysis/Value/Trait_.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class Trait_ +{ + /** + * @var non-empty-string + */ + private string $name; + + /** + * @var non-empty-string + */ + private string $namespacedName; + private string $namespace; + + /** + * @var non-empty-string + */ + private string $file; + + /** + * @var non-negative-int + */ + private int $startLine; + + /** + * @var non-negative-int + */ + private int $endLine; + + /** + * @var list + */ + private array $traits; + + /** + * @var array + */ + private array $methods; + + /** + * @param non-empty-string $name + * @param non-empty-string $namespacedName + * @param non-empty-string $file + * @param non-negative-int $startLine + * @param non-negative-int $endLine + * @param list $traits + * @param array $methods + */ + public function __construct(string $name, string $namespacedName, string $namespace, string $file, int $startLine, int $endLine, array $traits, array $methods) + { + $this->name = $name; + $this->namespacedName = $namespacedName; + $this->namespace = $namespace; + $this->file = $file; + $this->startLine = $startLine; + $this->endLine = $endLine; + $this->traits = $traits; + $this->methods = $methods; + } + + /** + * @return non-empty-string + */ + public function name(): string + { + return $this->name; + } + + /** + * @return non-empty-string + */ + public function namespacedName(): string + { + return $this->namespacedName; + } + + public function isNamespaced(): bool + { + return $this->namespace !== ''; + } + + public function namespace(): string + { + return $this->namespace; + } + + /** + * @return non-empty-string + */ + public function file(): string + { + return $this->file; + } + + /** + * @return non-negative-int + */ + public function startLine(): int + { + return $this->startLine; + } + + /** + * @return non-negative-int + */ + public function endLine(): int + { + return $this->endLine; + } + + /** + * @return list + */ + public function traits(): array + { + return $this->traits; + } + + /** + * @return array + */ + public function methods(): array + { + return $this->methods; + } +} diff --git a/src/StaticAnalysis/Value/Visibility.php b/src/StaticAnalysis/Value/Visibility.php new file mode 100644 index 000000000..c92797751 --- /dev/null +++ b/src/StaticAnalysis/Value/Visibility.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +/** + * @internal This enumeration is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +enum Visibility: string +{ + case Public = 'public'; + case Protected = 'protected'; + case Private = 'private'; +} diff --git a/src/StaticAnalysis/Visitor/AttributeParentConnectingVisitor.php b/src/StaticAnalysis/Visitor/AttributeParentConnectingVisitor.php new file mode 100644 index 000000000..2e2141405 --- /dev/null +++ b/src/StaticAnalysis/Visitor/AttributeParentConnectingVisitor.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function array_pop; +use function count; +use PhpParser\Node; +use PhpParser\NodeVisitor; + +/** + * Visitor that connects a child node to its parent node optimized for Attribute nodes. + * + * On the child node, the parent node can be accessed through + * $node->getAttribute('parent'). + * + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class AttributeParentConnectingVisitor implements NodeVisitor +{ + /** + * @var Node[] + */ + private array $stack = []; + + public function beforeTraverse(array $nodes): null + { + $this->stack = []; + + return null; + } + + public function enterNode(Node $node): null + { + if ($this->stack !== [] && + ($node instanceof Node\Attribute || $node instanceof Node\AttributeGroup)) { + $node->setAttribute('parent', $this->stack[count($this->stack) - 1]); + } + + $this->stack[] = $node; + + return null; + } + + public function leaveNode(Node $node): null + { + array_pop($this->stack); + + return null; + } + + public function afterTraverse(array $nodes): null + { + return null; + } +} diff --git a/src/StaticAnalysis/Visitor/CodeUnitFindingVisitor.php b/src/StaticAnalysis/Visitor/CodeUnitFindingVisitor.php new file mode 100644 index 000000000..558d3e053 --- /dev/null +++ b/src/StaticAnalysis/Visitor/CodeUnitFindingVisitor.php @@ -0,0 +1,435 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function assert; +use function implode; +use function rtrim; +use function trim; +use PhpParser\Node; +use PhpParser\Node\ComplexType; +use PhpParser\Node\Identifier; +use PhpParser\Node\IntersectionType; +use PhpParser\Node\Name; +use PhpParser\Node\NullableType; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Enum_; +use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\Interface_; +use PhpParser\Node\Stmt\Trait_; +use PhpParser\Node\UnionType; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; +use SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class CodeUnitFindingVisitor extends NodeVisitorAbstract +{ + /** + * @var non-empty-string + */ + private string $file; + + /** + * @var array + */ + private array $interfaces = []; + + /** + * @var array + */ + private array $classes = []; + + /** + * @var array + */ + private array $traits = []; + + /** + * @var array + */ + private array $functions = []; + + /** + * @param non-empty-string $file + */ + public function __construct(string $file) + { + $this->file = $file; + } + + public function enterNode(Node $node): null + { + if ($node instanceof Interface_) { + $this->processInterface($node); + } + + if ($node instanceof Class_) { + if ($node->isAnonymous()) { + return null; + } + + $this->processClass($node); + } + + if ($node instanceof Enum_) { + $this->processClass($node); + } + + if ($node instanceof Trait_) { + $this->processTrait($node); + } + + if (!$node instanceof Function_) { + return null; + } + + $this->processFunction($node); + + return null; + } + + public function leaveNode(Node $node): null + { + if ($node instanceof Class_ && $node->isAnonymous()) { + return null; + } + + if (!$node instanceof Class_ && !$node instanceof Trait_) { + return null; + } + + $traits = []; + + foreach ($node->getTraitUses() as $traitUse) { + foreach ($traitUse->traits as $trait) { + $traits[] = $trait->toString(); + } + } + + if ($traits === []) { + return null; + } + + $this->postProcessClassOrTrait($node, $traits); + + return null; + } + + /** + * @return array + */ + public function interfaces(): array + { + return $this->interfaces; + } + + /** + * @return array + */ + public function classes(): array + { + return $this->classes; + } + + /** + * @return array + */ + public function traits(): array + { + return $this->traits; + } + + /** + * @return array + */ + public function functions(): array + { + return $this->functions; + } + + private function cyclomaticComplexity(ClassMethod|Function_ $node): int + { + $nodes = $node->getStmts(); + + if ($nodes === null) { + return 0; + } + + $traverser = new NodeTraverser; + + $cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor; + + $traverser->addVisitor($cyclomaticComplexityCalculatingVisitor); + + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($nodes); + + return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity(); + } + + private function signature(ClassMethod|Function_ $node): string + { + $signature = ($node->returnsByRef() ? '&' : '') . $node->name->toString() . '('; + $parameters = []; + + foreach ($node->getParams() as $parameter) { + assert(isset($parameter->var->name)); + + $parameterAsString = ''; + + if ($parameter->type !== null) { + $parameterAsString = $this->type($parameter->type) . ' '; + } + + $parameterAsString .= '$' . $parameter->var->name; + + /* @todo Handle default values */ + + $parameters[] = $parameterAsString; + } + + $signature .= implode(', ', $parameters) . ')'; + + $returnType = $node->getReturnType(); + + if ($returnType !== null) { + $signature .= ': ' . $this->type($returnType); + } + + return $signature; + } + + private function type(ComplexType|Identifier|Name $type): string + { + if ($type instanceof NullableType) { + return '?' . $type->type; + } + + if ($type instanceof UnionType) { + return $this->unionTypeAsString($type); + } + + if ($type instanceof IntersectionType) { + return $this->intersectionTypeAsString($type); + } + + return $type->toString(); + } + + private function visibility(ClassMethod $node): Visibility + { + if ($node->isPrivate()) { + return Visibility::Private; + } + + if ($node->isProtected()) { + return Visibility::Protected; + } + + return Visibility::Public; + } + + private function processInterface(Interface_ $node): void + { + $name = $node->name->toString(); + $namespacedName = $node->namespacedName->toString(); + $parentInterfaces = []; + + foreach ($node->extends as $parentInterface) { + $parentInterfaces[] = $parentInterface->toString(); + } + + $this->interfaces[$namespacedName] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Interface_( + $name, + $namespacedName, + $this->namespace($namespacedName, $name), + $node->getStartLine(), + $node->getEndLine(), + $parentInterfaces, + ); + } + + private function processClass(Class_|Enum_ $node): void + { + $name = $node->name->toString(); + $namespacedName = $node->namespacedName->toString(); + $parentClass = null; + $interfaces = []; + + if (!$node instanceof Enum_) { + if ($node->extends instanceof Name) { + $parentClass = $node->extends->toString(); + } + + foreach ($node->implements as $interface) { + $interfaces[] = $interface->toString(); + } + } + + $this->classes[$namespacedName] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Class_( + $name, + $namespacedName, + $this->namespace($namespacedName, $name), + $this->file, + $node->getStartLine(), + $node->getEndLine(), + $parentClass, + $interfaces, + [], + $this->processMethods($node->getMethods()), + ); + } + + private function processTrait(Trait_ $node): void + { + $name = $node->name->toString(); + $namespacedName = $node->namespacedName->toString(); + + $this->traits[$namespacedName] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_( + $name, + $namespacedName, + $this->namespace($namespacedName, $name), + $this->file, + $node->getStartLine(), + $node->getEndLine(), + [], + $this->processMethods($node->getMethods()), + ); + } + + /** + * @param list $nodes + * + * @return array + */ + private function processMethods(array $nodes): array + { + $methods = []; + + foreach ($nodes as $node) { + $methods[$node->name->toString()] = new Method( + $node->name->toString(), + $node->getStartLine(), + $node->getEndLine(), + $this->signature($node), + $this->visibility($node), + $this->cyclomaticComplexity($node), + ); + } + + return $methods; + } + + private function processFunction(Function_ $node): void + { + assert(isset($node->name)); + assert(isset($node->namespacedName)); + assert($node->namespacedName instanceof Name); + + $name = $node->name->toString(); + $namespacedName = $node->namespacedName->toString(); + + $this->functions[$namespacedName] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Function_( + $name, + $namespacedName, + $this->namespace($namespacedName, $name), + $node->getStartLine(), + $node->getEndLine(), + $this->signature($node), + $this->cyclomaticComplexity($node), + ); + } + + private function namespace(string $namespacedName, string $name): string + { + return trim(rtrim($namespacedName, $name), '\\'); + } + + private function unionTypeAsString(UnionType $node): string + { + $types = []; + + foreach ($node->types as $type) { + if ($type instanceof IntersectionType) { + $types[] = '(' . $this->intersectionTypeAsString($type) . ')'; + + continue; + } + + $types[] = $this->typeAsString($type); + } + + return implode('|', $types); + } + + private function intersectionTypeAsString(IntersectionType $node): string + { + $types = []; + + foreach ($node->types as $type) { + $types[] = $this->typeAsString($type); + } + + return implode('&', $types); + } + + private function typeAsString(Identifier|Name $node): string + { + if ($node instanceof Name) { + return $node->toCodeString(); + } + + return $node->toString(); + } + + /** + * @param list $traits + */ + private function postProcessClassOrTrait(Class_|Trait_ $node, array $traits): void + { + $name = $node->namespacedName->toString(); + + if ($node instanceof Class_) { + assert(isset($this->classes[$name])); + + $this->classes[$name] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Class_( + $this->classes[$name]->name(), + $this->classes[$name]->namespacedName(), + $this->classes[$name]->namespace(), + $this->classes[$name]->file(), + $this->classes[$name]->startLine(), + $this->classes[$name]->endLine(), + $this->classes[$name]->parentClass(), + $this->classes[$name]->interfaces(), + $traits, + $this->classes[$name]->methods(), + ); + + return; + } + + assert(isset($this->traits[$name])); + + $this->traits[$name] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_( + $this->traits[$name]->name(), + $this->traits[$name]->namespacedName(), + $this->traits[$name]->namespace(), + $this->traits[$name]->file(), + $this->traits[$name]->startLine(), + $this->traits[$name]->endLine(), + $traits, + $this->traits[$name]->methods(), + ); + } +} diff --git a/src/StaticAnalysis/Visitor/ExecutableLinesFindingVisitor.php b/src/StaticAnalysis/Visitor/ExecutableLinesFindingVisitor.php new file mode 100644 index 000000000..6a114e1b9 --- /dev/null +++ b/src/StaticAnalysis/Visitor/ExecutableLinesFindingVisitor.php @@ -0,0 +1,402 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function array_diff_key; +use function assert; +use function count; +use function current; +use function end; +use function explode; +use function max; +use function preg_match; +use function preg_quote; +use function range; +use function reset; +use function sprintf; +use PhpParser\Node; +use PhpParser\NodeVisitorAbstract; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @phpstan-import-type LinesType from AnalysisResult + */ +final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract +{ + private int $nextBranch = 0; + private readonly string $source; + + /** + * @var LinesType + */ + private array $executableLinesGroupedByBranch = []; + + /** + * @var array + */ + private array $unsets = []; + + /** + * @var array + */ + private array $commentsToCheckForUnset = []; + + public function __construct(string $source) + { + $this->source = $source; + } + + public function enterNode(Node $node): null + { + foreach ($node->getComments() as $comment) { + $commentLine = $comment->getStartLine(); + + if (!isset($this->executableLinesGroupedByBranch[$commentLine])) { + continue; + } + + foreach (explode("\n", $comment->getText()) as $text) { + $this->commentsToCheckForUnset[$commentLine] = $text; + $commentLine++; + } + } + + if ($node instanceof Node\Scalar\String_ || + $node instanceof Node\Scalar\EncapsedStringPart) { + $startLine = $node->getStartLine() + 1; + $endLine = $node->getEndLine() - 1; + + if ($startLine <= $endLine) { + foreach (range($startLine, $endLine) as $line) { + unset($this->executableLinesGroupedByBranch[$line]); + } + } + + return null; + } + + if ($node instanceof Node\Stmt\Interface_) { + foreach (range($node->getStartLine(), $node->getEndLine()) as $line) { + $this->unsets[$line] = true; + } + + return null; + } + + if ($node instanceof Node\Stmt\Declare_ || + $node instanceof Node\Stmt\DeclareDeclare || + $node instanceof Node\Stmt\Else_ || + $node instanceof Node\Stmt\EnumCase || + $node instanceof Node\Stmt\Finally_ || + $node instanceof Node\Stmt\GroupUse || + $node instanceof Node\Stmt\Label || + $node instanceof Node\Stmt\Namespace_ || + $node instanceof Node\Stmt\Nop || + $node instanceof Node\Stmt\Switch_ || + $node instanceof Node\Stmt\TryCatch || + $node instanceof Node\Stmt\Use_ || + $node instanceof Node\Stmt\UseUse || + $node instanceof Node\Expr\ConstFetch || + $node instanceof Node\Expr\Variable || + $node instanceof Node\Expr\Throw_ || + $node instanceof Node\ComplexType || + $node instanceof Node\Const_ || + $node instanceof Node\Identifier || + $node instanceof Node\Name || + $node instanceof Node\Param || + $node instanceof Node\Scalar) { + return null; + } + + if ($node instanceof Node\Expr\Match_) { + foreach ($node->arms as $arm) { + $this->setLineBranch( + $arm->body->getStartLine(), + $arm->body->getEndLine(), + ++$this->nextBranch, + ); + } + + return null; + } + + if ($node instanceof Node\Stmt\Expression && $node->expr instanceof Node\Expr\Throw_) { + $this->setLineBranch($node->expr->expr->getEndLine(), $node->expr->expr->getEndLine(), ++$this->nextBranch); + + return null; + } + + if ($node instanceof Node\Stmt\Enum_ || + $node instanceof Node\Stmt\Function_ || + $node instanceof Node\Stmt\Class_ || + $node instanceof Node\Stmt\ClassMethod || + $node instanceof Node\Expr\Closure || + $node instanceof Node\Stmt\Trait_) { + if ($node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\ClassMethod) { + $unsets = []; + + foreach ($node->getParams() as $param) { + foreach (range($param->getStartLine(), $param->getEndLine()) as $line) { + $unsets[$line] = true; + } + } + + unset($unsets[$node->getEndLine()]); + + $this->unsets += $unsets; + } + + $isConcreteClassLike = $node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_; + + if (null !== $node->stmts) { + foreach ($node->stmts as $stmt) { + if ($stmt instanceof Node\Stmt\Nop) { + continue; + } + + foreach (range($stmt->getStartLine(), $stmt->getEndLine()) as $line) { + unset($this->executableLinesGroupedByBranch[$line]); + + if ( + $isConcreteClassLike && + !$stmt instanceof Node\Stmt\ClassMethod + ) { + $this->unsets[$line] = true; + } + } + } + } + + if ($isConcreteClassLike) { + return null; + } + + $hasEmptyBody = [] === $node->stmts || + null === $node->stmts || + ( + 1 === count($node->stmts) && + $node->stmts[0] instanceof Node\Stmt\Nop + ); + + if ($hasEmptyBody) { + if ($node->getEndLine() === $node->getStartLine() && isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) { + return null; + } + + $this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch); + + return null; + } + + return null; + } + + if ($node instanceof Node\Expr\ArrowFunction) { + $startLine = max( + $node->getStartLine() + 1, + $node->expr->getStartLine(), + ); + + $endLine = $node->expr->getEndLine(); + + if ($endLine < $startLine) { + return null; + } + + $this->setLineBranch($startLine, $endLine, ++$this->nextBranch); + + return null; + } + + if ($node instanceof Node\Expr\Ternary) { + if (null !== $node->if && + $node->getStartLine() !== $node->if->getEndLine()) { + $this->setLineBranch($node->if->getStartLine(), $node->if->getEndLine(), ++$this->nextBranch); + } + + if ($node->getStartLine() !== $node->else->getEndLine()) { + $this->setLineBranch($node->else->getStartLine(), $node->else->getEndLine(), ++$this->nextBranch); + } + + return null; + } + + if ($node instanceof Node\Expr\BinaryOp\Coalesce) { + if ($node->getStartLine() !== $node->getEndLine()) { + $this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch); + } + + return null; + } + + if ($node instanceof Node\Stmt\If_ || + $node instanceof Node\Stmt\ElseIf_ || + $node instanceof Node\Stmt\Case_) { + if (null === $node->cond) { + return null; + } + + $this->setLineBranch( + $node->cond->getStartLine(), + $node->cond->getStartLine(), + ++$this->nextBranch, + ); + + return null; + } + + if ($node instanceof Node\Stmt\For_) { + $startLine = null; + $endLine = null; + + if ([] !== $node->init) { + $startLine = $node->init[0]->getStartLine(); + + end($node->init); + + $endLine = current($node->init)->getEndLine(); + + reset($node->init); + } + + if ([] !== $node->cond) { + if (null === $startLine) { + $startLine = $node->cond[0]->getStartLine(); + } + + end($node->cond); + + $endLine = current($node->cond)->getEndLine(); + + reset($node->cond); + } + + if ([] !== $node->loop) { + if (null === $startLine) { + $startLine = $node->loop[0]->getStartLine(); + } + + end($node->loop); + + $endLine = current($node->loop)->getEndLine(); + + reset($node->loop); + } + + if (null === $startLine || null === $endLine) { + return null; + } + + $this->setLineBranch( + $startLine, + $endLine, + ++$this->nextBranch, + ); + + return null; + } + + if ($node instanceof Node\Stmt\Foreach_) { + $this->setLineBranch( + $node->expr->getStartLine(), + $node->valueVar->getEndLine(), + ++$this->nextBranch, + ); + + return null; + } + + if ($node instanceof Node\Stmt\While_ || + $node instanceof Node\Stmt\Do_) { + $this->setLineBranch( + $node->cond->getStartLine(), + $node->cond->getEndLine(), + ++$this->nextBranch, + ); + + return null; + } + + if ($node instanceof Node\Stmt\Catch_) { + assert([] !== $node->types); + $startLine = $node->types[0]->getStartLine(); + end($node->types); + $endLine = current($node->types)->getEndLine(); + + $this->setLineBranch( + $startLine, + $endLine, + ++$this->nextBranch, + ); + + return null; + } + + if ($node instanceof Node\Expr\CallLike) { + if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) { + $branch = $this->executableLinesGroupedByBranch[$node->getStartLine()]; + } else { + $branch = ++$this->nextBranch; + } + + $this->setLineBranch($node->getStartLine(), $node->getEndLine(), $branch); + + return null; + } + + if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) { + return null; + } + + $this->setLineBranch($node->getStartLine(), $node->getEndLine(), ++$this->nextBranch); + + return null; + } + + public function afterTraverse(array $nodes): null + { + $lines = explode("\n", $this->source); + + foreach ($lines as $lineNumber => $line) { + $lineNumber++; + + if (1 === preg_match('/^\s*$/', $line) || + ( + isset($this->commentsToCheckForUnset[$lineNumber]) && + 1 === preg_match(sprintf('/^\s*%s\s*$/', preg_quote($this->commentsToCheckForUnset[$lineNumber], '/')), $line) + )) { + unset($this->executableLinesGroupedByBranch[$lineNumber]); + } + } + + $this->executableLinesGroupedByBranch = array_diff_key( + $this->executableLinesGroupedByBranch, + $this->unsets, + ); + + return null; + } + + /** + * @return LinesType + */ + public function executableLinesGroupedByBranch(): array + { + return $this->executableLinesGroupedByBranch; + } + + private function setLineBranch(int $start, int $end, int $branch): void + { + foreach (range($start, $end) as $line) { + $this->executableLinesGroupedByBranch[$line] = $branch; + } + } +} diff --git a/src/StaticAnalysis/Visitor/IgnoredLinesFindingVisitor.php b/src/StaticAnalysis/Visitor/IgnoredLinesFindingVisitor.php new file mode 100644 index 000000000..fefe646c8 --- /dev/null +++ b/src/StaticAnalysis/Visitor/IgnoredLinesFindingVisitor.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function assert; +use function str_contains; +use PhpParser\Node; +use PhpParser\Node\Attribute; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Enum_; +use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\Interface_; +use PhpParser\Node\Stmt\Trait_; +use PhpParser\NodeVisitorAbstract; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract +{ + /** + * @var array + */ + private array $ignoredLines = []; + private readonly bool $useAnnotationsForIgnoringCode; + private readonly bool $ignoreDeprecated; + + public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecated) + { + $this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode; + $this->ignoreDeprecated = $ignoreDeprecated; + } + + public function enterNode(Node $node): null + { + if (!$node instanceof Class_ && + !$node instanceof Trait_ && + !$node instanceof Interface_ && + !$node instanceof Enum_ && + !$node instanceof ClassMethod && + !$node instanceof Function_ && + !$node instanceof Attribute) { + return null; + } + + if ($node instanceof Class_ && $node->isAnonymous()) { + return null; + } + + if ($node instanceof Class_ || + $node instanceof Trait_ || + $node instanceof Interface_ || + $node instanceof Attribute) { + $this->ignoredLines[] = $node->getStartLine(); + + assert($node->name !== null); + + // Workaround for https://github.com/nikic/PHP-Parser/issues/886 + $this->ignoredLines[] = $node->name->getStartLine(); + } + + if (!$this->useAnnotationsForIgnoringCode) { + return null; + } + + if ($node instanceof Interface_) { + return null; + } + + if ($node instanceof Attribute && + $node->name->toString() === 'PHPUnit\Framework\Attributes\CodeCoverageIgnore') { + $attributeGroup = $node->getAttribute('parent'); + $attributedNode = $attributeGroup->getAttribute('parent'); + + for ($line = $attributedNode->getStartLine(); $line <= $attributedNode->getEndLine(); $line++) { + $this->ignoredLines[] = $line; + } + + return null; + } + + $this->processDocComment($node); + + return null; + } + + /** + * @return array + */ + public function ignoredLines(): array + { + return $this->ignoredLines; + } + + private function processDocComment(Node $node): void + { + $docComment = $node->getDocComment(); + + if ($docComment === null) { + return; + } + + if (str_contains($docComment->getText(), '@codeCoverageIgnore')) { + for ($line = $node->getStartLine(); $line <= $node->getEndLine(); $line++) { + $this->ignoredLines[] = $line; + } + } + + if ($this->ignoreDeprecated && str_contains($docComment->getText(), '@deprecated')) { + for ($line = $node->getStartLine(); $line <= $node->getEndLine(); $line++) { + $this->ignoredLines[] = $line; + } + } + } +} diff --git a/src/Target/Class_.php b/src/Target/Class_.php new file mode 100644 index 000000000..9e6697802 --- /dev/null +++ b/src/Target/Class_.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Class_ extends Target +{ + /** + * @var class-string + */ + private string $className; + + /** + * @param class-string $className + */ + protected function __construct(string $className) + { + $this->className = $className; + } + + public function isClass(): true + { + return true; + } + + /** + * @return class-string + */ + public function className(): string + { + return $this->className; + } + + /** + * @return non-empty-string + */ + public function key(): string + { + return 'classes'; + } + + /** + * @return non-empty-string + */ + public function target(): string + { + return $this->className; + } + + /** + * @return non-empty-string + */ + public function description(): string + { + return 'Class ' . $this->target(); + } +} diff --git a/src/Target/ClassesThatExtendClass.php b/src/Target/ClassesThatExtendClass.php new file mode 100644 index 000000000..94fdb56d8 --- /dev/null +++ b/src/Target/ClassesThatExtendClass.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class ClassesThatExtendClass extends Target +{ + /** + * @var class-string + */ + private string $className; + + /** + * @param class-string $className + */ + protected function __construct(string $className) + { + $this->className = $className; + } + + public function isClassesThatExtendClass(): true + { + return true; + } + + /** + * @return class-string + */ + public function className(): string + { + return $this->className; + } + + /** + * @return non-empty-string + */ + public function key(): string + { + return 'classesThatExtendClass'; + } + + /** + * @return non-empty-string + */ + public function target(): string + { + return $this->className; + } + + /** + * @return non-empty-string + */ + public function description(): string + { + return 'Classes that extend class ' . $this->target(); + } +} diff --git a/src/Target/ClassesThatImplementInterface.php b/src/Target/ClassesThatImplementInterface.php new file mode 100644 index 000000000..6bbe18efe --- /dev/null +++ b/src/Target/ClassesThatImplementInterface.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class ClassesThatImplementInterface extends Target +{ + /** + * @var class-string + */ + private string $interfaceName; + + /** + * @param class-string $interfaceName + */ + protected function __construct(string $interfaceName) + { + $this->interfaceName = $interfaceName; + } + + public function isClassesThatImplementInterface(): true + { + return true; + } + + /** + * @return class-string + */ + public function interfaceName(): string + { + return $this->interfaceName; + } + + /** + * @return non-empty-string + */ + public function key(): string + { + return 'classesThatImplementInterface'; + } + + /** + * @return non-empty-string + */ + public function target(): string + { + return $this->interfaceName; + } + + /** + * @return non-empty-string + */ + public function description(): string + { + return 'Classes that implement interface ' . $this->target(); + } +} diff --git a/src/Target/Function_.php b/src/Target/Function_.php new file mode 100644 index 000000000..1d7cdddc0 --- /dev/null +++ b/src/Target/Function_.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Function_ extends Target +{ + /** + * @var non-empty-string + */ + private string $functionName; + + /** + * @param non-empty-string $functionName + */ + protected function __construct(string $functionName) + { + $this->functionName = $functionName; + } + + public function isFunction(): true + { + return true; + } + + /** + * @return non-empty-string + */ + public function functionName(): string + { + return $this->functionName; + } + + /** + * @return non-empty-string + */ + public function key(): string + { + return 'functions'; + } + + /** + * @return non-empty-string + */ + public function target(): string + { + return $this->functionName; + } + + /** + * @return non-empty-string + */ + public function description(): string + { + return 'Function ' . $this->target(); + } +} diff --git a/src/Target/MapBuilder.php b/src/Target/MapBuilder.php new file mode 100644 index 000000000..be859e2eb --- /dev/null +++ b/src/Target/MapBuilder.php @@ -0,0 +1,287 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use function array_keys; +use function array_merge; +use function array_merge_recursive; +use function array_slice; +use function array_unique; +use function count; +use function explode; +use function implode; +use function range; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\StaticAnalysis\Class_; +use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser; +use SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_; + +/** + * @phpstan-import-type TargetMap from Mapper + * @phpstan-import-type TargetMapPart from Mapper + * + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class MapBuilder +{ + /** + * @return TargetMap + */ + public function build(Filter $filter, FileAnalyser $analyser): array + { + /** + * @var array $classDetails + */ + $classDetails = []; + + $namespaces = []; + $classes = []; + $classesThatExtendClass = []; + $classesThatImplementInterface = []; + $traits = []; + $methods = []; + $functions = []; + $reverseLookup = []; + + foreach ($filter->files() as $file) { + foreach ($analyser->analyse($file)->traits() as $trait) { + if ($trait->isNamespaced()) { + $this->processNamespace($namespaces, $trait->namespace(), $file, $trait->startLine(), $trait->endLine()); + } + + $this->process($traits, $trait->namespacedName(), $file, $trait->startLine(), $trait->endLine()); + $this->processMethods($trait, $file, $methods, $reverseLookup); + } + } + + foreach ($filter->files() as $file) { + foreach ($analyser->analyse($file)->traits() as $trait) { + foreach ($trait->traits() as $traitName) { + if (!isset($traits[$traitName])) { + continue; + } + + $file = array_keys($traits[$traitName])[0]; + + if (!isset($traits[$trait->namespacedName()][$file])) { + $traits[$trait->namespacedName()][$file] = $traits[$traitName][$file]; + + continue; + } + + $traits[$trait->namespacedName()][$file] = array_unique( + array_merge( + $traits[$trait->namespacedName()][$file], + $traits[$traitName][$file], + ), + ); + } + } + } + + foreach ($filter->files() as $file) { + $analysisResult = $analyser->analyse($file); + + foreach ($analysisResult->interfaces() as $interface) { + $classesThatImplementInterface[$interface->namespacedName()] = []; + } + + foreach ($analysisResult->classes() as $class) { + if ($class->isNamespaced()) { + $this->processNamespace($namespaces, $class->namespace(), $file, $class->startLine(), $class->endLine()); + } + + $this->process($classes, $class->namespacedName(), $file, $class->startLine(), $class->endLine()); + + foreach ($class->traits() as $traitName) { + if (!isset($traits[$traitName])) { + continue; + } + + foreach ($traits[$traitName] as $traitFile => $lines) { + if (!isset($classes[$class->namespacedName()][$traitFile])) { + $classes[$class->namespacedName()][$traitFile] = $lines; + + continue; + } + + $classes[$class->namespacedName()][$traitFile] = array_unique( + array_merge( + $classes[$class->namespacedName()][$traitFile], + $lines, + ), + ); + } + } + + $this->processMethods($class, $file, $methods, $reverseLookup); + + $classesThatExtendClass[$class->namespacedName()] = []; + $classDetails[$class->namespacedName()] = $class; + } + + foreach ($analysisResult->functions() as $function) { + if ($function->isNamespaced()) { + $this->processNamespace($namespaces, $function->namespace(), $file, $function->startLine(), $function->endLine()); + } + + $this->process($functions, $function->namespacedName(), $file, $function->startLine(), $function->endLine()); + + foreach (range($function->startLine(), $function->endLine()) as $line) { + $reverseLookup[$file . ':' . $line] = $function->namespacedName(); + } + } + } + + foreach (array_keys($namespaces) as $namespace) { + foreach (array_keys($namespaces[$namespace]) as $file) { + $namespaces[$namespace][$file] = array_unique($namespaces[$namespace][$file]); + } + } + + foreach ($classDetails as $class) { + foreach ($class->interfaces() as $interfaceName) { + if (!isset($classesThatImplementInterface[$interfaceName])) { + continue; + } + + $this->process($classesThatImplementInterface, $interfaceName, $class->file(), $class->startLine(), $class->endLine()); + } + + foreach ($this->parentClasses($classDetails, $class) as $parentClass) { + $classes[$class->namespacedName()] = array_merge_recursive( + $classes[$class->namespacedName()], + $classes[$parentClass->namespacedName()], + ); + + if (isset($classesThatExtendClass[$parentClass->namespacedName()])) { + $this->process($classesThatExtendClass, $parentClass->namespacedName(), $class->file(), $class->startLine(), $class->endLine()); + } + } + } + + foreach (array_keys($classesThatImplementInterface) as $className) { + if ($classesThatImplementInterface[$className] !== []) { + continue; + } + + unset($classesThatImplementInterface[$className]); + } + + foreach (array_keys($classesThatExtendClass) as $className) { + if ($classesThatExtendClass[$className] !== []) { + continue; + } + + unset($classesThatExtendClass[$className]); + } + + /** + * @todo Avoid duplication and remove this loop + */ + foreach (array_keys($classes) as $className) { + foreach (array_keys($classes[$className]) as $file) { + $classes[$className][$file] = array_unique($classes[$className][$file]); + } + } + + return [ + 'namespaces' => $namespaces, + 'traits' => $traits, + 'classes' => $classes, + 'classesThatExtendClass' => $classesThatExtendClass, + 'classesThatImplementInterface' => $classesThatImplementInterface, + 'methods' => $methods, + 'functions' => $functions, + 'reverseLookup' => $reverseLookup, + ]; + } + + private function processMethods(Class_|Trait_ $classOrTrait, string $file, array &$methods, array &$reverseLookup): void + { + foreach ($classOrTrait->methods() as $method) { + $methodName = $classOrTrait->namespacedName() . '::' . $method->name(); + + $this->process($methods, $methodName, $file, $method->startLine(), $method->endLine()); + + foreach (range($method->startLine(), $method->endLine()) as $line) { + $reverseLookup[$file . ':' . $line] = $methodName; + } + } + } + + /** + * @param TargetMapPart $data + * @param non-empty-string $namespace + * @param non-empty-string $file + * @param positive-int $startLine + * @param positive-int $endLine + * + * @param-out TargetMapPart $data + */ + private function processNamespace(array &$data, string $namespace, string $file, int $startLine, int $endLine): void + { + $parts = explode('\\', $namespace); + + foreach (range(1, count($parts)) as $i) { + $this->process($data, implode('\\', array_slice($parts, 0, $i)), $file, $startLine, $endLine); + } + } + + /** + * @param TargetMapPart $data + * @param non-empty-string $unit + * @param non-empty-string $file + * @param positive-int $startLine + * @param positive-int $endLine + * + * @param-out TargetMapPart $data + */ + private function process(array &$data, string $unit, string $file, int $startLine, int $endLine): void + { + if (!isset($data[$unit])) { + $data[$unit] = []; + } + + if (!isset($data[$unit][$file])) { + $data[$unit][$file] = []; + } + + $data[$unit][$file] = array_merge( + $data[$unit][$file], + range($startLine, $endLine), + ); + } + + /** + * @param array $classDetails + * + * @return array + */ + private function parentClasses(array $classDetails, Class_ $class): array + { + if (!$class->hasParent()) { + return []; + } + + if (!isset($classDetails[$class->parentClass()])) { + return []; + } + + return array_merge( + [$classDetails[$class->parentClass()]], + $this->parentClasses($classDetails, $classDetails[$class->parentClass()]), + ); + } +} diff --git a/src/Target/Mapper.php b/src/Target/Mapper.php new file mode 100644 index 000000000..50c243bec --- /dev/null +++ b/src/Target/Mapper.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use function array_keys; +use function array_merge; +use function array_unique; +use function strcasecmp; + +/** + * @phpstan-type TargetMap array{namespaces: TargetMapPart, traits: TargetMapPart, classes: TargetMapPart, classesThatExtendClass: TargetMapPart, classesThatImplementInterface: TargetMapPart, methods: TargetMapPart, functions: TargetMapPart, reverseLookup: ReverseLookup} + * @phpstan-type TargetMapPart array>> + * @phpstan-type ReverseLookup array + * + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class Mapper +{ + /** + * @var TargetMap + */ + private array $map; + + /** + * @param TargetMap $map + */ + public function __construct(array $map) + { + $this->map = $map; + } + + /** + * @return array> + */ + public function mapTargets(TargetCollection $targets): array + { + $result = []; + + foreach ($targets as $target) { + foreach ($this->mapTarget($target) as $file => $lines) { + if (!isset($result[$file])) { + $result[$file] = $lines; + + continue; + } + + $result[$file] = array_unique(array_merge($result[$file], $lines)); + } + } + + return $result; + } + + /** + * @throws InvalidCodeCoverageTargetException + * + * @return array> + */ + public function mapTarget(Target $target): array + { + if (isset($this->map[$target->key()][$target->target()])) { + return $this->map[$target->key()][$target->target()]; + } + + foreach (array_keys($this->map[$target->key()]) as $key) { + if (strcasecmp($key, $target->target()) === 0) { + return $this->map[$target->key()][$key]; + } + } + + throw new InvalidCodeCoverageTargetException($target); + } + + /** + * @param non-empty-string $file + * @param positive-int $line + * + * @return non-empty-string + */ + public function lookup(string $file, int $line): string + { + $key = $file . ':' . $line; + + if (isset($this->map['reverseLookup'][$key])) { + return $this->map['reverseLookup'][$key]; + } + + return $key; + } +} diff --git a/src/Target/Method.php b/src/Target/Method.php new file mode 100644 index 000000000..0b46ba90f --- /dev/null +++ b/src/Target/Method.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Method extends Target +{ + /** + * @var class-string + */ + private string $className; + + /** + * @var non-empty-string + */ + private string $methodName; + + /** + * @param class-string $className + * @param non-empty-string $methodName + */ + protected function __construct(string $className, string $methodName) + { + $this->className = $className; + $this->methodName = $methodName; + } + + public function isMethod(): true + { + return true; + } + + /** + * @return class-string + */ + public function className(): string + { + return $this->className; + } + + /** + * @return non-empty-string + */ + public function methodName(): string + { + return $this->methodName; + } + + /** + * @return non-empty-string + */ + public function key(): string + { + return 'methods'; + } + + /** + * @return non-empty-string + */ + public function target(): string + { + return $this->className . '::' . $this->methodName; + } + + /** + * @return non-empty-string + */ + public function description(): string + { + return 'Method ' . $this->target(); + } +} diff --git a/src/Target/Namespace_.php b/src/Target/Namespace_.php new file mode 100644 index 000000000..651927d9f --- /dev/null +++ b/src/Target/Namespace_.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Namespace_ extends Target +{ + /** + * @var non-empty-string + */ + private string $namespace; + + /** + * @param non-empty-string $namespace + */ + protected function __construct(string $namespace) + { + $this->namespace = $namespace; + } + + public function isNamespace(): true + { + return true; + } + + /** + * @return non-empty-string + */ + public function namespace(): string + { + return $this->namespace; + } + + /** + * @return non-empty-string + */ + public function key(): string + { + return 'namespaces'; + } + + /** + * @return non-empty-string + */ + public function target(): string + { + return $this->namespace; + } + + /** + * @return non-empty-string + */ + public function description(): string + { + return 'Namespace ' . $this->target(); + } +} diff --git a/src/Target/Target.php b/src/Target/Target.php new file mode 100644 index 000000000..7432c81a9 --- /dev/null +++ b/src/Target/Target.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +abstract class Target +{ + /** + * @param non-empty-string $namespace + */ + public static function forNamespace(string $namespace): Namespace_ + { + return new Namespace_($namespace); + } + + /** + * @param class-string $className + */ + public static function forClass(string $className): Class_ + { + return new Class_($className); + } + + /** + * @param class-string $className + * @param non-empty-string $methodName + */ + public static function forMethod(string $className, string $methodName): Method + { + return new Method($className, $methodName); + } + + /** + * @param class-string $interfaceName + */ + public static function forClassesThatImplementInterface(string $interfaceName): ClassesThatImplementInterface + { + return new ClassesThatImplementInterface($interfaceName); + } + + /** + * @param class-string $className + */ + public static function forClassesThatExtendClass(string $className): ClassesThatExtendClass + { + return new ClassesThatExtendClass($className); + } + + /** + * @param non-empty-string $functionName + */ + public static function forFunction(string $functionName): Function_ + { + return new Function_($functionName); + } + + /** + * @param trait-string $traitName + */ + public static function forTrait(string $traitName): Trait_ + { + return new Trait_($traitName); + } + + public function isNamespace(): bool + { + return false; + } + + public function isClass(): bool + { + return false; + } + + public function isMethod(): bool + { + return false; + } + + public function isClassesThatImplementInterface(): bool + { + return false; + } + + public function isClassesThatExtendClass(): bool + { + return false; + } + + public function isFunction(): bool + { + return false; + } + + public function isTrait(): bool + { + return false; + } + + /** + * @return non-empty-string + */ + abstract public function key(): string; + + /** + * @return non-empty-string + */ + abstract public function target(): string; + + /** + * @return non-empty-string + */ + abstract public function description(): string; +} diff --git a/src/Target/TargetCollection.php b/src/Target/TargetCollection.php new file mode 100644 index 000000000..ef6e32ac8 --- /dev/null +++ b/src/Target/TargetCollection.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use function count; +use Countable; +use IteratorAggregate; + +/** + * @template-implements IteratorAggregate + * + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class TargetCollection implements Countable, IteratorAggregate +{ + /** + * @var list + */ + private array $targets; + + /** + * @param list $targets + */ + public static function fromArray(array $targets): self + { + return new self(...$targets); + } + + private function __construct(Target ...$targets) + { + $this->targets = $targets; + } + + /** + * @return list + */ + public function asArray(): array + { + return $this->targets; + } + + public function count(): int + { + return count($this->targets); + } + + public function isEmpty(): bool + { + return $this->count() === 0; + } + + public function isNotEmpty(): bool + { + return $this->count() > 0; + } + + public function getIterator(): TargetCollectionIterator + { + return new TargetCollectionIterator($this); + } +} diff --git a/src/Target/TargetCollectionIterator.php b/src/Target/TargetCollectionIterator.php new file mode 100644 index 000000000..9a5ca06d9 --- /dev/null +++ b/src/Target/TargetCollectionIterator.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use function count; +use Iterator; + +/** + * @template-implements Iterator + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class TargetCollectionIterator implements Iterator +{ + /** + * @var list + */ + private readonly array $targets; + private int $position = 0; + + public function __construct(TargetCollection $metadata) + { + $this->targets = $metadata->asArray(); + } + + public function rewind(): void + { + $this->position = 0; + } + + public function valid(): bool + { + return $this->position < count($this->targets); + } + + public function key(): int + { + return $this->position; + } + + public function current(): Target + { + return $this->targets[$this->position]; + } + + public function next(): void + { + $this->position++; + } +} diff --git a/src/Target/TargetCollectionValidator.php b/src/Target/TargetCollectionValidator.php new file mode 100644 index 000000000..370341438 --- /dev/null +++ b/src/Target/TargetCollectionValidator.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use function implode; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + * + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class TargetCollectionValidator +{ + public function validate(Mapper $mapper, TargetCollection $targets): ValidationResult + { + $errors = []; + + foreach ($targets as $target) { + try { + $mapper->mapTarget($target); + } catch (InvalidCodeCoverageTargetException $e) { + $errors[] = $e->getMessage(); + } + } + + if ($errors === []) { + return ValidationResult::success(); + } + + return ValidationResult::failure(implode("\n", $errors)); + } +} diff --git a/src/Target/Trait_.php b/src/Target/Trait_.php new file mode 100644 index 000000000..a698a18ae --- /dev/null +++ b/src/Target/Trait_.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Trait_ extends Target +{ + /** + * @var trait-string + */ + private string $traitName; + + /** + * @param trait-string $traitName + */ + protected function __construct(string $traitName) + { + $this->traitName = $traitName; + } + + public function isTrait(): true + { + return true; + } + + /** + * @return trait-string + */ + public function traitName(): string + { + return $this->traitName; + } + + /** + * @return non-empty-string + */ + public function key(): string + { + return 'traits'; + } + + /** + * @return non-empty-string + */ + public function target(): string + { + return $this->traitName; + } + + /** + * @return non-empty-string + */ + public function description(): string + { + return 'Trait ' . $this->target(); + } +} diff --git a/src/Target/ValidationFailure.php b/src/Target/ValidationFailure.php new file mode 100644 index 000000000..e43791f5a --- /dev/null +++ b/src/Target/ValidationFailure.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class ValidationFailure extends ValidationResult +{ + /** + * @var non-empty-string + */ + private string $message; + + /** + * @param non-empty-string $message + * + * @noinspection PhpMissingParentConstructorInspection + */ + protected function __construct(string $message) + { + $this->message = $message; + } + + public function isFailure(): true + { + return true; + } + + /** + * @return non-empty-string + */ + public function message(): string + { + return $this->message; + } +} diff --git a/src/Target/ValidationResult.php b/src/Target/ValidationResult.php new file mode 100644 index 000000000..8abf5112c --- /dev/null +++ b/src/Target/ValidationResult.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +abstract readonly class ValidationResult +{ + public static function success(): ValidationSuccess + { + return new ValidationSuccess; + } + + /** + * @param non-empty-string $message + */ + public static function failure(string $message): ValidationFailure + { + return new ValidationFailure($message); + } + + /** + * @phpstan-assert-if-true ValidationSuccess $this + */ + public function isSuccess(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true ValidationFailure $this + */ + public function isFailure(): bool + { + return false; + } +} diff --git a/src/Target/ValidationSuccess.php b/src/Target/ValidationSuccess.php new file mode 100644 index 000000000..1dffd0d2d --- /dev/null +++ b/src/Target/ValidationSuccess.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class ValidationSuccess extends ValidationResult +{ + public function isSuccess(): true + { + return true; + } +} diff --git a/src/TestSize/Known.php b/src/TestSize/Known.php new file mode 100644 index 000000000..2bd72abe2 --- /dev/null +++ b/src/TestSize/Known.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @immutable + */ +abstract class Known extends TestSize +{ + public function isKnown(): true + { + return true; + } + + abstract public function isGreaterThan(self $other): bool; +} diff --git a/src/TestSize/Large.php b/src/TestSize/Large.php new file mode 100644 index 000000000..f6a1b9532 --- /dev/null +++ b/src/TestSize/Large.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @immutable + */ +final class Large extends Known +{ + public function isLarge(): true + { + return true; + } + + public function isGreaterThan(TestSize $other): bool + { + return !$other->isLarge(); + } + + public function asString(): string + { + return 'large'; + } +} diff --git a/src/TestSize/Medium.php b/src/TestSize/Medium.php new file mode 100644 index 000000000..f7fd7c86f --- /dev/null +++ b/src/TestSize/Medium.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @immutable + */ +final class Medium extends Known +{ + public function isMedium(): true + { + return true; + } + + public function isGreaterThan(TestSize $other): bool + { + return $other->isSmall(); + } + + public function asString(): string + { + return 'medium'; + } +} diff --git a/src/TestSize/Small.php b/src/TestSize/Small.php new file mode 100644 index 000000000..7c695ef46 --- /dev/null +++ b/src/TestSize/Small.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @immutable + */ +final class Small extends Known +{ + public function isSmall(): true + { + return true; + } + + public function isGreaterThan(TestSize $other): bool + { + return false; + } + + public function asString(): string + { + return 'small'; + } +} diff --git a/src/TestSize/TestSize.php b/src/TestSize/TestSize.php new file mode 100644 index 000000000..a2c92dcc3 --- /dev/null +++ b/src/TestSize/TestSize.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @immutable + */ +abstract class TestSize +{ + public static function unknown(): Unknown + { + return new Unknown; + } + + public static function small(): Small + { + return new Small; + } + + public static function medium(): Medium + { + return new Medium; + } + + public static function large(): Large + { + return new Large; + } + + /** + * @phpstan-assert-if-true Known $this + */ + public function isKnown(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true Unknown $this + */ + public function isUnknown(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true Small $this + */ + public function isSmall(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true Medium $this + */ + public function isMedium(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true Large $this + */ + public function isLarge(): bool + { + return false; + } + + abstract public function asString(): string; +} diff --git a/src/TestSize/Unknown.php b/src/TestSize/Unknown.php new file mode 100644 index 000000000..92808f688 --- /dev/null +++ b/src/TestSize/Unknown.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestSize; + +/** + * @immutable + */ +final class Unknown extends TestSize +{ + public function isUnknown(): true + { + return true; + } + + public function asString(): string + { + return 'unknown'; + } +} diff --git a/src/TestStatus/Failure.php b/src/TestStatus/Failure.php new file mode 100644 index 000000000..a08ac3f6e --- /dev/null +++ b/src/TestStatus/Failure.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestStatus; + +/** + * @immutable + */ +final class Failure extends Known +{ + public function isFailure(): true + { + return true; + } + + public function asString(): string + { + return 'failure'; + } +} diff --git a/src/TestStatus/Known.php b/src/TestStatus/Known.php new file mode 100644 index 000000000..358abe165 --- /dev/null +++ b/src/TestStatus/Known.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestStatus; + +/** + * @immutable + */ +abstract class Known extends TestStatus +{ + public function isKnown(): true + { + return true; + } +} diff --git a/src/TestStatus/Success.php b/src/TestStatus/Success.php new file mode 100644 index 000000000..55f84a6b2 --- /dev/null +++ b/src/TestStatus/Success.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestStatus; + +/** + * @immutable + */ +final class Success extends Known +{ + public function isSuccess(): true + { + return true; + } + + public function asString(): string + { + return 'success'; + } +} diff --git a/src/TestStatus/TestStatus.php b/src/TestStatus/TestStatus.php new file mode 100644 index 000000000..c8015992f --- /dev/null +++ b/src/TestStatus/TestStatus.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestStatus; + +/** + * @immutable + */ +abstract class TestStatus +{ + public static function unknown(): self + { + return new Unknown; + } + + public static function success(): self + { + return new Success; + } + + public static function failure(): self + { + return new Failure; + } + + /** + * @phpstan-assert-if-true Known $this + */ + public function isKnown(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true Unknown $this + */ + public function isUnknown(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true Success $this + */ + public function isSuccess(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true Failure $this + */ + public function isFailure(): bool + { + return false; + } + + abstract public function asString(): string; +} diff --git a/src/TestStatus/Unknown.php b/src/TestStatus/Unknown.php new file mode 100644 index 000000000..ecde844dd --- /dev/null +++ b/src/TestStatus/Unknown.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestStatus; + +/** + * @immutable + */ +final class Unknown extends TestStatus +{ + public function isUnknown(): true + { + return true; + } + + public function asString(): string + { + return 'unknown'; + } +} diff --git a/src/Util/Filesystem.php b/src/Util/Filesystem.php new file mode 100644 index 000000000..0e99b1593 --- /dev/null +++ b/src/Util/Filesystem.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Util; + +use function is_dir; +use function mkdir; +use function sprintf; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Filesystem +{ + /** + * @throws DirectoryCouldNotBeCreatedException + */ + public static function createDirectory(string $directory): void + { + $success = !(!is_dir($directory) && !@mkdir($directory, 0o777, true) && !is_dir($directory)); + + if (!$success) { + throw new DirectoryCouldNotBeCreatedException( + sprintf( + 'Directory "%s" could not be created', + $directory, + ), + ); + } + } +} diff --git a/src/Util/Percentage.php b/src/Util/Percentage.php new file mode 100644 index 000000000..1de640f80 --- /dev/null +++ b/src/Util/Percentage.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Util; + +use function sprintf; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final readonly class Percentage +{ + private float $fraction; + private float $total; + + public static function fromFractionAndTotal(float $fraction, float $total): self + { + return new self($fraction, $total); + } + + private function __construct(float $fraction, float $total) + { + $this->fraction = $fraction; + $this->total = $total; + } + + public function asFloat(): float + { + if ($this->total > 0) { + return ($this->fraction / $this->total) * 100; + } + + return 100.0; + } + + public function asString(): string + { + if ($this->total > 0) { + return sprintf('%01.2F%%', $this->asFloat()); + } + + return ''; + } + + public function asFixedWidthString(): string + { + if ($this->total > 0) { + return sprintf('%6.2F%%', $this->asFloat()); + } + + return ''; + } +} diff --git a/src/Version.php b/src/Version.php new file mode 100644 index 000000000..fe7c20d6c --- /dev/null +++ b/src/Version.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use function dirname; +use SebastianBergmann\Version as VersionId; + +final class Version +{ + private static string $version = ''; + + public static function id(): string + { + if (self::$version === '') { + self::$version = (new VersionId('12.3.2', dirname(__DIR__)))->asString(); + } + + return self::$version; + } +} diff --git a/Tests/_files/BankAccount.php b/tests/_files/BankAccount.php similarity index 94% rename from Tests/_files/BankAccount.php rename to tests/_files/BankAccount.php index 4238c1559..70f4ed78e 100644 --- a/Tests/_files/BankAccount.php +++ b/tests/_files/BankAccount.php @@ -29,5 +29,6 @@ public function withdrawMoney($balance) $this->setBalance($this->getBalance() - $balance); return $this->getBalance(); + return $this->getBalance(); } } diff --git a/Tests/_files/BankAccountTest.php b/tests/_files/BankAccountTest.php similarity index 65% rename from Tests/_files/BankAccountTest.php rename to tests/_files/BankAccountTest.php index 827cd2452..f20da40e5 100644 --- a/Tests/_files/BankAccountTest.php +++ b/tests/_files/BankAccountTest.php @@ -1,33 +1,25 @@ ba = new BankAccount; } - /** - * @covers BankAccount::getBalance - */ public function testBalanceIsInitiallyZero() { $this->assertEquals(0, $this->ba->getBalance()); } - /** - * @covers BankAccount::withdrawMoney - */ public function testBalanceCannotBecomeNegative() { try { $this->ba->withdrawMoney(1); - } - - catch (RuntimeException $e) { + } catch (RuntimeException $e) { $this->assertEquals(0, $this->ba->getBalance()); return; @@ -36,16 +28,11 @@ public function testBalanceCannotBecomeNegative() $this->fail(); } - /** - * @covers BankAccount::depositMoney - */ public function testBalanceCannotBecomeNegative2() { try { $this->ba->depositMoney(-1); - } - - catch (RuntimeException $e) { + } catch (RuntimeException $e) { $this->assertEquals(0, $this->ba->getBalance()); return; @@ -54,11 +41,6 @@ public function testBalanceCannotBecomeNegative2() $this->fail(); } - /** - * @covers BankAccount::getBalance - * @covers BankAccount::depositMoney - * @covers BankAccount::withdrawMoney - */ public function testDepositWithdrawMoney() { $this->assertEquals(0, $this->ba->getBalance()); diff --git a/tests/_files/ClassThatUsesAnonymousClass.php b/tests/_files/ClassThatUsesAnonymousClass.php new file mode 100644 index 000000000..d3a8a964c --- /dev/null +++ b/tests/_files/ClassThatUsesAnonymousClass.php @@ -0,0 +1,17 @@ +method(); + } +} diff --git a/tests/_files/ClassWithNameThatIsPartOfItsNamespacesName.php b/tests/_files/ClassWithNameThatIsPartOfItsNamespacesName.php new file mode 100644 index 000000000..e5b35ffb4 --- /dev/null +++ b/tests/_files/ClassWithNameThatIsPartOfItsNamespacesName.php @@ -0,0 +1,6 @@ +balance; + } + + protected function setBalance($balance) + { + if ($balance >= 0) { + $this->balance = $balance; + } else { + throw new \RuntimeException; + } + } + + public function depositMoney($balance) + { + $this->setBalance($this->getBalance() + $balance); + + return $this->getBalance(); + } + + public function withdrawMoney($balance) + { + $this->setBalance($this->getBalance() - $balance); + + return $this->getBalance(); + } +} diff --git a/tests/_files/Report/Clover/BankAccount-line.xml b/tests/_files/Report/Clover/BankAccount-line.xml new file mode 100644 index 000000000..9a6fca706 --- /dev/null +++ b/tests/_files/Report/Clover/BankAccount-line.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/Clover/BankAccount-path.xml b/tests/_files/Report/Clover/BankAccount-path.xml new file mode 100644 index 000000000..b49f4c915 --- /dev/null +++ b/tests/_files/Report/Clover/BankAccount-path.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/Clover/class-with-anonymous-function.xml b/tests/_files/Report/Clover/class-with-anonymous-function.xml new file mode 100644 index 000000000..34a84690a --- /dev/null +++ b/tests/_files/Report/Clover/class-with-anonymous-function.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/Clover/ignored-lines.xml b/tests/_files/Report/Clover/ignored-lines.xml new file mode 100644 index 000000000..39a32558a --- /dev/null +++ b/tests/_files/Report/Clover/ignored-lines.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/Cobertura/BankAccount-line.xml b/tests/_files/Report/Cobertura/BankAccount-line.xml new file mode 100644 index 000000000..75401a6cb --- /dev/null +++ b/tests/_files/Report/Cobertura/BankAccount-line.xml @@ -0,0 +1,51 @@ + + + + + %s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/Cobertura/BankAccount-path.xml b/tests/_files/Report/Cobertura/BankAccount-path.xml new file mode 100644 index 000000000..9ce9efe6e --- /dev/null +++ b/tests/_files/Report/Cobertura/BankAccount-path.xml @@ -0,0 +1,51 @@ + + + + + %s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/Cobertura/class-with-anonymous-function.xml b/tests/_files/Report/Cobertura/class-with-anonymous-function.xml new file mode 100644 index 000000000..3beb9d9ff --- /dev/null +++ b/tests/_files/Report/Cobertura/class-with-anonymous-function.xml @@ -0,0 +1,39 @@ + + + + + %s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/Cobertura/class-with-outside-function.xml b/tests/_files/Report/Cobertura/class-with-outside-function.xml new file mode 100644 index 000000000..fe6c005fc --- /dev/null +++ b/tests/_files/Report/Cobertura/class-with-outside-function.xml @@ -0,0 +1,41 @@ + + + + + %s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/Cobertura/ignored-lines.xml b/tests/_files/Report/Cobertura/ignored-lines.xml new file mode 100644 index 000000000..2650a3dde --- /dev/null +++ b/tests/_files/Report/Cobertura/ignored-lines.xml @@ -0,0 +1,21 @@ + + + + + %s + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/Crap4j/BankAccount.xml b/tests/_files/Report/Crap4j/BankAccount.xml new file mode 100644 index 000000000..f2f56eabb --- /dev/null +++ b/tests/_files/Report/Crap4j/BankAccount.xml @@ -0,0 +1,59 @@ + + + BankAccount + %s + + Method Crap Stats + 4 + 0 + 0 + 9 + 0 + + + + global + BankAccount + getBalance + getBalance() + getBalance() + 1 + 1 + 100 + 0 + + + global + BankAccount + setBalance + setBalance($balance) + setBalance($balance) + 6 + 2 + 0 + 0 + + + global + BankAccount + depositMoney + depositMoney($balance) + depositMoney($balance) + 1 + 1 + 100 + 0 + + + global + BankAccount + withdrawMoney + withdrawMoney($balance) + withdrawMoney($balance) + 1 + 1 + 100 + 0 + + + diff --git a/tests/_files/Report/Crap4j/class-with-anonymous-function.xml b/tests/_files/Report/Crap4j/class-with-anonymous-function.xml new file mode 100644 index 000000000..11ace3eb9 --- /dev/null +++ b/tests/_files/Report/Crap4j/class-with-anonymous-function.xml @@ -0,0 +1,26 @@ + + + CoverageForClassWithAnonymousFunction + %s + + Method Crap Stats + 1 + 0 + 0 + 1 + 0 + + + + global + CoveredClassWithAnonymousFunctionInStaticMethod + runAnonymous + runAnonymous() + runAnonymous() + 1 + 1 + 100 + 0 + + + diff --git a/tests/_files/Report/Crap4j/ignored-lines.xml b/tests/_files/Report/Crap4j/ignored-lines.xml new file mode 100644 index 000000000..2607b59ac --- /dev/null +++ b/tests/_files/Report/Crap4j/ignored-lines.xml @@ -0,0 +1,37 @@ + + + CoverageForFileWithIgnoredLines + %s + + Method Crap Stats + 2 + 0 + 0 + 2 + 0 + + + + global + Foo + bar + bar() + bar() + 1 + 1 + 100 + 0 + + + global + Bar + foo + foo() + foo() + 1 + 1 + 100 + 0 + + + diff --git a/tests/_files/Report/HTML/CoverageForBankAccount/BankAccount.php.html b/tests/_files/Report/HTML/CoverageForBankAccount/BankAccount.php.html new file mode 100644 index 000000000..d03cee310 --- /dev/null +++ b/tests/_files/Report/HTML/CoverageForBankAccount/BankAccount.php.html @@ -0,0 +1,251 @@ + + + + + Code Coverage for %s%eBankAccount.php + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Functions and Methods
    Classes and Traits
    Total
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    CRAP
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    BankAccount
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    6.32
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
     getBalance
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
     setBalance
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 3
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    6
     depositMoney
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    2 / 2
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
     withdrawMoney
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    2 / 2
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    1<?php
    2class BankAccount
    3{
    4    protected $balance = 0;
    5
    6    public function getBalance()
    7    {
    8        return $this->balance;
    9    }
    10
    11    protected function setBalance($balance)
    12    {
    13        if ($balance >= 0) {
    14            $this->balance = $balance;
    15        } else {
    16            throw new RuntimeException;
    17        }
    18    }
    19
    20    public function depositMoney($balance)
    21    {
    22        $this->setBalance($this->getBalance() + $balance);
    23
    24        return $this->getBalance();
    25    }
    26
    27    public function withdrawMoney($balance)
    28    {
    29        $this->setBalance($this->getBalance() - $balance);
    30
    31        return $this->getBalance();
    32        return $this->getBalance();
    33    }
    34}
    + + +
    +
    +

    Legend

    +

    Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

    +

    + Generated by php-code-coverage %s using %s at %s. +

    + + + +
    +
    + + + + + diff --git a/tests/_files/Report/HTML/CoverageForBankAccount/dashboard.html b/tests/_files/Report/HTML/CoverageForBankAccount/dashboard.html new file mode 100644 index 000000000..48a7ac744 --- /dev/null +++ b/tests/_files/Report/HTML/CoverageForBankAccount/dashboard.html @@ -0,0 +1,305 @@ + + + + + Dashboard for %s + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + + + + +
    ClassCoverage
    BankAccount62%
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + + + + + + +
    ClassCoverageComplexityCRAP
    BankAccount62.5%56
    +
    +
    +
    +
    +
    +

    Methods

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + + + + +
    MethodCoverage
    setBalance0%
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + + + + + + +
    MethodCoverageComplexityCRAP
    setBalance0.0%26
    +
    +
    +
    + +
    + + + + + + diff --git a/tests/_files/Report/HTML/CoverageForBankAccount/index.html b/tests/_files/Report/HTML/CoverageForBankAccount/index.html new file mode 100644 index 000000000..1112add13 --- /dev/null +++ b/tests/_files/Report/HTML/CoverageForBankAccount/index.html @@ -0,0 +1,118 @@ + + + + + Code Coverage for %s + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Functions and Methods
    Classes and Traits
    Total
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    BankAccount.php
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    +
    +
    +
    +

    Legend

    +

    + Low: 0% to 50% + Medium: 50% to 90% + High: 90% to 100% +

    +

    + Generated by php-code-coverage %s using %s at %s. +

    +
    +
    + + diff --git a/tests/_files/Report/HTML/CoverageForClassWithAnonymousFunction/dashboard.html b/tests/_files/Report/HTML/CoverageForClassWithAnonymousFunction/dashboard.html new file mode 100644 index 000000000..37fce2d95 --- /dev/null +++ b/tests/_files/Report/HTML/CoverageForClassWithAnonymousFunction/dashboard.html @@ -0,0 +1,301 @@ + + + + + Dashboard for %s + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + + + +
    ClassCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + + + + + +
    ClassCoverageComplexityCRAP
    +
    +
    +
    +
    +
    +

    Methods

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + + + +
    MethodCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + + + + + +
    MethodCoverageComplexityCRAP
    +
    +
    +
    + +
    + + + + + + diff --git a/tests/_files/Report/HTML/CoverageForClassWithAnonymousFunction/index.html b/tests/_files/Report/HTML/CoverageForClassWithAnonymousFunction/index.html new file mode 100644 index 000000000..ff7813e53 --- /dev/null +++ b/tests/_files/Report/HTML/CoverageForClassWithAnonymousFunction/index.html @@ -0,0 +1,118 @@ + + + + + Code Coverage for %s + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Functions and Methods
    Classes and Traits
    Total
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    8 / 8
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    source_with_class_and_anonymous_function.php
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    8 / 8
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    +
    +
    +

    Legend

    +

    + Low: 0% to 50% + Medium: 50% to 90% + High: 90% to 100% +

    +

    + Generated by php-code-coverage %s using %s at %s. +

    +
    +
    + + diff --git a/tests/_files/Report/HTML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.html b/tests/_files/Report/HTML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.html new file mode 100644 index 000000000..3d1f80d19 --- /dev/null +++ b/tests/_files/Report/HTML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.html @@ -0,0 +1,170 @@ + + + + + Code Coverage for %s%esource_with_class_and_anonymous_function.php + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Functions and Methods
    Classes and Traits
    Total
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    8 / 8
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    CRAP
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    CoveredClassWithAnonymousFunctionInStaticMethod
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    8 / 8
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
     runAnonymous
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    8 / 8
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
    +
    + + + + + + + + + + + + + + + + + + + + + + + +
    1<?php
    2
    3class CoveredClassWithAnonymousFunctionInStaticMethod
    4{
    5    public static function runAnonymous()
    6    {
    7        $filter = ['abc124', 'abc123', '123'];
    8
    9        array_walk(
    10            $filter,
    11            function (&$val, $key) {
    12                $val = preg_replace('|[^0-9]|', '', $val);
    13            }
    14        );
    15
    16        // Should be covered
    17        $extravar = true;
    18    }
    19}
    + + +
    +
    +

    Legend

    +

    Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

    +

    + Generated by php-code-coverage %s using %s at %s. +

    + + + +
    +
    + + + + + diff --git a/tests/_files/Report/HTML/CoverageForFileWithIgnoredLines/dashboard.html b/tests/_files/Report/HTML/CoverageForFileWithIgnoredLines/dashboard.html new file mode 100644 index 000000000..4f6cc068a --- /dev/null +++ b/tests/_files/Report/HTML/CoverageForFileWithIgnoredLines/dashboard.html @@ -0,0 +1,301 @@ + + + + + Dashboard for %s + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + + + +
    ClassCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + + + + + +
    ClassCoverageComplexityCRAP
    +
    +
    +
    +
    +
    +

    Methods

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + + + +
    MethodCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + + + + + +
    MethodCoverageComplexityCRAP
    +
    +
    +
    + +
    + + + + + + diff --git a/tests/_files/Report/HTML/CoverageForFileWithIgnoredLines/index.html b/tests/_files/Report/HTML/CoverageForFileWithIgnoredLines/index.html new file mode 100644 index 000000000..73c808041 --- /dev/null +++ b/tests/_files/Report/HTML/CoverageForFileWithIgnoredLines/index.html @@ -0,0 +1,108 @@ + + + + + Code Coverage for %s + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Functions and Methods
    Classes and Traits
    Total
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    n/a
    0 / 0
    source_with_ignore.php
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    n/a
    0 / 0
    +
    +
    +
    +

    Legend

    +

    + Low: 0% to 50% + Medium: 50% to 90% + High: 90% to 100% +

    +

    + Generated by php-code-coverage %s using %s at %s. +

    +
    +
    + + diff --git a/tests/_files/Report/HTML/CoverageForFileWithIgnoredLines/source_with_ignore.php.html b/tests/_files/Report/HTML/CoverageForFileWithIgnoredLines/source_with_ignore.php.html new file mode 100644 index 000000000..c8ed1fb11 --- /dev/null +++ b/tests/_files/Report/HTML/CoverageForFileWithIgnoredLines/source_with_ignore.php.html @@ -0,0 +1,201 @@ + + + + + Code Coverage for %s%esource_with_ignore.php + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Functions and Methods
    Classes and Traits
    Total
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    CRAP
    n/a
    0 / 0
    baz
    n/a
    0 / 0
    n/a
    0 / 0
    1
    Foo
    n/a
    0 / 0
    n/a
    0 / 0
    1
    n/a
    0 / 0
     bar
    n/a
    0 / 0
    n/a
    0 / 0
    1
    Bar
    n/a
    0 / 0
    n/a
    0 / 0
    1
    n/a
    0 / 0
     foo
    n/a
    0 / 0
    n/a
    0 / 0
    1
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    1<?php
    2if ($neverHappens) {
    3    // @codeCoverageIgnoreStart
    4    print '*';
    5    // @codeCoverageIgnoreEnd
    6}
    7
    8/**
    9 * @codeCoverageIgnore
    10 */
    11class Foo
    12{
    13    public function bar()
    14    {
    15    }
    16}
    17
    18class Bar
    19{
    20    /**
    21     * @codeCoverageIgnore
    22     */
    23    public function foo()
    24    {
    25    }
    26}
    27
    28function baz()
    29{
    30    print '*'; // @codeCoverageIgnore
    31}
    32
    33interface Bor
    34{
    35    public function foo();
    36}
    37
    38// @codeCoverageIgnoreStart
    39print '
    40Multiline
    41';
    42// @codeCoverageIgnoreEnd
    + + +
    +
    +

    Legend

    +

    Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

    +

    + Generated by php-code-coverage %s using %s at %s. +

    + + + +
    +
    + + + + + diff --git a/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php.html b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php.html new file mode 100644 index 000000000..913f95d2a --- /dev/null +++ b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php.html @@ -0,0 +1,349 @@ + + + + + Code Coverage for %s%eBankAccount.php + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Branches
    Paths
    Functions and Methods
    Classes and Traits
    Total
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 42.86% covered (danger) +
    +
    +
    42.86%
    3 / 7
    +
    + 60.00% covered (warning) +
    +
    +
    60.00%
    3 / 5
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    CRAP
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    BankAccount
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 42.86% covered (danger) +
    +
    +
    42.86%
    3 / 7
    +
    + 60.00% covered (warning) +
    +
    +
    60.00%
    3 / 5
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    6.60
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
     getBalance
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
     setBalance
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 3
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 2
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    6
     depositMoney
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    2 / 2
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
     withdrawMoney
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    2 / 2
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    1<?php
    2class BankAccount
    3{
    4    protected $balance = 0;
    5
    6    public function getBalance()
    7    {
    8        return $this->balance;
    9    }
    10
    11    protected function setBalance($balance)
    12    {
    13        if ($balance >= 0) {
    14            $this->balance = $balance;
    15        } else {
    16            throw new RuntimeException;
    17        }
    18    }
    19
    20    public function depositMoney($balance)
    21    {
    22        $this->setBalance($this->getBalance() + $balance);
    23
    24        return $this->getBalance();
    25    }
    26
    27    public function withdrawMoney($balance)
    28    {
    29        $this->setBalance($this->getBalance() - $balance);
    30
    31        return $this->getBalance();
    32        return $this->getBalance();
    33    }
    34}
    + + +
    +
    +

    Legend

    +

    Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

    +

    + Generated by php-code-coverage %s using %s at %s. +

    + + + +
    +
    + + + + + diff --git a/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_branch.html b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_branch.html new file mode 100644 index 000000000..316a6bbe6 --- /dev/null +++ b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_branch.html @@ -0,0 +1,403 @@ + + + + + Code Coverage for %s%eBankAccount.php + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Branches
    Paths
    Functions and Methods
    Classes and Traits
    Total
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 42.86% covered (danger) +
    +
    +
    42.86%
    3 / 7
    +
    + 60.00% covered (warning) +
    +
    +
    60.00%
    3 / 5
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    CRAP
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    BankAccount
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 42.86% covered (danger) +
    +
    +
    42.86%
    3 / 7
    +
    + 60.00% covered (warning) +
    +
    +
    60.00%
    3 / 5
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    6.60
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
     getBalance
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
     setBalance
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 3
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 2
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    6
     depositMoney
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    2 / 2
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
     withdrawMoney
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    2 / 2
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    1%s
    2%s
    3%s
    4%s
    5
    6%s
    7%s
    8%s
    9%s
    10
    11%s
    12%s
    13%s
    14%s
    15%s
    16%s
    17%s
    18%s
    19
    20%s
    21%s
    22%s
    23
    24%s
    25%s
    26
    27%s
    28%s
    29%s
    30
    31%s
    32        return $this->getBalance();
    33    }
    34}
    + +
    +

    Branches

    +

    + %s + %s + %s + %s +

    +
    BankAccount->depositMoney
    + + + + + + + + + + +
    20%s
    21%s
    22%s
    23
    24%s
    25%s
    +
    BankAccount->getBalance
    + + + + + + + + +
    6%s
    7%s
    8%s
    9%s
    +
    BankAccount->setBalance
    + + + + + + + +
    11%s
    12%s
    13%s
    + + + + + +
    14%s
    + + + + + +
    16%s
    + + + + + +
    18%s
    +
    BankAccount->withdrawMoney
    + + + + + + + + + + +
    27%s
    28%s
    29%s
    30
    31%s
    32%s
    +%a + + diff --git a/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_path.html b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_path.html new file mode 100644 index 000000000..c5e1d0f01 --- /dev/null +++ b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_path.html @@ -0,0 +1,415 @@ + + + + + Code Coverage for %s%eBankAccount.php + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Branches
    Paths
    Functions and Methods
    Classes and Traits
    Total
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 42.86% covered (danger) +
    +
    +
    42.86%
    3 / 7
    +
    + 60.00% covered (warning) +
    +
    +
    60.00%
    3 / 5
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    CRAP
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    BankAccount
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 42.86% covered (danger) +
    +
    +
    42.86%
    3 / 7
    +
    + 60.00% covered (warning) +
    +
    +
    60.00%
    3 / 5
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    6.60
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
     getBalance
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
     setBalance
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 3
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 2
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    6
     depositMoney
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    2 / 2
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
     withdrawMoney
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    2 / 2
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    +
    + 100.00% covered (success) +
    +
    +
    100.00%
    1 / 1
    1
    +
    + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    1%s
    %s
    3%s
    4%s
    5
    6%s
    7%s
    8%s
    9%s
    10
    11%s
    12%s
    13%s
    14%s
    15%s
    16%s
    17%s
    18%s
    19
    20%s
    21%s
    22%s
    23
    24%s
    25%s
    26
    27%s
    28%s
    29%s
    30
    31%s
    32%s
    33    }
    34}
    + +
    +

    Paths

    +

    + %s + %s + %s + %s +

    +
    BankAccount->depositMoney
    + + + + + + + + + + +
    20%s
    21%s
    22%s
    23
    24%s
    25%s
    +
    BankAccount->getBalance
    + + + + + + + + +
    6%s
    7%s
    8%s
    9%s
    +
    BankAccount->setBalance
    + + + + + + + + + + + +
    11%s
    12%s
    13%s
     
    14%s
     
    18%s
    + + + + + + + + + +
    11%s
    12%s
    13%s
     
    16%s
    +
    BankAccount->withdrawMoney
    + + + + + + + + + + +
    27%s
    28%s
    29%s
    30
    31%s
    32%s
    + + + +
    + + + + + diff --git a/tests/_files/Report/HTML/PathCoverageForBankAccount/dashboard.html b/tests/_files/Report/HTML/PathCoverageForBankAccount/dashboard.html new file mode 100644 index 000000000..8bb78fd8d --- /dev/null +++ b/tests/_files/Report/HTML/PathCoverageForBankAccount/dashboard.html @@ -0,0 +1,295 @@ + + + + + Dashboard for %s + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + + + +
    ClassCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + + + +
    ClassCRAP
    +
    +
    +
    +
    +
    +

    Methods

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + + + +
    MethodCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + + + +
    MethodCRAP
    +
    +
    +
    + +
    + + + + + + diff --git a/tests/_files/Report/HTML/PathCoverageForBankAccount/index.html b/tests/_files/Report/HTML/PathCoverageForBankAccount/index.html new file mode 100644 index 000000000..0947cd929 --- /dev/null +++ b/tests/_files/Report/HTML/PathCoverageForBankAccount/index.html @@ -0,0 +1,152 @@ + + + + + Code Coverage for %s + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Branches
    Paths
    Functions and Methods
    Classes and Traits
    Total
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 42.86% covered (danger) +
    +
    +
    42.86%
    3 / 7
    +
    + 60.00% covered (warning) +
    +
    +
    60.00%
    3 / 5
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    BankAccount.php [line] [branch] [path]
    +
    + 62.50% covered (warning) +
    +
    +
    62.50%
    5 / 8
    +
    + 42.86% covered (danger) +
    +
    +
    42.86%
    3 / 7
    +
    + 60.00% covered (warning) +
    +
    +
    60.00%
    3 / 5
    +
    + 75.00% covered (warning) +
    +
    +
    75.00%
    3 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    +
    +
    +
    +

    Legend

    +

    + Low: 0% to 50% + Medium: 50% to 90% + High: 90% to 100% +

    +

    + Generated by php-code-coverage %s using %s at %s. +

    +
    +
    + + diff --git a/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/dashboard.html b/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/dashboard.html new file mode 100644 index 000000000..9b0dfd33a --- /dev/null +++ b/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/dashboard.html @@ -0,0 +1,295 @@ + + + + + Dashboard for %s + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + + + +
    ClassCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + + + +
    ClassCRAP
    +
    +
    +
    +
    +
    +

    Methods

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + + + +
    MethodCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + + + +
    MethodCRAP
    +
    +
    +
    + +
    + + + + + + diff --git a/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/index.html b/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/index.html new file mode 100644 index 000000000..51d43fe05 --- /dev/null +++ b/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/index.html @@ -0,0 +1,142 @@ + + + + + Code Coverage for %s + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Branches
    Paths
    Functions and Methods
    Classes and Traits
    Total
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 2
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    n/a
    0 / 0
    source_without_namespace.php [line] [branch] [path]
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 2
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    n/a
    0 / 0
    +
    +
    +
    +

    Legend

    +

    + Low: 0% to 50% + Medium: 50% to 90% + High: 90% to 100% +

    +

    + Generated by php-code-coverage %s using %s at %s. +

    +
    +
    + + diff --git a/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/source_without_namespace.php.html b/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/source_without_namespace.php.html new file mode 100644 index 000000000..1446f796d --- /dev/null +++ b/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/source_without_namespace.php.html @@ -0,0 +1,189 @@ + + + + + Code Coverage for %ssource_without_namespace.php + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Branches
    Paths
    Functions and Methods
    Classes and Traits
    Total
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 2
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    CRAP
    n/a
    0 / 0
    foo
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 2
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    6
    Foo
    n/a
    0 / 0
    n/a
    0 / 0
    n/a
    0 / 0
    n/a
    0 / 0
    0
    n/a
    0 / 0
    +
    + + + + + + + + + + + + + + + + + + + + + + +
    1<?php
    2/**
    3 * Represents foo.
    4 */
    5class Foo
    6{
    7}
    8
    9/**
    10 * @param mixed $bar
    11 */
    12function &foo($bar)
    13{
    14    $baz = function () {};
    15    $a   = true ? true : false;
    16    $b   = "{$a}";
    17    $c   = "${b}";
    18}
    + + +
    +
    +

    Legend

    +

    Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

    +

    + Generated by php-code-coverage %s using %s at %s. +

    + + + +
    +
    + + + + + diff --git a/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/source_without_namespace.php_branch.html b/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/source_without_namespace.php_branch.html new file mode 100644 index 000000000..590d2010b --- /dev/null +++ b/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/source_without_namespace.php_branch.html @@ -0,0 +1,235 @@ + + + + + Code Coverage for %ssource_without_namespace.php + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Branches
    Paths
    Functions and Methods
    Classes and Traits
    Total
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 2
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    CRAP
    n/a
    0 / 0
    foo
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 2
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    6
    Foo
    n/a
    0 / 0
    n/a
    0 / 0
    n/a
    0 / 0
    n/a
    0 / 0
    0
    n/a
    0 / 0
    +
    + + + + + + + + + + + + + + + + + + + + + + +
    1<?php
    2/**
    3 * Represents foo.
    4 */
    5class Foo
    6{
    7}
    8
    9/**
    10 * @param mixed $bar
    11 */
    12function &foo($bar)
    13{
    14    $baz = function () {};
    15    $a   = true ? true : false;
    16    $b   = "{$a}";
    17    $c   = "${b}";
    18}
    + +
    +

    Branches

    +

    + Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not + necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once. + Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement + always has an else as part of its logical flow even if you didn't write one. +

    +
    foo
    + + + + + + + + +
    12function &foo($bar)
    13{
    14    $baz = function () {};
    15    $a   = true ? true : false;
    + + + + + +
    15    $a   = true ? true : false;
    + + + + + +
    15    $a   = true ? true : false;
    + + + + + + + +
    15    $a   = true ? true : false;
    16    $b   = "{$a}";
    17    $c   = "${b}";
    +
    {closure:%ssource_without_namespace.php:14-14}
    + + + + + +
    14    $baz = function () {};
    + + + +
    + + + + + diff --git a/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/source_without_namespace.php_path.html b/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/source_without_namespace.php_path.html new file mode 100644 index 000000000..640b60b1f --- /dev/null +++ b/tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/source_without_namespace.php_path.html @@ -0,0 +1,236 @@ + + + + + Code Coverage for %ssource_without_namespace.php + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    Code Coverage
     
    Lines
    Branches
    Paths
    Functions and Methods
    Classes and Traits
    Total
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 2
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    CRAP
    n/a
    0 / 0
    foo
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 4
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 2
    +
    + 0.00% covered (danger) +
    +
    +
    0.00%
    0 / 1
    6
    Foo
    n/a
    0 / 0
    n/a
    0 / 0
    n/a
    0 / 0
    n/a
    0 / 0
    0
    n/a
    0 / 0
    +
    + + + + + + + + + + + + + + + + + + + + + + +
    1<?php
    2/**
    3 * Represents foo.
    4 */
    5class Foo
    6{
    7}
    8
    9/**
    10 * @param mixed $bar
    11 */
    12function &foo($bar)
    13{
    14    $baz = function () {};
    15    $a   = true ? true : false;
    16    $b   = "{$a}";
    17    $c   = "${b}";
    18}
    + +
    +

    Paths

    +

    + Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not + necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. + Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement + always has an else as part of its logical flow even if you didn't write one. +

    +
    foo
    + + + + + + + + + + + + + + +
    12function &foo($bar)
    13{
    14    $baz = function () {};
    15    $a   = true ? true : false;
     
    15    $a   = true ? true : false;
     
    15    $a   = true ? true : false;
    16    $b   = "{$a}";
    17    $c   = "${b}";
    + + + + + + + + + + + + + + +
    12function &foo($bar)
    13{
    14    $baz = function () {};
    15    $a   = true ? true : false;
     
    15    $a   = true ? true : false;
     
    15    $a   = true ? true : false;
    16    $b   = "{$a}";
    17    $c   = "${b}";
    +
    {closure:%ssource_without_namespace.php:14-14}
    + + + + + +
    14    $baz = function () {};
    + + + +
    + + + + + diff --git a/tests/_files/Report/OpenClover/BankAccount-line.xml b/tests/_files/Report/OpenClover/BankAccount-line.xml new file mode 100644 index 000000000..eef39f2aa --- /dev/null +++ b/tests/_files/Report/OpenClover/BankAccount-line.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/OpenClover/BankAccount-path.xml b/tests/_files/Report/OpenClover/BankAccount-path.xml new file mode 100644 index 000000000..2612cb850 --- /dev/null +++ b/tests/_files/Report/OpenClover/BankAccount-path.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/OpenClover/class-with-anonymous-function.xml b/tests/_files/Report/OpenClover/class-with-anonymous-function.xml new file mode 100644 index 000000000..2ec261e6c --- /dev/null +++ b/tests/_files/Report/OpenClover/class-with-anonymous-function.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/OpenClover/clover.xsd b/tests/_files/Report/OpenClover/clover.xsd new file mode 100644 index 000000000..3b41fac49 --- /dev/null +++ b/tests/_files/Report/OpenClover/clover.xsd @@ -0,0 +1,236 @@ + + + + + + + Top-most element describing the coverage report. Contains a + project and a test project. + + + + + + + + + + + + + + Project metrics relating to non-test source. + @name - project name (optional) + @timestamp - seconds since UTC + + + + + + + + + + + + + + + Project metrics relating to test source. + @name - project name (optional) + @timestamp - seconds since UTC + + + + + + + + + + + + + + + Package metrics. + @name - the.package.name + + + + + + + + + + + + + + File metrics. + @name - the file name e.g. Foo.java or Bar.groovy + @path - the filesystem-specific original path to the file e.g. c:\path\to\Bar.groovy + + + + + + + + + + + + + + + + Class metrics. + @name - the unqualified class name + + + + + + + + + + + + + + + + + + + + + Line-specific information. + @line - the line number + @type - the type of syntactic construct - one of method|stmt|cond + @complexity - only applicable if @type == 'method'; the cyclomatic complexity of the construct + @count - only applicable if @type == 'stmt' or 'method'; the number of times the construct was executed + @truecount - only applicable if @type == 'cond'; the number of times the true branch was executed + @falsecount - only applicable if @type == 'cond'; the number of times the false branch was executed + @signature - only applicable if @type == 'method'; the signature of the method + @testduration - only applicable if @type == 'method' and the method was identified as a test method; the duration of the test + @testsuccess - only applicable if @type == 'method' and the method was identified as a test method; true if the test passed, false otherwise + @visibility - only applicable if @type == 'method' + + + + + + + + + + + + + + + + + + + + Metrics information for projects/packages/files/classes. + @complexity - the cyclomatic complexity + @conditionals - the number of contained conditionals (2 * number of branches) + @coveredconditionals - the number of contained conditionals (2 * number of branches) with coverage + @elements - the number of contained statements, conditionals and methods + @coveredelements - the number of contained statements, conditionals and methods with coverage + @statements - the number of contained statements + @coveredstatements - the number of contained statements with coverage + @methods - the number of contained methods + @coveredmethods - the number of contained methods with coverage + @testduration - the total duration of all contained test methods + @testfailures - the total number of test method failures + @testpasses - the total number of test method passes + @testruns - the total number of test methods run + + + + + + + + + + + + + + + + + + + + + Metrics information for projects/packages/files. + @classes - the total number of contained classes + @loc - the total number of lines of code + @ncloc - the total number of non-comment lines of code + + + + + + + + + + + + + + Metrics information for projects/packages. + @files - the total number of contained files + + + + + + + + + + + + Metrics information for projects. + @files - the total number of packages + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/OpenClover/ignored-lines.xml b/tests/_files/Report/OpenClover/ignored-lines.xml new file mode 100644 index 000000000..063db4b79 --- /dev/null +++ b/tests/_files/Report/OpenClover/ignored-lines.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/Text/BankAccount-line.txt b/tests/_files/Report/Text/BankAccount-line.txt new file mode 100644 index 000000000..cbc92cdab --- /dev/null +++ b/tests/_files/Report/Text/BankAccount-line.txt @@ -0,0 +1,12 @@ + + +Code Coverage Report: + %s + + Summary: + Classes: 0.00% (0/1) + Methods: 75.00% (3/4) + Lines: 62.50% (5/8) + +BankAccount + Methods: 75.00% ( 3/ 4) Lines: 62.50% ( 5/ 8) diff --git a/tests/_files/Report/Text/BankAccount-path.txt b/tests/_files/Report/Text/BankAccount-path.txt new file mode 100644 index 000000000..b18ac71cf --- /dev/null +++ b/tests/_files/Report/Text/BankAccount-path.txt @@ -0,0 +1,14 @@ + + +Code Coverage Report: + %s + + Summary: + Classes: 0.00% (0/1) + Methods: 75.00% (3/4) + Paths: 60.00% (3/5) + Branches: 42.86% (3/7) + Lines: 62.50% (5/8) + +BankAccount + Methods: 75.00% ( 3/ 4) Paths: 60.00% ( 3/ 5) Branches: 42.86% ( 3/ 7) Lines: 62.50% ( 5/ 8) diff --git a/tests/_files/Report/Text/BankAccount-summary.txt b/tests/_files/Report/Text/BankAccount-summary.txt new file mode 100644 index 000000000..cf8e52385 --- /dev/null +++ b/tests/_files/Report/Text/BankAccount-summary.txt @@ -0,0 +1,7 @@ + + +Code Coverage Report Summary: + Classes: 0.00% (0/1) + Methods: 75.00% (3/4) + Lines: 62.50% (5/8) + diff --git a/tests/_files/Report/Text/BankAccountWithUncovered-line.txt b/tests/_files/Report/Text/BankAccountWithUncovered-line.txt new file mode 100644 index 000000000..ee7757bad --- /dev/null +++ b/tests/_files/Report/Text/BankAccountWithUncovered-line.txt @@ -0,0 +1,12 @@ + + +Code Coverage Report: + %s + + Summary: + Classes: 0.00% (0/2) + Methods: 37.50% (3/8) + Lines: 31.25% (5/16) + +BankAccount + Methods: 75.00% ( 3/ 4) Lines: 62.50% ( 5/ 8) diff --git a/tests/_files/Report/Text/BankAccountWithoutUncovered-line.txt b/tests/_files/Report/Text/BankAccountWithoutUncovered-line.txt new file mode 100644 index 000000000..cbc92cdab --- /dev/null +++ b/tests/_files/Report/Text/BankAccountWithoutUncovered-line.txt @@ -0,0 +1,12 @@ + + +Code Coverage Report: + %s + + Summary: + Classes: 0.00% (0/1) + Methods: 75.00% (3/4) + Lines: 62.50% (5/8) + +BankAccount + Methods: 75.00% ( 3/ 4) Lines: 62.50% ( 5/ 8) diff --git a/tests/_files/Report/Text/NamespacedBankAccount-colors.txt b/tests/_files/Report/Text/NamespacedBankAccount-colors.txt new file mode 100644 index 000000000..83f3901e1 --- /dev/null +++ b/tests/_files/Report/Text/NamespacedBankAccount-colors.txt @@ -0,0 +1,14 @@ + + +Code Coverage Report:  + %s  +  + Summary:  + Classes: 0.00% (0/1) + Methods: 75.00% (3/4) + Lines: 62.50% (5/8) + +SomeNamespace\BankAccount + Methods: ( 0/ 0) Lines: ( 0/ 0) +SomeNamespace\BankAccountTrait + Methods: 75.00% ( 3/ 4) Lines: 62.50% ( 5/ 8) diff --git a/tests/_files/Report/Text/NamespacedBankAccount.txt b/tests/_files/Report/Text/NamespacedBankAccount.txt new file mode 100644 index 000000000..f535d120b --- /dev/null +++ b/tests/_files/Report/Text/NamespacedBankAccount.txt @@ -0,0 +1,14 @@ + + +Code Coverage Report: + %s + + Summary: + Classes: 0.00% (0/1) + Methods: 75.00% (3/4) + Lines: 62.50% (5/8) + +SomeNamespace\BankAccount + Methods: ( 0/ 0) Lines: ( 0/ 0) +SomeNamespace\BankAccountTrait + Methods: 75.00% ( 3/ 4) Lines: 62.50% ( 5/ 8) diff --git a/tests/_files/Report/Text/class-with-anonymous-function.txt b/tests/_files/Report/Text/class-with-anonymous-function.txt new file mode 100644 index 000000000..905bdd1a2 --- /dev/null +++ b/tests/_files/Report/Text/class-with-anonymous-function.txt @@ -0,0 +1,12 @@ + + +Code Coverage Report: + %s + + Summary: + Classes: 100.00% (1/1) + Methods: 100.00% (1/1) + Lines: 100.00% (8/8) + +CoveredClassWithAnonymousFunctionInStaticMethod + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 8/ 8) diff --git a/tests/_files/Report/Text/ignored-lines.txt b/tests/_files/Report/Text/ignored-lines.txt new file mode 100644 index 000000000..8592fb471 --- /dev/null +++ b/tests/_files/Report/Text/ignored-lines.txt @@ -0,0 +1,10 @@ + + +Code Coverage Report: + %s + + Summary: + Classes: (0/0) + Methods: (0/0) + Lines: 100.00% (1/1) + diff --git a/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml b/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml new file mode 100644 index 000000000..b49cdf8ed --- /dev/null +++ b/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml @@ -0,0 +1,272 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <?php + + + class + + BankAccount + + + { + + + + protected + + $balance + + = + + 0 + ; + + + + + public + + function + + getBalance + ( + ) + + + + { + + + + return + + $this + -> + balance + ; + + + + } + + + + + protected + + function + + setBalance + ( + $balance + ) + + + + { + + + + if + + ( + $balance + + >= + + 0 + ) + + { + + + + $this + -> + balance + + = + + $balance + ; + + + + } + + else + + { + + + + throw + + new + + RuntimeException + ; + + + + } + + + + } + + + + + public + + function + + depositMoney + ( + $balance + ) + + + + { + + + + $this + -> + setBalance + ( + $this + -> + getBalance + ( + ) + + + + + $balance + ) + ; + + + + + return + + $this + -> + getBalance + ( + ) + ; + + + + } + + + + + public + + function + + withdrawMoney + ( + $balance + ) + + + + { + + + + $this + -> + setBalance + ( + $this + -> + getBalance + ( + ) + + - + + $balance + ) + ; + + + + + return + + $this + -> + getBalance + ( + ) + ; + + + + return + + $this + -> + getBalance + ( + ) + ; + + + + } + + + } + + + + + diff --git a/tests/_files/Report/XML/CoverageForBankAccount/index.xml b/tests/_files/Report/XML/CoverageForBankAccount/index.xml new file mode 100644 index 000000000..6e551a135 --- /dev/null +++ b/tests/_files/Report/XML/CoverageForBankAccount/index.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/index.xml b/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/index.xml new file mode 100644 index 000000000..3bfba4dda --- /dev/null +++ b/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/index.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.xml b/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.xml new file mode 100644 index 000000000..95d121dc8 --- /dev/null +++ b/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <?php + + + + class + + CoveredClassWithAnonymousFunctionInStaticMethod + + + { + + + + public + + static + + function + + runAnonymous + ( + ) + + + + { + + + + $filter + + = + + [ + 'abc124' + , + + 'abc123' + , + + '123' + ] + ; + + + + + array_walk + ( + + + + $filter + , + + + + function + + ( + & + $val + , + + $key + ) + + { + + + + $val + + = + + preg_replace + ( + '|[^0-9]|' + , + + '' + , + + $val + ) + ; + + + + } + + + + ) + ; + + + + + // Should be covered + + + + $extravar + + = + + true + ; + + + + } + + + } + + + + + diff --git a/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/index.xml b/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/index.xml new file mode 100644 index 000000000..a53b12c53 --- /dev/null +++ b/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/index.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/source_with_ignore.php.xml b/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/source_with_ignore.php.xml new file mode 100644 index 000000000..b862f3d1e --- /dev/null +++ b/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/source_with_ignore.php.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + <?php + + + if + + ( + $neverHappens + ) + + { + + + + // @codeCoverageIgnoreStart + + + + print + + '*' + ; + + + + // @codeCoverageIgnoreEnd + + + } + + + + /** + + + * @codeCoverageIgnore + + + */ + + + class + + Foo + + + { + + + + public + + function + + bar + ( + ) + + + + { + + + + } + + + } + + + + class + + Bar + + + { + + + + /** + + + * @codeCoverageIgnore + + + */ + + + + public + + function + + foo + ( + ) + + + + { + + + + } + + + } + + + + function + + baz + ( + ) + + + { + + + + print + + '*' + ; + + // @codeCoverageIgnore + + + } + + + + interface + + Bor + + + { + + + + public + + function + + foo + ( + ) + ; + + + } + + + + // @codeCoverageIgnoreStart + + + print + + ' + + + Multiline + + + ' + ; + + + // @codeCoverageIgnoreEnd + + + + + diff --git a/tests/_files/Target/ChildClass.php b/tests/_files/Target/ChildClass.php new file mode 100644 index 000000000..868bf3e99 --- /dev/null +++ b/tests/_files/Target/ChildClass.php @@ -0,0 +1,9 @@ + // 0 + $value // 0 + ) + { + $var += 2; // +1 + } + } + public function withWhile() + { + $var = 1; // +1 + while (0 === $var); // +1 + while (0 === $var) ++$var; // +1 + while (0 === $var) { // +1 + ++$var; // +1 + } + while (0 === $var) { ++$var; // +1 + ++$var; // +1 + ++$var; } // +1 + while (0 === $var): // +1 + ++$var; // +1 + endwhile; + while ( + 0 // +1 + === // 0 + $var // 0 + ) + { + ++$var; // +1 + } + } + public function withIfElseifElse() + { + $var = 1; // +1 + if (0 === $var); // +1 + if (0 === $var) { ++$var; } // +1 + if (1 === $var): // +1 + ++$var; // +1 + elseif (1 === $var): // +1 + ++$var; // +1 + else: + ++$var; // +1 + endif; + if (1 === $var) { // +1 + ++$var; // +1 + } elseif (1 === $var) { // +1 + ++$var; // +1 + } else { + ++$var; // +1 + } + if (1 === $var) { ++$var; // +1 + ++$var; // +1 + } elseif (1 === $var) { ++$var; // +1 + ++$var; // +1 + ++$var; } else { ++$var; // +1 + ++$var; // +1 + } + if ( + 1 === $var // +1 + ) + { + ++$var; // +1 + } + elseif + ( + 1 === $var // +1 + ) + { + ++$var; // +1 + } + else + { + ++$var; // +1 + } + } + public function withFor() + { + $var = 1; // +1 + for (;false;); // +1 + for (;false;) $var += 2; // +1 + for (;false;) { // +1 + $var += 2; // +1 + } + for (;false;): // +1 + $var += 2; // +1 + endfor; + for (;false;) { $var +=2; // +1 + $var += 2; // +1 + $var += 2; } // +1 + for ( + $inc = 0; // +1 + false; // 0 + ++$inc // 0 + ) + { + $var += 2; // +1 + } + } + public function withDoWhile() + { + $var = 1; // +1 + do {} while (0 === $var); // +1 + do ++$var; while (0 === $var); // +1 + do + ++$var; // +2 + while (0 === $var); // -1 + do { + ++$var; // +3 + } while (0 === $var); // -1 + do { ++$var; // +3 + ++$var; // +1 + ++$var; } while (0 === $var); // -2 + do { + ++$var; // +4 + } + while + ( + 0 // -1 + === // 0 + $var // 0 + ) + ; + } + public function withSwitch() + { + $var = 1; // +2 + switch ($var) { + case 0: // +1 + case 1: // +1 + ++$var; // +1 + break; // +1 + case 2: // +1 + ++$var; // +1 + case 3: // +1 + ++$var; // +1 + break; // +1 + default: + ++$var; // +1 + } + switch ($var): + case 0: // +1 + case 1: // +1 + ++$var; // +1 + break; // +1 + case 2: // +1 + ++$var; // +1 + case 3: // +1 + ++$var; // +1 + break; // +1 + default: + ++$var; // +1 + endswitch; + } + public function withReturn() + { + $var = 1; // +1 + if (false) { // +1 + ++$var; // +1 + return // +1 + $var // 0 + ; // 0 + ++$var; // +1 + if (false) { // +1 + ++$var; // +1 + } + } + return; // +1 + ++$var; // +1 + } + public function withContinue() + { + $var = 1; // +1 + for ($i = 0; $i < 10; $i++) { // +1 + if (false) { // +1 + ++$var; // +1 + continue // +1 + 1 // 0 + ; // 0 + ++$var; // +1 + } + ++$var; // +1 + continue; // +1 + ++$var; // +1 + } + } + public function withBreak() + { + $var = 1; // +1 + for ($i = 0; $i < 10; $i++) { // +1 + if (false) { // +1 + ++$var; // +1 + break // +1 + 1 // 0 + ; // 0 + ++$var; // +1 + } + ++$var; // +1 + break; // +1 + ++$var; // +1 + } + } + public function withGoto() + { + $var = 1; // +1 + if (false) { // +1 + ++$var; // +1 + goto // +1 + a // 0 + ; // 0 + ++$var; // +1 + } + ++$var; // +1 + a + : + ++$var; // +1 + b: + ++$var; // +1 + } + public function withThrow() + { + $var = 1; // +1 + try { + ++$var; // +1 + throw + new // +2 + \Exception() // 0 + ; + $myex = new \Exception(); // +1 + throw + $myex // +1 + ; + ++$var; // +1 + } catch (\Exception $exception) { // +1 + ++$var; // +1 + } catch (\RuntimeException $re) { // +1 + } + catch + ( + \Throwable // +1 + $throwable + ) + { + ++$var; // +1 + } finally { + ++$var; // +1 + } + ++$var; // +1 + } + public function withTernaryOperator() + { + $var = true ? 'a' : 'b'; // +1 + $var // +1 + = // 0 + true // 0 + ? // 0 + 'a' // +1 + : // -1 + 'b' // +2 + ; // -2 + + $short = $var ?: null; // +3 + $short = $var // +1 + ?: null; // +1 + + $short = $var ?? null; // +1 + $short = $var // +1 + ?? null; // +1 + } + public function withCall() + { + $var = 1; // +1 + $var = intval( // +1 + $var // 0 + ); // 0 + $var = time( // +1 + + ); // 0 + $var // +1 + = // 0 + intval( // 0 + $var // 0 + ); // 0 + ++$var; // +1 + $date = new \DateTimeImmutable(); // +1 + $date // +1 + = // 0 + new // 0 + \DateTimeImmutable // 0 + ( // 0 + 'now' // 0 + ) // 0 + ; // 0 + ++$var; // +1 + $ymd = $date->format('Ymd'); // +1 + $ymd // +1 + = // 0 + $date // 0 + ->format( // 0 + 'Ymd' // 0 + ) // 0 + ; // 0 + ++$var; // +1 + $date = \DateTime::createFromImmutable($date); // +1 + $date // +1 + = // 0 + \DateTimeImmutable // 0 + :: // 0 + createFromMutable // 0 + ( // 0 + $date // 0 + ) // 0 + ; // 0 + ++$var; // +1 + } + public function withClosure() + { + $myf = function(){}; // +1 + $myf = function(){ // +1 + }; // +1 + $myf = function() // +1 + { // 0 + }; // +1 + $myf = function(){ // +1 + return 1; // +1 + }; // -1 + $myf = function() // +2 + { // 0 + return 1; // +1 + }; // -1 + $var = 1; // +2 + $myf // +1 + = // 0 + function // 0 + ( // 0 + $var2 // 0 + = // 0 + 2, // 0 + $var3 // 0 + = // 0 + null // 0 + ) // 0 + use // 0 + ( // 0 + & // 0 + $var // 0 + ) // 0 + : // 0 + void // 0 + { // 0 + }; // +1 + $myf = function(){ $var = 1;}; // +2 + } + public function withAnonymousClass() + { + $var = 1; // +1 + $myClass // +1 + = // 0 + new // 0 + class // 0 + extends // 0 + \RuntimeException // 0 + implements // 0 + \Throwable // 0 + { // 0 + private const MY_CONST = 1; + private $var = 1; + public function myMethod() + { + return; // +3 + } + + public function m1(): void {} // +1 + public function m2(): void { + } // +1 + public function m3(): void + {} // +1 + } // -6 + ; // 0 + } + public function withComments() + { + $var = 1; // +7 + /** @var int $var */ + $var = 2; // +1 + // C3 + $var = 3; // +1 + # C4 + $var = 3; // +1 + /* @var int $var */ + $var = 5; // +1 + $var = [ // +1 + // within nodes + new \DateTimeImmutable(), // 0 + # within nodes + new \DateTimeImmutable(), // 0 + /* + * within nodes + */ + new \DateTimeImmutable(), // 0 + /* + * within nodes + */ + new \DateTimeImmutable(), // 0 + ]; // 0 + // Comment2 + } + public function withCommentsOnly() + { + /** + $var = 1; + */ + } // +1 + public function withEarlyReturns() + { + foreach ([] as $value) { // +1 + $var = 1; // +1 + if (false) { // +1 + ++$var; // +1 + continue; // +1 + ++$var; // +1 + } + if (false) { // +1 + ++$var; // +1 + break; // +1 + ++$var; // +1 + } + if (false) { // +1 + ++$var; // +1 + throw new \Exception(); // +1 + ++$var; // +1 + } + if (false) { // +1 + ++$var; // +1 + return; // +1 + ++$var; // +1 + } + if (false) { // +1 + ++$var; // +1 + intval(1); // +1 + ++$var; // +1 + } + } + + return; // +1 + $var = 2; // +1 + } + public function withMultilineStrings() + { + $var = 1; // +1 + $singleQuote = // +1 + 'start // 0 + a + $var + z + end'; // 0 + $doubleQuote = // +1 + "start // 0 + a + $var // 0 + z + end"; // 0 + $nowDoc = // +1 +<<<'LINE_ADDED_IN_TEST' + start + a + $var + z + end +LINE_ADDED_IN_TEST; // 0 + $hereDoc = // +1 +<< 'bar', + ]; + private string $var; + public function m1(): void {} // +4 + public function m2(): void { + } // +1 + public function m3(): void + {} // +1 + public function m4(): void + { + } // +1 +} diff --git a/tests/_files/source_for_branched_exec_lines_php81.php b/tests/_files/source_for_branched_exec_lines_php81.php new file mode 100644 index 000000000..846b62c45 --- /dev/null +++ b/tests/_files/source_for_branched_exec_lines_php81.php @@ -0,0 +1,85 @@ +shape(); // +1 +BackedSuit::Clubs; // +1 + + +// Intersection types +interface MyIntersection +{ + public function check(MyIntOne&MyIntTwo $intersection); + public function neverReturn(): never; +} + +// New in initializers +class NewInInit_NoInit +{ + public function __construct(private DateTimeInterface $dateTime) { + } // +3 + public function noinit(DateTimeInterface $dateTime) { + } // +1 +} +class NewInInit_OneLineNewLine +{ + public function __construct(private DateTimeInterface $dateTime = new DateTime()) { + } // +1 + public function onelinenewline(DateTimeInterface $dateTime = new DateTime()) { + } // +2 +} +class NewInInit_OneLineSameLine +{ + public function __construct(private DateTimeInterface $dateTime = new DateTime()) {} // +2 + public function onelinesameline(DateTimeInterface $dateTime = new DateTime()) {} // +1 +} +class NewInInit_MultiLine +{ + public function __construct( + private + DateTimeInterface + $dateTime + = + new + DateTime() + , + private + bool + $var + = + true + ) + { + } // +1 + public function multiline( + DateTimeInterface $dateTime = new DateTime() + ) { + } // +2 +} +function newInInit_OneLineNewLine(DateTimeInterface $dateTime = new DateTime()) { +} // +2 +function newInInit_OneLineSameLine(DateTimeInterface $dateTime = new DateTime()) {} // +2 +function newInInit_multiline( + DateTimeInterface $dateTime = new DateTime() +) { +} // +1 diff --git a/tests/_files/source_for_branched_exec_lines_php82.php b/tests/_files/source_for_branched_exec_lines_php82.php new file mode 100644 index 000000000..056bb30db --- /dev/null +++ b/tests/_files/source_for_branched_exec_lines_php82.php @@ -0,0 +1,16 @@ +result = match ($value) { + 0 => 4, + 1 => 5, + 2 => 6, + 3 => 7, + default => 8, + }; + } +} diff --git a/tests/_files/source_with_class_and_anonymous_function.php b/tests/_files/source_with_class_and_anonymous_function.php new file mode 100644 index 000000000..72aa938e9 --- /dev/null +++ b/tests/_files/source_with_class_and_anonymous_function.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +require __DIR__ . '/../vendor/autoload.php'; + +\define( + 'TEST_FILES_PATH', + __DIR__ . \DIRECTORY_SEPARATOR . '_files' . \DIRECTORY_SEPARATOR +); diff --git a/tests/src/SourceAnalyserTestCase.php b/tests/src/SourceAnalyserTestCase.php new file mode 100644 index 000000000..f50e1a9d6 --- /dev/null +++ b/tests/src/SourceAnalyserTestCase.php @@ -0,0 +1,247 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function file_get_contents; +use function range; +use PHPUnit\Framework\Attributes\Ticket; +use PHPUnit\Framework\TestCase; + +abstract class SourceAnalyserTestCase extends TestCase +{ + public function testGetLinesToBeIgnored(): void + { + $this->assertSame( + [ + 3, + 4, + 5, + 11, + 12, + 13, + 14, + 15, + 16, + 18, + 23, + 24, + 25, + 30, + 33, + 38, + 39, + 40, + 41, + 42, + ], + $this->analyser()->analyse( + TEST_FILES_PATH . 'source_with_ignore.php', + file_get_contents(TEST_FILES_PATH . 'source_with_ignore.php'), + true, + true, + )->ignoredLines(), + ); + } + + public function testGetLinesToBeIgnored2(): void + { + $this->assertSame( + [], + $this->analyser()->analyse( + TEST_FILES_PATH . 'source_without_ignore.php', + file_get_contents(TEST_FILES_PATH . 'source_without_ignore.php'), + true, + true, + )->ignoredLines(), + ); + } + + public function testGetLinesToBeIgnored3(): void + { + $this->assertSame( + [ + 3, + ], + $this->analyser()->analyse( + TEST_FILES_PATH . 'source_with_class_and_anonymous_function.php', + file_get_contents(TEST_FILES_PATH . 'source_with_class_and_anonymous_function.php'), + true, + true, + )->ignoredLines(), + ); + } + + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/issues/793')] + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/issues/794')] + public function testLineWithFullyQualifiedClassNameConstantIsNotIgnored(): void + { + $this->assertSame( + [ + 2, + ], + $this->analyser()->analyse( + TEST_FILES_PATH . 'source_with_class_and_fqcn_constant.php', + file_get_contents(TEST_FILES_PATH . 'source_with_class_and_fqcn_constant.php'), + true, + true, + )->ignoredLines(), + ); + } + + public function testGetLinesToBeIgnoredOneLineAnnotations(): void + { + $this->assertSame( + [ + 4, + 9, + 29, + 31, + 32, + 33, + ], + $this->analyser()->analyse( + TEST_FILES_PATH . 'source_with_oneline_annotations.php', + file_get_contents(TEST_FILES_PATH . 'source_with_oneline_annotations.php'), + true, + true, + )->ignoredLines(), + ); + } + + public function testGetLinesToBeIgnoredWhenIgnoreIsDisabled(): void + { + $this->assertSame( + [ + 11, + 18, + 33, + ], + $this->analyser()->analyse( + TEST_FILES_PATH . 'source_with_ignore.php', + file_get_contents(TEST_FILES_PATH . 'source_with_ignore.php'), + false, + false, + )->ignoredLines(), + ); + } + + public function testGetLinesOfCodeForFileWithoutNewline(): void + { + $this->assertSame( + 1, + $this->analyser()->analyse( + TEST_FILES_PATH . 'source_without_newline.php', + file_get_contents(TEST_FILES_PATH . 'source_without_newline.php'), + false, + false, + )->linesOfCode()->linesOfCode(), + ); + } + + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/issues/885')] + public function testGetLinesOfCodeForFileCrLineEndings(): void + { + $result = $this->analyser()->analyse( + TEST_FILES_PATH . 'source_without_lf_only_cr.php', + file_get_contents(TEST_FILES_PATH . 'source_without_lf_only_cr.php'), + false, + false, + )->linesOfCode(); + + $this->assertSame(4, $result->linesOfCode()); + $this->assertSame(2, $result->commentLinesOfCode()); + $this->assertSame(2, $result->nonCommentLinesOfCode()); + } + + public function testLinesCanBeIgnoredUsingAttribute(): void + { + $this->assertSame( + [ + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 13, + 15, + 16, + 17, + 18, + 19, + ], + $this->analyser()->analyse( + TEST_FILES_PATH . 'source_with_ignore_attributes.php', + file_get_contents(TEST_FILES_PATH . 'source_with_ignore_attributes.php'), + true, + true, + )->ignoredLines(), + ); + } + + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/issues/1033')] + public function testEnumWithEnumLevelIgnore(): void + { + $this->assertSame( + range(5, 13), + $this->analyser()->analyse( + TEST_FILES_PATH . 'source_with_enum_and_enum_level_ignore_annotation.php', + file_get_contents(TEST_FILES_PATH . 'source_with_enum_and_enum_level_ignore_annotation.php'), + true, + true, + )->ignoredLines(), + ); + } + + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/issues/1033')] + public function testEnumWithMethodLevelIgnore(): void + { + $this->assertSame( + range(9, 12), + $this->analyser()->analyse( + TEST_FILES_PATH . 'source_with_enum_and_method_level_ignore_annotation.php', + file_get_contents(TEST_FILES_PATH . 'source_with_enum_and_method_level_ignore_annotation.php'), + true, + true, + )->ignoredLines(), + ); + } + + public function testCodeUnitsAreFound(): void + { + $analyser = new ParsingSourceAnalyser; + + $analysisResult = $analyser->analyse( + __DIR__ . '/../_files/source_with_interfaces_classes_traits_functions.php', + file_get_contents(__DIR__ . '/../_files/source_with_interfaces_classes_traits_functions.php'), + true, + true, + ); + + $this->assertCount(3, $analysisResult->interfaces()); + $this->assertArrayHasKey('SebastianBergmann\CodeCoverage\StaticAnalysis\A', $analysisResult->interfaces()); + $this->assertArrayHasKey('SebastianBergmann\CodeCoverage\StaticAnalysis\B', $analysisResult->interfaces()); + $this->assertArrayHasKey('SebastianBergmann\CodeCoverage\StaticAnalysis\C', $analysisResult->interfaces()); + + $this->assertCount(2, $analysisResult->classes()); + $this->assertArrayHasKey('SebastianBergmann\CodeCoverage\StaticAnalysis\ParentClass', $analysisResult->classes()); + $this->assertArrayHasKey('SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass', $analysisResult->classes()); + + $this->assertCount(1, $analysisResult->traits()); + $this->assertArrayHasKey('SebastianBergmann\CodeCoverage\StaticAnalysis\T', $analysisResult->traits()); + + $this->assertCount(1, $analysisResult->functions()); + $this->assertArrayHasKey('SebastianBergmann\CodeCoverage\StaticAnalysis\f', $analysisResult->functions()); + } + + abstract protected function analyser(): SourceAnalyser; +} diff --git a/tests/src/TestCase.php b/tests/src/TestCase.php new file mode 100644 index 000000000..aff94901c --- /dev/null +++ b/tests/src/TestCase.php @@ -0,0 +1,2058 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use function rmdir; +use function unlink; +use BankAccount; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use SebastianBergmann\CodeCoverage\Data\RawCodeCoverageData; +use SebastianBergmann\CodeCoverage\Driver\Driver; +use SebastianBergmann\CodeCoverage\Test\Target\Target; +use SebastianBergmann\CodeCoverage\Test\Target\TargetCollection; +use SomeNamespace\BankAccountTrait; + +abstract class TestCase extends \PHPUnit\Framework\TestCase +{ + protected function getLineCoverageXdebugDataForBankAccount() + { + return [ + RawCodeCoverageData::fromXdebugWithoutPathCoverage([ + TEST_FILES_PATH . 'BankAccount.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + 14 => -1, + 15 => -1, + 16 => -1, + 18 => -1, + 22 => -1, + 24 => -1, + 25 => -2, + 29 => -1, + 31 => -1, + 32 => -2, + 33 => -2, + 35 => 1, + ], + ]), + RawCodeCoverageData::fromXdebugWithoutPathCoverage([ + TEST_FILES_PATH . 'BankAccount.php' => [ + 8 => 1, + 9 => -2, + 13 => 1, + 14 => -1, + 15 => -1, + 16 => 1, + 18 => -1, + 29 => 1, + 31 => -1, + 32 => -2, + 33 => -2, + ], + ]), + RawCodeCoverageData::fromXdebugWithoutPathCoverage([ + TEST_FILES_PATH . 'BankAccount.php' => [ + 8 => 1, + 9 => -2, + 13 => 1, + 14 => -1, + 15 => -1, + 16 => 1, + 18 => -1, + 22 => 1, + 24 => -1, + 25 => -2, + ], + ]), + RawCodeCoverageData::fromXdebugWithoutPathCoverage([ + TEST_FILES_PATH . 'BankAccount.php' => [ + 8 => 1, + 9 => -2, + 13 => 1, + 14 => 1, + 15 => 1, + 16 => -1, + 18 => 1, + 22 => 1, + 24 => 1, + 25 => -2, + 29 => 1, + 31 => 1, + 32 => -2, + 33 => -2, + ], + ]), + ]; + } + + protected function getPathCoverageXdebugDataForBankAccount() + { + return [ + RawCodeCoverageData::fromXdebugWithPathCoverage([ + TEST_FILES_PATH . 'BankAccount.php' => [ + 'lines' => [ + 8 => 1, + 9 => -2, + 13 => -1, + 14 => -1, + 15 => -1, + 16 => -1, + 18 => -1, + 22 => -1, + 24 => -1, + 25 => -2, + 29 => -1, + 31 => -1, + 32 => -2, + ], + 'functions' => [ + 'BankAccount->depositMoney' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 20, + 'line_end' => 25, + 'hit' => 0, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + 'BankAccount->getBalance' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 5, + 'line_start' => 6, + 'line_end' => 9, + 'hit' => 1, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 1, + ], + ], + ], + 'BankAccount->withdrawMoney' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 27, + 'line_end' => 32, + 'hit' => 0, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + '{main}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 1, + 'line_start' => 34, + 'line_end' => 34, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + 'BankAccount->setBalance' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 4, + 'line_start' => 11, + 'line_end' => 13, + 'hit' => 0, + 'out' => [ + 0 => 5, + 1 => 9, + ], + 'out_hit' => [ + 0 => 0, + 1 => 0, + ], + ], + 5 => [ + 'op_start' => 5, + 'op_end' => 8, + 'line_start' => 14, + 'line_end' => 14, + 'hit' => 0, + 'out' => [ + 0 => 13, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 9 => [ + 'op_start' => 9, + 'op_end' => 12, + 'line_start' => 16, + 'line_end' => 16, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 13 => [ + 'op_start' => 13, + 'op_end' => 14, + 'line_start' => 18, + 'line_end' => 18, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + 1 => 5, + 2 => 13, + ], + 'hit' => 0, + ], + 1 => [ + 'path' => [ + 0 => 0, + 1 => 9, + ], + 'hit' => 0, + ], + ], + ], + ], + ], + ]), + RawCodeCoverageData::fromXdebugWithPathCoverage([ + TEST_FILES_PATH . 'BankAccount.php' => [ + 'lines' => [ + 8 => 1, + 9 => -2, + 13 => 1, + 14 => -1, + 15 => -1, + 16 => 1, + 18 => -1, + 29 => 1, + 31 => -1, + 32 => -2, + ], + 'functions' => [ + 'BankAccount->depositMoney' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 20, + 'line_end' => 25, + 'hit' => 0, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + 'BankAccount->getBalance' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 5, + 'line_start' => 6, + 'line_end' => 9, + 'hit' => 1, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 1, + ], + ], + ], + 'BankAccount->withdrawMoney' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 27, + 'line_end' => 32, + 'hit' => 1, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 1, + ], + ], + ], + '{main}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 1, + 'line_start' => 34, + 'line_end' => 34, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + 'BankAccount->setBalance' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 4, + 'line_start' => 11, + 'line_end' => 13, + 'hit' => 1, + 'out' => [ + 0 => 5, + 1 => 9, + ], + 'out_hit' => [ + 0 => 0, + 1 => 0, + ], + ], + 5 => [ + 'op_start' => 5, + 'op_end' => 8, + 'line_start' => 14, + 'line_end' => 14, + 'hit' => 0, + 'out' => [ + 0 => 13, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 9 => [ + 'op_start' => 9, + 'op_end' => 12, + 'line_start' => 16, + 'line_end' => 16, + 'hit' => 1, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 13 => [ + 'op_start' => 13, + 'op_end' => 14, + 'line_start' => 18, + 'line_end' => 18, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + 1 => 5, + 2 => 13, + ], + 'hit' => 0, + ], + 1 => [ + 'path' => [ + 0 => 0, + 1 => 9, + ], + 'hit' => 1, + ], + ], + ], + ], + ], + ]), + RawCodeCoverageData::fromXdebugWithPathCoverage([ + TEST_FILES_PATH . 'BankAccount.php' => [ + 'lines' => [ + 8 => 1, + 9 => -2, + 13 => 1, + 14 => -1, + 15 => -1, + 16 => 1, + 18 => -1, + 22 => 1, + 24 => -1, + 25 => -2, + ], + 'functions' => [ + 'BankAccount->depositMoney' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 20, + 'line_end' => 25, + 'hit' => 1, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 1, + ], + ], + ], + 'BankAccount->getBalance' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 5, + 'line_start' => 6, + 'line_end' => 9, + 'hit' => 1, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 1, + ], + ], + ], + 'BankAccount->withdrawMoney' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 27, + 'line_end' => 32, + 'hit' => 0, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + '{main}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 1, + 'line_start' => 34, + 'line_end' => 34, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + 'BankAccount->setBalance' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 4, + 'line_start' => 11, + 'line_end' => 13, + 'hit' => 1, + 'out' => [ + 0 => 5, + 1 => 9, + ], + 'out_hit' => [ + 0 => 0, + 1 => 0, + ], + ], + 5 => [ + 'op_start' => 5, + 'op_end' => 8, + 'line_start' => 14, + 'line_end' => 14, + 'hit' => 0, + 'out' => [ + 0 => 13, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 9 => [ + 'op_start' => 9, + 'op_end' => 12, + 'line_start' => 16, + 'line_end' => 16, + 'hit' => 1, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 13 => [ + 'op_start' => 13, + 'op_end' => 14, + 'line_start' => 18, + 'line_end' => 18, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + 1 => 5, + 2 => 13, + ], + 'hit' => 0, + ], + 1 => [ + 'path' => [ + 0 => 0, + 1 => 9, + ], + 'hit' => 1, + ], + ], + ], + ], + ], + ]), + RawCodeCoverageData::fromXdebugWithPathCoverage([ + TEST_FILES_PATH . 'BankAccount.php' => [ + 'lines' => [ + 8 => 1, + 9 => -2, + 13 => 1, + 14 => 1, + 15 => 1, + 16 => -1, + 18 => 1, + 22 => 1, + 24 => 1, + 25 => -2, + 29 => 1, + 31 => 1, + 32 => -2, + ], + 'functions' => [ + 'BankAccount->depositMoney' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 20, + 'line_end' => 25, + 'hit' => 1, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 1, + ], + ], + ], + 'BankAccount->getBalance' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 5, + 'line_start' => 6, + 'line_end' => 9, + 'hit' => 1, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 1, + ], + ], + ], + 'BankAccount->withdrawMoney' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 27, + 'line_end' => 32, + 'hit' => 1, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 1, + ], + ], + ], + '{main}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 1, + 'line_start' => 34, + 'line_end' => 34, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + 'BankAccount->setBalance' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 4, + 'line_start' => 11, + 'line_end' => 13, + 'hit' => 1, + 'out' => [ + 0 => 5, + 1 => 9, + ], + 'out_hit' => [ + 0 => 0, + 1 => 0, + ], + ], + 5 => [ + 'op_start' => 5, + 'op_end' => 8, + 'line_start' => 14, + 'line_end' => 14, + 'hit' => 1, + 'out' => [ + 0 => 13, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 9 => [ + 'op_start' => 9, + 'op_end' => 12, + 'line_start' => 16, + 'line_end' => 16, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 13 => [ + 'op_start' => 13, + 'op_end' => 14, + 'line_start' => 18, + 'line_end' => 18, + 'hit' => 1, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + 1 => 5, + 2 => 13, + ], + 'hit' => 1, + ], + 1 => [ + 'path' => [ + 0 => 0, + 1 => 9, + ], + 'hit' => 0, + ], + ], + ], + ], + ], + ]), + ]; + } + + protected function getPathCoverageXdebugDataForSourceWithoutNamespace() + { + return [ + RawCodeCoverageData::fromXdebugWithPathCoverage( + [ + TEST_FILES_PATH . 'source_without_namespace.php' => [ + 'lines' => [ + 14 => -1, + 15 => -1, + 16 => -1, + 17 => -1, + 18 => -1, + 19 => 1, + ], + 'functions' => [ + '{closure:' . TEST_FILES_PATH . 'source_without_namespace.php:14-14}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 2, + 'line_start' => 14, + 'line_end' => 14, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + 'foo' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 6, + 'line_start' => 12, + 'line_end' => 15, + 'hit' => 0, + 'out' => [ + 0 => 7, + 1 => 9, + ], + 'out_hit' => [ + 0 => 0, + 1 => 0, + ], + ], + 7 => [ + 'op_start' => 7, + 'op_end' => 8, + 'line_start' => 15, + 'line_end' => 15, + 'hit' => 0, + 'out' => [ + 0 => 10, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 9 => [ + 'op_start' => 9, + 'op_end' => 9, + 'line_start' => 15, + 'line_end' => 15, + 'hit' => 0, + 'out' => [ + 0 => 10, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 10 => [ + 'op_start' => 10, + 'op_end' => 18, + 'line_start' => 15, + 'line_end' => 18, + 'hit' => 0, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + 1 => 7, + 2 => 10, + ], + 'hit' => 0, + ], + 1 => [ + 'path' => [ + 0 => 0, + 1 => 9, + 2 => 10, + ], + 'hit' => 0, + ], + ], + ], + '{main}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 0, + 'line_start' => 19, + 'line_end' => 19, + 'hit' => 1, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 1, + ], + ], + ], + ], + ], + ], + ), + ]; + } + + protected function getLineCoverageForBankAccount(): CodeCoverage + { + $data = $this->getLineCoverageXdebugDataForBankAccount(); + + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn(...$data); + + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'BankAccount.php'); + + $coverage = new CodeCoverage($stub, $filter); + + $coverage->start( + 'BankAccountTest::testBalanceIsInitiallyZero', + null, + true, + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'getBalance'), + ], + ), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ], + ), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative2', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'depositMoney'), + ], + ), + ); + + $coverage->start( + 'BankAccountTest::testDepositWithdrawMoney', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'getBalance'), + Target::forMethod(BankAccount::class, 'depositMoney'), + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ], + ), + ); + + return $coverage; + } + + protected function getPathCoverageForBankAccount(): CodeCoverage + { + $data = $this->getPathCoverageXdebugDataForBankAccount(); + + $stub = $this->createStub(Driver::class); + + $stub->method('collectsBranchAndPathCoverage')->willReturn(true); + + $stub->method('stop') + ->willReturn(...$data); + + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'BankAccount.php'); + + $coverage = new CodeCoverage($stub, $filter); + + $coverage->start( + 'BankAccountTest::testBalanceIsInitiallyZero', + null, + true, + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'getBalance'), + ], + ), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ], + ), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative2', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'depositMoney'), + ], + ), + ); + + $coverage->start( + 'BankAccountTest::testDepositWithdrawMoney', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'getBalance'), + Target::forMethod(BankAccount::class, 'depositMoney'), + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ], + ), + ); + + return $coverage; + } + + protected function getPathCoverageForSourceWithoutNamespace(): CodeCoverage + { + $data = $this->getPathCoverageXdebugDataForSourceWithoutNamespace(); + + $stub = $this->createStub(Driver::class); + + $stub->method('collectsBranchAndPathCoverage')->willReturn(true); + + $stub->method('stop') + ->willReturn(...$data); + + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'source_without_namespace.php'); + + $coverage = new CodeCoverage($stub, $filter); + + $coverage->start( + 'faketest', + null, + true, + ); + + $coverage->stop(); + + return $coverage; + } + + protected function getXdebugDataForNamespacedBankAccount() + { + return [ + RawCodeCoverageData::fromXdebugWithoutPathCoverage([ + TEST_FILES_PATH . 'NamespacedBankAccount.php' => [ + 13 => 1, + 14 => -2, + 18 => -1, + 19 => -1, + 20 => -1, + 21 => -1, + 23 => -1, + 27 => -1, + 29 => -1, + 30 => -2, + 34 => -1, + 36 => -1, + 37 => -2, + ], + ]), + RawCodeCoverageData::fromXdebugWithoutPathCoverage([ + TEST_FILES_PATH . 'NamespacedBankAccount.php' => [ + 13 => 1, + 18 => 1, + 21 => 1, + 34 => 1, + ], + ]), + RawCodeCoverageData::fromXdebugWithoutPathCoverage([ + TEST_FILES_PATH . 'NamespacedBankAccount.php' => [ + 13 => 1, + 18 => 1, + 21 => 1, + 27 => 1, + ], + ]), + RawCodeCoverageData::fromXdebugWithoutPathCoverage([ + TEST_FILES_PATH . 'NamespacedBankAccount.php' => [ + 13 => 1, + 18 => 1, + 19 => 1, + 20 => 1, + 23 => 1, + 27 => 1, + 29 => 1, + 34 => 1, + 36 => 1, + ], + ]), + ]; + } + + protected function getLineCoverageForNamespacedBankAccount(): CodeCoverage + { + $data = $this->getXdebugDataForNamespacedBankAccount(); + + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn(...$data); + + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'NamespacedBankAccount.php'); + + $coverage = new CodeCoverage($stub, $filter); + + $coverage->start( + 'BankAccountTest::testBalanceIsInitiallyZero', + null, + true, + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccountTrait::class, 'getBalance'), + ]), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccountTrait::class, 'withdrawMoney'), + ]), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative2', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccountTrait::class, 'depositMoney'), + ]), + ); + + $coverage->start( + 'BankAccountTest::testDepositWithdrawMoney', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccountTrait::class, 'getBalance'), + Target::forMethod(BankAccountTrait::class, 'depositMoney'), + Target::forMethod(BankAccountTrait::class, 'withdrawMoney'), + ]), + ); + + return $coverage; + } + + protected function getLineCoverageForBankAccountForFirstTwoTests(): CodeCoverage + { + $data = $this->getLineCoverageXdebugDataForBankAccount(); + + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn(...$data); + + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'BankAccount.php'); + + $coverage = new CodeCoverage($stub, $filter); + + $coverage->start( + 'BankAccountTest::testBalanceIsInitiallyZero', + null, + true, + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'getBalance'), + ], + ), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ], + ), + ); + + return $coverage; + } + + protected function getLineCoverageForBankAccountForLastTwoTests(): CodeCoverage + { + $data = $this->getLineCoverageXdebugDataForBankAccount(); + + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn($data[2], $data[3]); + + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'BankAccount.php'); + + $coverage = new CodeCoverage($stub, $filter); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative2', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'depositMoney'), + ], + ), + ); + + $coverage->start( + 'BankAccountTest::testDepositWithdrawMoney', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'getBalance'), + Target::forMethod(BankAccount::class, 'depositMoney'), + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ], + ), + ); + + return $coverage; + } + + protected function getExpectedLineCoverageDataArrayForBankAccount(): array + { + return [ + TEST_FILES_PATH . 'BankAccount.php' => [ + 8 => [ + 0 => 'BankAccountTest::testBalanceIsInitiallyZero', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ], + 13 => [], + 14 => [], + 16 => [], + 22 => [ + 0 => 'BankAccountTest::testBalanceCannotBecomeNegative2', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ], + 24 => [ + 0 => 'BankAccountTest::testDepositWithdrawMoney', + ], + 29 => [ + 0 => 'BankAccountTest::testBalanceCannotBecomeNegative', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ], + 31 => [ + 0 => 'BankAccountTest::testDepositWithdrawMoney', + ], + 32 => null, + ], + ]; + } + + protected function getExpectedLineCoverageDataArrayForBankAccountInReverseOrder(): array + { + return [ + TEST_FILES_PATH . 'BankAccount.php' => [ + 8 => [ + 0 => 'BankAccountTest::testDepositWithdrawMoney', + 1 => 'BankAccountTest::testBalanceIsInitiallyZero', + ], + 13 => [], + 14 => [], + 16 => [], + 22 => [ + 0 => 'BankAccountTest::testBalanceCannotBecomeNegative2', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ], + 24 => [ + 0 => 'BankAccountTest::testDepositWithdrawMoney', + ], + 29 => [ + 0 => 'BankAccountTest::testDepositWithdrawMoney', + 1 => 'BankAccountTest::testBalanceCannotBecomeNegative', + ], + 31 => [ + 0 => 'BankAccountTest::testDepositWithdrawMoney', + ], + 32 => null, + ], + ]; + } + + protected function getPathCoverageForBankAccountForFirstTwoTests(): CodeCoverage + { + $data = $this->getPathCoverageXdebugDataForBankAccount(); + + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn(...$data); + + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'BankAccount.php'); + + $coverage = new CodeCoverage($stub, $filter); + + $coverage->start( + 'BankAccountTest::testBalanceIsInitiallyZero', + null, + true, + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'getBalance'), + ], + ), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ], + ), + ); + + return $coverage; + } + + protected function getPathCoverageForBankAccountForLastTwoTests(): CodeCoverage + { + $data = $this->getPathCoverageXdebugDataForBankAccount(); + + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn($data[2], $data[3]); + + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'BankAccount.php'); + + $coverage = new CodeCoverage($stub, $filter); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative2', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'depositMoney'), + ], + ), + ); + + $coverage->start( + 'BankAccountTest::testDepositWithdrawMoney', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray( + [ + Target::forMethod(BankAccount::class, 'getBalance'), + Target::forMethod(BankAccount::class, 'depositMoney'), + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ], + ), + ); + + return $coverage; + } + + protected function getExpectedPathCoverageDataArrayForBankAccount(): array + { + return [ + TEST_FILES_PATH . 'BankAccount.php' => [ + 'BankAccount->depositMoney' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 20, + 'line_end' => 25, + 'hit' => [ + 0 => 'BankAccountTest::testBalanceCannotBecomeNegative2', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ], + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => [ + 0 => 'BankAccountTest::testBalanceCannotBecomeNegative2', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ], + ], + ], + ], + 'BankAccount->getBalance' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 5, + 'line_start' => 6, + 'line_end' => 9, + 'hit' => [ + 0 => 'BankAccountTest::testBalanceIsInitiallyZero', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ], + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => [ + 0 => 'BankAccountTest::testBalanceIsInitiallyZero', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ], + ], + ], + ], + 'BankAccount->withdrawMoney' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 27, + 'line_end' => 32, + 'hit' => [ + 0 => 'BankAccountTest::testBalanceCannotBecomeNegative', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ], + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => [ + 0 => 'BankAccountTest::testBalanceCannotBecomeNegative', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ], + ], + ], + ], + '{main}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 1, + 'line_start' => 34, + 'line_end' => 34, + 'hit' => [ + ], + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => [ + ], + ], + ], + ], + 'BankAccount->setBalance' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 4, + 'line_start' => 11, + 'line_end' => 13, + 'hit' => [ + ], + 'out' => [ + 0 => 5, + 1 => 9, + ], + 'out_hit' => [ + 0 => 0, + 1 => 0, + ], + ], + 5 => [ + 'op_start' => 5, + 'op_end' => 8, + 'line_start' => 14, + 'line_end' => 14, + 'hit' => [ + ], + 'out' => [ + 0 => 13, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 9 => [ + 'op_start' => 9, + 'op_end' => 12, + 'line_start' => 16, + 'line_end' => 16, + 'hit' => [ + ], + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 13 => [ + 'op_start' => 13, + 'op_end' => 14, + 'line_start' => 18, + 'line_end' => 18, + 'hit' => [ + ], + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + 1 => 5, + 2 => 13, + ], + 'hit' => [ + ], + ], + 1 => [ + 'path' => [ + 0 => 0, + 1 => 9, + ], + 'hit' => [ + ], + ], + ], + ], + ], + ]; + } + + protected function getCoverageForFileWithIgnoredLines(): CodeCoverage + { + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'source_with_ignore.php'); + + $coverage = new CodeCoverage( + $this->setUpXdebugStubForFileWithIgnoredLines(), + $filter, + ); + + $coverage->start('FileWithIgnoredLines', null, true); + $coverage->stop(); + + return $coverage; + } + + protected function setUpXdebugStubForFileWithIgnoredLines(): Driver + { + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn(RawCodeCoverageData::fromXdebugWithoutPathCoverage( + [ + TEST_FILES_PATH . 'source_with_ignore.php' => [ + 2 => 1, + 4 => -1, + 6 => -1, + ], + ], + )); + + return $stub; + } + + protected function getLineCoverageForFileWithEval(): CodeCoverage + { + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'source_with_eval.php'); + + $coverage = new CodeCoverage( + $this->setUpXdebugStubForFileWithEval(), + $filter, + ); + + $coverage->start('FileWithEval', null, true); + $coverage->stop(); + + return $coverage; + } + + protected function setUpXdebugStubForFileWithEval(): Driver + { + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn(RawCodeCoverageData::fromXdebugWithoutPathCoverage( + [ + TEST_FILES_PATH . 'source_with_eval.php' => [ + 3 => 1, + 5 => 1, + ], + TEST_FILES_PATH . 'source_with_eval.php(5) : eval()\'d code' => [ + 1 => 1, + ], + ], + )); + + return $stub; + } + + protected function getCoverageForClassWithAnonymousFunction(): CodeCoverage + { + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'source_with_class_and_anonymous_function.php'); + + $coverage = new CodeCoverage( + $this->setUpXdebugStubForClassWithAnonymousFunction(), + $filter, + ); + + $coverage->start('ClassWithAnonymousFunction', null, true); + $coverage->stop(); + + return $coverage; + } + + protected function setUpXdebugStubForClassWithAnonymousFunction(): Driver + { + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn(RawCodeCoverageData::fromXdebugWithoutPathCoverage( + [ + TEST_FILES_PATH . 'source_with_class_and_anonymous_function.php' => [ + 7 => 1, + 9 => 1, + 10 => -1, + 11 => 1, + 12 => 1, + 13 => 1, + 14 => 1, + 17 => 1, + 18 => 1, + ], + ], + )); + + return $stub; + } + + protected function getCoverageForClassWithOutsideFunction(): CodeCoverage + { + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'source_with_class_and_outside_function.php'); + + $coverage = new CodeCoverage( + $this->setUpXdebugStubForClassWithOutsideFunction(), + $filter, + ); + + $coverage->start('ClassWithOutsideFunction', null, true); + $coverage->stop(); + + return $coverage; + } + + protected function setUpXdebugStubForClassWithOutsideFunction(): Driver + { + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn(RawCodeCoverageData::fromXdebugWithoutPathCoverage( + [ + TEST_FILES_PATH . 'source_with_class_and_outside_function.php' => [ + 6 => 1, + 12 => 1, + 13 => 1, + 16 => -1, + ], + ], + )); + + return $stub; + } + + protected function removeTemporaryFiles(): void + { + $tmpFilesIterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator(TEST_FILES_PATH . 'tmp', RecursiveDirectoryIterator::SKIP_DOTS), + RecursiveIteratorIterator::CHILD_FIRST, + ); + + foreach ($tmpFilesIterator as $path => $fileInfo) { + /* @var \SplFileInfo $fileInfo */ + $pathname = $fileInfo->getPathname(); + $fileInfo->isDir() ? rmdir($pathname) : unlink($pathname); + } + } + + protected function getCoverageForFilesWithUncoveredIncluded(): CodeCoverage + { + $data = $this->getLineCoverageXdebugDataForBankAccount(); + + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn(...$data); + + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'BankAccount.php'); + $filter->includeFile(TEST_FILES_PATH . 'NamespacedBankAccount.php'); + + $coverage = new CodeCoverage($stub, $filter); + $coverage->includeUncoveredFiles(); + + $coverage->start( + 'BankAccountTest::testBalanceIsInitiallyZero', + null, + true, + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccount::class, 'getBalance'), + ]), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ]), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative2', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccount::class, 'depositMoney'), + ]), + ); + + $coverage->start( + 'BankAccountTest::testDepositWithdrawMoney', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccount::class, 'getBalance'), + Target::forMethod(BankAccount::class, 'depositMoney'), + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ]), + ); + + return $coverage; + } + + protected function getCoverageForFilesWithUncoveredExcluded(): CodeCoverage + { + $data = $this->getLineCoverageXdebugDataForBankAccount(); + + $stub = $this->createStub(Driver::class); + + $stub->method('stop') + ->willReturn(...$data); + + $filter = new Filter; + $filter->includeFile(TEST_FILES_PATH . 'BankAccount.php'); + $filter->includeFile(TEST_FILES_PATH . 'NamespacedBankAccount.php'); + + $coverage = new CodeCoverage($stub, $filter); + $coverage->excludeUncoveredFiles(); + + $coverage->start( + 'BankAccountTest::testBalanceIsInitiallyZero', + null, + true, + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccount::class, 'getBalance'), + ]), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ]), + ); + + $coverage->start( + 'BankAccountTest::testBalanceCannotBecomeNegative2', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccount::class, 'depositMoney'), + ]), + ); + + $coverage->start( + 'BankAccountTest::testDepositWithdrawMoney', + ); + + $coverage->stop( + true, + null, + TargetCollection::fromArray([ + Target::forMethod(BankAccount::class, 'getBalance'), + Target::forMethod(BankAccount::class, 'depositMoney'), + Target::forMethod(BankAccount::class, 'withdrawMoney'), + ]), + ); + + return $coverage; + } +} diff --git a/tests/tests/CodeCoverageTest.php b/tests/tests/CodeCoverageTest.php new file mode 100644 index 000000000..32f2f1de0 --- /dev/null +++ b/tests/tests/CodeCoverageTest.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use function array_fill; +use PHPUnit\Framework\Attributes\CoversClass; +use SebastianBergmann\CodeCoverage\Data\RawCodeCoverageData; +use SebastianBergmann\CodeCoverage\Driver\Driver; +use SebastianBergmann\CodeCoverage\Driver\Selector; +use SebastianBergmann\Environment\Runtime; + +#[CoversClass(CodeCoverage::class)] +final class CodeCoverageTest extends TestCase +{ + private CodeCoverage $coverage; + + protected function setUp(): void + { + $runtime = new Runtime; + + if (!$runtime->canCollectCodeCoverage()) { + $this->markTestSkipped('No code coverage driver available'); + } + + $filter = new Filter; + + $this->coverage = new CodeCoverage( + (new Selector)->forLineCoverage($filter), + $filter, + ); + } + + public function testCollect(): void + { + $coverage = $this->getLineCoverageForBankAccount(); + + $this->assertEquals( + $this->getExpectedLineCoverageDataArrayForBankAccount(), + $coverage->getData()->lineCoverage(), + ); + + $this->assertEquals( + [ + 'BankAccountTest::testBalanceIsInitiallyZero' => ['size' => 'unknown', 'status' => 'unknown'], + 'BankAccountTest::testBalanceCannotBecomeNegative' => ['size' => 'unknown', 'status' => 'unknown'], + 'BankAccountTest::testBalanceCannotBecomeNegative2' => ['size' => 'unknown', 'status' => 'unknown'], + 'BankAccountTest::testDepositWithdrawMoney' => ['size' => 'unknown', 'status' => 'unknown'], + ], + $coverage->getTests(), + ); + } + + public function testIncludeListFiltering(): void + { + $this->coverage->filter()->includeFile(TEST_FILES_PATH . 'BankAccount.php'); + + $data = RawCodeCoverageData::fromXdebugWithoutPathCoverage([ + TEST_FILES_PATH . 'BankAccount.php' => [ + 29 => -1, + 31 => -1, + ], + TEST_FILES_PATH . 'CoverageClassTest.php' => [ + 29 => -1, + 31 => -1, + ], + ]); + + $this->coverage->append($data, 'A test', true); + $this->assertContains(TEST_FILES_PATH . 'BankAccount.php', $this->coverage->getData()->coveredFiles()); + $this->assertNotContains(TEST_FILES_PATH . 'CoverageClassTest.php', $this->coverage->getData()->coveredFiles()); + } + + public function testExcludeNonExecutableLines(): void + { + $this->coverage->filter()->includeFile(TEST_FILES_PATH . 'BankAccount.php'); + + $data = RawCodeCoverageData::fromXdebugWithoutPathCoverage([ + TEST_FILES_PATH . 'BankAccount.php' => array_fill(1, 100, -1), + ]); + + $this->coverage->append($data, 'A test', true); + + $expectedLineCoverage = [ + TEST_FILES_PATH . 'BankAccount.php' => [ + 8 => [], + 13 => [], + 14 => [], + 16 => [], + 22 => [], + 24 => [], + 29 => [], + 31 => [], + 32 => [], + ], + ]; + + $this->assertEquals($expectedLineCoverage, $this->coverage->getData()->lineCoverage()); + } + + public function testMerge(): void + { + $coverage = $this->getLineCoverageForBankAccountForFirstTwoTests(); + $coverage->merge($this->getLineCoverageForBankAccountForLastTwoTests()); + + $this->assertEquals( + $this->getExpectedLineCoverageDataArrayForBankAccount(), + $coverage->getData()->lineCoverage(), + ); + } + + public function testMergeReverseOrder(): void + { + $coverage = $this->getLineCoverageForBankAccountForLastTwoTests(); + $coverage->merge($this->getLineCoverageForBankAccountForFirstTwoTests()); + + $this->assertEquals( + $this->getExpectedLineCoverageDataArrayForBankAccountInReverseOrder(), + $coverage->getData()->lineCoverage(), + ); + } + + public function testMerge2(): void + { + $coverage = new CodeCoverage( + $this->createStub(Driver::class), + new Filter, + ); + + $coverage->merge($this->getLineCoverageForBankAccount()); + + $this->assertEquals( + $this->getExpectedLineCoverageDataArrayForBankAccount(), + $coverage->getData()->lineCoverage(), + ); + } +} diff --git a/tests/tests/Data/ProcessedCodeCoverageDataTest.php b/tests/tests/Data/ProcessedCodeCoverageDataTest.php new file mode 100644 index 000000000..46e144eec --- /dev/null +++ b/tests/tests/Data/ProcessedCodeCoverageDataTest.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Data; + +use SebastianBergmann\CodeCoverage\TestCase; + +final class ProcessedCodeCoverageDataTest extends TestCase +{ + public function testMergeWithLineCoverage(): void + { + $coverage = $this->getLineCoverageForBankAccountForFirstTwoTests()->getData(); + + $coverage->merge($this->getLineCoverageForBankAccountForLastTwoTests()->getData()); + + $this->assertEquals( + $this->getExpectedLineCoverageDataArrayForBankAccount(), + $coverage->lineCoverage(), + ); + } + + public function testMergeWithPathCoverage(): void + { + $coverage = $this->getPathCoverageForBankAccountForFirstTwoTests()->getData(); + + $coverage->merge($this->getPathCoverageForBankAccountForLastTwoTests()->getData()); + + $this->assertEquals( + $this->getExpectedPathCoverageDataArrayForBankAccount(), + $coverage->functionCoverage(), + ); + } + + public function testMergeWithPathCoverageIntoEmpty(): void + { + $coverage = new ProcessedCodeCoverageData; + + $coverage->merge($this->getPathCoverageForBankAccount()->getData()); + + $this->assertEquals( + $this->getExpectedPathCoverageDataArrayForBankAccount(), + $coverage->functionCoverage(), + ); + } + + public function testMergeOfAPreviouslyUnseenLine(): void + { + $newCoverage = new ProcessedCodeCoverageData; + + $newCoverage->setLineCoverage( + [ + '/some/path/SomeClass.php' => [ + 12 => [], + 34 => null, + ], + ], + ); + + $existingCoverage = new ProcessedCodeCoverageData; + + $existingCoverage->merge($newCoverage); + + $this->assertArrayHasKey(12, $existingCoverage->lineCoverage()['/some/path/SomeClass.php']); + } + + public function testMergeDoesNotCrashWhenFileContentsHaveChanged(): void + { + $coverage = new ProcessedCodeCoverageData; + $coverage->setFunctionCoverage( + [ + '/some/path/SomeClass.php' => [ + 'SomeClass->firstFunction' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 20, + 'line_end' => 25, + 'hit' => [], + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => [], + ], + ], + ], + ], + ], + ); + + $newCoverage = new ProcessedCodeCoverageData; + $newCoverage->setFunctionCoverage( + [ + '/some/path/SomeClass.php' => [ + 'SomeClass->firstFunction' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 14, + 'line_start' => 20, + 'line_end' => 25, + 'hit' => [], + 'out' => [ + ], + 'out_hit' => [ + ], + ], + 1 => [ + 'op_start' => 15, + 'op_end' => 16, + 'line_start' => 26, + 'line_end' => 27, + 'hit' => [], + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => [], + ], + 1 => [ + 'path' => [ + 0 => 1, + ], + 'hit' => [], + ], + ], + ], + 'SomeClass->secondFunction' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 24, + 'line_start' => 30, + 'line_end' => 35, + 'hit' => [], + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => [], + ], + ], + ], + ], + ], + ); + + $coverage->merge($newCoverage); + + $this->assertArrayHasKey('SomeClass->secondFunction', $newCoverage->functionCoverage()['/some/path/SomeClass.php']); + } +} diff --git a/tests/tests/Data/RawCodeCoverageDataTest.php b/tests/tests/Data/RawCodeCoverageDataTest.php new file mode 100644 index 000000000..98c4f45cb --- /dev/null +++ b/tests/tests/Data/RawCodeCoverageDataTest.php @@ -0,0 +1,601 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Data; + +use SebastianBergmann\CodeCoverage\TestCase; + +final class RawCodeCoverageDataTest extends TestCase +{ + /** + * In the standard XDebug format, there is only line data. Therefore output should match input. + */ + public function testLineDataFromStandardXDebugFormat(): void + { + $lineDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + ]; + + $dataObject = RawCodeCoverageData::fromXdebugWithoutPathCoverage($lineDataFromDriver); + + $this->assertEquals($lineDataFromDriver, $dataObject->lineCoverage()); + } + + /** + * In the path-coverage XDebug format, the line data exists inside a "lines" array key. + */ + public function testLineDataFromPathCoverageXDebugFormat(): void + { + $rawDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 'lines' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + 'functions' => [ + ], + ], + '/some/path/justAScript.php' => [ + 'lines' => [ + 18 => 1, + 19 => -2, + 113 => -1, + ], + 'functions' => [ + ], + ], + ]; + + $lineData = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + '/some/path/justAScript.php' => [ + 18 => 1, + 19 => -2, + 113 => -1, + ], + ]; + + $dataObject = RawCodeCoverageData::fromXdebugWithPathCoverage($rawDataFromDriver); + + $this->assertEquals($lineData, $dataObject->lineCoverage()); + } + + public function testClear(): void + { + $lineDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + ]; + + $dataObject = RawCodeCoverageData::fromXdebugWithoutPathCoverage($lineDataFromDriver); + + $dataObject->clear(); + + $this->assertEmpty($dataObject->lineCoverage()); + } + + public function testRemoveCoverageDataForFile(): void + { + $lineDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + '/some/path/SomeOtherClass.php' => [ + 18 => 1, + 19 => -2, + 113 => -1, + ], + '/some/path/AnotherClass.php' => [ + 28 => 1, + 29 => -2, + 213 => -1, + ], + ]; + + $expectedFilterResult = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + '/some/path/AnotherClass.php' => [ + 28 => 1, + 29 => -2, + 213 => -1, + ], + ]; + + $dataObject = RawCodeCoverageData::fromXdebugWithoutPathCoverage($lineDataFromDriver); + + $dataObject->removeCoverageDataForFile('/some/path/SomeOtherClass.php'); + + $this->assertEquals($expectedFilterResult, $dataObject->lineCoverage()); + } + + public function testKeepCoverageDataOnlyForLines(): void + { + $lineDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + '/some/path/SomeOtherClass.php' => [ + 18 => 1, + 19 => -2, + 113 => -1, + ], + '/some/path/AnotherClass.php' => [ + 28 => 1, + 29 => -2, + 213 => -1, + ], + ]; + + $expectedFilterResult = [ + '/some/path/SomeClass.php' => [ + 9 => -2, + 13 => -1, + ], + '/some/path/SomeOtherClass.php' => [ + ], + '/some/path/AnotherClass.php' => [ + 28 => 1, + ], + ]; + + $dataObject = RawCodeCoverageData::fromXdebugWithoutPathCoverage($lineDataFromDriver); + + $dataObject->keepLineCoverageDataOnlyForLines('/some/path/SomeClass.php', [9, 13]); + $dataObject->keepLineCoverageDataOnlyForLines('/some/path/SomeOtherClass.php', [999]); + $dataObject->keepLineCoverageDataOnlyForLines('/some/path/AnotherClass.php', [28]); + + $this->assertEquals($expectedFilterResult, $dataObject->lineCoverage()); + } + + public function testRemoveCoverageDataForLines(): void + { + $lineDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + '/some/path/SomeOtherClass.php' => [ + 18 => 1, + 19 => -2, + 113 => -1, + ], + '/some/path/AnotherClass.php' => [ + 28 => 1, + 29 => -2, + 213 => -1, + ], + ]; + + $expectedFilterResult = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + ], + '/some/path/SomeOtherClass.php' => [ + 18 => 1, + 19 => -2, + 113 => -1, + ], + '/some/path/AnotherClass.php' => [ + 29 => -2, + 213 => -1, + ], + ]; + + $dataObject = RawCodeCoverageData::fromXdebugWithoutPathCoverage($lineDataFromDriver); + + $dataObject->removeCoverageDataForLines('/some/path/SomeClass.php', [9, 13]); + $dataObject->removeCoverageDataForLines('/some/path/SomeOtherClass.php', [999]); + $dataObject->removeCoverageDataForLines('/some/path/AnotherClass.php', [28]); + + $this->assertEquals($expectedFilterResult, $dataObject->lineCoverage()); + } + + public function testCoverageForFileWithInlineAnnotations(): void + { + $filename = TEST_FILES_PATH . 'source_with_oneline_annotations.php'; + $coverage = RawCodeCoverageData::fromXdebugWithPathCoverage( + [ + $filename => [ + 'lines' => [ + 13 => -1, + 19 => -1, + 22 => -1, + 26 => -1, + 29 => -1, + 31 => -1, + 32 => -1, + 33 => -1, + 35 => -1, + 36 => -1, + 37 => -1, + ], + 'functions' => [ + '{main}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 0, + 'line_start' => 37, + 'line_end' => 37, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + 'Foo->bar' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 2, + 'line_start' => 11, + 'line_end' => 13, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + 'baz' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 18, + 'line_start' => 16, + 'line_end' => 36, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + ], + ], + ], + ); + + $coverage->removeCoverageDataForLines( + $filename, + [ + 29, + 31, + 32, + 33, + ], + ); + + $this->assertEquals( + [ + 13 => -1, + 19 => -1, + 22 => -1, + 26 => -1, + 35 => -1, + 36 => -1, + ], + $coverage->lineCoverage()[$filename], + ); + + $this->assertEquals( + [ + '{main}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 0, + 'line_start' => 37, + 'line_end' => 37, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + 'Foo->bar' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 2, + 'line_start' => 11, + 'line_end' => 13, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 0, + ], + ], + ], + 'baz' => [ + 'branches' => [ + ], + 'paths' => [ + ], + ], + ], + $coverage->functionCoverage()[$filename], + ); + } + + /** + * Xdebug annotates function names inside trait classes. + */ + public function testTraitFunctionNamesDecodedPathCoverageXDebugFormat(): void + { + $rawDataFromDriver = [ + '/some/path/FooTrait.php' => [ + 'lines' => [ + 11 => 1, + 12 => -1, + 15 => 1, + 16 => -2, + 18 => 1, + ], + 'functions' => [ + 'App\\FooTrait->returnsTrue{trait-method:/some/path/FooTrait.php:9-16}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 5, + 'line_start' => 11, + 'line_end' => 11, + 'hit' => 1, + 'out' => [ + 0 => 6, + 1 => 8, + ], + 'out_hit' => [ + 0 => 0, + 1 => 1, + ], + ], + 6 => [ + 'op_start' => 6, + 'op_end' => 7, + 'line_start' => 12, + 'line_end' => 12, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 8 => [ + 'op_start' => 8, + 'op_end' => 12, + 'line_start' => 15, + 'line_end' => 16, + 'hit' => 1, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + 1 => 6, + ], + 'hit' => 0, + ], + 1 => [ + 'path' => [ + 0 => 0, + 1 => 8, + ], + 'hit' => 1, + ], + ], + ], + '{main}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 2, + 'line_start' => 3, + 'line_end' => 18, + 'hit' => 1, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 1, + ], + ], + ], + ], + ], + ]; + + $functionData = [ + '/some/path/FooTrait.php' => [ + 'App\\FooTrait->returnsTrue' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 5, + 'line_start' => 11, + 'line_end' => 11, + 'hit' => 1, + 'out' => [ + 0 => 6, + 1 => 8, + ], + 'out_hit' => [ + 0 => 0, + 1 => 1, + ], + ], + 6 => [ + 'op_start' => 6, + 'op_end' => 7, + 'line_start' => 12, + 'line_end' => 12, + 'hit' => 0, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + 8 => [ + 'op_start' => 8, + 'op_end' => 12, + 'line_start' => 15, + 'line_end' => 16, + 'hit' => 1, + 'out' => [ + ], + 'out_hit' => [ + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + 1 => 6, + ], + 'hit' => 0, + ], + 1 => [ + 'path' => [ + 0 => 0, + 1 => 8, + ], + 'hit' => 1, + ], + ], + ], + '{main}' => [ + 'branches' => [ + 0 => [ + 'op_start' => 0, + 'op_end' => 2, + 'line_start' => 3, + 'line_end' => 18, + 'hit' => 1, + 'out' => [ + 0 => 2147483645, + ], + 'out_hit' => [ + 0 => 0, + ], + ], + ], + 'paths' => [ + 0 => [ + 'path' => [ + 0 => 0, + ], + 'hit' => 1, + ], + ], + ], + ], + ]; + + $dataObject = RawCodeCoverageData::fromXdebugWithPathCoverage($rawDataFromDriver); + + $this->assertEquals($functionData, $dataObject->functionCoverage()); + } +} diff --git a/tests/tests/Driver/PcovDriverTest.php b/tests/tests/Driver/PcovDriverTest.php new file mode 100644 index 000000000..a15dda1e9 --- /dev/null +++ b/tests/tests/Driver/PcovDriverTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use const PHP_SAPI; +use function extension_loaded; +use function ini_get; +use SebastianBergmann\CodeCoverage\BranchAndPathCoverageNotSupportedException; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\TestCase; + +final class PcovDriverTest extends TestCase +{ + protected function setUp(): void + { + if (PHP_SAPI !== 'cli') { + $this->markTestSkipped('This test requires the PHP commandline interpreter'); + } + + if (!extension_loaded('pcov')) { + $this->markTestSkipped('This test requires the PCOV extension to be loaded'); + } + + if (!ini_get('pcov.enabled')) { + $this->markTestSkipped('This test requires the PCOV extension to be enabled'); + } + } + + public function testDoesNotSupportBranchAndPathCoverage(): void + { + $this->assertFalse($this->driver()->canCollectBranchAndPathCoverage()); + } + + public function testBranchAndPathCoverageCanBeDisabled(): void + { + $driver = $this->driver(); + + $driver->disableBranchAndPathCoverage(); + + $this->assertFalse($driver->collectsBranchAndPathCoverage()); + } + + public function testBranchAndPathCoverageCannotBeEnabled(): void + { + $this->expectException(BranchAndPathCoverageNotSupportedException::class); + + $this->driver()->enableBranchAndPathCoverage(); + } + + public function testBranchAndPathCoverageIsNotCollected(): void + { + $this->assertFalse($this->driver()->collectsBranchAndPathCoverage()); + } + + public function testHasNameAndVersion(): void + { + $this->assertStringMatchesFormat('PCOV %s', $this->driver()->nameAndVersion()); + } + + private function driver(): PcovDriver + { + return new PcovDriver(new Filter); + } +} diff --git a/tests/tests/Driver/XdebugDriverTest.php b/tests/tests/Driver/XdebugDriverTest.php new file mode 100644 index 000000000..b733df2be --- /dev/null +++ b/tests/tests/Driver/XdebugDriverTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use const PHP_SAPI; +use function extension_loaded; +use function in_array; +use function phpversion; +use function version_compare; +use function xdebug_get_code_coverage; +use function xdebug_info; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\TestCase; + +final class XdebugDriverTest extends TestCase +{ + protected function setUp(): void + { + if (PHP_SAPI !== 'cli') { + $this->markTestSkipped('This test requires the PHP commandline interpreter'); + } + + if (!extension_loaded('xdebug') || !version_compare(phpversion('xdebug'), '3.1', '>=')) { + $this->markTestSkipped('This test requires the Xdebug extension (version >= 3.1) to be loaded'); + } + + if (!in_array('coverage', xdebug_info('mode'), true)) { + $this->markTestSkipped('This test requires code coverage data collection using Xdebug to be enabled'); + } + } + + public function testSupportsBranchAndPathCoverage(): void + { + $this->assertTrue($this->driver()->canCollectBranchAndPathCoverage()); + } + + public function testBranchAndPathCoverageCanBeDisabled(): void + { + $driver = $this->driver(); + + $driver->disableBranchAndPathCoverage(); + + $this->assertFalse($driver->collectsBranchAndPathCoverage()); + } + + public function testBranchAndPathCoverageCanBeEnabled(): void + { + $driver = $this->driver(); + + $driver->enableBranchAndPathCoverage(); + + $this->assertTrue($driver->collectsBranchAndPathCoverage()); + } + + public function testBranchAndPathCoverageIsNotCollectedByDefault(): void + { + $this->assertFalse($this->driver()->collectsBranchAndPathCoverage()); + } + + public function testHasNameAndVersion(): void + { + $this->assertStringMatchesFormat('Xdebug %s', $this->driver()->nameAndVersion()); + } + + public function testFilterWorks(): void + { + $bankAccount = TEST_FILES_PATH . 'BankAccount.php'; + + require $bankAccount; + + $this->assertArrayNotHasKey($bankAccount, xdebug_get_code_coverage()); + } + + private function driver(): XdebugDriver + { + return new XdebugDriver(new Filter); + } +} diff --git a/tests/tests/Exception/InvalidCodeCoverageTargetExceptionTest.php b/tests/tests/Exception/InvalidCodeCoverageTargetExceptionTest.php new file mode 100644 index 000000000..4961f7c50 --- /dev/null +++ b/tests/tests/Exception/InvalidCodeCoverageTargetExceptionTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(InvalidCodeCoverageTargetException::class)] +#[Small] +final class InvalidCodeCoverageTargetExceptionTest extends TestCase +{ + public function testHasMessage(): void + { + /** @phpstan-ignore argument.type */ + $e = new InvalidCodeCoverageTargetException(Target::forClass('DoesNotExist')); + + $this->assertSame('Class DoesNotExist is not a valid target for code coverage', $e->getMessage()); + } +} diff --git a/tests/tests/Exception/NoCodeCoverageDriverAvailableExceptionTest.php b/tests/tests/Exception/NoCodeCoverageDriverAvailableExceptionTest.php new file mode 100644 index 000000000..41b0a613d --- /dev/null +++ b/tests/tests/Exception/NoCodeCoverageDriverAvailableExceptionTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(NoCodeCoverageDriverAvailableException::class)] +#[Small] +final class NoCodeCoverageDriverAvailableExceptionTest extends TestCase +{ + public function testHasMessage(): void + { + $e = new NoCodeCoverageDriverAvailableException; + + $this->assertSame('No code coverage driver available', $e->getMessage()); + } +} diff --git a/tests/tests/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableExceptionTest.php b/tests/tests/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableExceptionTest.php new file mode 100644 index 000000000..875caffde --- /dev/null +++ b/tests/tests/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableExceptionTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(NoCodeCoverageDriverWithPathCoverageSupportAvailableException::class)] +#[Small] +final class NoCodeCoverageDriverWithPathCoverageSupportAvailableExceptionTest extends TestCase +{ + public function testHasMessage(): void + { + $e = new NoCodeCoverageDriverWithPathCoverageSupportAvailableException; + + $this->assertSame('No code coverage driver with path coverage support available', $e->getMessage()); + } +} diff --git a/tests/tests/Exception/PathExistsButIsNotDirectoryExceptionTest.php b/tests/tests/Exception/PathExistsButIsNotDirectoryExceptionTest.php new file mode 100644 index 000000000..daae0bab1 --- /dev/null +++ b/tests/tests/Exception/PathExistsButIsNotDirectoryExceptionTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(PathExistsButIsNotDirectoryException::class)] +#[Small] +final class PathExistsButIsNotDirectoryExceptionTest extends TestCase +{ + public function testHasMessage(): void + { + $e = new PathExistsButIsNotDirectoryException('/not/a/directory'); + + $this->assertSame('"/not/a/directory" exists but is not a directory', $e->getMessage()); + } +} diff --git a/tests/tests/Exception/PcovNotAvailableExceptionTest.php b/tests/tests/Exception/PcovNotAvailableExceptionTest.php new file mode 100644 index 000000000..7fdacded8 --- /dev/null +++ b/tests/tests/Exception/PcovNotAvailableExceptionTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(PcovNotAvailableException::class)] +#[Small] +final class PcovNotAvailableExceptionTest extends TestCase +{ + public function testHasMessage(): void + { + $e = new PcovNotAvailableException; + + $this->assertSame('The PCOV extension is not available', $e->getMessage()); + } +} diff --git a/tests/tests/Exception/ReportAlreadyFinalizedExceptionTest.php b/tests/tests/Exception/ReportAlreadyFinalizedExceptionTest.php new file mode 100644 index 000000000..490970130 --- /dev/null +++ b/tests/tests/Exception/ReportAlreadyFinalizedExceptionTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ReportAlreadyFinalizedException::class)] +#[Small] +final class ReportAlreadyFinalizedExceptionTest extends TestCase +{ + public function testHasMessage(): void + { + $e = new ReportAlreadyFinalizedException; + + $this->assertSame('The code coverage report has already been finalized', $e->getMessage()); + } +} diff --git a/tests/tests/Exception/TestIdMissingExceptionTest.php b/tests/tests/Exception/TestIdMissingExceptionTest.php new file mode 100644 index 000000000..c39e1e119 --- /dev/null +++ b/tests/tests/Exception/TestIdMissingExceptionTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(TestIdMissingException::class)] +#[Small] +final class TestIdMissingExceptionTest extends TestCase +{ + public function testHasMessage(): void + { + $e = new TestIdMissingException; + + $this->assertSame('Test ID is missing', $e->getMessage()); + } +} diff --git a/tests/tests/Exception/UnintentionallyCoveredCodeExceptionTest.php b/tests/tests/Exception/UnintentionallyCoveredCodeExceptionTest.php new file mode 100644 index 000000000..5cafd013a --- /dev/null +++ b/tests/tests/Exception/UnintentionallyCoveredCodeExceptionTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(UnintentionallyCoveredCodeException::class)] +#[Small] +final class UnintentionallyCoveredCodeExceptionTest extends TestCase +{ + public function testCanConstructWithEmptyArray(): void + { + $unintentionallyCoveredUnits = []; + + $exception = new UnintentionallyCoveredCodeException($unintentionallyCoveredUnits); + + $this->assertSame($unintentionallyCoveredUnits, $exception->getUnintentionallyCoveredUnits()); + $this->assertSame('', $exception->getMessage()); + } + + public function testCanConstructWithNonEmptyArray(): void + { + $unintentionallyCoveredUnits = [ + 'foo', + 'bar', + 'baz', + ]; + + $exception = new UnintentionallyCoveredCodeException($unintentionallyCoveredUnits); + + $this->assertSame($unintentionallyCoveredUnits, $exception->getUnintentionallyCoveredUnits()); + + $expected = <<<'TXT' +- foo +- bar +- baz + +TXT; + + $this->assertSame($expected, $exception->getMessage()); + } +} diff --git a/tests/tests/Exception/WriteOperationFailedExceptionTest.php b/tests/tests/Exception/WriteOperationFailedExceptionTest.php new file mode 100644 index 000000000..c7903723e --- /dev/null +++ b/tests/tests/Exception/WriteOperationFailedExceptionTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(WriteOperationFailedException::class)] +#[Small] +final class WriteOperationFailedExceptionTest extends TestCase +{ + public function testHasMessage(): void + { + $e = new WriteOperationFailedException('/path'); + + $this->assertSame('Cannot write to "/path"', $e->getMessage()); + } +} diff --git a/tests/tests/Exception/XdebugNotAvailableExceptionTest.php b/tests/tests/Exception/XdebugNotAvailableExceptionTest.php new file mode 100644 index 000000000..ef36ec148 --- /dev/null +++ b/tests/tests/Exception/XdebugNotAvailableExceptionTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(XdebugNotAvailableException::class)] +#[Small] +final class XdebugNotAvailableExceptionTest extends TestCase +{ + public function testHasMessage(): void + { + $e = new XdebugNotAvailableException; + + $this->assertSame('The Xdebug extension is not available', $e->getMessage()); + } +} diff --git a/tests/tests/Exception/XdebugNotEnabledExceptionTest.php b/tests/tests/Exception/XdebugNotEnabledExceptionTest.php new file mode 100644 index 000000000..9203e69ad --- /dev/null +++ b/tests/tests/Exception/XdebugNotEnabledExceptionTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(XdebugNotEnabledException::class)] +#[Small] +final class XdebugNotEnabledExceptionTest extends TestCase +{ + public function testHasMessage(): void + { + $e = new XdebugNotEnabledException; + + $this->assertSame('XDEBUG_MODE=coverage (environment variable) or xdebug.mode=coverage (PHP configuration setting) has to be set', $e->getMessage()); + } +} diff --git a/tests/tests/Exception/XdebugVersionNotSupportedExceptionTest.php b/tests/tests/Exception/XdebugVersionNotSupportedExceptionTest.php new file mode 100644 index 000000000..94aec6f53 --- /dev/null +++ b/tests/tests/Exception/XdebugVersionNotSupportedExceptionTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(XdebugVersionNotSupportedException::class)] +#[Small] +final class XdebugVersionNotSupportedExceptionTest extends TestCase +{ + public function testHasMessage(): void + { + $e = new XdebugVersionNotSupportedException('1.2.3'); + + $this->assertSame('Version 1.2.3 of the Xdebug extension is not supported', $e->getMessage()); + } +} diff --git a/tests/tests/FilterTest.php b/tests/tests/FilterTest.php new file mode 100644 index 000000000..eae464d5d --- /dev/null +++ b/tests/tests/FilterTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use function realpath; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\Ticket; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Filter::class)] +#[Small] +final class FilterTest extends TestCase +{ + public function testIsInitiallyEmpty(): void + { + $filter = new Filter; + + $this->assertTrue($filter->isEmpty()); + } + + public function testSingleFileCanBeAdded(): void + { + $filter = new Filter; + + $file = realpath(__DIR__ . '/../_files/filter/a.php'); + + $filter->includeFile($file); + + $this->assertFalse($filter->isEmpty()); + + $this->assertSame( + [ + $file, + ], + $filter->files(), + ); + } + + public function testMultipleFilesCanBeAdded(): void + { + $filter = new Filter; + + $files = [ + realpath(__DIR__ . '/../_files/filter/a.php'), + realpath(__DIR__ . '/../_files/filter/b.php'), + ]; + + $filter->includeFiles($files); + + $this->assertSame($files, $filter->files()); + } + + public function testDeterminesWhetherStringContainsNameOfRealFileThatExists(): void + { + $filter = new Filter; + + $this->assertFalse($filter->isFile('vfs://root/a/path')); + $this->assertFalse($filter->isFile('xdebug://debug-eval')); + $this->assertFalse($filter->isFile('eval()\'d code')); + $this->assertFalse($filter->isFile('runtime-created function')); + $this->assertFalse($filter->isFile('assert code')); + $this->assertFalse($filter->isFile('regexp code')); + $this->assertTrue($filter->isFile(__DIR__ . '/../_files/filter/a.php')); + } + + public function testIncludedFileIsNotFiltered(): void + { + $filter = new Filter; + + $filter->includeFile(realpath(__DIR__ . '/../_files/filter/a.php')); + + $this->assertFalse($filter->isExcluded(realpath(__DIR__ . '/../_files/filter/a.php'))); + + /** + * Assertion is performed twice to test the is-cached path. + */ + $this->assertFalse($filter->isExcluded(realpath(__DIR__ . '/../_files/filter/a.php'))); + } + + public function testNotIncludedFileIsFiltered(): void + { + $filter = new Filter; + + $filter->includeFile(realpath(__DIR__ . '/../_files/filter/a.php')); + + $this->assertTrue($filter->isExcluded(realpath(__DIR__ . '/../_files/filter/b.php'))); + } + + public function testNonFilesAreFiltered(): void + { + $filter = new Filter; + + $this->assertTrue($filter->isExcluded('vfs://root/a/path')); + $this->assertTrue($filter->isExcluded('xdebug://debug-eval')); + $this->assertTrue($filter->isExcluded('eval()\'d code')); + $this->assertTrue($filter->isExcluded('runtime-created function')); + $this->assertTrue($filter->isExcluded('assert code')); + $this->assertTrue($filter->isExcluded('regexp code')); + } + + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/issues/664')] + public function testTryingToAddFileThatDoesNotExistDoesNotChangeFilter(): void + { + $filter = new Filter; + + $filter->includeFile('does_not_exist'); + + $this->assertTrue($filter->isEmpty()); + $this->assertSame([], $filter->files()); + } +} diff --git a/tests/tests/Report/CloverTest.php b/tests/tests/Report/CloverTest.php new file mode 100644 index 000000000..114642061 --- /dev/null +++ b/tests/tests/Report/CloverTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use PHPUnit\Framework\Attributes\CoversClass; +use SebastianBergmann\CodeCoverage\TestCase; + +#[CoversClass(Clover::class)] +final class CloverTest extends TestCase +{ + public function testLineCoverageForBankAccountTest(): void + { + $clover = new Clover; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Clover/BankAccount-line.xml', + $clover->process($this->getLineCoverageForBankAccount(), null, 'BankAccount'), + ); + } + + public function testPathCoverageForBankAccountTest(): void + { + $clover = new Clover; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Clover/BankAccount-path.xml', + $clover->process($this->getPathCoverageForBankAccount(), null, 'BankAccount'), + ); + } + + public function testCloverForFileWithIgnoredLines(): void + { + $clover = new Clover; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Clover/ignored-lines.xml', + $clover->process($this->getCoverageForFileWithIgnoredLines()), + ); + } + + public function testCloverForClassWithAnonymousFunction(): void + { + $clover = new Clover; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Clover/class-with-anonymous-function.xml', + $clover->process($this->getCoverageForClassWithAnonymousFunction()), + ); + } +} diff --git a/tests/tests/Report/CoberturaTest.php b/tests/tests/Report/CoberturaTest.php new file mode 100644 index 000000000..48f0667d2 --- /dev/null +++ b/tests/tests/Report/CoberturaTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use PHPUnit\Framework\Attributes\CoversClass; +use SebastianBergmann\CodeCoverage\TestCase; + +#[CoversClass(Cobertura::class)] +final class CoberturaTest extends TestCase +{ + public function testLineCoverageForBankAccountTest(): void + { + $cobertura = new Cobertura; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Cobertura/BankAccount-line.xml', + $cobertura->process($this->getLineCoverageForBankAccount(), null), + ); + } + + public function testPathCoverageForBankAccountTest(): void + { + $cobertura = new Cobertura; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Cobertura/BankAccount-path.xml', + $cobertura->process($this->getPathCoverageForBankAccount(), null), + ); + } + + public function testCoberturaForFileWithIgnoredLines(): void + { + $cobertura = new Cobertura; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Cobertura/ignored-lines.xml', + $cobertura->process($this->getCoverageForFileWithIgnoredLines()), + ); + } + + public function testCoberturaForClassWithAnonymousFunction(): void + { + $cobertura = new Cobertura; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Cobertura/class-with-anonymous-function.xml', + $cobertura->process($this->getCoverageForClassWithAnonymousFunction()), + ); + } + + public function testCoberturaForClassAndOutsideFunction(): void + { + $cobertura = new Cobertura; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Cobertura/class-with-outside-function.xml', + $cobertura->process($this->getCoverageForClassWithOutsideFunction()), + ); + } +} diff --git a/tests/tests/Report/Crap4jTest.php b/tests/tests/Report/Crap4jTest.php new file mode 100644 index 000000000..8e0fb1b06 --- /dev/null +++ b/tests/tests/Report/Crap4jTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use PHPUnit\Framework\Attributes\CoversClass; +use SebastianBergmann\CodeCoverage\TestCase; + +#[CoversClass(Crap4j::class)] +final class Crap4jTest extends TestCase +{ + public function testForBankAccountTest(): void + { + $crap4j = new Crap4j; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Crap4j/BankAccount.xml', + $crap4j->process($this->getLineCoverageForBankAccount(), null, 'BankAccount'), + ); + } + + public function testForFileWithIgnoredLines(): void + { + $crap4j = new Crap4j; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Crap4j/ignored-lines.xml', + $crap4j->process($this->getCoverageForFileWithIgnoredLines(), null, 'CoverageForFileWithIgnoredLines'), + ); + } + + public function testForClassWithAnonymousFunction(): void + { + $crap4j = new Crap4j; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Crap4j/class-with-anonymous-function.xml', + $crap4j->process($this->getCoverageForClassWithAnonymousFunction(), null, 'CoverageForClassWithAnonymousFunction'), + ); + } +} diff --git a/tests/tests/Report/Html/ColorsTest.php b/tests/tests/Report/Html/ColorsTest.php new file mode 100644 index 000000000..78f17b58f --- /dev/null +++ b/tests/tests/Report/Html/ColorsTest.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Html; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Colors::class)] +#[Small] +final class ColorsTest extends TestCase +{ + public function testCanBeCreatedFromDefaults(): void + { + $colors = Colors::default(); + + $this->assertSame('#dff0d8', $colors->successLow()); + $this->assertSame('#c3e3b5', $colors->successMedium()); + $this->assertSame('#99cb84', $colors->successHigh()); + $this->assertSame('#fcf8e3', $colors->warning()); + $this->assertSame('#f2dede', $colors->danger()); + } + + public function testCanBeCreatedFromCustomValues(): void + { + $colors = Colors::from('successLow', 'successMedium', 'successHigh', 'warning', 'danger'); + + $this->assertSame('successLow', $colors->successLow()); + $this->assertSame('successMedium', $colors->successMedium()); + $this->assertSame('successHigh', $colors->successHigh()); + $this->assertSame('warning', $colors->warning()); + $this->assertSame('danger', $colors->danger()); + } +} diff --git a/tests/tests/Report/Html/CustomCssFileTest.php b/tests/tests/Report/Html/CustomCssFileTest.php new file mode 100644 index 000000000..e2df312a4 --- /dev/null +++ b/tests/tests/Report/Html/CustomCssFileTest.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Html; + +use function realpath; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; +use SebastianBergmann\CodeCoverage\InvalidArgumentException; + +#[CoversClass(CustomCssFile::class)] +#[Small] +final class CustomCssFileTest extends TestCase +{ + public function testCanBeCreatedFromDefaults(): void + { + $file = CustomCssFile::default(); + + $this->assertSame( + realpath(__DIR__ . '/../../../../src/Report/Html/Renderer/Template/css/custom.css'), + realpath($file->path()), + ); + } + + public function testCanBeCreatedFromValidPath(): void + { + $file = CustomCssFile::from(__FILE__); + + $this->assertSame(__FILE__, $file->path()); + } + + public function testCannotBeCreatedFromInvalidPath(): void + { + $this->expectException(InvalidArgumentException::class); + + CustomCssFile::from('does-not-exist'); + } +} diff --git a/tests/tests/Report/Html/EndToEndTest.php b/tests/tests/Report/Html/EndToEndTest.php new file mode 100644 index 000000000..6262bf6a1 --- /dev/null +++ b/tests/tests/Report/Html/EndToEndTest.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Html; + +use const DIRECTORY_SEPARATOR; +use const PHP_EOL; +use function file_get_contents; +use function iterator_count; +use function str_replace; +use FilesystemIterator; +use RegexIterator; +use SebastianBergmann\CodeCoverage\TestCase; + +final class EndToEndTest extends TestCase +{ + protected function tearDown(): void + { + $this->removeTemporaryFiles(); + } + + public function testLineCoverageForBankAccountTest(): void + { + $expectedFilesPath = TEST_FILES_PATH . 'Report' . DIRECTORY_SEPARATOR . 'HTML' . DIRECTORY_SEPARATOR . 'CoverageForBankAccount'; + + $report = new Facade; + $report->process($this->getLineCoverageForBankAccount(), TEST_FILES_PATH . 'tmp'); + + $this->assertFilesEquals($expectedFilesPath, TEST_FILES_PATH . 'tmp'); + } + + public function testPathCoverageForBankAccountTest(): void + { + $this->markTestIncomplete('This test fails after https://github.com/sebastianbergmann/php-code-coverage/pull/1037 and I have not figured out how to update it.'); + + /** @phpstan-ignore deadCode.unreachable */ + $expectedFilesPath = TEST_FILES_PATH . 'Report' . DIRECTORY_SEPARATOR . 'HTML' . DIRECTORY_SEPARATOR . 'PathCoverageForBankAccount'; + + $report = new Facade; + $report->process($this->getPathCoverageForBankAccount(), TEST_FILES_PATH . 'tmp'); + + $this->assertFilesEquals($expectedFilesPath, TEST_FILES_PATH . 'tmp'); + } + + public function testPathCoverageForSourceWithoutNamespace(): void + { + $expectedFilesPath = TEST_FILES_PATH . 'Report' . DIRECTORY_SEPARATOR . 'HTML' . DIRECTORY_SEPARATOR . 'PathCoverageForSourceWithoutNamespace'; + + $report = new Facade; + $report->process($this->getPathCoverageForSourceWithoutNamespace(), TEST_FILES_PATH . 'tmp'); + + $this->assertFilesEquals($expectedFilesPath, TEST_FILES_PATH . 'tmp'); + } + + public function testForFileWithIgnoredLines(): void + { + $expectedFilesPath = TEST_FILES_PATH . 'Report' . DIRECTORY_SEPARATOR . 'HTML' . DIRECTORY_SEPARATOR . 'CoverageForFileWithIgnoredLines'; + + $report = new Facade; + $report->process($this->getCoverageForFileWithIgnoredLines(), TEST_FILES_PATH . 'tmp'); + + $this->assertFilesEquals($expectedFilesPath, TEST_FILES_PATH . 'tmp'); + } + + public function testForClassWithAnonymousFunction(): void + { + $expectedFilesPath = TEST_FILES_PATH . 'Report' . DIRECTORY_SEPARATOR . 'HTML' . DIRECTORY_SEPARATOR . 'CoverageForClassWithAnonymousFunction'; + + $report = new Facade; + $report->process($this->getCoverageForClassWithAnonymousFunction(), TEST_FILES_PATH . 'tmp'); + + $this->assertFilesEquals($expectedFilesPath, TEST_FILES_PATH . 'tmp'); + } + + private function assertFilesEquals(string $expectedFilesPath, string $actualFilesPath): void + { + $expectedFilesIterator = new FilesystemIterator($expectedFilesPath); + $actualFilesIterator = new RegexIterator(new FilesystemIterator($actualFilesPath), '/.html/'); + + $this->assertEquals( + iterator_count($expectedFilesIterator), + iterator_count($actualFilesIterator), + 'Generated files and expected files not match', + ); + + foreach ($expectedFilesIterator as $path => $fileInfo) { + /* @var \SplFileInfo $fileInfo */ + $filename = $fileInfo->getFilename(); + + $actualFile = $actualFilesPath . DIRECTORY_SEPARATOR . $filename; + + $this->assertFileExists($actualFile); + + $this->assertStringMatchesFormatFile( + $fileInfo->getPathname(), + str_replace(PHP_EOL, "\n", file_get_contents($actualFile)), + "{$filename} not match", + ); + } + } +} diff --git a/tests/tests/Report/Html/ThresholdsTest.php b/tests/tests/Report/Html/ThresholdsTest.php new file mode 100644 index 000000000..2f348730d --- /dev/null +++ b/tests/tests/Report/Html/ThresholdsTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Html; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; +use SebastianBergmann\CodeCoverage\InvalidArgumentException; +use SebastianBergmann\CodeCoverage\Report\Thresholds; + +#[CoversClass(Thresholds::class)] +#[Small] +final class ThresholdsTest extends TestCase +{ + public function testCanBeCreatedFromDefaults(): void + { + $thresholds = Thresholds::default(); + + $this->assertSame(50, $thresholds->lowUpperBound()); + $this->assertSame(90, $thresholds->highLowerBound()); + } + + public function testCanBeCreatedFromValidCustomValues(): void + { + $thresholds = Thresholds::from(60, 95); + + $this->assertSame(60, $thresholds->lowUpperBound()); + $this->assertSame(95, $thresholds->highLowerBound()); + } + + public function testCannotBeCreatedFromInvalidValues(): void + { + $this->expectException(InvalidArgumentException::class); + + Thresholds::from(90, 50); + } +} diff --git a/tests/tests/Report/OpenCloverTest.php b/tests/tests/Report/OpenCloverTest.php new file mode 100644 index 000000000..0b5a6c659 --- /dev/null +++ b/tests/tests/Report/OpenCloverTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use const PHP_EOL; +use function libxml_clear_errors; +use function libxml_get_errors; +use function libxml_use_internal_errors; +use function sprintf; +use function trim; +use DOMDocument; +use PHPUnit\Framework\Attributes\CoversClass; +use SebastianBergmann\CodeCoverage\TestCase; + +#[CoversClass(OpenClover::class)] +final class OpenCloverTest extends TestCase +{ + public function testLineCoverageForBankAccountTest(): void + { + $clover = new OpenClover; + + $this->assertAndValidate( + TEST_FILES_PATH . 'Report/OpenClover/BankAccount-line.xml', + $clover->process($this->getLineCoverageForBankAccount(), null, 'BankAccount'), + ); + } + + public function testPathCoverageForBankAccountTest(): void + { + $clover = new OpenClover; + + $this->assertAndValidate( + TEST_FILES_PATH . 'Report/OpenClover/BankAccount-path.xml', + $clover->process($this->getPathCoverageForBankAccount(), null, 'BankAccount'), + ); + } + + public function testCloverForFileWithIgnoredLines(): void + { + $clover = new OpenClover; + + $this->assertAndValidate( + TEST_FILES_PATH . 'Report/OpenClover/ignored-lines.xml', + $clover->process($this->getCoverageForFileWithIgnoredLines()), + ); + } + + public function testCloverForClassWithAnonymousFunction(): void + { + $clover = new OpenClover; + + $this->assertAndValidate( + TEST_FILES_PATH . 'Report/OpenClover/class-with-anonymous-function.xml', + $clover->process($this->getCoverageForClassWithAnonymousFunction()), + ); + } + + /** + * @param non-empty-string $expectationFile + * @param non-empty-string $cloverXml + */ + private function assertAndValidate(string $expectationFile, string $cloverXml): void + { + $this->assertStringMatchesFormatFile($expectationFile, $cloverXml); + + libxml_use_internal_errors(true); + + $document = new DOMDocument; + $document->loadXML($cloverXml); + + if (!$document->schemaValidate(__DIR__ . '/../../_files/Report/OpenClover/clover.xsd')) { + $buffer = 'Generated XML document does not validate against Clover schema:' . PHP_EOL . PHP_EOL; + + foreach (libxml_get_errors() as $error) { + $buffer .= sprintf( + '- Line %d: %s' . PHP_EOL, + $error->line, + trim($error->message), + ); + } + + $buffer .= PHP_EOL; + } + + libxml_clear_errors(); + + if (isset($buffer)) { + $this->fail($buffer); + } + } +} diff --git a/tests/tests/Report/PhpTest.php b/tests/tests/Report/PhpTest.php new file mode 100644 index 000000000..358e588a7 --- /dev/null +++ b/tests/tests/Report/PhpTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use ReflectionProperty; +use SebastianBergmann\CodeCoverage\TestCase; + +final class PhpTest extends TestCase +{ + protected function tearDown(): void + { + parent::tearDown(); + + $this->removeTemporaryFiles(); + } + + public function testPHPSerialisationProducesValidCode(): void + { + $coverage = $this->getLineCoverageForBankAccount(); + + /* @noinspection UnusedFunctionResultInspection */ + (new PHP)->process($coverage, TEST_FILES_PATH . 'tmp/serialized.php'); + + $unserialized = require TEST_FILES_PATH . 'tmp/serialized.php'; + + $this->assertEquals($coverage, $unserialized); + } + + public function testPHPSerialisationProducesValidCodeWhenOutputIncludesSingleQuote(): void + { + $coverage = $this->getLineCoverageForFileWithEval(); + + /* @noinspection UnusedFunctionResultInspection */ + (new PHP)->process($coverage, TEST_FILES_PATH . 'tmp/serialized.php'); + + $unserialized = require TEST_FILES_PATH . 'tmp/serialized.php'; + + $this->assertEquals($coverage, $unserialized); + } + + public function testCacheDataNeverGetSaved(): void + { + $coverage = $this->getLineCoverageForBankAccount(); + + // Warm up cache + $coverage->getReport(); + + $refProperty = new ReflectionProperty($coverage, 'cachedReport'); + + $this->assertNotNull($refProperty->getValue($coverage)); + + /* @noinspection UnusedFunctionResultInspection */ + (new PHP)->process($coverage, TEST_FILES_PATH . 'tmp/serialized.php'); + + $this->assertNull($refProperty->getValue($coverage)); + } +} diff --git a/tests/tests/Report/TextTest.php b/tests/tests/Report/TextTest.php new file mode 100644 index 000000000..ea36cc3ae --- /dev/null +++ b/tests/tests/Report/TextTest.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use const PHP_EOL; +use function str_replace; +use PHPUnit\Framework\Attributes\CoversClass; +use SebastianBergmann\CodeCoverage\TestCase; + +#[CoversClass(Text::class)] +final class TextTest extends TestCase +{ + public function testLineCoverageForBankAccountTest(): void + { + $text = new Text(Thresholds::default(), false, false); + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Text/BankAccount-line.txt', + str_replace(PHP_EOL, "\n", $text->process($this->getLineCoverageForBankAccount())), + ); + } + + public function testPathCoverageForBankAccountTest(): void + { + $text = new Text(Thresholds::default(), false, false); + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Text/BankAccount-path.txt', + str_replace(PHP_EOL, "\n", $text->process($this->getPathCoverageForBankAccount())), + ); + } + + public function testTextOnlySummaryForBankAccountTest(): void + { + $text = new Text(Thresholds::default(), false, true); + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Text/BankAccount-summary.txt', + str_replace(PHP_EOL, "\n", $text->process($this->getLineCoverageForBankAccount())), + ); + } + + public function testTextForNamespacedBankAccountTest(): void + { + $text = new Text(Thresholds::default(), true, false); + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Text/NamespacedBankAccount.txt', + str_replace(PHP_EOL, "\n", $text->process($this->getLineCoverageForNamespacedBankAccount())), + ); + } + + public function testTextForNamespacedBankAccountTestWhenColorsAreEnabled(): void + { + $text = new Text(Thresholds::default(), true, false); + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Text/NamespacedBankAccount-colors.txt', + str_replace(PHP_EOL, "\n", $text->process($this->getLineCoverageForNamespacedBankAccount(), true)), + ); + } + + public function testTextForFileWithIgnoredLines(): void + { + $text = new Text(Thresholds::default(), false, false); + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Text/ignored-lines.txt', + str_replace(PHP_EOL, "\n", $text->process($this->getCoverageForFileWithIgnoredLines())), + ); + } + + public function testTextForClassWithAnonymousFunction(): void + { + $text = new Text(Thresholds::default(), false, false); + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Text/class-with-anonymous-function.txt', + str_replace(PHP_EOL, "\n", $text->process($this->getCoverageForClassWithAnonymousFunction())), + ); + } + + public function testUncoveredFilesAreIncludedWhenConfiguredTest(): void + { + $text = new Text(Thresholds::default(), false, false); + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Text/BankAccountWithUncovered-line.txt', + str_replace(PHP_EOL, "\n", $text->process($this->getCoverageForFilesWithUncoveredIncluded())), + ); + } + + public function testUncoveredFilesAreExcludedWhenConfiguredTest(): void + { + $text = new Text(Thresholds::default(), false, false); + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'Report/Text/BankAccountWithoutUncovered-line.txt', + str_replace(PHP_EOL, "\n", $text->process($this->getCoverageForFilesWithUncoveredExcluded())), + ); + } +} diff --git a/tests/tests/Report/XmlTest.php b/tests/tests/Report/XmlTest.php new file mode 100644 index 000000000..53deb784e --- /dev/null +++ b/tests/tests/Report/XmlTest.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use const DIRECTORY_SEPARATOR; +use function file_get_contents; +use function iterator_count; +use function unlink; +use FilesystemIterator; +use SebastianBergmann\CodeCoverage\TestCase; + +final class XmlTest extends TestCase +{ + private static string $TEST_REPORT_PATH_SOURCE; + + public static function setUpBeforeClass(): void + { + parent::setUpBeforeClass(); + + self::$TEST_REPORT_PATH_SOURCE = TEST_FILES_PATH . 'Report' . DIRECTORY_SEPARATOR . 'XML'; + } + + protected function tearDown(): void + { + parent::tearDown(); + + foreach (new FilesystemIterator(TEST_FILES_PATH . 'tmp') as $path => $fileInfo) { + /* @var \SplFileInfo $fileInfo */ + unlink($fileInfo->getPathname()); + } + } + + public function testForBankAccountTest(): void + { + $expectedFilesPath = self::$TEST_REPORT_PATH_SOURCE . DIRECTORY_SEPARATOR . 'CoverageForBankAccount'; + + $xml = new Facade('1.0.0'); + $xml->process($this->getLineCoverageForBankAccount(), TEST_FILES_PATH . 'tmp'); + + $this->assertFilesEquals($expectedFilesPath, TEST_FILES_PATH . 'tmp'); + } + + public function testForFileWithIgnoredLines(): void + { + $expectedFilesPath = self::$TEST_REPORT_PATH_SOURCE . DIRECTORY_SEPARATOR . 'CoverageForFileWithIgnoredLines'; + + $xml = new Facade('1.0.0'); + $xml->process($this->getCoverageForFileWithIgnoredLines(), TEST_FILES_PATH . 'tmp'); + + $this->assertFilesEquals($expectedFilesPath, TEST_FILES_PATH . 'tmp'); + } + + public function testForClassWithAnonymousFunction(): void + { + $expectedFilesPath = self::$TEST_REPORT_PATH_SOURCE . DIRECTORY_SEPARATOR . 'CoverageForClassWithAnonymousFunction'; + + $xml = new Facade('1.0.0'); + $xml->process($this->getCoverageForClassWithAnonymousFunction(), TEST_FILES_PATH . 'tmp'); + + $this->assertFilesEquals($expectedFilesPath, TEST_FILES_PATH . 'tmp'); + } + + private function assertFilesEquals(string $expectedFilesPath, string $actualFilesPath): void + { + $expectedFilesIterator = new FilesystemIterator($expectedFilesPath); + $actualFilesIterator = new FilesystemIterator($actualFilesPath); + + $this->assertEquals( + iterator_count($expectedFilesIterator), + iterator_count($actualFilesIterator), + 'Generated files and expected files not match', + ); + + foreach ($expectedFilesIterator as $path => $fileInfo) { + /* @var \SplFileInfo $fileInfo */ + $filename = $fileInfo->getFilename(); + + $actualFile = $actualFilesPath . DIRECTORY_SEPARATOR . $filename; + + $this->assertFileExists($actualFile); + + $this->assertStringMatchesFormatFile( + $fileInfo->getPathname(), + file_get_contents($actualFile), + "{$filename} not match", + ); + } + } +} diff --git a/tests/tests/StaticAnalysis/CachingSourceAnalyserTest.php b/tests/tests/StaticAnalysis/CachingSourceAnalyserTest.php new file mode 100644 index 000000000..58c982e0a --- /dev/null +++ b/tests/tests/StaticAnalysis/CachingSourceAnalyserTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use const DIRECTORY_SEPARATOR; +use function sys_get_temp_dir; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; + +#[CoversClass(CachingSourceAnalyser::class)] +#[UsesClass(ParsingSourceAnalyser::class)] +#[UsesClass(CodeUnitFindingVisitor::class)] +#[UsesClass(IgnoredLinesFindingVisitor::class)] +#[Small] +final class CachingSourceAnalyserTest extends SourceAnalyserTestCase +{ + protected function analyser(): SourceAnalyser + { + return new CachingSourceAnalyser( + sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'php-code-coverage-tests-for-static-analysis', + new ParsingSourceAnalyser, + ); + } +} diff --git a/tests/tests/StaticAnalysis/ParsingSourceAnalyserTest.php b/tests/tests/StaticAnalysis/ParsingSourceAnalyserTest.php new file mode 100644 index 000000000..07eea3e51 --- /dev/null +++ b/tests/tests/StaticAnalysis/ParsingSourceAnalyserTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(ParsingSourceAnalyser::class)] +#[CoversClass(CodeUnitFindingVisitor::class)] +#[CoversClass(IgnoredLinesFindingVisitor::class)] +#[Small] +final class ParsingSourceAnalyserTest extends SourceAnalyserTestCase +{ + protected function analyser(): SourceAnalyser + { + return new ParsingSourceAnalyser; + } +} diff --git a/tests/tests/StaticAnalysis/Value/ClassTest.php b/tests/tests/StaticAnalysis/Value/ClassTest.php new file mode 100644 index 000000000..b1ee61619 --- /dev/null +++ b/tests/tests/StaticAnalysis/Value/ClassTest.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Class_::class)] +#[Small] +final class ClassTest extends TestCase +{ + public function testHasName(): void + { + $this->assertSame('Example', $this->class()->name()); + } + + public function testHasNamespacedName(): void + { + $this->assertSame('example\Example', $this->class()->namespacedName()); + } + + public function testHasNamespace(): void + { + $this->assertSame('example', $this->class()->namespace()); + } + + public function testHasFile(): void + { + $this->assertSame('file.php', $this->class()->file()); + } + + public function testHasStartLine(): void + { + $this->assertSame(1, $this->class()->startLine()); + } + + public function testHasEndLine(): void + { + $this->assertSame(2, $this->class()->endLine()); + } + + public function testMayHaveParentClass(): void + { + $parentClass = 'example\ParentClass'; + + $class = $this->class(parentClass: $parentClass); + + $this->assertTrue($class->hasParent()); + $this->assertSame($parentClass, $class->parentClass()); + } + + public function testMayNotHaveParentClass(): void + { + $this->assertFalse($this->class()->hasParent()); + $this->assertNull($this->class()->parentClass()); + } + + public function testMayImplementInterfaces(): void + { + $interfaces = ['example\AnInterface']; + + $this->assertSame($interfaces, $this->class(interfaces: $interfaces)->interfaces()); + } + + public function testMayUseTraits(): void + { + $traits = ['example\ATrait']; + + $this->assertSame($traits, $this->class(traits: $traits)->traits()); + } + + public function testMayHaveMethods(): void + { + $methods = [ + 'method' => new Method( + 'method', + 0, + 0, + 'method(): void', + Visibility::Public, + 1, + ), + ]; + + $this->assertSame($methods, $this->class(methods: $methods)->methods()); + } + + /** + * @param list $interfaces + * @param list $traits + * @param array $methods + */ + private function class(?string $parentClass = null, array $interfaces = [], array $traits = [], array $methods = []): Class_ + { + return new Class_( + 'Example', + 'example\Example', + 'example', + 'file.php', + 1, + 2, + $parentClass, + $interfaces, + $traits, + $methods, + ); + } +} diff --git a/tests/tests/StaticAnalysis/Value/FunctionTest.php b/tests/tests/StaticAnalysis/Value/FunctionTest.php new file mode 100644 index 000000000..41a987d7c --- /dev/null +++ b/tests/tests/StaticAnalysis/Value/FunctionTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Function_::class)] +#[Small] +final class FunctionTest extends TestCase +{ + public function testHasName(): void + { + $this->assertSame('example', $this->function()->name()); + } + + public function testHasNamespacedName(): void + { + $this->assertSame('example\example', $this->function()->namespacedName()); + } + + public function testHasNamespace(): void + { + $this->assertSame('example', $this->function()->namespace()); + } + + public function testHasStartLine(): void + { + $this->assertSame(1, $this->function()->startLine()); + } + + public function testHasEndLine(): void + { + $this->assertSame(2, $this->function()->endLine()); + } + + public function testHasSignature(): void + { + $this->assertSame('the-signature', $this->function()->signature()); + } + + public function testHasCyclomaticComplexity(): void + { + $this->assertSame(3, $this->function()->cyclomaticComplexity()); + } + + private function function(): Function_ + { + return new Function_( + 'example', + 'example\example', + 'example', + 1, + 2, + 'the-signature', + 3, + ); + } +} diff --git a/tests/tests/StaticAnalysis/Value/InterfaceTest.php b/tests/tests/StaticAnalysis/Value/InterfaceTest.php new file mode 100644 index 000000000..674f8b63f --- /dev/null +++ b/tests/tests/StaticAnalysis/Value/InterfaceTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Interface_::class)] +#[Small] +final class InterfaceTest extends TestCase +{ + public function testHasName(): void + { + $this->assertSame('Example', $this->interface()->name()); + } + + public function testHasNamespacedName(): void + { + $this->assertSame('example\Example', $this->interface()->namespacedName()); + } + + public function testHasNamespace(): void + { + $this->assertSame('example', $this->interface()->namespace()); + } + + public function testHasStartLine(): void + { + $this->assertSame(1, $this->interface()->startLine()); + } + + public function testHasEndLine(): void + { + $this->assertSame(2, $this->interface()->endLine()); + } + + public function testMayHaveParentInterfaces(): void + { + $interfaces = ['example\AnInterface']; + + $this->assertSame($interfaces, $this->interface($interfaces)->parentInterfaces()); + } + + /** + * @param list $parentInterfaces + */ + private function interface(array $parentInterfaces = []): Interface_ + { + return new Interface_( + 'Example', + 'example\Example', + 'example', + 1, + 2, + $parentInterfaces, + ); + } +} diff --git a/tests/tests/StaticAnalysis/Value/LinesOfCodeTest.php b/tests/tests/StaticAnalysis/Value/LinesOfCodeTest.php new file mode 100644 index 000000000..f3eedb16a --- /dev/null +++ b/tests/tests/StaticAnalysis/Value/LinesOfCodeTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(LinesOfCode::class)] +#[Small] +final class LinesOfCodeTest extends TestCase +{ + public function testHasLinesOfCode(): void + { + $this->assertSame(1, $this->linesOfCode()->linesOfCode()); + } + + public function testHasCommentLinesOfCode(): void + { + $this->assertSame(2, $this->linesOfCode()->commentLinesOfCode()); + } + + public function testHasNonCommentLinesOfCode(): void + { + $this->assertSame(3, $this->linesOfCode()->nonCommentLinesOfCode()); + } + + private function linesOfCode(): LinesOfCode + { + return new LinesOfCode(1, 2, 3); + } +} diff --git a/tests/tests/StaticAnalysis/Value/MethodTest.php b/tests/tests/StaticAnalysis/Value/MethodTest.php new file mode 100644 index 000000000..dd96cc9d6 --- /dev/null +++ b/tests/tests/StaticAnalysis/Value/MethodTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Method::class)] +#[Small] +final class MethodTest extends TestCase +{ + public function testHasName(): void + { + $this->assertSame('example', $this->method()->name()); + } + + public function testHasStartLine(): void + { + $this->assertSame(1, $this->method()->startLine()); + } + + public function testHasEndLine(): void + { + $this->assertSame(2, $this->method()->endLine()); + } + + public function testHasSignature(): void + { + $this->assertSame('the-signature', $this->method()->signature()); + } + + public function testHasVisibility(): void + { + $this->assertSame(Visibility::Public, $this->method()->visibility()); + } + + public function testHasCyclomaticComplexity(): void + { + $this->assertSame(3, $this->method()->cyclomaticComplexity()); + } + + private function method(): Method + { + return new Method( + 'example', + 1, + 2, + 'the-signature', + Visibility::Public, + 3, + ); + } +} diff --git a/tests/tests/StaticAnalysis/Value/TraitTest.php b/tests/tests/StaticAnalysis/Value/TraitTest.php new file mode 100644 index 000000000..dbefcd208 --- /dev/null +++ b/tests/tests/StaticAnalysis/Value/TraitTest.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Trait_::class)] +#[Small] +final class TraitTest extends TestCase +{ + public function testHasName(): void + { + $this->assertSame('Example', $this->trait()->name()); + } + + public function testHasNamespacedName(): void + { + $this->assertSame('example\Example', $this->trait()->namespacedName()); + } + + public function testHasNamespace(): void + { + $this->assertSame('example', $this->trait()->namespace()); + } + + public function testHasFile(): void + { + $this->assertSame('file.php', $this->trait()->file()); + } + + public function testHasStartLine(): void + { + $this->assertSame(1, $this->trait()->startLine()); + } + + public function testHasEndLine(): void + { + $this->assertSame(2, $this->trait()->endLine()); + } + + public function testMayHaveTraits(): void + { + $traits = ['trait']; + + $this->assertSame($traits, $this->trait(traits: $traits)->traits()); + } + + public function testMayHaveMethods(): void + { + $methods = [ + 'method' => new Method( + 'method', + 0, + 0, + 'method(): void', + Visibility::Public, + 1, + ), + ]; + + $this->assertSame($methods, $this->trait(methods: $methods)->methods()); + } + + /** + * @param list $traits + * @param array $methods + */ + private function trait(array $traits = [], array $methods = []): Trait_ + { + return new Trait_( + 'Example', + 'example\Example', + 'example', + 'file.php', + 1, + 2, + $traits, + $methods, + ); + } +} diff --git a/tests/tests/StaticAnalysis/Visitor/CodeUnitFindingVisitorTest.php b/tests/tests/StaticAnalysis/Visitor/CodeUnitFindingVisitorTest.php new file mode 100644 index 000000000..9b5066c91 --- /dev/null +++ b/tests/tests/StaticAnalysis/Visitor/CodeUnitFindingVisitorTest.php @@ -0,0 +1,278 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function assert; +use function file_get_contents; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitor\NameResolver; +use PhpParser\NodeVisitor\ParentConnectingVisitor; +use PhpParser\ParserFactory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Ticket; +use PHPUnit\Framework\TestCase; +use SebastianBergmann\CodeCoverage\TestFixture\ClassThatUsesAnonymousClass; +use SebastianBergmann\CodeCoverage\TestFixture\ClassWithNameThatIsPartOfItsNamespacesName\ClassWithNameThatIsPartOfItsNamespacesName; + +#[CoversClass(CodeUnitFindingVisitor::class)] +final class CodeUnitFindingVisitorTest extends TestCase +{ + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/issues/786')] + public function testDoesNotFindAnonymousClass(): void + { + $codeUnitFindingVisitor = $this->findCodeUnits(__DIR__ . '/../../../_files/ClassThatUsesAnonymousClass.php'); + + $this->assertEmpty($codeUnitFindingVisitor->functions()); + $this->assertEmpty($codeUnitFindingVisitor->traits()); + + $classes = $codeUnitFindingVisitor->classes(); + + $this->assertCount(1, $classes); + $this->assertArrayHasKey(ClassThatUsesAnonymousClass::class, $classes); + + $class = $classes[ClassThatUsesAnonymousClass::class]; + + $this->assertSame('ClassThatUsesAnonymousClass', $class->name()); + $this->assertSame(ClassThatUsesAnonymousClass::class, $class->namespacedName()); + $this->assertSame('SebastianBergmann\CodeCoverage\TestFixture', $class->namespace()); + $this->assertSame(4, $class->startLine()); + $this->assertSame(17, $class->endLine()); + + $this->assertCount(1, $class->methods()); + $this->assertArrayHasKey('method', $class->methods()); + + $method = $class->methods()['method']; + + $this->assertSame('method', $method->name()); + $this->assertSame('method(): string', $method->signature()); + $this->assertSame(Visibility::Public, $method->visibility()); + $this->assertSame(6, $method->startLine()); + $this->assertSame(16, $method->endLine()); + $this->assertSame(1, $method->cyclomaticComplexity()); + } + + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/pull/797')] + public function testHandlesClassWithNameThatIsPartOfItsNamespacesName(): void + { + $codeUnitFindingVisitor = $this->findCodeUnits(__DIR__ . '/../../../_files/ClassWithNameThatIsPartOfItsNamespacesName.php'); + + $this->assertEmpty($codeUnitFindingVisitor->functions()); + $this->assertEmpty($codeUnitFindingVisitor->traits()); + + $classes = $codeUnitFindingVisitor->classes(); + + $this->assertCount(1, $classes); + $this->assertArrayHasKey(ClassWithNameThatIsPartOfItsNamespacesName::class, $classes); + + $class = $classes[ClassWithNameThatIsPartOfItsNamespacesName::class]; + + $this->assertSame('ClassWithNameThatIsPartOfItsNamespacesName', $class->name()); + $this->assertSame(ClassWithNameThatIsPartOfItsNamespacesName::class, $class->namespacedName()); + $this->assertSame('SebastianBergmann\CodeCoverage\TestFixture\ClassWithNameThatIsPartOfItsNamespacesName', $class->namespace()); + } + + public function testHandlesFunctionOrMethodWithUnionTypes(): void + { + $codeUnitFindingVisitor = $this->findCodeUnits(__DIR__ . '/../../../_files/FunctionWithUnionTypes.php'); + + $this->assertEmpty($codeUnitFindingVisitor->classes()); + $this->assertEmpty($codeUnitFindingVisitor->traits()); + + $functions = $codeUnitFindingVisitor->functions(); + + $this->assertCount(1, $functions); + + $this->assertSame( + 'functionWithUnionTypes(string|bool $x): string|bool', + $functions['SebastianBergmann\CodeCoverage\TestFixture\functionWithUnionTypes']->signature(), + ); + } + + public function testHandlesFunctionOrMethodWithIntersectionTypes(): void + { + $codeUnitFindingVisitor = $this->findCodeUnits(__DIR__ . '/../../../_files/FunctionWithIntersectionTypes.php'); + + $this->assertEmpty($codeUnitFindingVisitor->classes()); + $this->assertEmpty($codeUnitFindingVisitor->traits()); + + $functions = $codeUnitFindingVisitor->functions(); + + $this->assertCount(1, $functions); + + $this->assertSame( + 'functionWithIntersectionTypes(\SebastianBergmann\CodeCoverage\TestFixture\IntersectionPartOne&\SebastianBergmann\CodeCoverage\TestFixture\IntersectionPartTwo $x): \SebastianBergmann\CodeCoverage\TestFixture\IntersectionPartOne&\SebastianBergmann\CodeCoverage\TestFixture\IntersectionPartTwo', + $functions['SebastianBergmann\CodeCoverage\TestFixture\functionWithIntersectionTypes']->signature(), + ); + } + + public function testHandlesFunctionOrMethodWithDisjunctiveNormalFormTypes(): void + { + $codeUnitFindingVisitor = $this->findCodeUnits(__DIR__ . '/../../../_files/FunctionWithDisjunctiveNormalFormTypes.php'); + + $this->assertEmpty($codeUnitFindingVisitor->classes()); + $this->assertEmpty($codeUnitFindingVisitor->traits()); + + $functions = $codeUnitFindingVisitor->functions(); + + $this->assertCount(3, $functions); + + $this->assertSame( + 'f((\SebastianBergmann\CodeCoverage\TestFixture\A&\SebastianBergmann\CodeCoverage\TestFixture\B)|\SebastianBergmann\CodeCoverage\TestFixture\D $x): void', + $functions['SebastianBergmann\CodeCoverage\TestFixture\f']->signature(), + ); + + $this->assertSame( + 'g(\SebastianBergmann\CodeCoverage\TestFixture\C|(\SebastianBergmann\CodeCoverage\TestFixture\X&\SebastianBergmann\CodeCoverage\TestFixture\D)|null $x): void', + $functions['SebastianBergmann\CodeCoverage\TestFixture\g']->signature(), + ); + + $this->assertSame( + 'h((\SebastianBergmann\CodeCoverage\TestFixture\A&\SebastianBergmann\CodeCoverage\TestFixture\B&\SebastianBergmann\CodeCoverage\TestFixture\D)|int|null $x): void', + $functions['SebastianBergmann\CodeCoverage\TestFixture\h']->signature(), + ); + } + + public function testDetailsAboutExtendedClassesImplementedInterfacesAndUsedTraitsAreAvailable(): void + { + $codeUnitFindingVisitor = $this->findCodeUnits(__DIR__ . '/../../../_files/source_with_interfaces_classes_traits_functions.php'); + + $interfaces = $codeUnitFindingVisitor->interfaces(); + + $this->assertCount(3, $interfaces); + + $a = 'SebastianBergmann\CodeCoverage\StaticAnalysis\A'; + $b = 'SebastianBergmann\CodeCoverage\StaticAnalysis\B'; + $c = 'SebastianBergmann\CodeCoverage\StaticAnalysis\C'; + + $this->assertArrayHasKey($a, $interfaces); + $this->assertArrayHasKey($b, $interfaces); + $this->assertArrayHasKey($c, $interfaces); + + $this->assertSame('A', $interfaces[$a]->name()); + $this->assertSame($a, $interfaces[$a]->namespacedName()); + $this->assertSame('SebastianBergmann\CodeCoverage\StaticAnalysis', $interfaces[$a]->namespace()); + $this->assertSame(4, $interfaces[$a]->startLine()); + $this->assertSame(7, $interfaces[$a]->endLine()); + $this->assertSame([], $interfaces[$a]->parentInterfaces()); + + $this->assertSame('B', $interfaces[$b]->name()); + $this->assertSame($b, $interfaces[$b]->namespacedName()); + $this->assertSame('SebastianBergmann\CodeCoverage\StaticAnalysis', $interfaces[$b]->namespace()); + $this->assertSame(9, $interfaces[$b]->startLine()); + $this->assertSame(12, $interfaces[$b]->endLine()); + $this->assertSame([], $interfaces[$b]->parentInterfaces()); + + $this->assertSame('C', $interfaces[$c]->name()); + $this->assertSame($c, $interfaces[$c]->namespacedName()); + $this->assertSame('SebastianBergmann\CodeCoverage\StaticAnalysis', $interfaces[$c]->namespace()); + $this->assertSame(14, $interfaces[$c]->startLine()); + $this->assertSame(17, $interfaces[$c]->endLine()); + $this->assertSame([$a, $b], $interfaces[$c]->parentInterfaces()); + + $traits = $codeUnitFindingVisitor->traits(); + + $this->assertCount(1, $traits); + + $t = 'SebastianBergmann\CodeCoverage\StaticAnalysis\T'; + + $this->assertArrayHasKey($t, $traits); + + $this->assertSame('T', $traits[$t]->name()); + $this->assertSame($t, $traits[$t]->namespacedName()); + $this->assertSame('SebastianBergmann\CodeCoverage\StaticAnalysis', $traits[$t]->namespace()); + $this->assertSame(19, $traits[$t]->startLine()); + $this->assertSame(24, $traits[$t]->endLine()); + + $methods = $traits[$t]->methods(); + + $this->assertCount(1, $methods); + $this->assertArrayHasKey('four', $methods); + + $method = $methods['four']; + + $this->assertSame('four', $method->name()); + $this->assertSame(21, $method->startLine()); + $this->assertSame(23, $method->endLine()); + $this->assertSame(Visibility::Public, $method->visibility()); + $this->assertSame('four(): void', $method->signature()); + $this->assertSame(1, $method->cyclomaticComplexity()); + + $classes = $codeUnitFindingVisitor->classes(); + + $this->assertCount(2, $classes); + + $parentClass = 'SebastianBergmann\CodeCoverage\StaticAnalysis\ParentClass'; + $childClass = 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass'; + + $this->assertArrayHasKey($parentClass, $classes); + $this->assertArrayHasKey($childClass, $classes); + + $this->assertSame('ParentClass', $classes[$parentClass]->name()); + $this->assertSame($parentClass, $classes[$parentClass]->namespacedName()); + $this->assertSame('SebastianBergmann\CodeCoverage\StaticAnalysis', $classes[$parentClass]->namespace()); + $this->assertSame(26, $classes[$parentClass]->startLine()); + $this->assertSame(31, $classes[$parentClass]->endLine()); + $this->assertNull($classes[$parentClass]->parentClass()); + $this->assertSame([$c], $classes[$parentClass]->interfaces()); + $this->assertSame([], $classes[$parentClass]->traits()); + + $methods = $classes[$parentClass]->methods(); + + $this->assertCount(1, $methods); + $this->assertArrayHasKey('five', $methods); + + $method = $methods['five']; + + $this->assertSame('five', $method->name()); + $this->assertSame(28, $method->startLine()); + $this->assertSame(30, $method->endLine()); + $this->assertSame(Visibility::Public, $method->visibility()); + $this->assertSame('five(A $a, B $b): void', $method->signature()); + $this->assertSame(1, $method->cyclomaticComplexity()); + + $this->assertSame('ChildClass', $classes[$childClass]->name()); + $this->assertSame($childClass, $classes[$childClass]->namespacedName()); + $this->assertSame('SebastianBergmann\CodeCoverage\StaticAnalysis', $classes[$childClass]->namespace()); + $this->assertSame(33, $classes[$childClass]->startLine()); + $this->assertSame(52, $classes[$childClass]->endLine()); + $this->assertSame($parentClass, $classes[$childClass]->parentClass()); + $this->assertSame([$a, $b], $classes[$childClass]->interfaces()); + $this->assertSame([$t], $classes[$childClass]->traits()); + + $methods = $classes[$childClass]->methods(); + + $this->assertCount(4, $methods); + $this->assertArrayHasKey('one', $methods); + $this->assertArrayHasKey('two', $methods); + $this->assertArrayHasKey('three', $methods); + $this->assertArrayHasKey('six', $methods); + } + + private function findCodeUnits(string $filename): CodeUnitFindingVisitor + { + $nodes = (new ParserFactory)->createForHostVersion()->parse( + file_get_contents($filename), + ); + + assert($nodes !== null); + + $traverser = new NodeTraverser; + $codeUnitFindingVisitor = new CodeUnitFindingVisitor($filename); + + $traverser->addVisitor(new NameResolver); + $traverser->addVisitor(new ParentConnectingVisitor); + $traverser->addVisitor($codeUnitFindingVisitor); + + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($nodes); + + return $codeUnitFindingVisitor; + } +} diff --git a/tests/tests/StaticAnalysis/Visitor/ExecutableLinesFindingVisitorTest.php b/tests/tests/StaticAnalysis/Visitor/ExecutableLinesFindingVisitorTest.php new file mode 100644 index 000000000..0b21469c8 --- /dev/null +++ b/tests/tests/StaticAnalysis/Visitor/ExecutableLinesFindingVisitorTest.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function explode; +use function file_get_contents; +use function preg_match; +use function str_contains; +use PhpParser\NodeTraverser; +use PhpParser\ParserFactory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\RequiresPhp; +use PHPUnit\Framework\Attributes\Ticket; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ExecutableLinesFindingVisitor::class)] +final class ExecutableLinesFindingVisitorTest extends TestCase +{ + public function testExecutableLinesAreGroupedByBranch(): void + { + $this->doTestSelfDescribingAssert(TEST_FILES_PATH . 'source_for_branched_exec_lines.php'); + } + + #[RequiresPhp('>=8.1')] + public function testExecutableLinesAreGroupedByBranchPhp81(): void + { + $this->doTestSelfDescribingAssert(TEST_FILES_PATH . 'source_for_branched_exec_lines_php81.php'); + } + + #[RequiresPhp('>=8.2')] + public function testExecutableLinesAreGroupedByBranchPhp82(): void + { + $this->doTestSelfDescribingAssert(TEST_FILES_PATH . 'source_for_branched_exec_lines_php82.php'); + } + + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/issues/967')] + public function testMatchArmsAreProcessedCorrectly(): void + { + $source = file_get_contents(__DIR__ . '/../../../_files/source_match_expression.php'); + $parser = (new ParserFactory)->createForHostVersion(); + $nodes = $parser->parse($source); + $executableLinesFindingVisitor = new ExecutableLinesFindingVisitor($source); + + $traverser = new NodeTraverser; + $traverser->addVisitor($executableLinesFindingVisitor); + $traverser->traverse($nodes); + + $this->assertSame( + [ + 8 => 2, + 9 => 3, + 10 => 4, + 11 => 5, + 12 => 6, + 13 => 7, + 14 => 2, + ], + $executableLinesFindingVisitor->executableLinesGroupedByBranch(), + ); + } + + private function doTestSelfDescribingAssert(string $filename): void + { + $source = file_get_contents($filename); + $parser = (new ParserFactory)->createForHostVersion(); + $nodes = $parser->parse($source); + $executableLinesFindingVisitor = new ExecutableLinesFindingVisitor($source); + + $traverser = new NodeTraverser; + $traverser->addVisitor($executableLinesFindingVisitor); + $traverser->traverse($nodes); + + $executableLinesGroupedByBranch = $executableLinesFindingVisitor->executableLinesGroupedByBranch(); + + $linesFromSource = explode("\n", $source); + $expected = []; + + $branch = 0; + + foreach ($linesFromSource as $lineNumber => $line) { + if (str_contains($line, 'LINE_ADDED_IN_TEST')) { + $expected[1 + $lineNumber] = $branch; + + continue; + } + + if (1 !== preg_match('#^\s*[^/].+// (?[+-]?\d+)$#', $line, $matches)) { + continue; + } + + $branch += (int) ($matches['branchIncrement']); + $expected[1 + $lineNumber] = $branch; + } + + $this->assertEquals( + $expected, + $executableLinesGroupedByBranch, + ); + } +} diff --git a/tests/tests/Target/MapBuilderTest.php b/tests/tests/Target/MapBuilderTest.php new file mode 100644 index 000000000..0c03682b4 --- /dev/null +++ b/tests/tests/Target/MapBuilderTest.php @@ -0,0 +1,586 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use function array_merge; +use function range; +use function realpath; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\Ticket; +use PHPUnit\Framework\TestCase; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser; +use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingSourceAnalyser; +use SebastianBergmann\CodeCoverage\TestFixture\Target\ChildClass; +use SebastianBergmann\CodeCoverage\TestFixture\Target\GrandParentClass; +use SebastianBergmann\CodeCoverage\TestFixture\Target\Issue1066\BaseDummy; +use SebastianBergmann\CodeCoverage\TestFixture\Target\Issue1066\Dummy; +use SebastianBergmann\CodeCoverage\TestFixture\Target\Issue1066\Dummy2; +use SebastianBergmann\CodeCoverage\TestFixture\Target\Issue1066\DummyWithTrait; +use SebastianBergmann\CodeCoverage\TestFixture\Target\Issue1066\SomeTrait; +use SebastianBergmann\CodeCoverage\TestFixture\Target\ParentClass; +use SebastianBergmann\CodeCoverage\TestFixture\Target\T1; +use SebastianBergmann\CodeCoverage\TestFixture\Target\T2; +use SebastianBergmann\CodeCoverage\TestFixture\Target\TargetEnumeration; +use SebastianBergmann\CodeCoverage\TestFixture\Target\TraitOne; +use SebastianBergmann\CodeCoverage\TestFixture\Target\TraitTwo; + +/** + * @phpstan-import-type TargetMap from Mapper + */ +#[CoversClass(MapBuilder::class)] +#[Small] +final class MapBuilderTest extends TestCase +{ + /** + * @return non-empty-array}> + */ + public static function provider(): array + { + $file = realpath(__DIR__ . '/../../_files/source_with_interfaces_classes_traits_functions.php'); + $traitOne = realpath(__DIR__ . '/../../_files/Target/TraitOne.php'); + $traitTwo = realpath(__DIR__ . '/../../_files/Target/TraitTwo.php'); + $twoTraits = realpath(__DIR__ . '/../../_files/Target/two_traits.php'); + $enum = realpath(__DIR__ . '/../../_files/Target/TargetEnumeration.php'); + $grandParentClass = realpath(__DIR__ . '/../../_files/Target/GrandParentClass.php'); + $parentClass = realpath(__DIR__ . '/../../_files/Target/ParentClass.php'); + $childClass = realpath(__DIR__ . '/../../_files/Target/ChildClass.php'); + + return [ + 'generic' => [ + [ + 'namespaces' => [ + 'SebastianBergmann' => [ + $file => array_merge( + range(19, 24), + range(26, 31), + range(33, 52), + range(54, 56), + ), + ], + 'SebastianBergmann\\CodeCoverage' => [ + $file => array_merge( + range(19, 24), + range(26, 31), + range(33, 52), + range(54, 56), + ), + ], + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis' => [ + $file => array_merge( + range(19, 24), + range(26, 31), + range(33, 52), + range(54, 56), + ), + ], + ], + 'traits' => [ + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\T' => [ + $file => range(19, 24), + ], + ], + 'classes' => [ + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParentClass' => [ + $file => range(26, 31), + ], + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ChildClass' => [ + $file => array_merge( + range(33, 52), + range(19, 24), + range(26, 31), + ), + ], + ], + 'classesThatExtendClass' => [ + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParentClass' => [ + $file => range(33, 52), + ], + ], + 'classesThatImplementInterface' => [ + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\A' => [ + $file => range(33, 52), + ], + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\B' => [ + $file => range(33, 52), + ], + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\C' => [ + $file => range(26, 31), + ], + ], + 'methods' => [ + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\T::four' => [ + $file => range(21, 23), + ], + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParentClass::five' => [ + $file => range(28, 30), + ], + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ChildClass::six' => [ + $file => range(37, 39), + ], + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ChildClass::one' => [ + $file => range(41, 43), + ], + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ChildClass::two' => [ + $file => range(45, 47), + ], + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ChildClass::three' => [ + $file => range(49, 51), + ], + ], + 'functions' => [ + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\f' => [ + $file => range(54, 56), + ], + ], + 'reverseLookup' => [ + $file . ':21' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\T::four', + $file . ':22' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\T::four', + $file . ':23' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\T::four', + $file . ':28' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ParentClass::five', + $file . ':29' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ParentClass::five', + $file . ':30' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ParentClass::five', + $file . ':37' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::six', + $file . ':38' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::six', + $file . ':39' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::six', + $file . ':41' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::one', + $file . ':42' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::one', + $file . ':43' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::one', + $file . ':45' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::two', + $file . ':46' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::two', + $file . ':47' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::two', + $file . ':49' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::three', + $file . ':50' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::three', + $file . ':51' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\ChildClass::three', + $file . ':54' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\f', + $file . ':55' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\f', + $file . ':56' => 'SebastianBergmann\CodeCoverage\StaticAnalysis\f', + ], + ], + [$file], + ], + 'trait using trait declared in another file' => [ + [ + 'namespaces' => [ + 'SebastianBergmann' => [ + $traitOne => range(4, 9), + $traitTwo => range(4, 11), + ], + 'SebastianBergmann\\CodeCoverage' => [ + $traitOne => range(4, 9), + $traitTwo => range(4, 11), + ], + 'SebastianBergmann\\CodeCoverage\\TestFixture' => [ + $traitOne => range(4, 9), + $traitTwo => range(4, 11), + ], + 'SebastianBergmann\\CodeCoverage\\TestFixture\\Target' => [ + $traitOne => range(4, 9), + $traitTwo => range(4, 11), + ], + ], + 'traits' => [ + TraitOne::class => [ + $traitOne => range(4, 9), + ], + TraitTwo::class => [ + $traitTwo => range(4, 11), + $traitOne => range(4, 9), + ], + ], + 'classes' => [ + ], + 'classesThatExtendClass' => [ + ], + 'classesThatImplementInterface' => [ + ], + 'methods' => [ + TraitOne::class . '::one' => [ + $traitOne => range(6, 8), + ], + TraitTwo::class . '::two' => [ + $traitTwo => range(8, 10), + ], + ], + 'functions' => [ + ], + 'reverseLookup' => [ + $traitOne . ':6' => TraitOne::class . '::one', + $traitOne . ':7' => TraitOne::class . '::one', + $traitOne . ':8' => TraitOne::class . '::one', + $traitTwo . ':8' => TraitTwo::class . '::two', + $traitTwo . ':9' => TraitTwo::class . '::two', + $traitTwo . ':10' => TraitTwo::class . '::two', + ], + ], + [ + $traitOne, + $traitTwo, + ], + ], + 'trait using trait declared in same file' => [ + [ + 'namespaces' => [ + 'SebastianBergmann' => [ + $twoTraits => array_merge( + range(4, 9), + range(11, 18), + ), + ], + 'SebastianBergmann\\CodeCoverage' => [ + $twoTraits => array_merge( + range(4, 9), + range(11, 18), + ), + ], + 'SebastianBergmann\\CodeCoverage\\TestFixture' => [ + $twoTraits => array_merge( + range(4, 9), + range(11, 18), + ), + ], + 'SebastianBergmann\\CodeCoverage\\TestFixture\\Target' => [ + $twoTraits => array_merge( + range(4, 9), + range(11, 18), + ), + ], + ], + 'traits' => [ + T1::class => [ + $twoTraits => range(4, 9), + ], + T2::class => [ + $twoTraits => array_merge( + range(11, 18), + range(4, 9), + ), + ], + ], + 'classes' => [ + ], + 'classesThatExtendClass' => [ + ], + 'classesThatImplementInterface' => [ + ], + 'methods' => [ + T1::class . '::one' => [ + $twoTraits => range(6, 8), + ], + T2::class . '::two' => [ + $twoTraits => range(15, 17), + ], + ], + 'functions' => [ + ], + 'reverseLookup' => [ + $twoTraits . ':6' => T1::class . '::one', + $twoTraits . ':7' => T1::class . '::one', + $twoTraits . ':8' => T1::class . '::one', + $twoTraits . ':15' => T2::class . '::two', + $twoTraits . ':16' => T2::class . '::two', + $twoTraits . ':17' => T2::class . '::two', + ], + ], + [ + $twoTraits, + ], + ], + 'enumeration' => [ + [ + 'namespaces' => [ + 'SebastianBergmann' => [ + $enum => range(4, 8), + ], + 'SebastianBergmann\\CodeCoverage' => [ + $enum => range(4, 8), + ], + 'SebastianBergmann\\CodeCoverage\\TestFixture' => [ + $enum => range(4, 8), + ], + 'SebastianBergmann\\CodeCoverage\\TestFixture\\Target' => [ + $enum => range(4, 8), + ], + ], + 'traits' => [ + ], + 'classes' => [ + TargetEnumeration::class => [ + $enum => range(4, 8), + ], + ], + 'classesThatExtendClass' => [ + ], + 'classesThatImplementInterface' => [ + ], + 'methods' => [ + ], + 'functions' => [ + ], + 'reverseLookup' => [ + ], + ], + [ + $enum, + ], + ], + 'class with parent classes' => [ + [ + 'namespaces' => [ + 'SebastianBergmann' => [ + $grandParentClass => range(4, 9), + $parentClass => range(4, 9), + $childClass => range(4, 9), + ], + 'SebastianBergmann\CodeCoverage' => [ + $grandParentClass => range(4, 9), + $parentClass => range(4, 9), + $childClass => range(4, 9), + ], + 'SebastianBergmann\CodeCoverage\TestFixture' => [ + $grandParentClass => range(4, 9), + $parentClass => range(4, 9), + $childClass => range(4, 9), + ], + 'SebastianBergmann\CodeCoverage\TestFixture\Target' => [ + $grandParentClass => range(4, 9), + $parentClass => range(4, 9), + $childClass => range(4, 9), + ], + ], + 'traits' => [ + ], + 'classes' => [ + GrandParentClass::class => [ + $grandParentClass => range(4, 9), + ], + ParentClass::class => [ + $parentClass => range(4, 9), + $grandParentClass => range(4, 9), + ], + ChildClass::class => [ + $childClass => range(4, 9), + $parentClass => range(4, 9), + $grandParentClass => range(4, 9), + ], + ], + 'classesThatExtendClass' => [ + GrandParentClass::class => [ + $parentClass => range(4, 9), + $childClass => range(4, 9), + ], + ParentClass::class => [ + $childClass => range(4, 9), + ], + ], + 'classesThatImplementInterface' => [ + ], + 'methods' => [ + GrandParentClass::class . '::one' => [ + $grandParentClass => range(6, 8), + ], + ParentClass::class . '::two' => [ + $parentClass => range(6, 8), + ], + ChildClass::class . '::three' => [ + $childClass => range(6, 8), + ], + ], + 'functions' => [ + ], + 'reverseLookup' => [ + $grandParentClass . ':6' => 'SebastianBergmann\CodeCoverage\TestFixture\Target\GrandParentClass::one', + $grandParentClass . ':7' => 'SebastianBergmann\CodeCoverage\TestFixture\Target\GrandParentClass::one', + $grandParentClass . ':8' => 'SebastianBergmann\CodeCoverage\TestFixture\Target\GrandParentClass::one', + $parentClass . ':6' => 'SebastianBergmann\CodeCoverage\TestFixture\Target\ParentClass::two', + $parentClass . ':7' => 'SebastianBergmann\CodeCoverage\TestFixture\Target\ParentClass::two', + $parentClass . ':8' => 'SebastianBergmann\CodeCoverage\TestFixture\Target\ParentClass::two', + $childClass . ':6' => 'SebastianBergmann\CodeCoverage\TestFixture\Target\ChildClass::three', + $childClass . ':7' => 'SebastianBergmann\CodeCoverage\TestFixture\Target\ChildClass::three', + $childClass . ':8' => 'SebastianBergmann\CodeCoverage\TestFixture\Target\ChildClass::three', + ], + ], + [ + $grandParentClass, + $parentClass, + $childClass, + ], + ], + ]; + } + + #[DataProvider('provider')] + public function testBuildsMap(array $expected, array $files): void + { + $this->assertSame($expected, $this->map($files)); + } + + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/issues/1066')] + public function testIssue1066(): void + { + $baseDummy = realpath(__DIR__ . '/../../_files/Target/regression/1066/BaseDummy.php'); + $dummy = realpath(__DIR__ . '/../../_files/Target/regression/1066/Dummy.php'); + $dummy2 = realpath(__DIR__ . '/../../_files/Target/regression/1066/Dummy2.php'); + $dummyWithTrait = realpath(__DIR__ . '/../../_files/Target/regression/1066/DummyWithTrait.php'); + $someTrait = realpath(__DIR__ . '/../../_files/Target/regression/1066/SomeTrait.php'); + + $this->assertSame( + [ + 'namespaces' => [ + 'SebastianBergmann' => [ + $someTrait => range(4, 6), + $baseDummy => range(4, 6), + $dummy => range(4, 15), + $dummy2 => range(4, 15), + $dummyWithTrait => range(4, 17), + ], + 'SebastianBergmann\CodeCoverage' => [ + $someTrait => range(4, 6), + $baseDummy => range(4, 6), + $dummy => range(4, 15), + $dummy2 => range(4, 15), + $dummyWithTrait => range(4, 17), + ], + 'SebastianBergmann\CodeCoverage\TestFixture' => [ + $someTrait => range(4, 6), + $baseDummy => range(4, 6), + $dummy => range(4, 15), + $dummy2 => range(4, 15), + $dummyWithTrait => range(4, 17), + ], + 'SebastianBergmann\CodeCoverage\TestFixture\Target' => [ + $someTrait => range(4, 6), + $baseDummy => range(4, 6), + $dummy => range(4, 15), + $dummy2 => range(4, 15), + $dummyWithTrait => range(4, 17), + ], + 'SebastianBergmann\CodeCoverage\TestFixture\Target\Issue1066' => [ + $someTrait => range(4, 6), + $baseDummy => range(4, 6), + $dummy => range(4, 15), + $dummy2 => range(4, 15), + $dummyWithTrait => range(4, 17), + ], + ], + 'traits' => [ + SomeTrait::class => [ + $someTrait => range(4, 6), + ], + ], + 'classes' => [ + BaseDummy::class => [ + $baseDummy => range(4, 6), + ], + Dummy::class => [ + $dummy => range(4, 15), + $baseDummy => range(4, 6), + ], + Dummy2::class => [ + $dummy2 => range(4, 15), + ], + DummyWithTrait::class => [ + $dummyWithTrait => range(4, 17), + $someTrait => range(4, 6), + $baseDummy => range(4, 6), + ], + ], + 'classesThatExtendClass' => [ + BaseDummy::class => [ + $dummy => range(4, 15), + $dummyWithTrait => range(4, 17), + ], + ], + 'classesThatImplementInterface' => [ + ], + 'methods' => [ + Dummy::class . '::method1' => [ + $dummy => range(6, 9), + ], + Dummy::class . '::method2' => [ + $dummy => range(11, 14), + ], + Dummy2::class . '::method1' => [ + $dummy2 => range(6, 9), + ], + Dummy2::class . '::method2' => [ + $dummy2 => range(11, 14), + ], + DummyWithTrait::class . '::method1' => [ + $dummyWithTrait => range(8, 11), + ], + DummyWithTrait::class . '::method2' => [ + $dummyWithTrait => range(13, 16), + ], + ], + 'functions' => [ + ], + 'reverseLookup' => [ + $dummy . ':6' => Dummy::class . '::method1', + $dummy . ':7' => Dummy::class . '::method1', + $dummy . ':8' => Dummy::class . '::method1', + $dummy . ':9' => Dummy::class . '::method1', + $dummy . ':11' => Dummy::class . '::method2', + $dummy . ':12' => Dummy::class . '::method2', + $dummy . ':13' => Dummy::class . '::method2', + $dummy . ':14' => Dummy::class . '::method2', + $dummy2 . ':6' => Dummy2::class . '::method1', + $dummy2 . ':7' => Dummy2::class . '::method1', + $dummy2 . ':8' => Dummy2::class . '::method1', + $dummy2 . ':9' => Dummy2::class . '::method1', + $dummy2 . ':11' => Dummy2::class . '::method2', + $dummy2 . ':12' => Dummy2::class . '::method2', + $dummy2 . ':13' => Dummy2::class . '::method2', + $dummy2 . ':14' => Dummy2::class . '::method2', + $dummyWithTrait . ':8' => DummyWithTrait::class . '::method1', + $dummyWithTrait . ':9' => DummyWithTrait::class . '::method1', + $dummyWithTrait . ':10' => DummyWithTrait::class . '::method1', + $dummyWithTrait . ':11' => DummyWithTrait::class . '::method1', + $dummyWithTrait . ':13' => DummyWithTrait::class . '::method2', + $dummyWithTrait . ':14' => DummyWithTrait::class . '::method2', + $dummyWithTrait . ':15' => DummyWithTrait::class . '::method2', + $dummyWithTrait . ':16' => DummyWithTrait::class . '::method2', + ], + ], + $this->map( + [ + $baseDummy, + $dummy, + $dummy2, + $dummyWithTrait, + $someTrait, + ], + ), + ); + } + + /** + * @param list $files + * + * @return TargetMap + */ + private function map(array $files): array + { + $filter = new Filter; + + $filter->includeFiles($files); + + return (new MapBuilder)->build( + $filter, + new FileAnalyser( + new ParsingSourceAnalyser, + false, + false, + ), + ); + } +} diff --git a/tests/tests/Target/MapperTest.php b/tests/tests/Target/MapperTest.php new file mode 100644 index 000000000..421a0a786 --- /dev/null +++ b/tests/tests/Target/MapperTest.php @@ -0,0 +1,367 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use function array_keys; +use function array_merge; +use function range; +use function realpath; +use function strtolower; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\Attributes\Ticket; +use PHPUnit\Framework\TestCase; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser; +use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingSourceAnalyser; +use SebastianBergmann\CodeCoverage\TestFixture\Target\GrandParentClass; +use SebastianBergmann\CodeCoverage\TestFixture\Target\Issue1066\DummyWithTrait; +use SebastianBergmann\CodeCoverage\TestFixture\Target\TargetClass; +use SebastianBergmann\CodeCoverage\TestFixture\Target\TargetEnumeration; +use SebastianBergmann\CodeCoverage\TestFixture\Target\TraitOne; + +/** + * @phpstan-import-type TargetMap from Mapper + */ +#[CoversClass(Mapper::class)] +#[CoversClass(InvalidCodeCoverageTargetException::class)] +#[Small] +final class MapperTest extends TestCase +{ + /** + * @return non-empty-array>, 1: TargetCollection}> + */ + public static function provider(): array + { + $file = realpath(__DIR__ . '/../../_files/source_with_interfaces_classes_traits_functions.php'); + + return [ + 'class' => [ + [ + $file => array_merge( + range(33, 52), + range(19, 24), + range(26, 31), + ), + ], + TargetCollection::fromArray( + [ + Target::forClass('SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ChildClass'), + ], + ), + ], + + 'class (which uses traits)' => [ + [ + realpath(__DIR__ . '/../../_files/Target/ClassUsingTraitUsingTrait.php') => range(4, 11), + realpath(__DIR__ . '/../../_files/Target/TraitTwo.php') => range(4, 11), + realpath(__DIR__ . '/../../_files/Target/TraitOne.php') => range(4, 9), + ], + TargetCollection::fromArray( + [ + Target::forClass('SebastianBergmann\\CodeCoverage\\TestFixture\\Target\\ClassUsingTraitUsingTrait'), + ], + ), + ], + + 'classes that extend class (parent and child)' => [ + [ + $file => range(33, 52), + ], + TargetCollection::fromArray( + [ + Target::forClassesThatExtendClass('SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParentClass'), + ], + ), + ], + + 'classes that extend class (grand parent, parent, and child)' => [ + [ + realpath(__DIR__ . '/../../_files/Target/GrandParentClass.php') => range(4, 9), + realpath(__DIR__ . '/../../_files/Target/ParentClass.php') => range(4, 9), + realpath(__DIR__ . '/../../_files/Target/ChildClass.php') => range(4, 9), + ], + TargetCollection::fromArray( + [ + Target::forClass(GrandParentClass::class), + Target::forClassesThatExtendClass(GrandParentClass::class), + ], + ), + ], + + 'classes that implement interface' => [ + [ + $file => range(26, 31), + ], + TargetCollection::fromArray( + [ + Target::forClassesThatImplementInterface('SebastianBergmann\\CodeCoverage\\StaticAnalysis\\C'), + ], + ), + ], + + 'trait' => [ + [ + realpath(__DIR__ . '/../../_files/Target/TraitOne.php') => range(4, 9), + ], + TargetCollection::fromArray( + [ + Target::forTrait(TraitOne::class), + ], + ), + ], + + 'function' => [ + [ + $file => range(54, 56), + ], + TargetCollection::fromArray( + [ + Target::forFunction('SebastianBergmann\\CodeCoverage\\StaticAnalysis\\f'), + ], + ), + ], + + 'method of class' => [ + [ + $file => range(37, 39), + ], + TargetCollection::fromArray( + [ + Target::forMethod('SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ChildClass', 'six'), + ], + ), + ], + + 'methods of class' => [ + [ + $file => array_merge(range(37, 39), range(41, 43)), + ], + TargetCollection::fromArray( + [ + Target::forMethod('SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ChildClass', 'six'), + Target::forMethod('SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ChildClass', 'one'), + ], + ), + ], + + 'method of trait' => [ + [ + $file => range(21, 23), + ], + TargetCollection::fromArray( + [ + Target::forMethod('SebastianBergmann\\CodeCoverage\\StaticAnalysis\\T', 'four'), + ], + ), + ], + + 'namespace' => [ + [ + $file => array_merge( + range(19, 24), + range(26, 31), + range(33, 52), + range(54, 56), + ), + ], + TargetCollection::fromArray( + [ + Target::forNamespace('SebastianBergmann\\CodeCoverage\\StaticAnalysis'), + ], + ), + ], + + 'enumeration' => [ + [ + realpath(__DIR__ . '/../../_files/Target/TargetEnumeration.php') => range(4, 8), + ], + TargetCollection::fromArray( + [ + Target::forClass(TargetEnumeration::class), + ], + ), + ], + ]; + } + + /** + * @return non-empty-array + */ + public static function invalidProvider(): array + { + return [ + 'class' => [ + 'Class DoesNotExist is not a valid target for code coverage', + TargetCollection::fromArray( + [ + /** @phpstan-ignore argument.type */ + Target::forClass('DoesNotExist'), + ], + ), + ], + 'classes that extend class' => [ + 'Classes that extend class DoesNotExist is not a valid target for code coverage', + TargetCollection::fromArray( + [ + /** @phpstan-ignore argument.type */ + Target::forClassesThatExtendClass('DoesNotExist'), + ], + ), + ], + 'classes that implement interface' => [ + 'Classes that implement interface DoesNotExist is not a valid target for code coverage', + TargetCollection::fromArray( + [ + /** @phpstan-ignore argument.type */ + Target::forClassesThatImplementInterface('DoesNotExist'), + ], + ), + ], + 'function' => [ + 'Function does_not_exist is not a valid target for code coverage', + TargetCollection::fromArray( + [ + Target::forFunction('does_not_exist'), + ], + ), + ], + 'method' => [ + 'Method DoesNotExist::doesNotExist is not a valid target for code coverage', + TargetCollection::fromArray( + [ + /** @phpstan-ignore argument.type */ + Target::forMethod('DoesNotExist', 'doesNotExist'), + ], + ), + ], + 'namespace' => [ + 'Namespace DoesNotExist is not a valid target for code coverage', + TargetCollection::fromArray( + [ + Target::forNamespace('DoesNotExist'), + ], + ), + ], + ]; + } + + /** + * @param array> $expected + */ + #[DataProvider('provider')] + #[TestDox('Maps TargetCollection with $_dataName to source locations')] + public function testMapsTargetValueObjectsToSourceLocations(array $expected, TargetCollection $targets): void + { + $this->assertSame( + $expected, + $this->mapper(array_keys($expected))->mapTargets($targets), + ); + } + + #[DataProvider('invalidProvider')] + #[TestDox('Cannot map $_dataName that does not exist to source location')] + public function testCannotMapInvalidTargets(string $exceptionMessage, TargetCollection $targets): void + { + $this->expectException(InvalidCodeCoverageTargetException::class); + $this->expectExceptionMessage($exceptionMessage); + + $this->mapper([])->mapTargets($targets); + } + + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/issues/1064')] + public function testCodeUnitTargetingIsCaseInsensitive(): void + { + $path = realpath(__DIR__ . '/../../_files/Target/TargetClass.php'); + $mapper = $this->mapper([$path]); + + $this->assertSame( + [ + $path => range(4, 9), + ], + $mapper->mapTarget( + Target::forClass(strtolower(TargetClass::class)), + ), + ); + } + + #[Ticket('https://github.com/sebastianbergmann/php-code-coverage/issues/1066')] + public function testIssue1066(): void + { + $baseDummy = realpath(__DIR__ . '/../../_files/Target/regression/1066/BaseDummy.php'); + $dummy = realpath(__DIR__ . '/../../_files/Target/regression/1066/Dummy.php'); + $dummy2 = realpath(__DIR__ . '/../../_files/Target/regression/1066/Dummy2.php'); + $dummyWithTrait = realpath(__DIR__ . '/../../_files/Target/regression/1066/DummyWithTrait.php'); + $someTrait = realpath(__DIR__ . '/../../_files/Target/regression/1066/SomeTrait.php'); + + $mapper = $this->mapper( + [ + $baseDummy, + $dummy, + $dummy2, + $dummyWithTrait, + $someTrait, + ], + ); + + $this->assertSame( + [ + $dummyWithTrait => range(8, 11), + ], + $mapper->mapTarget( + Target::forMethod(DummyWithTrait::class, 'method1'), + ), + ); + + $this->assertSame( + DummyWithTrait::class . '::method1', + $mapper->lookup($dummyWithTrait, 10), + ); + } + + public function testLineOfCodeInGlobalScopeDoesNotBelongToCodeUnit(): void + { + $file = realpath(__DIR__ . '/../../_files/source_without_ignore.php'); + $mapper = $this->mapper([$file]); + + $this->assertSame($file . ':2', $mapper->lookup($file, 2)); + } + + /** + * @param list $files + */ + private function mapper(array $files): Mapper + { + return new Mapper($this->map($files)); + } + + /** + * @param list $files + * + * @return TargetMap + */ + private function map(array $files): array + { + $filter = new Filter; + + $filter->includeFiles($files); + + return (new MapBuilder)->build( + $filter, + new FileAnalyser( + new ParsingSourceAnalyser, + false, + false, + ), + ); + } +} diff --git a/tests/tests/Target/TargetCollectionTest.php b/tests/tests/Target/TargetCollectionTest.php new file mode 100644 index 000000000..101494074 --- /dev/null +++ b/tests/tests/Target/TargetCollectionTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; +use SebastianBergmann\CodeCoverage\TestFixture\Target\TargetClass; + +#[CoversClass(TargetCollection::class)] +#[CoversClass(TargetCollectionIterator::class)] +#[UsesClass(Class_::class)] +#[Small] +final class TargetCollectionTest extends TestCase +{ + public function testCanBeEmpty(): void + { + $collection = TargetCollection::fromArray([]); + + $this->assertCount(0, $collection); + $this->assertTrue($collection->isEmpty()); + $this->assertFalse($collection->isNotEmpty()); + } + + public function testCanBeCreatedFromArray(): void + { + $target = Target::forClass(TargetClass::class); + $collection = TargetCollection::fromArray([$target]); + + $this->assertContains($target, $collection); + } + + public function testIsCountable(): void + { + $target = Target::forClass(TargetClass::class); + $collection = TargetCollection::fromArray([$target]); + + $this->assertCount(1, $collection); + $this->assertFalse($collection->isEmpty()); + $this->assertTrue($collection->isNotEmpty()); + } + + public function testIsIterable(): void + { + $target = Target::forClass(TargetClass::class); + $collection = TargetCollection::fromArray([$target]); + + foreach ($collection as $key => $value) { + $this->assertSame(0, $key); + $this->assertSame($target, $value); + } + } +} diff --git a/tests/tests/Target/TargetCollectionValidatorTest.php b/tests/tests/Target/TargetCollectionValidatorTest.php new file mode 100644 index 000000000..659c9fed0 --- /dev/null +++ b/tests/tests/Target/TargetCollectionValidatorTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use function assert; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser; +use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingSourceAnalyser; +use SebastianBergmann\CodeCoverage\TestFixture\Target\TargetClass; + +/** + * @phpstan-import-type TargetMap from Mapper + */ +#[CoversClass(TargetCollectionValidator::class)] +#[CoversClass(InvalidCodeCoverageTargetException::class)] +#[Small] +final class TargetCollectionValidatorTest extends TestCase +{ + public function test_TargetCollection_is_valid_when_all_targets_can_be_mapped(): void + { + $targets = TargetCollection::fromArray([Target::forClass(TargetClass::class)]); + $mapper = $this->mapper([__DIR__ . '/../../_files/Target/TargetClass.php']); + $validator = new TargetCollectionValidator; + + $result = $validator->validate($mapper, $targets); + + $this->assertTrue($result->isSuccess()); + } + + public function test_TargetCollection_is_invalid_when_target_cannot_be_mapped(): void + { + $targets = TargetCollection::fromArray([Target::forClass(TargetClass::class)]); + $mapper = $this->mapper([]); + $validator = new TargetCollectionValidator; + + $result = $validator->validate($mapper, $targets); + + $this->assertTrue($result->isFailure()); + + assert($result instanceof ValidationFailure); + + $this->assertSame( + 'Class SebastianBergmann\CodeCoverage\TestFixture\Target\TargetClass is not a valid target for code coverage', + $result->message(), + ); + } + + /** + * @param list $files + */ + private function mapper(array $files): Mapper + { + return new Mapper($this->map($files)); + } + + /** + * @param list $files + * + * @return TargetMap + */ + private function map(array $files): array + { + $filter = new Filter; + + $filter->includeFiles($files); + + return (new MapBuilder)->build( + $filter, + new FileAnalyser( + new ParsingSourceAnalyser, + false, + false, + ), + ); + } +} diff --git a/tests/tests/Target/TargetTest.php b/tests/tests/Target/TargetTest.php new file mode 100644 index 000000000..526670095 --- /dev/null +++ b/tests/tests/Target/TargetTest.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; +use SebastianBergmann\CodeCoverage\TestFixture\Target\TargetClass; +use SebastianBergmann\CodeCoverage\TestFixture\Target\TargetInterface; +use SebastianBergmann\CodeCoverage\TestFixture\Target\TraitOne; + +#[CoversClass(Target::class)] +#[CoversClass(Class_::class)] +#[CoversClass(ClassesThatExtendClass::class)] +#[CoversClass(ClassesThatImplementInterface::class)] +#[CoversClass(Function_::class)] +#[CoversClass(Method::class)] +#[CoversClass(Namespace_::class)] +#[CoversClass(Trait_::class)] +#[Small] +final class TargetTest extends TestCase +{ + public function testCanBeClass(): void + { + $className = TargetClass::class; + + $target = Target::forClass($className); + + $this->assertTrue($target->isClass()); + $this->assertFalse($target->isClassesThatExtendClass()); + $this->assertFalse($target->isClassesThatImplementInterface()); + $this->assertFalse($target->isFunction()); + $this->assertFalse($target->isMethod()); + $this->assertFalse($target->isNamespace()); + $this->assertFalse($target->isTrait()); + + $this->assertSame($className, $target->className()); + $this->assertSame('classes', $target->key()); + $this->assertSame($className, $target->target()); + $this->assertSame('Class ' . $className, $target->description()); + } + + public function testCanBeClassesThatExtendClass(): void + { + $className = TargetClass::class; + + $target = Target::forClassesThatExtendClass($className); + + $this->assertFalse($target->isClass()); + $this->assertTrue($target->isClassesThatExtendClass()); + $this->assertFalse($target->isClassesThatImplementInterface()); + $this->assertFalse($target->isFunction()); + $this->assertFalse($target->isMethod()); + $this->assertFalse($target->isNamespace()); + $this->assertFalse($target->isTrait()); + + $this->assertSame($className, $target->className()); + $this->assertSame('classesThatExtendClass', $target->key()); + $this->assertSame($className, $target->target()); + $this->assertSame('Classes that extend class ' . $className, $target->description()); + } + + public function testCanBeClassesThatImplementInterface(): void + { + $interfaceName = TargetInterface::class; + + $target = Target::forClassesThatImplementInterface($interfaceName); + + $this->assertFalse($target->isClass()); + $this->assertFalse($target->isClassesThatExtendClass()); + $this->assertTrue($target->isClassesThatImplementInterface()); + $this->assertFalse($target->isFunction()); + $this->assertFalse($target->isMethod()); + $this->assertFalse($target->isNamespace()); + $this->assertFalse($target->isTrait()); + + $this->assertSame($interfaceName, $target->interfaceName()); + $this->assertSame('classesThatImplementInterface', $target->key()); + $this->assertSame($interfaceName, $target->target()); + $this->assertSame('Classes that implement interface ' . $interfaceName, $target->description()); + } + + public function testCanBeFunction(): void + { + $functionName = 'function'; + + $target = Target::forFunction($functionName); + + $this->assertFalse($target->isClass()); + $this->assertFalse($target->isClassesThatExtendClass()); + $this->assertFalse($target->isClassesThatImplementInterface()); + $this->assertTrue($target->isFunction()); + $this->assertFalse($target->isMethod()); + $this->assertFalse($target->isNamespace()); + $this->assertFalse($target->isTrait()); + + $this->assertSame($functionName, $target->functionName()); + $this->assertSame('functions', $target->key()); + $this->assertSame($functionName, $target->target()); + $this->assertSame('Function ' . $functionName, $target->description()); + } + + public function testCanBeMethod(): void + { + $className = TargetClass::class; + $methodName = 'method'; + + $target = Target::forMethod($className, $methodName); + + $this->assertFalse($target->isClass()); + $this->assertFalse($target->isClassesThatExtendClass()); + $this->assertFalse($target->isClassesThatImplementInterface()); + $this->assertFalse($target->isFunction()); + $this->assertTrue($target->isMethod()); + $this->assertFalse($target->isNamespace()); + $this->assertFalse($target->isTrait()); + + $this->assertSame($className, $target->className()); + $this->assertSame($methodName, $target->methodName()); + $this->assertSame('methods', $target->key()); + $this->assertSame($className . '::' . $methodName, $target->target()); + $this->assertSame('Method ' . $className . '::' . $methodName, $target->description()); + } + + public function testCanBeNamespace(): void + { + $namespace = 'namespace'; + + $target = Target::forNamespace($namespace); + + $this->assertFalse($target->isClass()); + $this->assertFalse($target->isClassesThatExtendClass()); + $this->assertFalse($target->isClassesThatImplementInterface()); + $this->assertFalse($target->isFunction()); + $this->assertFalse($target->isMethod()); + $this->assertTrue($target->isNamespace()); + $this->assertFalse($target->isTrait()); + + $this->assertSame($namespace, $target->namespace()); + $this->assertSame('namespaces', $target->key()); + $this->assertSame($namespace, $target->target()); + $this->assertSame('Namespace ' . $namespace, $target->description()); + } + + public function testCanBeTrait(): void + { + $traitName = TraitOne::class; + + $target = Target::forTrait($traitName); + + $this->assertTrue($target->isTrait()); + $this->assertFalse($target->isClass()); + $this->assertFalse($target->isClassesThatExtendClass()); + $this->assertFalse($target->isClassesThatImplementInterface()); + $this->assertFalse($target->isFunction()); + $this->assertFalse($target->isMethod()); + $this->assertFalse($target->isNamespace()); + + $this->assertSame($traitName, $target->traitName()); + $this->assertSame('traits', $target->key()); + $this->assertSame($traitName, $target->target()); + $this->assertSame('Trait ' . $traitName, $target->description()); + } +} diff --git a/tests/tests/Target/ValidationResultTest.php b/tests/tests/Target/ValidationResultTest.php new file mode 100644 index 000000000..6f427573e --- /dev/null +++ b/tests/tests/Target/ValidationResultTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\Target; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ValidationResult::class)] +#[CoversClassesThatExtendClass(ValidationResult::class)] +#[Small] +final class ValidationResultTest extends TestCase +{ + public function testCanBeSuccess(): void + { + $this->assertTrue(ValidationResult::success()->isSuccess()); + $this->assertFalse(ValidationResult::success()->isFailure()); + } + + public function testCanBeFailure(): void + { + $message = 'message'; + + $this->assertTrue(ValidationResult::failure($message)->isFailure()); + $this->assertFalse(ValidationResult::failure($message)->isSuccess()); + $this->assertSame($message, ValidationResult::failure($message)->message()); + } +} diff --git a/tests/tests/TestSizeTest.php b/tests/tests/TestSizeTest.php new file mode 100644 index 000000000..f6fecef0e --- /dev/null +++ b/tests/tests/TestSizeTest.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestSize; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(TestSize::class)] +#[CoversClassesThatExtendClass(TestSize::class)] +#[Small] +final class TestSizeTest extends TestCase +{ + public function testCanBeUnknown(): void + { + $size = TestSize::unknown(); + + $this->assertTrue($size->isUnknown()); + $this->assertFalse($size->isKnown()); + $this->assertFalse($size->isSmall()); + $this->assertFalse($size->isMedium()); + $this->assertFalse($size->isLarge()); + $this->assertSame('unknown', $size->asString()); + } + + public function testCanBeSmall(): void + { + $size = TestSize::small(); + + $this->assertFalse($size->isUnknown()); + $this->assertTrue($size->isKnown()); + $this->assertTrue($size->isSmall()); + $this->assertFalse($size->isMedium()); + $this->assertFalse($size->isLarge()); + $this->assertSame('small', $size->asString()); + } + + public function testCanBeMedium(): void + { + $size = TestSize::medium(); + + $this->assertFalse($size->isUnknown()); + $this->assertTrue($size->isKnown()); + $this->assertFalse($size->isSmall()); + $this->assertTrue($size->isMedium()); + $this->assertFalse($size->isLarge()); + $this->assertSame('medium', $size->asString()); + } + + public function testCanBeLarge(): void + { + $size = TestSize::large(); + + $this->assertFalse($size->isUnknown()); + $this->assertTrue($size->isKnown()); + $this->assertFalse($size->isSmall()); + $this->assertFalse($size->isMedium()); + $this->assertTrue($size->isLarge()); + $this->assertSame('large', $size->asString()); + } + + public function testCanBeCompared(): void + { + $this->assertFalse(TestSize::small()->isGreaterThan(TestSize::small())); + $this->assertFalse(TestSize::medium()->isGreaterThan(TestSize::large())); + $this->assertTrue(TestSize::medium()->isGreaterThan(TestSize::small())); + $this->assertFalse(TestSize::large()->isGreaterThan(TestSize::large())); + $this->assertTrue(TestSize::large()->isGreaterThan(TestSize::small())); + $this->assertTrue(TestSize::large()->isGreaterThan(TestSize::medium())); + } +} diff --git a/tests/tests/TestStatusTest.php b/tests/tests/TestStatusTest.php new file mode 100644 index 000000000..0c297e4d8 --- /dev/null +++ b/tests/tests/TestStatusTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Test\TestStatus; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(TestStatus::class)] +#[CoversClassesThatExtendClass(TestStatus::class)] +#[Small] +final class TestStatusTest extends TestCase +{ + public function testCanBeUnknown(): void + { + $status = TestStatus::unknown(); + + $this->assertTrue($status->isUnknown()); + $this->assertFalse($status->isKnown()); + $this->assertFalse($status->isSuccess()); + $this->assertFalse($status->isFailure()); + $this->assertSame('unknown', $status->asString()); + } + + public function testCanBeSuccess(): void + { + $status = TestStatus::success(); + + $this->assertFalse($status->isUnknown()); + $this->assertTrue($status->isKnown()); + $this->assertTrue($status->isSuccess()); + $this->assertFalse($status->isFailure()); + $this->assertSame('success', $status->asString()); + } + + public function testCanBeFailure(): void + { + $status = TestStatus::failure(); + + $this->assertFalse($status->isUnknown()); + $this->assertTrue($status->isKnown()); + $this->assertFalse($status->isSuccess()); + $this->assertTrue($status->isFailure()); + $this->assertSame('failure', $status->asString()); + } +} diff --git a/tests/tests/Util/PercentageTest.php b/tests/tests/Util/PercentageTest.php new file mode 100644 index 000000000..6949532c3 --- /dev/null +++ b/tests/tests/Util/PercentageTest.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Util; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Percentage::class)] +final class PercentageTest extends TestCase +{ + public function testCanBeRepresentedAsFloat(): void + { + $this->assertSame( + 50.0, + Percentage::fromFractionAndTotal(1, 2)->asFloat(), + ); + } + + public function testCanBeRepresentedAsString(): void + { + $this->assertSame( + '50.00%', + Percentage::fromFractionAndTotal(1, 2)->asString(), + ); + } + + public function testCanBeRepresentedAsFixedWidthString(): void + { + $this->assertSame( + ' 50.00%', + Percentage::fromFractionAndTotal(1, 2)->asFixedWidthString(), + ); + } + + public function testRepresentsTotalOfZeroAsEmptyString(): void + { + $this->assertSame( + '', + Percentage::fromFractionAndTotal(0, 0)->asString(), + ); + + $this->assertSame( + '', + Percentage::fromFractionAndTotal(0, 0)->asFixedWidthString(), + ); + } + + public function testRepresentsTotalOfZeroAs100PercentFloat(): void + { + $this->assertSame( + 100.0, + Percentage::fromFractionAndTotal(0, 0)->asFloat(), + ); + } +} diff --git a/tools/.phpstan/composer.json b/tools/.phpstan/composer.json new file mode 100644 index 000000000..37e97680a --- /dev/null +++ b/tools/.phpstan/composer.json @@ -0,0 +1,14 @@ +{ + "require-dev": { + "phpstan/phpstan": "^2.1.21", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan-strict-rules": "^2.0.6", + "tomasvotruba/type-coverage": "^2.0.2", + "ergebnis/phpstan-rules": "^2.10.5" + }, + "config": { + "allow-plugins": { + "phpstan/extension-installer": true + } + } +} diff --git a/tools/.phpstan/composer.lock b/tools/.phpstan/composer.lock new file mode 100644 index 000000000..bac49621d --- /dev/null +++ b/tools/.phpstan/composer.lock @@ -0,0 +1,390 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "21a2ca88ab1be049930ddca65ab02f99", + "packages": [], + "packages-dev": [ + { + "name": "ergebnis/phpstan-rules", + "version": "2.10.5", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/phpstan-rules.git", + "reference": "719a53e793e2417da46d497f21fb8d2f007ecb78" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/719a53e793e2417da46d497f21fb8d2f007ecb78", + "reference": "719a53e793e2417da46d497f21fb8d2f007ecb78", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "phpstan/phpstan": "^2.1.8" + }, + "require-dev": { + "codeception/codeception": "^4.0.0 || ^5.0.0", + "doctrine/orm": "^2.20.0 || ^3.3.0", + "ergebnis/composer-normalize": "^2.47.0", + "ergebnis/license": "^2.6.0", + "ergebnis/php-cs-fixer-config": "^6.46.0", + "ergebnis/phpunit-slow-test-detector": "^2.19.1", + "fakerphp/faker": "^1.24.1", + "nette/di": "^3.1.10", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.6", + "phpstan/phpstan-strict-rules": "^2.0.4", + "phpunit/phpunit": "^9.6.21", + "psr/container": "^2.0.2", + "symfony/finder": "^5.4.45", + "symfony/process": "^5.4.47" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Ergebnis\\PHPStan\\Rules\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "description": "Provides rules for phpstan/phpstan.", + "homepage": "https://github.com/ergebnis/phpstan-rules", + "keywords": [ + "PHPStan", + "phpstan-rules" + ], + "support": { + "issues": "https://github.com/ergebnis/phpstan-rules/issues", + "security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md", + "source": "https://github.com/ergebnis/phpstan-rules" + }, + "time": "2025-06-23T17:35:58+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.7", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/e67c4061eb40b9c113b218214e42cb5a0dda28f2", + "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.4" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.5", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.7" + }, + "time": "2025-06-03T04:55:08+00:00" + }, + { + "name": "phpstan/extension-installer", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" + }, + "time": "2024-09-04T20:21:43+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.1.21", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ccf445757458c06a04eb3f803603cb118fe5fa6", + "reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2025-07-28T19:35:08+00:00" + }, + { + "name": "phpstan/phpstan-strict-rules", + "version": "2.0.6", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-strict-rules.git", + "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/f9f77efa9de31992a832ff77ea52eb42d675b094", + "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0.4" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Extra strict and opinionated rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.6" + }, + "time": "2025-07-21T12:19:29+00:00" + }, + { + "name": "tomasvotruba/type-coverage", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/TomasVotruba/type-coverage.git", + "reference": "d033429580f2c18bda538fa44f2939236a990e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/d033429580f2c18bda538fa44f2939236a990e0c", + "reference": "d033429580f2c18bda538fa44f2939236a990e0c", + "shasum": "" + }, + "require": { + "nette/utils": "^3.2 || ^4.0", + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "config/extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "TomasVotruba\\TypeCoverage\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Measure type coverage of your project", + "keywords": [ + "phpstan-extension", + "static analysis" + ], + "support": { + "issues": "https://github.com/TomasVotruba/type-coverage/issues", + "source": "https://github.com/TomasVotruba/type-coverage/tree/2.0.2" + }, + "funding": [ + { + "url": "https://www.paypal.me/rectorphp", + "type": "custom" + }, + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2025-01-07T00:10:26+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/tools/.phpstan/vendor/autoload.php b/tools/.phpstan/vendor/autoload.php new file mode 100644 index 000000000..94a663ef0 --- /dev/null +++ b/tools/.phpstan/vendor/autoload.php @@ -0,0 +1,22 @@ +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan'); + } +} + +return include __DIR__ . '/..'.'/phpstan/phpstan/phpstan'; diff --git a/tools/.phpstan/vendor/bin/phpstan.phar b/tools/.phpstan/vendor/bin/phpstan.phar new file mode 100755 index 000000000..fecf96f69 --- /dev/null +++ b/tools/.phpstan/vendor/bin/phpstan.phar @@ -0,0 +1,119 @@ +#!/usr/bin/env php +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar'); + } +} + +return include __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar'; diff --git a/tools/.phpstan/vendor/composer/ClassLoader.php b/tools/.phpstan/vendor/composer/ClassLoader.php new file mode 100644 index 000000000..7824d8f7e --- /dev/null +++ b/tools/.phpstan/vendor/composer/ClassLoader.php @@ -0,0 +1,579 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + + // PSR-4 + /** + * @var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var list + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> + */ + private $prefixesPsr0 = array(); + /** + * @var list + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var array + */ + private $missingClasses = array(); + + /** @var string|null */ + private $apcuPrefix; + + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return list + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return list + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return array Array of classname => path + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/tools/.phpstan/vendor/composer/InstalledVersions.php b/tools/.phpstan/vendor/composer/InstalledVersions.php new file mode 100644 index 000000000..2052022fd --- /dev/null +++ b/tools/.phpstan/vendor/composer/InstalledVersions.php @@ -0,0 +1,396 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool + */ + private static $installedIsLocalDir; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; + } + + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + $copiedLocalDir = false; + + if (self::$canGetVendors) { + $selfDir = self::getSelfDir(); + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; + } + } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array() && !$copiedLocalDir) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/tools/.phpstan/vendor/composer/LICENSE b/tools/.phpstan/vendor/composer/LICENSE new file mode 100644 index 000000000..f27399a04 --- /dev/null +++ b/tools/.phpstan/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +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. + diff --git a/tools/.phpstan/vendor/composer/autoload_classmap.php b/tools/.phpstan/vendor/composer/autoload_classmap.php new file mode 100644 index 000000000..c66a95024 --- /dev/null +++ b/tools/.phpstan/vendor/composer/autoload_classmap.php @@ -0,0 +1,60 @@ + $vendorDir . '/composer/InstalledVersions.php', + 'Nette\\ArgumentOutOfRangeException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\DeprecatedException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\DirectoryNotFoundException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\FileNotFoundException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\HtmlStringable' => $vendorDir . '/nette/utils/src/HtmlStringable.php', + 'Nette\\IOException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidArgumentException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidStateException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\Iterators\\CachingIterator' => $vendorDir . '/nette/utils/src/Iterators/CachingIterator.php', + 'Nette\\Iterators\\Mapper' => $vendorDir . '/nette/utils/src/Iterators/Mapper.php', + 'Nette\\Localization\\ITranslator' => $vendorDir . '/nette/utils/src/compatibility.php', + 'Nette\\Localization\\Translator' => $vendorDir . '/nette/utils/src/Translator.php', + 'Nette\\MemberAccessException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\NotImplementedException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\NotSupportedException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\OutOfRangeException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\ShouldNotHappenException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\SmartObject' => $vendorDir . '/nette/utils/src/SmartObject.php', + 'Nette\\StaticClass' => $vendorDir . '/nette/utils/src/StaticClass.php', + 'Nette\\UnexpectedValueException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\Utils\\ArrayHash' => $vendorDir . '/nette/utils/src/Utils/ArrayHash.php', + 'Nette\\Utils\\ArrayList' => $vendorDir . '/nette/utils/src/Utils/ArrayList.php', + 'Nette\\Utils\\Arrays' => $vendorDir . '/nette/utils/src/Utils/Arrays.php', + 'Nette\\Utils\\AssertionException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Callback' => $vendorDir . '/nette/utils/src/Utils/Callback.php', + 'Nette\\Utils\\DateTime' => $vendorDir . '/nette/utils/src/Utils/DateTime.php', + 'Nette\\Utils\\FileInfo' => $vendorDir . '/nette/utils/src/Utils/FileInfo.php', + 'Nette\\Utils\\FileSystem' => $vendorDir . '/nette/utils/src/Utils/FileSystem.php', + 'Nette\\Utils\\Finder' => $vendorDir . '/nette/utils/src/Utils/Finder.php', + 'Nette\\Utils\\Floats' => $vendorDir . '/nette/utils/src/Utils/Floats.php', + 'Nette\\Utils\\Helpers' => $vendorDir . '/nette/utils/src/Utils/Helpers.php', + 'Nette\\Utils\\Html' => $vendorDir . '/nette/utils/src/Utils/Html.php', + 'Nette\\Utils\\IHtmlString' => $vendorDir . '/nette/utils/src/compatibility.php', + 'Nette\\Utils\\Image' => $vendorDir . '/nette/utils/src/Utils/Image.php', + 'Nette\\Utils\\ImageColor' => $vendorDir . '/nette/utils/src/Utils/ImageColor.php', + 'Nette\\Utils\\ImageException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\ImageType' => $vendorDir . '/nette/utils/src/Utils/ImageType.php', + 'Nette\\Utils\\Iterables' => $vendorDir . '/nette/utils/src/Utils/Iterables.php', + 'Nette\\Utils\\Json' => $vendorDir . '/nette/utils/src/Utils/Json.php', + 'Nette\\Utils\\JsonException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\ObjectHelpers' => $vendorDir . '/nette/utils/src/Utils/ObjectHelpers.php', + 'Nette\\Utils\\Paginator' => $vendorDir . '/nette/utils/src/Utils/Paginator.php', + 'Nette\\Utils\\Random' => $vendorDir . '/nette/utils/src/Utils/Random.php', + 'Nette\\Utils\\Reflection' => $vendorDir . '/nette/utils/src/Utils/Reflection.php', + 'Nette\\Utils\\ReflectionMethod' => $vendorDir . '/nette/utils/src/Utils/ReflectionMethod.php', + 'Nette\\Utils\\RegexpException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Strings' => $vendorDir . '/nette/utils/src/Utils/Strings.php', + 'Nette\\Utils\\Type' => $vendorDir . '/nette/utils/src/Utils/Type.php', + 'Nette\\Utils\\UnknownImageFileException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Validators' => $vendorDir . '/nette/utils/src/Utils/Validators.php', +); diff --git a/tools/.phpstan/vendor/composer/autoload_files.php b/tools/.phpstan/vendor/composer/autoload_files.php new file mode 100644 index 000000000..b62e293ba --- /dev/null +++ b/tools/.phpstan/vendor/composer/autoload_files.php @@ -0,0 +1,10 @@ + $vendorDir . '/phpstan/phpstan/bootstrap.php', +); diff --git a/tools/.phpstan/vendor/composer/autoload_namespaces.php b/tools/.phpstan/vendor/composer/autoload_namespaces.php new file mode 100644 index 000000000..15a2ff3ad --- /dev/null +++ b/tools/.phpstan/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/tomasvotruba/type-coverage/src'), + 'PHPStan\\ExtensionInstaller\\' => array($vendorDir . '/phpstan/extension-installer/src'), + 'PHPStan\\' => array($vendorDir . '/phpstan/phpstan-strict-rules/src'), + 'Ergebnis\\PHPStan\\Rules\\' => array($vendorDir . '/ergebnis/phpstan-rules/src'), +); diff --git a/tools/.phpstan/vendor/composer/autoload_real.php b/tools/.phpstan/vendor/composer/autoload_real.php new file mode 100644 index 000000000..c91f94e3d --- /dev/null +++ b/tools/.phpstan/vendor/composer/autoload_real.php @@ -0,0 +1,48 @@ +register(true); + + $filesToLoad = \Composer\Autoload\ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$files; + $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; + } + }, null, null); + foreach ($filesToLoad as $fileIdentifier => $file) { + $requireFile($fileIdentifier, $file); + } + + return $loader; + } +} diff --git a/tools/.phpstan/vendor/composer/autoload_static.php b/tools/.phpstan/vendor/composer/autoload_static.php new file mode 100644 index 000000000..17adb2b10 --- /dev/null +++ b/tools/.phpstan/vendor/composer/autoload_static.php @@ -0,0 +1,111 @@ + __DIR__ . '/..' . '/phpstan/phpstan/bootstrap.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'T' => + array ( + 'TomasVotruba\\TypeCoverage\\' => 26, + ), + 'P' => + array ( + 'PHPStan\\ExtensionInstaller\\' => 27, + 'PHPStan\\' => 8, + ), + 'E' => + array ( + 'Ergebnis\\PHPStan\\Rules\\' => 23, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'TomasVotruba\\TypeCoverage\\' => + array ( + 0 => __DIR__ . '/..' . '/tomasvotruba/type-coverage/src', + ), + 'PHPStan\\ExtensionInstaller\\' => + array ( + 0 => __DIR__ . '/..' . '/phpstan/extension-installer/src', + ), + 'PHPStan\\' => + array ( + 0 => __DIR__ . '/..' . '/phpstan/phpstan-strict-rules/src', + ), + 'Ergebnis\\PHPStan\\Rules\\' => + array ( + 0 => __DIR__ . '/..' . '/ergebnis/phpstan-rules/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'Nette\\ArgumentOutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\DeprecatedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\DirectoryNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\FileNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\HtmlStringable' => __DIR__ . '/..' . '/nette/utils/src/HtmlStringable.php', + 'Nette\\IOException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidArgumentException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidStateException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\Iterators\\CachingIterator' => __DIR__ . '/..' . '/nette/utils/src/Iterators/CachingIterator.php', + 'Nette\\Iterators\\Mapper' => __DIR__ . '/..' . '/nette/utils/src/Iterators/Mapper.php', + 'Nette\\Localization\\ITranslator' => __DIR__ . '/..' . '/nette/utils/src/compatibility.php', + 'Nette\\Localization\\Translator' => __DIR__ . '/..' . '/nette/utils/src/Translator.php', + 'Nette\\MemberAccessException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\NotImplementedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\NotSupportedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\OutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\ShouldNotHappenException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\SmartObject' => __DIR__ . '/..' . '/nette/utils/src/SmartObject.php', + 'Nette\\StaticClass' => __DIR__ . '/..' . '/nette/utils/src/StaticClass.php', + 'Nette\\UnexpectedValueException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\Utils\\ArrayHash' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayHash.php', + 'Nette\\Utils\\ArrayList' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayList.php', + 'Nette\\Utils\\Arrays' => __DIR__ . '/..' . '/nette/utils/src/Utils/Arrays.php', + 'Nette\\Utils\\AssertionException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Callback' => __DIR__ . '/..' . '/nette/utils/src/Utils/Callback.php', + 'Nette\\Utils\\DateTime' => __DIR__ . '/..' . '/nette/utils/src/Utils/DateTime.php', + 'Nette\\Utils\\FileInfo' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileInfo.php', + 'Nette\\Utils\\FileSystem' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileSystem.php', + 'Nette\\Utils\\Finder' => __DIR__ . '/..' . '/nette/utils/src/Utils/Finder.php', + 'Nette\\Utils\\Floats' => __DIR__ . '/..' . '/nette/utils/src/Utils/Floats.php', + 'Nette\\Utils\\Helpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/Helpers.php', + 'Nette\\Utils\\Html' => __DIR__ . '/..' . '/nette/utils/src/Utils/Html.php', + 'Nette\\Utils\\IHtmlString' => __DIR__ . '/..' . '/nette/utils/src/compatibility.php', + 'Nette\\Utils\\Image' => __DIR__ . '/..' . '/nette/utils/src/Utils/Image.php', + 'Nette\\Utils\\ImageColor' => __DIR__ . '/..' . '/nette/utils/src/Utils/ImageColor.php', + 'Nette\\Utils\\ImageException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\ImageType' => __DIR__ . '/..' . '/nette/utils/src/Utils/ImageType.php', + 'Nette\\Utils\\Iterables' => __DIR__ . '/..' . '/nette/utils/src/Utils/Iterables.php', + 'Nette\\Utils\\Json' => __DIR__ . '/..' . '/nette/utils/src/Utils/Json.php', + 'Nette\\Utils\\JsonException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\ObjectHelpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/ObjectHelpers.php', + 'Nette\\Utils\\Paginator' => __DIR__ . '/..' . '/nette/utils/src/Utils/Paginator.php', + 'Nette\\Utils\\Random' => __DIR__ . '/..' . '/nette/utils/src/Utils/Random.php', + 'Nette\\Utils\\Reflection' => __DIR__ . '/..' . '/nette/utils/src/Utils/Reflection.php', + 'Nette\\Utils\\ReflectionMethod' => __DIR__ . '/..' . '/nette/utils/src/Utils/ReflectionMethod.php', + 'Nette\\Utils\\RegexpException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Strings' => __DIR__ . '/..' . '/nette/utils/src/Utils/Strings.php', + 'Nette\\Utils\\Type' => __DIR__ . '/..' . '/nette/utils/src/Utils/Type.php', + 'Nette\\Utils\\UnknownImageFileException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Validators' => __DIR__ . '/..' . '/nette/utils/src/Utils/Validators.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/tools/.phpstan/vendor/composer/installed.json b/tools/.phpstan/vendor/composer/installed.json new file mode 100644 index 000000000..c13206e30 --- /dev/null +++ b/tools/.phpstan/vendor/composer/installed.json @@ -0,0 +1,402 @@ +{ + "packages": [ + { + "name": "ergebnis/phpstan-rules", + "version": "2.10.5", + "version_normalized": "2.10.5.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/phpstan-rules.git", + "reference": "719a53e793e2417da46d497f21fb8d2f007ecb78" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/719a53e793e2417da46d497f21fb8d2f007ecb78", + "reference": "719a53e793e2417da46d497f21fb8d2f007ecb78", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "phpstan/phpstan": "^2.1.8" + }, + "require-dev": { + "codeception/codeception": "^4.0.0 || ^5.0.0", + "doctrine/orm": "^2.20.0 || ^3.3.0", + "ergebnis/composer-normalize": "^2.47.0", + "ergebnis/license": "^2.6.0", + "ergebnis/php-cs-fixer-config": "^6.46.0", + "ergebnis/phpunit-slow-test-detector": "^2.19.1", + "fakerphp/faker": "^1.24.1", + "nette/di": "^3.1.10", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.6", + "phpstan/phpstan-strict-rules": "^2.0.4", + "phpunit/phpunit": "^9.6.21", + "psr/container": "^2.0.2", + "symfony/finder": "^5.4.45", + "symfony/process": "^5.4.47" + }, + "time": "2025-06-23T17:35:58+00:00", + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Ergebnis\\PHPStan\\Rules\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "description": "Provides rules for phpstan/phpstan.", + "homepage": "https://github.com/ergebnis/phpstan-rules", + "keywords": [ + "PHPStan", + "phpstan-rules" + ], + "support": { + "issues": "https://github.com/ergebnis/phpstan-rules/issues", + "security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md", + "source": "https://github.com/ergebnis/phpstan-rules" + }, + "install-path": "../ergebnis/phpstan-rules" + }, + { + "name": "nette/utils", + "version": "v4.0.7", + "version_normalized": "4.0.7.0", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/e67c4061eb40b9c113b218214e42cb5a0dda28f2", + "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.4" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.5", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "time": "2025-06-03T04:55:08+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.7" + }, + "install-path": "../nette/utils" + }, + { + "name": "phpstan/extension-installer", + "version": "1.4.3", + "version_normalized": "1.4.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "time": "2024-09-04T20:21:43+00:00", + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" + }, + "install-path": "../phpstan/extension-installer" + }, + { + "name": "phpstan/phpstan", + "version": "2.1.21", + "version_normalized": "2.1.21.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ccf445757458c06a04eb3f803603cb118fe5fa6", + "reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "time": "2025-07-28T19:35:08+00:00", + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "install-path": "../phpstan/phpstan" + }, + { + "name": "phpstan/phpstan-strict-rules", + "version": "2.0.6", + "version_normalized": "2.0.6.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-strict-rules.git", + "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/f9f77efa9de31992a832ff77ea52eb42d675b094", + "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0.4" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" + }, + "time": "2025-07-21T12:19:29+00:00", + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Extra strict and opinionated rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.6" + }, + "install-path": "../phpstan/phpstan-strict-rules" + }, + { + "name": "tomasvotruba/type-coverage", + "version": "2.0.2", + "version_normalized": "2.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/TomasVotruba/type-coverage.git", + "reference": "d033429580f2c18bda538fa44f2939236a990e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/d033429580f2c18bda538fa44f2939236a990e0c", + "reference": "d033429580f2c18bda538fa44f2939236a990e0c", + "shasum": "" + }, + "require": { + "nette/utils": "^3.2 || ^4.0", + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0" + }, + "time": "2025-01-07T00:10:26+00:00", + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "config/extension.neon" + ] + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "TomasVotruba\\TypeCoverage\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Measure type coverage of your project", + "keywords": [ + "phpstan-extension", + "static analysis" + ], + "support": { + "issues": "https://github.com/TomasVotruba/type-coverage/issues", + "source": "https://github.com/TomasVotruba/type-coverage/tree/2.0.2" + }, + "funding": [ + { + "url": "https://www.paypal.me/rectorphp", + "type": "custom" + }, + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "install-path": "../tomasvotruba/type-coverage" + } + ], + "dev": true, + "dev-package-names": [ + "ergebnis/phpstan-rules", + "nette/utils", + "phpstan/extension-installer", + "phpstan/phpstan", + "phpstan/phpstan-strict-rules", + "tomasvotruba/type-coverage" + ] +} diff --git a/tools/.phpstan/vendor/composer/installed.php b/tools/.phpstan/vendor/composer/installed.php new file mode 100644 index 000000000..25f75aa88 --- /dev/null +++ b/tools/.phpstan/vendor/composer/installed.php @@ -0,0 +1,77 @@ + array( + 'name' => '__root__', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => '530bb5df4bf62ecfdc3ad304fa6b9e8afe4acd66', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => '530bb5df4bf62ecfdc3ad304fa6b9e8afe4acd66', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'ergebnis/phpstan-rules' => array( + 'pretty_version' => '2.10.5', + 'version' => '2.10.5.0', + 'reference' => '719a53e793e2417da46d497f21fb8d2f007ecb78', + 'type' => 'phpstan-extension', + 'install_path' => __DIR__ . '/../ergebnis/phpstan-rules', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'nette/utils' => array( + 'pretty_version' => 'v4.0.7', + 'version' => '4.0.7.0', + 'reference' => 'e67c4061eb40b9c113b218214e42cb5a0dda28f2', + 'type' => 'library', + 'install_path' => __DIR__ . '/../nette/utils', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'phpstan/extension-installer' => array( + 'pretty_version' => '1.4.3', + 'version' => '1.4.3.0', + 'reference' => '85e90b3942d06b2326fba0403ec24fe912372936', + 'type' => 'composer-plugin', + 'install_path' => __DIR__ . '/../phpstan/extension-installer', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'phpstan/phpstan' => array( + 'pretty_version' => '2.1.21', + 'version' => '2.1.21.0', + 'reference' => '1ccf445757458c06a04eb3f803603cb118fe5fa6', + 'type' => 'library', + 'install_path' => __DIR__ . '/../phpstan/phpstan', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'phpstan/phpstan-strict-rules' => array( + 'pretty_version' => '2.0.6', + 'version' => '2.0.6.0', + 'reference' => 'f9f77efa9de31992a832ff77ea52eb42d675b094', + 'type' => 'phpstan-extension', + 'install_path' => __DIR__ . '/../phpstan/phpstan-strict-rules', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'tomasvotruba/type-coverage' => array( + 'pretty_version' => '2.0.2', + 'version' => '2.0.2.0', + 'reference' => 'd033429580f2c18bda538fa44f2939236a990e0c', + 'type' => 'phpstan-extension', + 'install_path' => __DIR__ . '/../tomasvotruba/type-coverage', + 'aliases' => array(), + 'dev_requirement' => true, + ), + ), +); diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md b/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md new file mode 100644 index 000000000..38ee8788b --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md @@ -0,0 +1,726 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +For a full diff see [`2.10.5...main`][2.10.5...main]. + +## [`2.10.5`][2.10.5] + +For a full diff see [`2.10.4...2.10.5`][2.10.4...2.10.5]. + +### Fixed + +- Adjusted `Methods\NoNamedArgumentRule` to handle calls to constructors of variable class names ([#957]), by [@localheinz] +- Adjusted `Methods\NoNamedArgumentRule` to describe known calls only ([#958]), by [@localheinz] + +## [`2.10.4`][2.10.4] + +For a full diff see [`2.10.3...2.10.4`][2.10.3...2.10.4]. + +### Fixed + +- Adjusted `Methods\NoNamedArgumentRule` to handle static calls on variable expressions ([#947]), by [@localheinz] +- Adjusted `Methods\NoNamedArgumentRule` to handle calls on invokables ([#948]), by [@localheinz] +- Adjusted `Methods\NoNamedArgumentRule` to handle calls on callables assigned to properties ([#949]), by [@localheinz] +- Adjusted `Methods\NoNamedArgumentRule` to handle all other calls with generic error message ([#951]), by [@localheinz] + +## [`2.10.3`][2.10.3] + +For a full diff see [`2.10.2...2.10.3`][2.10.2...2.10.3]. + +### Fixed + +- Adjusted `Methods\InvokeParentHookMethodRule` to ignore comments ([#944]), by [@localheinz] + +## [`2.10.2`][2.10.2] + +For a full diff see [`2.10.1...2.10.2`][2.10.1...2.10.2]. + +### Fixed + +- Renamed error identifier for `Methods\InvokeParentHookMethodRule` ([#943]), by [@localheinz] + +## [`2.10.1`][2.10.1] + +For a full diff see [`2.10.0...2.10.1`][2.10.0...2.10.1]. + +### Fixed + +- Fixed schema for configuration of `Methods\InvokeParentHookMethodRule` ([#940]), by [@localheinz] + +## [`2.10.0`][2.10.0] + +For a full diff see [`2.9.0...2.10.0`][2.9.0...2.10.0]. + +### Added + +- Added `Methods\InvokeParentHookMethodRule`, which reports an error when a hook method that overrides a hook method in a parent class does not invoke the overridden hook method in the expected order ([#939]), by [@localheinz] + +## [`2.9.0`][2.9.0] + +For a full diff see [`2.8.0...2.9.0`][2.8.0...2.9.0]. + +### Added + +- Added `CallLikes\NoNamedArgumentRule`, which reports an error when an anonymous function, a function, or a method is invoked using a named argument ([#914]), by [@localheinz] + +### Changed + +- Required `phpstan/phpstan:^2.1.8` ([#938]), by [@localheinz] + +## [`2.8.0`][2.8.0] + +For a full diff see [`2.7.0...2.8.0`][2.7.0...2.8.0]. + +### Added + +- Added `allRules` parameter to allow disabling and enabling all rules ([#913]), by [@localheinz] +- Added `Expressions\NoAssignByReferenceRule`, which reports an error when a variable is assigned by reference ([#914]), by [@localheinz] + +## [`2.7.0`][2.7.0] + +For a full diff see [`2.6.1...2.7.0`][2.6.1...2.7.0]. + +### Added + +- Added `Closures\NoParameterPassedByReferenceRule`, `Functions\NoParameterPassedByReferenceRule`, `Methods\NoParameterPassedByReferenceRule`, which report an error when a closure, a function, or a method has a parameter that is passed by reference ([#911]), by [@localheinz] +- Added `Functions\NoReturnByReferenceRule` and `Methods\NoReturnByReferenceRule`, which report an error when a function or a method returns by reference ([#912]), by [@localheinz] + +## [`2.6.1`][2.6.1] + +For a full diff see [`2.6.0...2.6.1`][2.6.0...2.6.1]. + +### Fixed + +- Adjusted `Methods\NoParameterWithNullableTypeDeclarationRule` to use the appropriate error identifier ([#902]), by [@manuelkiessling] + +## [`2.6.0`][2.6.0] + +For a full diff see [`2.5.2...2.6.0`][2.5.2...2.6.0]. + +### Added + +- Added support for `phpstan/phpstan:^2.0.0` ([#873]), by [@localheinz] + +## [`2.5.2`][2.5.2] + +For a full diff see [`2.5.1...2.5.2`][2.5.1...2.5.2]. + +### Fixed + +- Adjusted `Closures\NoNullableReturnTypeDeclarationRule`, `Closures\NoParameterWithNullableTypeDeclarationRule`, `Functions\NoNullableReturnTypeDeclarationRule`, `Functions\NoParameterWithNullableTypeDeclarationRule`, `Methods\NoNullableReturnTypeDeclarationRule`, `Methods\NoParameterWithNullableTypeDeclarationRule` to detect cases where `null` is referenced with incorrect case or relative to the root namespace ([#897]), by [@localheinz] + +## [`2.5.1`][2.5.1] + +For a full diff see [`2.5.0...2.5.1`][2.5.0...2.5.1]. + +### Fixed + +- Returned rule with error identifier ([#882]), by [@localheinz] +- Adjusted `Methods\FinalInAbstractClassRule` to ignore Doctrine embeddables and entities ([#396]), by [@localheinz] +- Adjusted `Expressions\NoCompactRule` to detect usages of `compact()` with incorrect case ([#889]), by [@localheinz] +- Adjusted `Methods\PrivateInFinalClassRule` to use more appropriate message when detecting a `protected` method in an anonymous class ([#890]), by [@localheinz] +- Adjusted `Methods\PrivateInFinalClassRule` to ignore `protected` methods from traits ([#891]), by [@localheinz] +- Adjusted `Methods\PrivateInFinalClassRule` to ignore `protected` methods with `phpunit/phpunit` attributes requiring methods to be `protected` ([#863]), by [@cosmastech] +- Adjusted `Methods\PrivateInFinalClassRule` to ignore `protected` methods with `phpunit/phpunit` annotations requiring methods to be `protected` ([#895]), by [@cosmastech] + +## [`2.5.0`][2.5.0] + +For a full diff see [`2.4.0...2.5.0`][2.4.0...2.5.0]. + +### Added + +- Added rule error identifiers ([#875]), by [@localheinz] +- Added support for PHP 8.0 ([#877]), by [@localheinz] +- Added support for PHP 7.4 ([#880]), by [@localheinz] + +### Changed + +- Removed dependency on `nikic/php-parser` ([#878]), by [@localheinz] + +## [`2.4.0`][2.4.0] + +For a full diff see [`2.3.0...2.4.0`][2.3.0...2.4.0]. + +### Added + +- Added support for PHP 8.4 ([#872]), by [@localheinz] + +## [`2.3.0`][2.3.0] + +For a full diff see [`2.2.0...2.3.0`][2.2.0...2.3.0]. + +### Changed + +- Allowed installation on PHP 8.4 ([#862]), by [@localheinz] + +## [`2.2.0`][2.2.0] + +For a full diff see [`2.1.0...2.2.0`][2.1.0...2.2.0]. + +### Changed + +- Allowed installation of `nikic/php-parser:^5.0.0` ([#735]), by [@localheinz] + +## [`2.1.0`][2.1.0] + +For a full diff see [`2.0.0...2.1.0`][2.0.0...2.1.0]. + +### Changed + +- Dropped support for PHP 8.0 ([#567]), by [@localheinz] +- Added support for PHP 8.3 ([#604]), by [@nunomaduro] + +## [`2.0.0`][2.0.0] + +For a full diff see [`1.0.0...2.0.0`][1.0.0...2.0.0]. + +### Added + +- Added `methodsAllowedToUseContainerTypeDeclarations` parameter to allow configuring a list of method names that are allowed to have container parameter type declarations ([#541), by [@localheinz] +- Allowed disabling rules ([#542), by [@localheinz] +- Added support for nullable union types ([#543), by [@localheinz] + +### Changed + +- Dropped support for PHP 7.2 ([#496]), by [@localheinz] +- Dropped support for PHP 7.3 ([#498]), by [@localheinz] +- Dropped support for PHP 7.4 ([#499]), by [@localheinz] +- Added support for PHP 8.2 ([#540]), by [@localheinz] + +### Removed + +- Removed `Expressions\NoEmptyRule` ([#525]), by [@enumag] + +## [`1.0.0`][1.0.0] + +For a full diff see [`0.15.3...1.0.0`][0.15.3...1.0.0]. + +### Changed + +- Added support for `phpstan/phpstan:^1.0.0` and dropped support for non-stable versions of `phpstan/phpstan` ([#381]), by [@rpkamp] + +### Fixed + +- Adjusted `Classes\FinalRule` to not report an error when a non-final class has a `Doctrinbe\ORM\Mapping\Entity` attribute ([#395]), by [@localheinz] + +## [`0.15.3`][0.15.3] + +For a full diff see [`0.15.2...0.15.3`][0.15.2...0.15.3]. + +### Changed + +- Allow installation with PHP 8.0 ([#294]), by [@localheinz] + +## [`0.15.2`][0.15.2] + +For a full diff see [`0.15.1...0.15.2`][0.15.1...0.15.2]. + +### Changed + +- Dropped support for PHP 7.1 ([#259]), by [@localheinz] + +## [`0.15.1`][0.15.1] + +For a full diff see [`0.15.0...0.15.1`][0.15.0...0.15.1]. + +### Changed + +- Adjusted `Methods\FinalInAbstractClass` rule to allow non-`final` `public` constructors in abstract classes ([#248]), by [@Slamdunk] + +## [`0.15.0`][0.15.0] + +For a full diff see [`0.14.4...0.15.0`][0.14.4...0.15.0]. + +### Added + +- Added `Classes\PHPUnit\Framework\TestCaseWithSuffixRule`, which reports an error when a concrete class extending `PHPUnit\Framework\TestCase` does not have a `Test` suffix ([#225]), by [@localheinz] + +## [`0.14.4`][0.14.4] + +For a full diff see [`0.14.3...0.14.4`][0.14.3...0.14.4]. + +### Fixed + +- Ignored classes with `@ORM\Mapping\Entity` annotations in `FinalRule` ([#202]), by [@localheinz] + +## [`0.14.3`][0.14.3] + +For a full diff see [`0.14.2...0.14.3`][0.14.2...0.14.3]. + +### Fixed + +- Ignored first line in `DeclareStrictTypesRule` when it is a shebang ([#186]), by [@Great-Antique] + +## [`0.14.2`][0.14.2] + +For a full diff see [`0.14.1...0.14.2`][0.14.1...0.14.2]. + +### Fixed + +- Brought back support for PHP 7.1 ([#166]), by [@localheinz] + +## [`0.14.1`][0.14.1] + +For a full diff see [`0.14.0...0.14.1`][0.14.0...0.14.1]. + +### Fixed + +- Removed an inappropriate `replace` configuration from `composer.json` ([#161]), by [@localheinz] + +## [`0.14.0`][0.14.0] + +For a full diff see [`0.13.0...0.14.0`][0.13.0...0.14.0]. + +### Changed + +- Allowed installation of `phpstan/phpstan:~0.12.0` ([#147]), by [@localheinz] +- Renamed vendor namespace `Localheinz` to `Ergebnis` after move to [@ergebnis] ([#157]), by [@localheinz] + + Run + + ```sh + composer remove localheinz/phpstan-rules + ``` + + and + + ```sh + composer require ergebnis/phpstan-rules + ``` + + to update. + + Run + + ```sh + find . -type f -exec sed -i '.bak' 's/Localheinz\\PHPStan/Ergebnis\\PHPStan/g' {} \; + ``` + + to replace occurrences of `Localheinz\PHPStan` with `Ergebnis\PHPStan`. + + Run + + ```sh + find -type f -name '*.bak' -delete + ``` + + to delete backup files created in the previous step. + +- Moved parameters into `ergebnis` section to prevent conflicts with global parameters ([#158]), by [@localheinz] + + Where previously `phpstan.neon` looked like the following + + ```neon + parameters: + allowAbstractClasses: true + classesAllowedToBeExtended: [] + classesNotRequiredToBeAbstractOrFinal: [] + interfacesImplementedByContainers: + - Psr\Container\ContainerInterface + ``` + + these parameters now need to be moved into an `ergebnis` section: + + ```diff + parameters: + - allowAbstractClasses: true + - classesAllowedToBeExtended: [] + - classesNotRequiredToBeAbstractOrFinal: [] + - interfacesImplementedByContainers: + - - Psr\Container\ContainerInterface + + ergebnis: + + allowAbstractClasses: true + + classesAllowedToBeExtended: [] + + classesNotRequiredToBeAbstractOrFinal: [] + + interfacesImplementedByContainers: + + - Psr\Container\ContainerInterface + ``` + +### Fixed + +- Dropped support for PHP 7.1 ([#141]), by [@localheinz] + +## [`0.13.0`][0.13.0] + +For a full diff see [`0.12.2...0.13.0`][0.12.2...0.13.0]. + +### Added + +- Added `Methods\PrivateInFinalClassRule` which reports an error when a method in a `final` class is `protected` when it could be `private` ([#126]), by [@localheinz] + +## [`0.12.2`][0.12.2] + +For a full diff see [`0.12.1...0.12.2`][0.12.1...0.12.2]. + +### Fixed + +- Started ignoring interfaces from analysis by `Methods\FinalInAbstractClassRule` to avoid inappropriate errors ([#132]), by [@localheinz] + +## [`0.12.1`][0.12.1] + +For a full diff see [`0.12.0...0.12.1`][0.12.0...0.12.1]. + +### Fixed + +- Started resolving class name in type declaration before attempting to analyze it in the `Methods\NoParameterWithContainerTypeDeclarationRule` to avoid errors where class `self` is not found ([#128]), by [@localheinz] + +## [`0.12.0`][0.12.0] + +For a full diff see [`0.11.0...0.12.0`][0.11.0...0.12.0]. + +### Added + +- Added `Methods\NoParameterWithContainerTypeDeclarationRule`, which reports an error when a method has a type declaration that corresponds to a known dependency injection container or service locator ([#122]), by [@localheinz] +- Added `Methods\FinalInAbstractClassRule`, which reports an error when a concrete `public` or `protected` method in an `abstract` class is not `final` ([#123]), by [@localheinz] + +## [`0.11.0`][0.11.0] + +For a full diff see [`0.10.0...0.11.0`][0.10.0...0.11.0]. + +### Added + +- Added `Files\DeclareStrictTypesRule`, which reports an error when a PHP file does not have a `declare(strict_types=1)` declaration ([#79] +- Added `Expressions\NoEmptyRule`, which reports an error when the language construct `empty()` is used ([#110]), by [@localheinz] +- Added `Expressions\NoEvalRule`, which reports an error when the language construct `eval()` is used ([#112]), by [@localheinz] +- Added `Expressions\NoErrorSuppressionRule`, which reports an error when `@` is used to suppress errors ([#113]), by [@localheinz] +- Added `Expressions\NoCompactRule`, which reports an error when the function `compact()` is used ([#116]), by [@localheinz] +- Added `Statements\NoSwitchRule`, which reports an error when the statement `switch()` is used ([#117]), by [@localheinz] + +### Changed + +- Require at least `nikic/php-parser:^4.2.3` ([#102]), by [@localheinz] +- Require at least `phpstan/phpstan:~0.11.15` ([#103]), by [@localheinz] + +## [`0.10.0`][0.10.0] + +For a full diff see [`0.9.1...0.10.0`][0.9.1...0.10.0]. + +### Changed + +- Require at least `phpstan/phpstan:~0.11.7` ([#91]), by [@localheinz] + +### Fixed + +- Added missing `parametersSchema` configuration to `rules.neon`, which is required for use with `bleedingEdge.neon`, see [`phpstan/phpstan@54a125d`](https://github.com/phpstan/phpstan/commit/54a125df47fa097b792cb9a3e70c49f753f66b85) ([#93]), by [@localheinz] +* +## [`0.9.1`][0.9.1] + +For a full diff see [`0.9.0...0.9.1`][0.9.0...0.9.1]. + +### Changed + +- Allow usage with [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer) ([#89]), by [@localheinz] + +## [`0.9.0`][0.9.0] + +For a full diff see [`0.8.1...0.9.0`][0.8.1...0.9.0]. + +### Changed + +- Adjusted `Classes\FinalRule` to ignore Doctrine entities when they are annotated ([#84]), by [@localheinz] + +## [`0.8.1`][0.8.1] + +For a full diff see [`0.8.0...0.8.1`][0.8.0...0.8.1]. + +### Fixed + +- Actually enable `Expressions\NoIssetRule` ([#83]), by [@localheinz] + +## [`0.8.0`][0.8.0] + +For a full diff see [`0.7.1...0.8.0`][0.7.1...0.8.0]. + +### Added + +- Added `Expressions\NoIssetRule`, which reports an error when the language construct `isset()` is used ([#81]), by [@localheinz] + +## [`0.7.1`][0.7.1] + +For a full diff see [`0.7.0...0.7.1`][0.7.0...0.7.1]. + +### Changed + +- Configured `Classes\NoExtendsRule` to allow a set of default classes to be extended ([#73]), by [@localheinz] + +## [`0.7.0`][0.7.0] + +For a full diff see [`0.6.0...0.7.0`][0.6.0...0.7.0]. + +### Added + +- Added `Classes\NoExtendsRule`, which reports an error when a class extends a class that is not allowed to be extended ([#68]), by [@localheinz] + +## [`0.6.0`][0.6.0] + +For a full diff see [`0.5.0...0.6.0`][0.5.0...0.6.0]. + +### Added + +- Allow installation with `phpstan/phpstan:~0.11.0` ([#65]), by [@localheinz] + +## [`0.5.0`][0.5.0] + +For a full diff see [`0.4.0...0.5.0`][0.4.0...0.5.0]. + +### Added + +- Added `Methods\NoConstructorParameterWithDefaultValueRule`, which reports an error when a constructor of an anonymous class or a class has a parameter with a default value ([#45]), by [@localheinz] +- Added parameters `$allowAbstractClasses` and `$classesNotRequiredToBeAbstractOrFinal` to allow configuration of `Classes`FinalRule` ([#51]), by [@localheinz] + +### Removed + +- Removed `Classes\AbstractOrFinalRule` after merging behaviour into `Classes\FinalRule` ([#51]), by [@localheinz] +- Removed default values from constructor of `Classes\FinalRule` ([#53]), by [@localheinz] + +## [`0.4.0`][0.4.0] + +For a full diff see [`0.3.0...0.4.0`][0.3.0...0.4.0] + +### Changed + +- Removed double-quotes from error messages to be more consistent with error messages generated by `phpstan/phstan` ([#39]), by [@localheinz] +- Modified wording and placement of method, function, and parameter names in error messages to be more consistent with error messages generated by `phpstan/phstan` ([#42]), by [@localheinz] + +## [`0.3.0`][0.3.0] + +For a full diff see [`0.2.0...0.3.0`][0.2.0...0.3.0] + +### Added + +- Added `Functions\NoNullableReturnTypeDeclarationRule`, which reports an error when a function has a nullable return type declaration, and `Methods\NoNullableReturnTypeDeclarationRule`, which reports an error when a method declared in an anonymous class, a class, or an interface has a nullable return type declaration ([#16]), by [@localheinz] +- Added `Closures\NoParameterWithNullDefaultValueRule`, which reports an error when a closure has a parameter with `null` as default value ([#26]), by [@localheinz] +- Added `Closures\NoNullableReturnTypeDeclarationRule`, which reports an error when a closure has a nullable return type declaration ([#29]), by [@localheinz] +- Added `Functions\NoParameterWithNullDefaultValueRule`, which reports an error when a function has a parameter with `null` as default value ([#31]), by [@localheinz] +- Added `Methods\NoParameterWithNullDefaultValueRule`, which reports an error when a method declared in an anonymous class, a class, or an interface has a parameter with `null` as default value ([#32]), by [@localheinz] +- Added `Closures\NoParameterWithNullableTypeDeclarationRule`, which reports an error when a closure has a parameter with a nullable type declaration ([#33]), by [@localheinz] +- Added `Functions\NoParameterWithNullableTypeDeclarationRule`, which reports an error when a function has a parameter with a nullable type declaration ([#34]), by [@localheinz] +- Added `Methods\NoParameterWithNullableTypeDeclarationRule`, which reports an error when a method declared in an anonymous class, a class, or an interface has a parameter with a nullable type declaration ([#35]), by [@localheinz] +- Extracted `rules.neon`, so you can easily enable all rules by including it in your `phpstan.neon` ([#37]), by [@localheinz] + +## [`0.2.0`][0.2.0] + +For a full diff see [`0.1.0...0.2.0`][0.1.0...0.2.0] + +### Added + +- Added `Classes\FinalRule`, which reports an error when a non-anonymous class is not `final`, ([#4]), by [@localheinz] + +### Changed + +- Added an `$excludeClassNames` argument to the constructors of `Classes\AbstractOrFinalRule` and `Classes\FinalRule` to allow whitelisting of classes, ([#11]), by [@localheinz] + +## [`0.1.0`][0.1.0] + +For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0]. + +### Added + +- Added `Classes\AbstractOrFinalRule`, which reports an error when a non-anonymous class is neither `abstract` nor `final`, ([#1]), by [@localheinz] + +[0.1.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.1.0 +[0.2.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.2.0 +[0.3.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.3.0 +[0.4.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.4.0 +[0.5.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.5.0 +[0.6.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.6.0 +[0.7.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.7.0 +[0.7.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.7.1 +[0.8.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.8.0 +[0.8.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.8.1 +[0.9.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.9.0 +[0.9.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.9.1 +[0.10.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.10.0 +[0.11.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.11.0 +[0.12.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.12.0 +[0.12.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.12.1 +[0.12.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.12.2 +[0.13.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.13.0 +[0.14.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.0 +[0.14.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.1 +[0.14.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.2 +[0.14.3]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.3 +[0.14.4]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.4 +[0.15.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.0 +[0.15.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.1 +[0.15.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.2 +[0.15.3]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.3 +[1.0.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/1.0.0 +[2.0.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.0.0 +[2.1.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.1.0 +[2.2.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.2.0 +[2.3.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.3.0 +[2.4.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.4.0 +[2.5.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.5.0 +[2.5.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.5.1 +[2.5.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.5.2 +[2.6.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.6.0 +[2.6.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.6.1 +[2.7.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.7.0 +[2.8.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.8.0 +[2.9.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.9.0 +[2.10.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.0 +[2.10.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.1 +[2.10.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.2 +[2.10.3]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.3 +[2.10.4]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.4 +[2.10.5]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.5 + +[362c7ea...0.1.0]: https://github.com/ergebnis/phpstan-rules/compare/362c7ea...0.1.0 +[0.1.0...0.2.0]: https://github.com/ergebnis/phpstan-rules/compare/0.1.0...0.2.0 +[0.2.0...0.3.0]: https://github.com/ergebnis/phpstan-rules/compare/0.2.0...0.3.0 +[0.3.0...0.4.0]: https://github.com/ergebnis/phpstan-rules/compare/0.3.0...0.4.0 +[0.4.0...0.5.0]: https://github.com/ergebnis/phpstan-rules/compare/0.4.0...0.5.0 +[0.5.0...0.6.0]: https://github.com/ergebnis/phpstan-rules/compare/0.5.0...0.6.0 +[0.6.0...0.7.0]: https://github.com/ergebnis/phpstan-rules/compare/0.6.0...0.7.0 +[0.7.0...0.7.1]: https://github.com/ergebnis/phpstan-rules/compare/0.7.0...0.7.1 +[0.7.1...0.8.0]: https://github.com/ergebnis/phpstan-rules/compare/0.7.1...0.8.0 +[0.8.0...0.8.1]: https://github.com/ergebnis/phpstan-rules/compare/0.8.0...0.8.1 +[0.8.1...0.9.0]: https://github.com/ergebnis/phpstan-rules/compare/0.8.1...0.9.0 +[0.9.0...0.9.1]: https://github.com/ergebnis/phpstan-rules/compare/0.9.0...0.9.1 +[0.9.1...0.10.0]: https://github.com/ergebnis/phpstan-rules/compare/0.9.1...0.10.0 +[0.10.0...0.11.0]: https://github.com/ergebnis/phpstan-rules/compare/0.10.0...0.11.0 +[0.11.0...0.12.0]: https://github.com/ergebnis/phpstan-rules/compare/0.11.0...0.12.0 +[0.12.0...0.12.1]: https://github.com/ergebnis/phpstan-rules/compare/0.12.0...0.12.1 +[0.12.1...0.12.2]: https://github.com/ergebnis/phpstan-rules/compare/0.12.1...0.12.2 +[0.12.2...0.13.0]: https://github.com/ergebnis/phpstan-rules/compare/0.12.2...0.13.0 +[0.13.0...0.14.0]: https://github.com/ergebnis/phpstan-rules/compare/0.13.0...0.14.0 +[0.14.0...0.14.1]: https://github.com/ergebnis/phpstan-rules/compare/0.14.0...0.14.1 +[0.14.1...0.14.2]: https://github.com/ergebnis/phpstan-rules/compare/0.14.1...0.14.2 +[0.14.2...0.14.3]: https://github.com/ergebnis/phpstan-rules/compare/0.14.2...0.14.3 +[0.14.3...0.14.4]: https://github.com/ergebnis/phpstan-rules/compare/0.14.3...0.14.4 +[0.14.4...0.15.0]: https://github.com/ergebnis/phpstan-rules/compare/0.14.4...0.15.0 +[0.15.0...0.15.1]: https://github.com/ergebnis/phpstan-rules/compare/0.15.0...0.15.1 +[0.15.1...0.15.2]: https://github.com/ergebnis/phpstan-rules/compare/0.15.1...0.15.2 +[0.15.2...0.15.3]: https://github.com/ergebnis/phpstan-rules/compare/0.15.2...0.15.3 +[0.15.3...1.0.0]: https://github.com/ergebnis/phpstan-rules/compare/0.15.3...1.0.0 +[1.0.0...2.0.0]: https://github.com/ergebnis/phpstan-rules/compare/1.0.0...2.0.0 +[2.0.0...2.1.0]: https://github.com/ergebnis/phpstan-rules/compare/2.0.0...2.1.0 +[2.1.0...2.2.0]: https://github.com/ergebnis/phpstan-rules/compare/2.1.0...2.2.0 +[2.2.0...2.3.0]: https://github.com/ergebnis/phpstan-rules/compare/2.2.0...2.3.0 +[2.3.0...2.4.0]: https://github.com/ergebnis/phpstan-rules/compare/2.3.0...2.4.0 +[2.4.0...2.5.0]: https://github.com/ergebnis/phpstan-rules/compare/2.4.0...2.5.0 +[2.5.0...2.5.1]: https://github.com/ergebnis/phpstan-rules/compare/2.5.0...2.5.1 +[2.5.1...2.5.2]: https://github.com/ergebnis/phpstan-rules/compare/2.5.1...2.5.2 +[2.5.2...2.6.0]: https://github.com/ergebnis/phpstan-rules/compare/2.5.2...2.6.0 +[2.6.0...2.6.1]: https://github.com/ergebnis/phpstan-rules/compare/2.6.0...2.6.1 +[2.6.1...2.7.0]: https://github.com/ergebnis/phpstan-rules/compare/2.6.1...2.7.0 +[2.7.0...2.8.0]: https://github.com/ergebnis/phpstan-rules/compare/2.7.0...2.8.0 +[2.8.0...2.9.0]: https://github.com/ergebnis/phpstan-rules/compare/2.8.0...2.9.0 +[2.9.0...2.10.0]: https://github.com/ergebnis/phpstan-rules/compare/2.9.0...2.10.0 +[2.10.0...2.10.1]: https://github.com/ergebnis/phpstan-rules/compare/2.10.0...2.10.1 +[2.10.1...2.10.2]: https://github.com/ergebnis/phpstan-rules/compare/2.10.1...2.10.2 +[2.10.2...2.10.3]: https://github.com/ergebnis/phpstan-rules/compare/2.10.2...2.10.3 +[2.10.3...2.10.4]: https://github.com/ergebnis/phpstan-rules/compare/2.10.3...2.10.4 +[2.10.4...2.10.5]: https://github.com/ergebnis/phpstan-rules/compare/2.10.4...2.10.5 +[2.10.5...main]: https://github.com/ergebnis/phpstan-rules/compare/2.10.5...main + +[#1]: https://github.com/ergebnis/phpstan-rules/pull/1 +[#4]: https://github.com/ergebnis/phpstan-rules/pull/4 +[#11]: https://github.com/ergebnis/phpstan-rules/pull/11 +[#16]: https://github.com/ergebnis/phpstan-rules/pull/16 +[#26]: https://github.com/ergebnis/phpstan-rules/pull/26 +[#29]: https://github.com/ergebnis/phpstan-rules/pull/29 +[#31]: https://github.com/ergebnis/phpstan-rules/pull/31 +[#32]: https://github.com/ergebnis/phpstan-rules/pull/32 +[#33]: https://github.com/ergebnis/phpstan-rules/pull/33 +[#34]: https://github.com/ergebnis/phpstan-rules/pull/34 +[#35]: https://github.com/ergebnis/phpstan-rules/pull/35 +[#37]: https://github.com/ergebnis/phpstan-rules/pull/37 +[#39]: https://github.com/ergebnis/phpstan-rules/pull/39 +[#42]: https://github.com/ergebnis/phpstan-rules/pull/42 +[#45]: https://github.com/ergebnis/phpstan-rules/pull/45 +[#51]: https://github.com/ergebnis/phpstan-rules/pull/51 +[#53]: https://github.com/ergebnis/phpstan-rules/pull/53 +[#65]: https://github.com/ergebnis/phpstan-rules/pull/65 +[#68]: https://github.com/ergebnis/phpstan-rules/pull/68 +[#73]: https://github.com/ergebnis/phpstan-rules/pull/73 +[#79]: https://github.com/ergebnis/phpstan-rules/pull/79 +[#81]: https://github.com/ergebnis/phpstan-rules/pull/81 +[#83]: https://github.com/ergebnis/phpstan-rules/pull/83 +[#84]: https://github.com/ergebnis/phpstan-rules/pull/84 +[#89]: https://github.com/ergebnis/phpstan-rules/pull/89 +[#91]: https://github.com/ergebnis/phpstan-rules/pull/91 +[#93]: https://github.com/ergebnis/phpstan-rules/pull/93 +[#102]: https://github.com/ergebnis/phpstan-rules/pull/102 +[#103]: https://github.com/ergebnis/phpstan-rules/pull/103 +[#110]: https://github.com/ergebnis/phpstan-rules/pull/110 +[#112]: https://github.com/ergebnis/phpstan-rules/pull/112 +[#113]: https://github.com/ergebnis/phpstan-rules/pull/113 +[#116]: https://github.com/ergebnis/phpstan-rules/pull/116 +[#117]: https://github.com/ergebnis/phpstan-rules/pull/117 +[#122]: https://github.com/ergebnis/phpstan-rules/pull/122 +[#123]: https://github.com/ergebnis/phpstan-rules/pull/123 +[#126]: https://github.com/ergebnis/phpstan-rules/pull/126 +[#128]: https://github.com/ergebnis/phpstan-rules/pull/128 +[#132]: https://github.com/ergebnis/phpstan-rules/pull/132 +[#141]: https://github.com/ergebnis/phpstan-rules/pull/141 +[#147]: https://github.com/ergebnis/phpstan-rules/pull/147 +[#157]: https://github.com/ergebnis/phpstan-rules/pull/157 +[#158]: https://github.com/ergebnis/phpstan-rules/pull/158 +[#161]: https://github.com/ergebnis/phpstan-rules/pull/161 +[#166]: https://github.com/ergebnis/phpstan-rules/pull/166 +[#186]: https://github.com/ergebnis/phpstan-rules/pull/186 +[#202]: https://github.com/ergebnis/phpstan-rules/pull/202 +[#225]: https://github.com/ergebnis/phpstan-rules/pull/225 +[#248]: https://github.com/ergebnis/phpstan-rules/pull/248 +[#259]: https://github.com/ergebnis/phpstan-rules/pull/259 +[#294]: https://github.com/ergebnis/phpstan-rules/pull/294 +[#381]: https://github.com/ergebnis/phpstan-rules/pull/381 +[#395]: https://github.com/ergebnis/phpstan-rules/pull/395 +[#396]: https://github.com/ergebnis/phpstan-rules/pull/396 +[#496]: https://github.com/ergebnis/phpstan-rules/pull/496 +[#498]: https://github.com/ergebnis/phpstan-rules/pull/498 +[#499]: https://github.com/ergebnis/phpstan-rules/pull/498 +[#525]: https://github.com/ergebnis/phpstan-rules/pull/525 +[#540]: https://github.com/ergebnis/phpstan-rules/pull/540 +[#541]: https://github.com/ergebnis/phpstan-rules/pull/541 +[#542]: https://github.com/ergebnis/phpstan-rules/pull/542 +[#543]: https://github.com/ergebnis/phpstan-rules/pull/543 +[#567]: https://github.com/ergebnis/phpstan-rules/pull/567 +[#735]: https://github.com/ergebnis/phpstan-rules/pull/735 +[#862]: https://github.com/ergebnis/phpstan-rules/pull/862 +[#863]: https://github.com/ergebnis/phpstan-rules/pull/863 +[#872]: https://github.com/ergebnis/phpstan-rules/pull/872 +[#873]: https://github.com/ergebnis/phpstan-rules/pull/873 +[#875]: https://github.com/ergebnis/phpstan-rules/pull/875 +[#877]: https://github.com/ergebnis/phpstan-rules/pull/877 +[#878]: https://github.com/ergebnis/phpstan-rules/pull/878 +[#880]: https://github.com/ergebnis/phpstan-rules/pull/880 +[#882]: https://github.com/ergebnis/phpstan-rules/pull/882 +[#889]: https://github.com/ergebnis/phpstan-rules/pull/889 +[#890]: https://github.com/ergebnis/phpstan-rules/pull/890 +[#891]: https://github.com/ergebnis/phpstan-rules/pull/891 +[#895]: https://github.com/ergebnis/phpstan-rules/pull/895 +[#897]: https://github.com/ergebnis/phpstan-rules/pull/897 +[#902]: https://github.com/ergebnis/phpstan-rules/pull/902 +[#911]: https://github.com/ergebnis/phpstan-rules/pull/911 +[#912]: https://github.com/ergebnis/phpstan-rules/pull/912 +[#913]: https://github.com/ergebnis/phpstan-rules/pull/913 +[#914]: https://github.com/ergebnis/phpstan-rules/pull/914 +[#938]: https://github.com/ergebnis/phpstan-rules/pull/938 +[#939]: https://github.com/ergebnis/phpstan-rules/pull/939 +[#940]: https://github.com/ergebnis/phpstan-rules/pull/940 +[#943]: https://github.com/ergebnis/phpstan-rules/pull/943 +[#944]: https://github.com/ergebnis/phpstan-rules/pull/944 +[#947]: https://github.com/ergebnis/phpstan-rules/pull/947 +[#948]: https://github.com/ergebnis/phpstan-rules/pull/948 +[#949]: https://github.com/ergebnis/phpstan-rules/pull/949 +[#951]: https://github.com/ergebnis/phpstan-rules/pull/951 +[#957]: https://github.com/ergebnis/phpstan-rules/pull/957 +[#958]: https://github.com/ergebnis/phpstan-rules/pull/958 + +[@cosmastech]: https://github.com/cosmastech +[@enumag]: https://github.com/enumag +[@ergebnis]: https://github.com/ergebnis +[@Great-Antique]: https://github.com/Great-Antique +[@localheinz]: https://github.com/localheinz +[@manuelkiessling]: https://github.com/manuelkiessling +[@nunomaduro]: https://github.com/nunomaduro +[@rpkamp]: https://github.com/rpkamp +[@Slamdunk]: https://github.com/Slamdunk diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/LICENSE.md b/tools/.phpstan/vendor/ergebnis/phpstan-rules/LICENSE.md new file mode 100644 index 000000000..130e719a2 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/LICENSE.md @@ -0,0 +1,16 @@ +# The MIT License (MIT) + +Copyright (c) 2018-2025 Andreas Möller + +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. diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md b/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md new file mode 100644 index 000000000..7f80534b7 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md @@ -0,0 +1,760 @@ +# phpstan-rules + +[![Integrate](https://github.com/ergebnis/phpstan-rules/workflows/Integrate/badge.svg)](https://github.com/ergebnis/phpstan-rules/actions) +[![Merge](https://github.com/ergebnis/phpstan-rules/workflows/Merge/badge.svg)](https://github.com/ergebnis/phpstan-rules/actions) +[![Release](https://github.com/ergebnis/phpstan-rules/workflows/Release/badge.svg)](https://github.com/ergebnis/phpstan-rules/actions) +[![Renew](https://github.com/ergebnis/phpstan-rules/workflows/Renew/badge.svg)](https://github.com/ergebnis/phpstan-rules/actions) + +[![Code Coverage](https://codecov.io/gh/ergebnis/phpstan-rules/branch/main/graph/badge.svg)](https://codecov.io/gh/ergebnis/phpstan-rules) + +[![Latest Stable Version](https://poser.pugx.org/ergebnis/phpstan-rules/v/stable)](https://packagist.org/packages/ergebnis/phpstan-rules) +[![Total Downloads](https://poser.pugx.org/ergebnis/phpstan-rules/downloads)](https://packagist.org/packages/ergebnis/phpstan-rules) +[![Monthly Downloads](http://poser.pugx.org/ergebnis/phpstan-rules/d/monthly)](https://packagist.org/packages/ergebnis/phpstan-rules) + +This project provides a [`composer`](https://getcomposer.org) package with rules for [`phpstan/phpstan`](https://github.com/phpstan/phpstan). + +## Installation + +Run + +```sh +composer require --dev ergebnis/phpstan-rules +``` + +## Usage + +All of the [rules](https://github.com/ergebnis/phpstan-rules#rules) provided (and used) by this library are included in [`rules.neon`](rules.neon). + +When you are using [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer), `rules.neon` will be automatically included. + +Otherwise you need to include `rules.neon` in your `phpstan.neon`: + +```neon +includes: + - vendor/ergebnis/phpstan-rules/rules.neon +``` + +:bulb: You probably want to use these rules on top of the rules provided by: + +- [`phpstan/phpstan`](https://github.com/phpstan/phpstan) +- [`phpstan/phpstan-deprecation-rules`](https://github.com/phpstan/phpstan-deprecation-rules) +- [`phpstan/phpstan-strict-rules`](https://github.com/phpstan/phpstan-strict-rules) + +## Rules + +This package provides the following rules for use with [`phpstan/phpstan`](https://github.com/phpstan/phpstan): + +- [`Ergebnis\PHPStan\Rules\CallLikes\NoNamedArgumentRule`](https://github.com/ergebnis/phpstan-rules#calllikesnonamedargumentrule) +- [`Ergebnis\PHPStan\Rules\Classes\FinalRule`](https://github.com/ergebnis/phpstan-rules#classesfinalrule) +- [`Ergebnis\PHPStan\Rules\Classes\NoExtendsRule`](https://github.com/ergebnis/phpstan-rules#classesnoextendsrule) +- [`Ergebnis\PHPStan\Rules\Classes\PHPUnit\Framework\TestCaseWithSuffixRule`](https://github.com/ergebnis/phpstan-rules#classesphpunitframeworktestcasewithsuffixrule) +- [`Ergebnis\PHPStan\Rules\Closures\NoNullableReturnTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#closuresnonullablereturntypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Closures\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterpassedbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterwithnullabletypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterwithnulldefaultvaluerule) +- [`Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoassignbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Expressions\NoCompactRule`](https://github.com/ergebnis/phpstan-rules#expressionsnocompactrule) +- [`Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoerrorsuppressionrule) +- [`Ergebnis\PHPStan\Rules\Expressions\NoEvalRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoevalrule) +- [`Ergebnis\PHPStan\Rules\Expressions\NoIssetRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoissetrule) +- [`Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule`](https://github.com/ergebnis/phpstan-rules#filesdeclarestricttypesrule) +- [`Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#functionsnonullablereturntypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterpassedbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterwithnullabletypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterwithnulldefaultvaluerule) +- [`Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule`](https://github.com/ergebnis/phpstan-rules#functionsnoreturnbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule`](https://github.com/ergebnis/phpstan-rules#methodsfinalinabstractclassrule) +- [`Ergebnis\PHPStan\Rules\Methods\InvokeParentHookMethodRule`](https://github.com/ergebnis/phpstan-rules#methodsinvokeparenthookmethodrule) +- [`Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#methodsnoconstructorparameterwithdefaultvaluerule) +- [`Ergebnis\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnonullablereturntypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Methods\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterpassedbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithContainerTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithcontainertypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithnullabletypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithnulldefaultvaluerule) +- [`Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule`](https://github.com/ergebnis/phpstan-rules#methodsnoreturnbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule`](https://github.com/ergebnis/phpstan-rules#methodsprivateinfinalclassrule) +- [`Ergebnis\PHPStan\Rules\Statements\NoSwitchRule`](https://github.com/ergebnis/phpstan-rules#statementsnoswitchrule) + + +### CallLikes + +#### `CallLikes\NoNamedArgumentRule` + +This rule reports an error when an anonymous function, a function, or a method is invoked using a [named argument](https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noNamedArgument: + enabled: false +``` + +### Classes + +#### `Classes\FinalRule` + +This rule reports an error when a non-anonymous class is not `final`. + +:bulb: This rule ignores classes that + +- use `@Entity`, `@ORM\Entity`, or `@ORM\Mapping\Entity` annotations +- use `Doctrine\ORM\Mapping\Entity` attributes + +on the class level. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + final: + enabled: false +``` + +##### Disallowing `abstract` classes + +By default, this rule allows to declare `abstract` classes. + +You can set the `allowAbstractClasses` parameter to `false` to disallow abstract classes. + +```neon +parameters: + ergebnis: + final: + allowAbstractClasses: false +``` + +##### Excluding classes from inspection + +You can set the `classesNotRequiredToBeAbstractOrFinal` parameter to a list of class names that you want to exclude from inspection. + +```neon +parameters: + ergebnis: + final: + classesNotRequiredToBeAbstractOrFinal: + - Foo\Bar\NeitherAbstractNorFinal + - Bar\Baz\NeitherAbstractNorFinal +``` + +#### `Classes\NoExtendsRule` + +This rule reports an error when a class extends another class. + +##### Defaults + +By default, this rule allows the following classes to be extended: + +- [`PHPUnit\Framework\TestCase`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php) + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noExtends: + enabled: false +``` + +##### Allowing classes to be extended + +You can set the `classesAllowedToBeExtended` parameter to a list of class names that you want to allow to be extended. + +```neon +parameters: + ergebnis: + noExtends: + classesAllowedToBeExtended: + - Ergebnis\PHPStan\Rules\Test\Integration\AbstractTestCase + - Ergebnis\PHPStan\Rules\Test\Integration\AbstractTestCase +``` + +#### `Classes\PHPUnit\Framework\TestCaseWithSuffixRule` + +This rule reports an error when a concrete class is a sub-class of `PHPUnit\Framework\TestCase` but does not have a `Test` suffix. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + testCaseWithSuffix: + enabled: false +``` + +### Closures + +#### `Closures\NoNullableReturnTypeDeclarationRule` + +This rule reports an error when a closure uses a nullable return type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noNullableReturnTypeDeclaration: + enabled: false +``` + +#### `Closures\NoParameterPassedByReferenceRule` + +This rule reports an error when a closure has a parameter that is [passed by reference](https://www.php.net/manual/en/language.references.pass.php). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterPassedByReference: + enabled: false +``` + +#### `Closures\NoParameterWithNullableTypeDeclarationRule` + +This rule reports an error when a closure has a parameter with a nullable type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullableTypeDeclaration: + enabled: false +``` + +#### `Closures\NoParameterWithNullDefaultValueRule` + +This rule reports an error when a closure has a parameter with `null` as default value. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullDefaultValue: + enabled: false +``` + +### Expressions + +#### `Expressions\NoAssignByReferenceRule` + +This rule reports an error when [a variable is assigned by reference](https://www.php.net/manual/en/language.references.whatdo.php#language.references.whatdo.assign). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noAssignByReference: + enabled: false +``` + +#### `Expressions\NoCompactRule` + +This rule reports an error when the function [`compact()`](https://www.php.net/compact) is used. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noCompact: + enabled: false +``` + +#### `Expressions\NoErrorSuppressionRule` + +This rule reports an error when [`@`](https://www.php.net/manual/en/language.operators.errorcontrol.php) is used to suppress errors. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noErrorSuppression: + enabled: false +``` + +#### `Expressions\NoEvalRule` + +This rule reports an error when the language construct [`eval()`](https://www.php.net/eval) is used. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noEval: + enabled: false +``` + +#### `Expressions\NoIssetRule` + +This rule reports an error when the language construct [`isset()`](https://www.php.net/isset) is used. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noIsset: + enabled: false +``` + +### Files + +#### `Files\DeclareStrictTypesRule` + +This rule reports an error when a non-empty file does not contain a `declare(strict_types=1)` declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + declareStrictTypes: + enabled: false +``` + +### Functions + +#### `Functions\NoNullableReturnTypeDeclarationRule` + +This rule reports an error when a function uses a nullable return type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noNullableReturnTypeDeclaration: + enabled: false +``` + +#### `Functions\NoParameterPassedByReferenceRule` + +This rule reports an error when a function has a parameter that is [passed by reference](https://www.php.net/manual/en/language.references.pass.php). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterPassedByReference: + enabled: false +``` + +#### `Functions\NoParameterWithNullableTypeDeclarationRule` + +This rule reports an error when a function has a parameter with a nullable type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullableTypeDeclaration: + enabled: false +``` + +#### `Functions\NoParameterWithNullDefaultValueRule` + +This rule reports an error when a function has a parameter with `null` as default value. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullDefaultValue: + enabled: false +``` + +#### `Functions\NoReturnByReferenceRule` + +This rule reports an error when a function [returns by reference](https://www.php.net/manual/en/language.references.return.php). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noReturnByReference: + enabled: false +``` + +### Methods + +#### `Methods\FinalInAbstractClassRule` + +This rule reports an error when a concrete `public` or `protected` method in an `abstract` class is not `final`. + +:bulb: This rule ignores + +- Doctrine embeddables +- Doctrine entities + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + finalInAbstractClass: + enabled: false +``` + +#### `Methods\InvokeParentHookMethodRule` + +This rule reports an error when a hook method that overrides a hook method in a parent class does not invoke the overridden hook method in the expected order. + +##### Defaults + +By default, this rule requires the following hook methods to be invoked before doing something in the overriding method: + +- [`Codeception\PHPUnit\TestCase::_setUp()`](https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L11-L13) +- [`Codeception\PHPUnit\TestCase::_setUpBeforeClass()`](https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L25-L27) +- [`Codeception\Test\Unit::_before()`](https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L63-L65) +- [`Codeception\Test\Unit::_setUp()`](https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L34-L58) +- [`PHPUnit\Framework\TestCase::assertPreConditions()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2073-L2075) +- [`PHPUnit\Framework\TestCase::setUp()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2063-L2065) +- [`PHPUnit\Framework\TestCase::setUpBeforeClass()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2055-L2057) + +By default, this rule requires the following hook methods to be invoked after doing something in the overriding method: + +- [`Codeception\PHPUnit\TestCase::_tearDown()`](https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L18-L20) +- [`Codeception\PHPUnit\TestCase::_tearDownAfterClass()`](https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L32-L34) +- [`Codeception\Test\Unit::_after()`](https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L75-L77) +- [`Codeception\Test\Unit::_tearDown()`](https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L67-L70) +- [`PHPUnit\Framework\TestCase::assertPostConditions()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2083-L2085) +- [`PHPUnit\Framework\TestCase::tearDown()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2091-L2093) +- [`PHPUnit\Framework\TestCase::tearDownAfterClass()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2098-L2100) + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + invokeParentHookMethod: + enabled: false +``` + +##### Configuring methods to invoke the parent method in the right order: + +You can set the `hookMethods` parameter to a list of hook methods: + +```neon +parameters: + ergebnis: + invokeParentHookMethod: + hookMethods: + - className: "Example\Test\Functional\AbstractCest" + methodName: "_before" + hasContent: "yes" + invocation: "first" +``` + +- `className`: name of the class that declares the hook method +- `methodName`: name of the hook method +- `hasContent`: one of `"yes"`, `"no"`, `"maybe"` +- `invocation`: one of `"any"` (needs to be invoked), `"first"` (needs to be invoked before all other statements in the overriding hook method, `"last"` (needs to be invoked after all other statements in the overriding hook method) + +#### `Methods\NoConstructorParameterWithDefaultValueRule` + +This rule reports an error when a constructor declared in + +- an anonymous class +- a class + +has a default value. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noConstructorParameterWithDefaultValue: + enabled: false +``` + +#### `Methods\NoParameterPassedByReferenceRule` + +This rule reports an error when a method has a parameter that is [passed by reference](https://www.php.net/manual/en/language.references.pass.php). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterPassedByReference: + enabled: false +``` + +#### `Methods\NoNullableReturnTypeDeclarationRule` + +This rule reports an error when a method declared in + +- an anonymous class +- a class +- an interface + +uses a nullable return type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noNullableReturnTypeDeclaration: + enabled: false +``` + +#### `Methods\NoParameterWithContainerTypeDeclarationRule` + +This rule reports an error when a method has a type declaration for a known dependency injection container or service locator. + +##### Defaults + +By default, this rule disallows the use of type declarations indicating an implementation of + +- [`Psr\Container\ContainerInterface`](https://github.com/php-fig/container/blob/1.0.0/src/ContainerInterface.php) + +is expected to be injected into a method. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithContainerTypeDeclaration: + enabled: false +``` + +##### Configuring container interfaces + +You can set the `interfacesImplementedByContainers` parameter to a list of interface names of additional containers and service locators. + +```neon +parameters: + ergebnis: + noParameterWithContainerTypeDeclaration: + interfacesImplementedByContainers: + - Fancy\DependencyInjection\ContainerInterface + - Other\ServiceLocatorInterface +``` + +##### Configuring methods allowed to use parameters with container type declarations + +You can set the `methodsAllowedToUseContainerTypeDeclarations` parameter to a list of method names that are allowed to use parameters with container type declarations. + +```neon +parameters: + ergebnis: + noParameterWithContainerTypeDeclaration: + methodsAllowedToUseContainerTypeDeclarations: + - loadExtension +``` + +#### `Methods\NoParameterWithNullableTypeDeclarationRule` + +This rule reports an error when a method declared in + +- an anonymous class +- a class +- an interface + +has a parameter with a nullable type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullableTypeDeclaration: + enabled: false +``` + +#### `Methods\NoParameterWithNullDefaultValueRule` + +This rule reports an error when a method declared in + +- an anonymous class +- a class +- an interface + +has a parameter with `null` as default value. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullDefaultValue: + enabled: false +``` + +#### `Functions\NoReturnByReferenceRule` + +This rule reports an error when a method [returns by reference](https://www.php.net/manual/en/language.references.return.php). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noReturnByReference: + enabled: false +``` + +#### `Methods\PrivateInFinalClassRule` + +This rule reports an error when a method in a `final` class is `protected` but could be `private`. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + privateInFinalClass: + enabled: false +``` + +### Statements + +#### `Statements\NoSwitchRule` + +This rule reports an error when the statement [`switch()`](https://www.php.net/manual/control-structures.switch.php) is used. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noSwitch: + enabled: false +``` + +## Disabling all rules + +You can disable all rules using the `allRules` configuration parameter: + +```neon +parameters: + ergebnis: + allRules: false +``` + +## Enabling rules one-by-one + +If you have disabled all rules using the `allRules` configuration parameter, you can re-enable individual rules with their corresponding configuration parameters: + +```neon +parameters: + ergebnis: + allRules: false + privateInFinalClass: + enabled: true +``` + +## Changelog + +The maintainers of this project record notable changes to this project in a [changelog](CHANGELOG.md). + +## Contributing + +The maintainers of this project suggest following the [contribution guide](.github/CONTRIBUTING.md). + +## Code of Conduct + +The maintainers of this project ask contributors to follow the [code of conduct](https://github.com/ergebnis/.github/blob/main/CODE_OF_CONDUCT.md). + +## General Support Policy + +The maintainers of this project provide limited support. + +You can support the maintenance of this project by [sponsoring @localheinz](https://github.com/sponsors/localheinz) or [requesting an invoice for services related to this project](mailto:am@localheinz.com?subject=ergebnis/phpstan-rules:%20Requesting%20invoice%20for%20services). + +## PHP Version Support Policy + +This project supports PHP versions with [active and security support](https://www.php.net/supported-versions.php). + +The maintainers of this project add support for a PHP version following its initial release and drop support for a PHP version when it has reached the end of security support. + +## Security Policy + +This project has a [security policy](.github/SECURITY.md). + +## License + +This project uses the [MIT license](LICENSE.md). + +## Credits + +The method [`FinalRule::isWhitelistedClass()`](src/Classes/FinalRule.php) is inspired by the work on [`FinalClassFixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalClassFixer.php) and [`FinalInternalClassFixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalInternalClassFixer.php), contributed by [Dariusz Rumiński](https://github.com/keradus), [Filippo Tessarotto](https://github.com/Slamdunk), and [Spacepossum](https://github.com/SpacePossum) for [`friendsofphp/php-cs-fixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer) (originally licensed under MIT). + +## Social + +Follow [@localheinz](https://twitter.com/intent/follow?screen_name=localheinz) and [@ergebnis](https://twitter.com/intent/follow?screen_name=ergebnis) on Twitter. diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json b/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json new file mode 100644 index 000000000..10429599c --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json @@ -0,0 +1,78 @@ +{ + "name": "ergebnis/phpstan-rules", + "description": "Provides rules for phpstan/phpstan.", + "license": "MIT", + "type": "phpstan-extension", + "keywords": [ + "phpstan", + "phpstan-rules" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "homepage": "https://github.com/ergebnis/phpstan-rules", + "support": { + "issues": "https://github.com/ergebnis/phpstan-rules/issues", + "source": "https://github.com/ergebnis/phpstan-rules", + "security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md" + }, + "require": { + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "ext-mbstring": "*", + "phpstan/phpstan": "^2.1.8" + }, + "require-dev": { + "codeception/codeception": "^4.0.0 || ^5.0.0", + "doctrine/orm": "^2.20.0 || ^3.3.0", + "ergebnis/composer-normalize": "^2.47.0", + "ergebnis/license": "^2.6.0", + "ergebnis/php-cs-fixer-config": "^6.46.0", + "ergebnis/phpunit-slow-test-detector": "^2.19.1", + "fakerphp/faker": "^1.24.1", + "nette/di": "^3.1.10", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.6", + "phpstan/phpstan-strict-rules": "^2.0.4", + "phpunit/phpunit": "^9.6.21", + "psr/container": "^2.0.2", + "symfony/finder": "^5.4.45", + "symfony/process": "^5.4.47" + }, + "autoload": { + "psr-4": { + "Ergebnis\\PHPStan\\Rules\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Ergebnis\\PHPStan\\Rules\\Test\\": "test/" + } + }, + "config": { + "allow-plugins": { + "ergebnis/composer-normalize": true, + "infection/extension-installer": true, + "phpstan/extension-installer": true + }, + "audit": { + "abandoned": "report" + }, + "platform": { + "php": "7.4.33" + }, + "preferred-install": "dist", + "sort-packages": true + }, + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/rules.neon b/tools/.phpstan/vendor/ergebnis/phpstan-rules/rules.neon new file mode 100644 index 000000000..0458e5a34 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/rules.neon @@ -0,0 +1,292 @@ +conditionalTags: + Ergebnis\PHPStan\Rules\CallLikes\NoNamedArgumentRule: + phpstan.rules.rule: %ergebnis.noNamedArgument.enabled% + Ergebnis\PHPStan\Rules\Classes\FinalRule: + phpstan.rules.rule: %ergebnis.final.enabled% + Ergebnis\PHPStan\Rules\Classes\NoExtendsRule: + phpstan.rules.rule: %ergebnis.noExtends.enabled% + Ergebnis\PHPStan\Rules\Classes\PHPUnit\Framework\TestCaseWithSuffixRule: + phpstan.rules.rule: %ergebnis.testCaseWithSuffix.enabled% + Ergebnis\PHPStan\Rules\Closures\NoNullableReturnTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noNullableReturnTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Closures\NoParameterPassedByReferenceRule: + phpstan.rules.rule: %ergebnis.noParameterPassedByReference.enabled% + Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule: + phpstan.rules.rule: %ergebnis.noAssignByReference.enabled% + Ergebnis\PHPStan\Rules\Expressions\NoCompactRule: + phpstan.rules.rule: %ergebnis.noCompact.enabled% + Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule: + phpstan.rules.rule: %ergebnis.noErrorSuppression.enabled% + Ergebnis\PHPStan\Rules\Expressions\NoEvalRule: + phpstan.rules.rule: %ergebnis.noEval.enabled% + Ergebnis\PHPStan\Rules\Expressions\NoIssetRule: + phpstan.rules.rule: %ergebnis.noIsset.enabled% + Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule: + phpstan.rules.rule: %ergebnis.declareStrictTypes.enabled% + Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noNullableReturnTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule: + phpstan.rules.rule: %ergebnis.noParameterPassedByReference.enabled% + Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule: + phpstan.rules.rule: %ergebnis.noParameterWithNullDefaultValue.enabled% + Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule: + phpstan.rules.rule: %ergebnis.noReturnByReference.enabled% + Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule: + phpstan.rules.rule: %ergebnis.finalInAbstractClass.enabled% + Ergebnis\PHPStan\Rules\Methods\InvokeParentHookMethodRule: + phpstan.rules.rule: %ergebnis.invokeParentHookMethod.enabled% + Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule: + phpstan.rules.rule: %ergebnis.noConstructorParameterWithDefaultValue.enabled% + Ergebnis\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noNullableReturnTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Methods\NoParameterPassedByReferenceRule: + phpstan.rules.rule: %ergebnis.noParameterPassedByReference.enabled% + Ergebnis\PHPStan\Rules\Methods\NoParameterWithContainerTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noParameterWithContainerTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule: + phpstan.rules.rule: %ergebnis.noParameterWithNullDefaultValue.enabled% + Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule: + phpstan.rules.rule: %ergebnis.noReturnByReference.enabled% + Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule: + phpstan.rules.rule: %ergebnis.privateInFinalClass.enabled% + Ergebnis\PHPStan\Rules\Statements\NoSwitchRule: + phpstan.rules.rule: %ergebnis.noSwitch.enabled% + +parameters: + ergebnis: + allRules: true + declareStrictTypes: + enabled: %ergebnis.allRules% + final: + allowAbstractClasses: true + classesNotRequiredToBeAbstractOrFinal: [] + enabled: %ergebnis.allRules% + finalInAbstractClass: + enabled: %ergebnis.allRules% + invokeParentHookMethod: + enabled: %ergebnis.allRules% + hookMethods: [] + noAssignByReference: + enabled: %ergebnis.allRules% + noCompact: + enabled: %ergebnis.allRules% + noConstructorParameterWithDefaultValue: + enabled: %ergebnis.allRules% + noErrorSuppression: + enabled: %ergebnis.allRules% + noEval: + enabled: %ergebnis.allRules% + noExtends: + classesAllowedToBeExtended: [] + enabled: %ergebnis.allRules% + noIsset: + enabled: %ergebnis.allRules% + noNamedArgument: + enabled: %ergebnis.allRules% + noNullableReturnTypeDeclaration: + enabled: %ergebnis.allRules% + noParameterPassedByReference: + enabled: %ergebnis.allRules% + noParameterWithContainerTypeDeclaration: + enabled: %ergebnis.allRules% + interfacesImplementedByContainers: + - Psr\Container\ContainerInterface + methodsAllowedToUseContainerTypeDeclarations: [] + noParameterWithNullableTypeDeclaration: + enabled: %ergebnis.allRules% + noParameterWithNullDefaultValue: + enabled: %ergebnis.allRules% + noReturnByReference: + enabled: %ergebnis.allRules% + noSwitch: + enabled: %ergebnis.allRules% + privateInFinalClass: + enabled: %ergebnis.allRules% + testCaseWithSuffix: + enabled: %ergebnis.allRules% + +parametersSchema: + ergebnis: structure([ + allRules: bool() + declareStrictTypes: structure([ + enabled: bool(), + ]) + final: structure([ + allowAbstractClasses: bool() + classesNotRequiredToBeAbstractOrFinal: listOf(string()) + enabled: bool(), + ]) + finalInAbstractClass: structure([ + enabled: bool(), + ]) + invokeParentHookMethod: structure([ + enabled: bool(), + hookMethods: listOf(structure([ + className: string(), + hasContent: anyOf("no", "yes"), + invocation: anyOf("any", "first", "last"), + methodName: string(), + ])) + ]) + noAssignByReference: structure([ + enabled: bool(), + ]) + noCompact: structure([ + enabled: bool(), + ]) + noConstructorParameterWithDefaultValue: structure([ + enabled: bool(), + ]) + noErrorSuppression: structure([ + enabled: bool(), + ]) + noExtends: structure([ + classesAllowedToBeExtended: listOf(string()) + enabled: bool(), + ]) + noEval: structure([ + enabled: bool(), + ]) + noIsset: structure([ + enabled: bool(), + ]) + noNamedArgument: structure([ + enabled: bool(), + ]) + noNullableReturnTypeDeclaration: structure([ + enabled: bool(), + ]) + noParameterPassedByReference: structure([ + enabled: bool(), + ]) + noParameterWithContainerTypeDeclaration: structure([ + enabled: bool(), + interfacesImplementedByContainers: listOf(string()) + methodsAllowedToUseContainerTypeDeclarations: listOf(string()) + ]) + noParameterWithNullableTypeDeclaration: structure([ + enabled: bool(), + ]) + noParameterWithNullDefaultValue: structure([ + enabled: bool(), + ]) + noReturnByReference: structure([ + enabled: bool(), + ]) + noSwitch: structure([ + enabled: bool(), + ]) + privateInFinalClass: structure([ + enabled: bool(), + ]) + testCaseWithSuffix: structure([ + enabled: bool(), + ]) + ]) + +services: + - + class: Ergebnis\PHPStan\Rules\Analyzer + + - + class: Ergebnis\PHPStan\Rules\CallLikes\NoNamedArgumentRule + + - + class: Ergebnis\PHPStan\Rules\Classes\FinalRule + arguments: + allowAbstractClasses: %ergebnis.final.allowAbstractClasses% + classesNotRequiredToBeAbstractOrFinal: %ergebnis.final.classesNotRequiredToBeAbstractOrFinal% + + - + class: Ergebnis\PHPStan\Rules\Classes\NoExtendsRule + arguments: + classesAllowedToBeExtended: %ergebnis.noExtends.classesAllowedToBeExtended% + + - + class: Ergebnis\PHPStan\Rules\Classes\PHPUnit\Framework\TestCaseWithSuffixRule + + - + class: Ergebnis\PHPStan\Rules\Closures\NoNullableReturnTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Closures\NoParameterPassedByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Expressions\NoCompactRule + + - + class: Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule + + - + class: Ergebnis\PHPStan\Rules\Expressions\NoEvalRule + + - + class: Ergebnis\PHPStan\Rules\Expressions\NoIssetRule + + - + class: Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule + + - + class: Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule + + - + class: Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule + + - + class: Ergebnis\PHPStan\Rules\Methods\InvokeParentHookMethodRule + arguments: + hookMethods: %ergebnis.invokeParentHookMethod.hookMethods% + + - + class: Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule + + - + class: Ergebnis\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Methods\NoParameterPassedByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Methods\NoParameterWithContainerTypeDeclarationRule + arguments: + interfacesImplementedByContainers: %ergebnis.noParameterWithContainerTypeDeclaration.interfacesImplementedByContainers% + methodsAllowedToUseContainerTypeDeclarations: %ergebnis.noParameterWithContainerTypeDeclaration.methodsAllowedToUseContainerTypeDeclarations% + + - + class: Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule + + - + class: Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule + + - + class: Ergebnis\PHPStan\Rules\Statements\NoSwitchRule diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Analyzer.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Analyzer.php new file mode 100644 index 000000000..938cab762 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Analyzer.php @@ -0,0 +1,68 @@ +default instanceof Node\Expr\ConstFetch) { + return false; + } + + return 'null' === $parameter->default->name->toLowerString(); + } + + /** + * @param null|Node\ComplexType|Node\Identifier|Node\Name $typeDeclaration + */ + public function isNullableTypeDeclaration($typeDeclaration): bool + { + if ($typeDeclaration instanceof Node\NullableType) { + return true; + } + + if ($typeDeclaration instanceof Node\UnionType) { + foreach ($typeDeclaration->types as $type) { + if ( + $type instanceof Node\Identifier + && 'null' === $type->toLowerString() + ) { + return true; + } + + if ( + $type instanceof Node\Name\FullyQualified + && $type->hasAttribute('originalName') + ) { + $originalName = $type->getAttribute('originalName'); + + if ( + $originalName instanceof Node\Name + && 'null' === $originalName->toLowerString() + ) { + return true; + } + } + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/CallLikes/NoNamedArgumentRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/CallLikes/NoNamedArgumentRule.php new file mode 100644 index 000000000..a87bbaffa --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/CallLikes/NoNamedArgumentRule.php @@ -0,0 +1,175 @@ + + */ +final class NoNamedArgumentRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\CallLike::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->getArgs())) { + return []; + } + + /** @var list $namedArguments */ + $namedArguments = \array_values(\array_filter($node->getArgs(), static function (Node\Arg $argument): bool { + if (!$argument->name instanceof Node\Identifier) { + return false; + } + + return true; + })); + + if (0 === \count($namedArguments)) { + return []; + } + + $callLike = self::describeCallLike( + $node, + $scope, + ); + + return \array_map(static function (Node\Arg $namedArgument) use ($callLike): Rules\RuleError { + /** @var Node\Identifier $argumentName */ + $argumentName = $namedArgument->name; + + $message = \sprintf( + '%s is invoked with named argument for parameter $%s.', + $callLike, + $argumentName->toString(), + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noNamedArgument()->toString()) + ->build(); + }, $namedArguments); + } + + private static function describeCallLike( + Node\Expr\CallLike $node, + Analyser\Scope $scope + ): string { + if ($node instanceof Node\Expr\FuncCall) { + $functionName = $node->name; + + if ($functionName instanceof Node\Expr\PropertyFetch) { + return \sprintf( + 'Callable referenced by property $%s', + $functionName->name, + ); + } + + if ($functionName instanceof Node\Expr\Variable) { + return \sprintf( + 'Callable referenced by $%s', + $functionName->name, + ); + } + + if ($functionName instanceof Node\Name) { + return \sprintf( + 'Function %s()', + $functionName->name, + ); + } + } + + if ($node instanceof Node\Expr\MethodCall) { + $methodName = $node->name; + + if ($methodName instanceof Node\Identifier) { + $objectType = $scope->getType($node->var); + + $methodReflection = $scope->getMethodReflection( + $objectType, + $methodName->name, + ); + + if (null === $methodReflection) { + throw new ShouldNotHappenException(); + } + + $declaringClass = $methodReflection->getDeclaringClass(); + + if ($declaringClass->isAnonymous()) { + return \sprintf( + 'Method %s() of anonymous class', + $methodName->toString(), + ); + } + + return \sprintf( + 'Method %s::%s()', + $declaringClass->getName(), + $methodName->toString(), + ); + } + + return 'Method'; + } + + if ($node instanceof Node\Expr\StaticCall) { + $methodName = $node->name; + + if ($methodName instanceof Node\Identifier) { + $className = $node->class; + + if ($className instanceof Node\Name) { + return \sprintf( + 'Method %s::%s()', + $className->toString(), + $methodName->toString(), + ); + } + + return \sprintf( + 'Method %s()', + $methodName->toString(), + ); + } + + return 'Method'; + } + + if ($node instanceof Node\Expr\New_) { + $className = $node->class; + + if ($className instanceof Node\Name) { + return \sprintf( + 'Constructor of %s', + $className->toString(), + ); + } + + return 'Constructor'; + } + + return 'Callable'; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ClassName.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ClassName.php new file mode 100644 index 000000000..e62b9083b --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ClassName.php @@ -0,0 +1,51 @@ +value = $value; + } + + /** + * @param class-string $value + */ + public static function fromString(string $value): self + { + return new self($value); + } + + /** + * @return class-string + */ + public function toString(): string + { + return $this->value; + } + + public function equals(self $other): bool + { + return $this->value === $other->value; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php new file mode 100644 index 000000000..937bf0ec2 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php @@ -0,0 +1,162 @@ + + */ +final class FinalRule implements Rules\Rule +{ + /** + * @var list + */ + private static array $whitelistedAnnotations = [ + 'Entity', + 'ORM\Entity', + 'ORM\Mapping\Entity', + ]; + + /** + * @var list + */ + private static array $whitelistedAttributes = [ + ORM\Mapping\Entity::class, + ]; + private bool $allowAbstractClasses; + + /** + * @var list + */ + private array $classesNotRequiredToBeAbstractOrFinal; + private string $errorMessageTemplate = 'Class %s is not final.'; + + /** + * @param list $classesNotRequiredToBeAbstractOrFinal + */ + public function __construct( + bool $allowAbstractClasses, + array $classesNotRequiredToBeAbstractOrFinal + ) { + $this->allowAbstractClasses = $allowAbstractClasses; + $this->classesNotRequiredToBeAbstractOrFinal = \array_map(static function (string $classNotRequiredToBeAbstractOrFinal): string { + return $classNotRequiredToBeAbstractOrFinal; + }, $classesNotRequiredToBeAbstractOrFinal); + + if ($allowAbstractClasses) { + $this->errorMessageTemplate = 'Class %s is neither abstract nor final.'; + } + } + + public function getNodeType(): string + { + return Node\Stmt\Class_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!isset($node->namespacedName)) { + return []; + } + + if (\in_array($node->namespacedName->toString(), $this->classesNotRequiredToBeAbstractOrFinal, true)) { + return []; + } + + if ( + $this->allowAbstractClasses + && $node->isAbstract() + ) { + return []; + } + + if ($node->isFinal()) { + return []; + } + + if ($this->hasWhitelistedAnnotation($node)) { + return []; + } + + if ($this->hasWhitelistedAttribute($node)) { + return []; + } + + $message = \sprintf( + $this->errorMessageTemplate, + $node->namespacedName->toString(), + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::final()->toString()) + ->build(), + ]; + } + + /** + * This method is inspired by the work on PhpCsFixer\Fixer\ClassNotation\FinalClassFixer and + * PhpCsFixer\Fixer\ClassNotation\FinalInternalClassFixer contributed by Dariusz Rumiński, Filippo Tessarotto, and + * Spacepossum for friendsofphp/php-cs-fixer. + * + * @see https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalClassFixer.php + * @see https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalInternalClassFixer.php + * @see https://github.com/keradus + * @see https://github.com/SpacePossum + * @see https://github.com/Slamdunk + */ + private function hasWhitelistedAnnotation(Node\Stmt\Class_ $node): bool + { + $docComment = $node->getDocComment(); + + if (!$docComment instanceof Comment\Doc) { + return false; + } + + $reformattedComment = $docComment->getReformattedText(); + + if (\is_int(\preg_match_all('/@(\S+)(?=\s|$)/', $reformattedComment, $matches))) { + foreach ($matches[1] as $annotation) { + foreach (self::$whitelistedAnnotations as $whitelistedAnnotation) { + if (0 === \mb_strpos($annotation, $whitelistedAnnotation)) { + return true; + } + } + } + } + + return false; + } + + private function hasWhitelistedAttribute(Node\Stmt\Class_ $node): bool + { + foreach ($node->attrGroups as $attributeGroup) { + foreach ($attributeGroup->attrs as $attribute) { + if (\in_array($attribute->name->toString(), self::$whitelistedAttributes, true)) { + return true; + } + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/NoExtendsRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/NoExtendsRule.php new file mode 100644 index 000000000..4d4864e92 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/NoExtendsRule.php @@ -0,0 +1,99 @@ + + */ +final class NoExtendsRule implements Rules\Rule +{ + /** + * @var list + */ + private static array $defaultClassesAllowedToBeExtended = [ + Framework\TestCase::class, + ]; + + /** + * @var list + */ + private array $classesAllowedToBeExtended; + + /** + * @param list $classesAllowedToBeExtended + */ + public function __construct(array $classesAllowedToBeExtended) + { + $this->classesAllowedToBeExtended = \array_values(\array_unique(\array_merge( + self::$defaultClassesAllowedToBeExtended, + \array_map(static function (string $classAllowedToBeExtended): string { + /** @var class-string $classAllowedToBeExtended */ + return $classAllowedToBeExtended; + }, $classesAllowedToBeExtended), + ))); + } + + public function getNodeType(): string + { + return Node\Stmt\Class_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!$node->extends instanceof Node\Name) { + return []; + } + + $extendedClassName = $node->extends->toString(); + + if (\in_array($extendedClassName, $this->classesAllowedToBeExtended, true)) { + return []; + } + + if (!isset($node->namespacedName)) { + $message = \sprintf( + 'Anonymous class is not allowed to extend "%s".', + $extendedClassName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noExtends()->toString()) + ->build(), + ]; + } + + $extendingClassName = $node->namespacedName->toString(); + + $message = \sprintf( + 'Class "%s" is not allowed to extend "%s".', + $extendingClassName, + $extendedClassName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noExtends()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/PHPUnit/Framework/TestCaseWithSuffixRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/PHPUnit/Framework/TestCaseWithSuffixRule.php new file mode 100644 index 000000000..4b6d47926 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/PHPUnit/Framework/TestCaseWithSuffixRule.php @@ -0,0 +1,95 @@ + + */ +final class TestCaseWithSuffixRule implements Rules\Rule +{ + /** + * @var list + */ + private static array $phpunitTestCaseClassNames = [ + 'PHPUnit\Framework\TestCase', + ]; + private Reflection\ReflectionProvider $reflectionProvider; + + public function __construct(Reflection\ReflectionProvider $reflectionProvider) + { + $this->reflectionProvider = $reflectionProvider; + } + + public function getNodeType(): string + { + return Node\Stmt\Class_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if ($node->isAbstract()) { + return []; + } + + if (!$node->extends instanceof Node\Name) { + return []; + } + + if (!isset($node->namespacedName)) { + return []; + } + + $fullyQualifiedClassName = $node->namespacedName->toString(); + + $classReflection = $this->reflectionProvider->getClass($fullyQualifiedClassName); + + $extendedPhpunitTestCaseClassName = ''; + + foreach (self::$phpunitTestCaseClassNames as $phpunitTestCaseClassName) { + if ($classReflection->isSubclassOfClass($this->reflectionProvider->getClass($phpunitTestCaseClassName))) { + $extendedPhpunitTestCaseClassName = $phpunitTestCaseClassName; + + break; + } + } + + if ('' === $extendedPhpunitTestCaseClassName) { + return []; + } + + if (1 === \preg_match('/Test$/', $fullyQualifiedClassName)) { + return []; + } + + $message = \sprintf( + 'Class %s extends %s, is concrete, but does not have a Test suffix.', + $fullyQualifiedClassName, + $extendedPhpunitTestCaseClassName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::testCaseWithSuffix()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoNullableReturnTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoNullableReturnTypeDeclarationRule.php new file mode 100644 index 000000000..643515791 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoNullableReturnTypeDeclarationRule.php @@ -0,0 +1,53 @@ + + */ +final class NoNullableReturnTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Expr\Closure::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!$this->analyzer->isNullableTypeDeclaration($node->getReturnType())) { + return []; + } + + return [ + Rules\RuleErrorBuilder::message('Closure has a nullable return type declaration.') + ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterPassedByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterPassedByReferenceRule.php new file mode 100644 index 000000000..2c47cb742 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterPassedByReferenceRule.php @@ -0,0 +1,64 @@ + + */ +final class NoParameterPassedByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\Closure::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersPassedByReference = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool { + return $parameter->byRef; + })); + + if (0 === \count($parametersPassedByReference)) { + return []; + } + + return \array_map(static function (Node\Param $parameterPassedByReference): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterPassedByReference->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Closure has parameter $%s that is passed by reference.', + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterPassedByReference()->toString()) + ->build(); + }, $parametersPassedByReference); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullDefaultValueRule.php new file mode 100644 index 000000000..94bd14635 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullDefaultValueRule.php @@ -0,0 +1,72 @@ + + */ +final class NoParameterWithNullDefaultValueRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Expr\Closure::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullDefaultValue = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->hasNullDefaultValue($parameter); + })); + + if (0 === \count($parametersWithNullDefaultValue)) { + return []; + } + + return \array_map(static function (Node\Param $parameterWithNullDefaultValue): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Closure has parameter $%s with null as default value.', + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString()) + ->build(); + }, $parametersWithNullDefaultValue); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullableTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullableTypeDeclarationRule.php new file mode 100644 index 000000000..3cff6ee6d --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullableTypeDeclarationRule.php @@ -0,0 +1,72 @@ + + */ +final class NoParameterWithNullableTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Expr\Closure::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullableTypeDeclaration = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->isNullableTypeDeclaration($parameter->type); + })); + + if (0 === \count($parametersWithNullableTypeDeclaration)) { + return []; + } + + return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullableTypeDeclaration->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Closure has parameter $%s with a nullable type declaration.', + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString()) + ->build(); + }, $parametersWithNullableTypeDeclaration); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ErrorIdentifier.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ErrorIdentifier.php new file mode 100644 index 000000000..3435c1998 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ErrorIdentifier.php @@ -0,0 +1,140 @@ +value = $value; + } + + public static function declareStrictTypes(): self + { + return new self('declareStrictTypes'); + } + + public static function final(): self + { + return new self('final'); + } + + public static function finalInAbstractClass(): self + { + return new self('finalInAbstractClass'); + } + + public static function invokeParentHookMethod(): self + { + return new self('invokeParentHookMethod'); + } + + public static function noCompact(): self + { + return new self('noCompact'); + } + + public static function noConstructorParameterWithDefaultValue(): self + { + return new self('noConstructorParameterWithDefaultValue'); + } + + public static function noAssignByReference(): self + { + return new self('noAssignByReference'); + } + + public static function noErrorSuppression(): self + { + return new self('noErrorSuppression'); + } + + public static function noEval(): self + { + return new self('noEval'); + } + + public static function noExtends(): self + { + return new self('noExtends'); + } + + public static function noIsset(): self + { + return new self('noIsset'); + } + + public static function noNamedArgument(): self + { + return new self('noNamedArgument'); + } + + public static function noParameterPassedByReference(): self + { + return new self('noParameterPassedByReference'); + } + + public static function noParameterWithContainerTypeDeclaration(): self + { + return new self('noParameterWithContainerTypeDeclaration'); + } + + public static function noParameterWithNullDefaultValue(): self + { + return new self('noParameterWithNullDefaultValue'); + } + + public static function noParameterWithNullableTypeDeclaration(): self + { + return new self('noParameterWithNullableTypeDeclaration'); + } + + public static function noNullableReturnTypeDeclaration(): self + { + return new self('noNullableReturnTypeDeclaration'); + } + + public static function noReturnByReference(): self + { + return new self('noReturnByReference'); + } + + public static function noSwitch(): self + { + return new self('noSwitch'); + } + + public static function privateInFinalClass(): self + { + return new self('privateInFinalClass'); + } + + public static function testCaseWithSuffix(): self + { + return new self('testCaseWithSuffix'); + } + + public function toString(): string + { + return \sprintf( + 'ergebnis.%s', + $this->value, + ); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoAssignByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoAssignByReferenceRule.php new file mode 100644 index 000000000..ad8cc1ef9 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoAssignByReferenceRule.php @@ -0,0 +1,41 @@ + + */ +final class NoAssignByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\AssignRef::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + return [ + Rules\RuleErrorBuilder::message('Assign by reference should not be used.') + ->identifier(ErrorIdentifier::noAssignByReference()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoCompactRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoCompactRule.php new file mode 100644 index 000000000..cd12ffea3 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoCompactRule.php @@ -0,0 +1,49 @@ + + */ +final class NoCompactRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\FuncCall::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!$node->name instanceof Node\Name) { + return []; + } + + if ('compact' !== \mb_strtolower($scope->resolveName($node->name))) { + return []; + } + + return [ + Rules\RuleErrorBuilder::message('Function compact() should not be used.') + ->identifier(ErrorIdentifier::noCompact()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoErrorSuppressionRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoErrorSuppressionRule.php new file mode 100644 index 000000000..49c0939c8 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoErrorSuppressionRule.php @@ -0,0 +1,41 @@ + + */ +final class NoErrorSuppressionRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\ErrorSuppress::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + return [ + Rules\RuleErrorBuilder::message('Error suppression via "@" should not be used.') + ->identifier(ErrorIdentifier::noErrorSuppression()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoEvalRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoEvalRule.php new file mode 100644 index 000000000..b54205787 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoEvalRule.php @@ -0,0 +1,41 @@ + + */ +final class NoEvalRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\Eval_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + return [ + Rules\RuleErrorBuilder::message('Language construct eval() should not be used.') + ->identifier(ErrorIdentifier::noEval()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoIssetRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoIssetRule.php new file mode 100644 index 000000000..f1443d667 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoIssetRule.php @@ -0,0 +1,41 @@ + + */ +final class NoIssetRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\Isset_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + return [ + Rules\RuleErrorBuilder::message('Language construct isset() should not be used.') + ->identifier(ErrorIdentifier::noIsset()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Files/DeclareStrictTypesRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Files/DeclareStrictTypesRule.php new file mode 100644 index 000000000..5f9ec1153 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Files/DeclareStrictTypesRule.php @@ -0,0 +1,70 @@ + + */ +final class DeclareStrictTypesRule implements Rules\Rule +{ + public function getNodeType(): string + { + return FileNode::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + $nodes = $node->getNodes(); + + if (0 === \count($nodes)) { + return []; + } + + $firstNode = \array_shift($nodes); + + if ( + $firstNode instanceof Node\Stmt\InlineHTML + && 2 === $firstNode->getEndLine() + && 0 === \mb_strpos($firstNode->value, '#!') + ) { + $firstNode = \array_shift($nodes); + } + + if ($firstNode instanceof Node\Stmt\Declare_) { + foreach ($firstNode->declares as $declare) { + if ( + 'strict_types' === $declare->key->toLowerString() + && $declare->value instanceof Node\Scalar\LNumber + && 1 === $declare->value->value + ) { + return []; + } + } + } + + return [ + Rules\RuleErrorBuilder::message('File is missing a "declare(strict_types=1)" declaration.') + ->identifier(ErrorIdentifier::declareStrictTypes()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoNullableReturnTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoNullableReturnTypeDeclarationRule.php new file mode 100644 index 000000000..06da4f724 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoNullableReturnTypeDeclarationRule.php @@ -0,0 +1,62 @@ + + */ +final class NoNullableReturnTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\Function_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!isset($node->namespacedName)) { + return []; + } + + if (!$this->analyzer->isNullableTypeDeclaration($node->getReturnType())) { + return []; + } + + $message = \sprintf( + 'Function %s() has a nullable return type declaration.', + $node->namespacedName->toString(), + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterPassedByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterPassedByReferenceRule.php new file mode 100644 index 000000000..e461aa139 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterPassedByReferenceRule.php @@ -0,0 +1,67 @@ + + */ +final class NoParameterPassedByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\Function_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersPassedByReference = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool { + return $parameter->byRef; + })); + + if (0 === \count($parametersPassedByReference)) { + return []; + } + + $functionName = $node->namespacedName; + + return \array_map(static function (Node\Param $parameterPassedByReference) use ($functionName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterPassedByReference->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Function %s() has parameter $%s that is passed by reference.', + $functionName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString()) + ->build(); + }, $parametersPassedByReference); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullDefaultValueRule.php new file mode 100644 index 000000000..fe82a4e3d --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullDefaultValueRule.php @@ -0,0 +1,75 @@ + + */ +final class NoParameterWithNullDefaultValueRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\Function_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullDefaultValue = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->hasNullDefaultValue($parameter); + })); + + if (0 === \count($parametersWithNullDefaultValue)) { + return []; + } + + $functionName = $node->namespacedName; + + return \array_map(static function (Node\Param $parameterWithNullDefaultValue) use ($functionName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Function %s() has parameter $%s with null as default value.', + $functionName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString()) + ->build(); + }, $parametersWithNullDefaultValue); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullableTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullableTypeDeclarationRule.php new file mode 100644 index 000000000..90275530b --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullableTypeDeclarationRule.php @@ -0,0 +1,75 @@ + + */ +final class NoParameterWithNullableTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\Function_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullableTypeDeclaration = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->isNullableTypeDeclaration($parameter->type); + })); + + if (0 === \count($parametersWithNullableTypeDeclaration)) { + return []; + } + + $functionName = $node->namespacedName; + + return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration) use ($functionName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullableTypeDeclaration->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Function %s() has parameter $%s with a nullable type declaration.', + $functionName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString()) + ->build(); + }, $parametersWithNullableTypeDeclaration); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoReturnByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoReturnByReferenceRule.php new file mode 100644 index 000000000..0efb7008b --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoReturnByReferenceRule.php @@ -0,0 +1,50 @@ + + */ +final class NoReturnByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\Function_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (false === $node->byRef) { + return []; + } + + $message = \sprintf( + 'Function %s() returns by reference.', + $node->namespacedName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noReturnByReference()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/HasContent.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/HasContent.php new file mode 100644 index 000000000..ced81778b --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/HasContent.php @@ -0,0 +1,74 @@ +value = $value; + } + + /** + * @throws \InvalidArgumentException + */ + public static function fromString(string $value): self + { + $values = [ + 'maybe', + 'no', + 'yes', + ]; + + if (!\in_array($value, $values, true)) { + throw new \InvalidArgumentException(\sprintf( + 'Value needs to be one of "%s", got "%s" instead.', + \implode('", "', $values), + $value, + )); + } + + return new self($value); + } + + public static function maybe(): self + { + return new self('maybe'); + } + + public static function no(): self + { + return new self('no'); + } + + public static function yes(): self + { + return new self('yes'); + } + + public function toString(): string + { + return $this->value; + } + + public function equals(self $other): bool + { + return $this->value === $other->value; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/HookMethod.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/HookMethod.php new file mode 100644 index 000000000..2d14cad7b --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/HookMethod.php @@ -0,0 +1,71 @@ +className = $className; + $this->methodName = $methodName; + $this->invocation = $invocation; + $this->hasContent = $hasContent; + } + + public static function create( + ClassName $className, + MethodName $methodName, + Invocation $invocation, + HasContent $hasContent + ): self { + return new self( + $className, + $methodName, + $invocation, + $hasContent, + ); + } + + public function className(): ClassName + { + return $this->className; + } + + public function methodName(): MethodName + { + return $this->methodName; + } + + public function invocation(): Invocation + { + return $this->invocation; + } + + public function hasContent(): HasContent + { + return $this->hasContent; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Invocation.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Invocation.php new file mode 100644 index 000000000..722ff5f1a --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Invocation.php @@ -0,0 +1,86 @@ +value = $value; + } + + /** + * @throws \InvalidArgumentException + */ + public static function fromString(string $value): self + { + $values = [ + 'any', + 'first', + 'last', + 'never', + ]; + + if (!\in_array($value, $values, true)) { + throw new \InvalidArgumentException(\sprintf( + 'Value needs to be one of "%s", got "%s" instead.', + \implode('", "', $values), + $value, + )); + } + + return new self($value); + } + + public static function any(): self + { + return new self('any'); + } + + public static function first(): self + { + return new self('first'); + } + + public static function last(): self + { + return new self('last'); + } + + public static function never(): self + { + return new self('never'); + } + + /** + * @return non-empty-string + */ + public function toString(): string + { + return $this->value; + } + + public function equals(self $other): bool + { + return $this->value === $other->value; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/MethodName.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/MethodName.php new file mode 100644 index 000000000..47116cefb --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/MethodName.php @@ -0,0 +1,51 @@ +value = $value; + } + + /** + * @param non-empty-string $value + */ + public static function fromString(string $value): self + { + return new self($value); + } + + /** + * @return non-empty-string + */ + public function toString(): string + { + return $this->value; + } + + public function equals(self $other): bool + { + return $this->value === $other->value; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/FinalInAbstractClassRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/FinalInAbstractClassRule.php new file mode 100644 index 000000000..271870aee --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/FinalInAbstractClassRule.php @@ -0,0 +1,124 @@ + + */ +final class FinalInAbstractClassRule implements Rules\Rule +{ + private const DOCTRINE_ATTRIBUTE_NAMES = [ + ORM\Mapping\Embeddable::class, + ORM\Mapping\Entity::class, + ]; + private const DOCTRINE_ANNOTATION_NAMES = [ + '@ORM\\Mapping\\Embeddable', + '@ORM\\Embeddable', + '@Embeddable', + '@ORM\\Mapping\\Entity', + '@ORM\\Entity', + '@Entity', + ]; + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + /** @var Reflection\ClassReflection $containingClass */ + $containingClass = $scope->getClassReflection(); + + if (self::isDoctrineEntity($containingClass)) { + return []; + } + + if (!$containingClass->isAbstract()) { + return []; + } + + if ($containingClass->isInterface()) { + return []; + } + + if ($node->isAbstract()) { + return []; + } + + if ($node->isFinal()) { + return []; + } + + if ($node->isPrivate()) { + return []; + } + + if ('__construct' === $node->name->name) { + return []; + } + + $message = \sprintf( + 'Method %s::%s() is not final, but since the containing class is abstract, it should be.', + $containingClass->getName(), + $node->name->toString(), + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::finalInAbstractClass()->toString()) + ->build(), + ]; + } + + private static function isDoctrineEntity(Reflection\ClassReflection $containingClass): bool + { + $attributes = $containingClass->getNativeReflection()->getAttributes(); + + foreach ($attributes as $attribute) { + if (\in_array($attribute->getName(), self::DOCTRINE_ATTRIBUTE_NAMES, true)) { + return true; + } + } + + $resolvedPhpDocBlock = $containingClass->getResolvedPhpDoc(); + + if ($resolvedPhpDocBlock instanceof PhpDoc\ResolvedPhpDocBlock) { + foreach ($resolvedPhpDocBlock->getPhpDocNodes() as $phpDocNode) { + foreach ($phpDocNode->children as $child) { + if (!$child instanceof PhpDocParser\Ast\PhpDoc\PhpDocTagNode) { + continue; + } + + if (\in_array($child->name, self::DOCTRINE_ANNOTATION_NAMES, true)) { + return true; + } + } + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/InvokeParentHookMethodRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/InvokeParentHookMethodRule.php new file mode 100644 index 000000000..888aa922e --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/InvokeParentHookMethodRule.php @@ -0,0 +1,436 @@ + + */ +final class InvokeParentHookMethodRule implements Rules\Rule +{ + private Reflection\ReflectionProvider $reflectionProvider; + + /** + * @var list + */ + private array $hookMethods; + + /** + * @param array> $hookMethods + */ + public function __construct( + Reflection\ReflectionProvider $reflectionProvider, + array $hookMethods = [] + ) { + $this->reflectionProvider = $reflectionProvider; + $this->hookMethods = self::sort( + $reflectionProvider, + ...self::filter( + $reflectionProvider, + ...\array_merge( + self::defaultHookMethods(), + \array_map(static function (array $hookMethod): HookMethod { + return HookMethod::create( + ClassName::fromString($hookMethod['className']), + MethodName::fromString($hookMethod['methodName']), + Invocation::fromString($hookMethod['invocation']), + HasContent::fromString($hookMethod['hasContent']), + ); + }, $hookMethods), + ), + ), + ); + } + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + $classReflection = $scope->getClassReflection(); + + if (null === $classReflection) { + return []; + } + + $parentClassReflection = $classReflection->getParentClass(); + + if (null === $parentClassReflection) { + return []; + } + + $methodName = $node->name->toString(); + + $hookMethod = $this->findMatchingHookMethod( + $scope, + $parentClassReflection, + $classReflection, + $methodName, + ); + + if (!$hookMethod instanceof HookMethod) { + return []; + } + + $statements = $node->getStmts(); + + if (!\is_array($statements)) { + throw new ShouldNotHappenException(); + } + + $parentHookMethodInvocation = self::findParentHookMethodInvocation( + \array_values($statements), + $hookMethod, + ); + + if ($parentHookMethodInvocation->equals(Invocation::never())) { + if ($hookMethod->hasContent()->equals(HasContent::no())) { + return []; + } + + $message = \sprintf( + 'Method %s::%s() does not invoke parent::%s().', + $classReflection->getName(), + $methodName, + $hookMethod->methodName()->toString(), + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::invokeParentHookMethod()->toString()) + ->build(), + ]; + } + + if ($parentHookMethodInvocation->equals($hookMethod->invocation())) { + return []; + } + + if ($hookMethod->invocation()->equals(Invocation::first())) { + $message = \sprintf( + 'Method %s::%s() does not invoke parent::%s() before all other statements.', + $classReflection->getName(), + $methodName, + $hookMethod->methodName()->toString(), + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::invokeParentHookMethod()->toString()) + ->build(), + ]; + } + + if ($hookMethod->invocation()->equals(Invocation::last())) { + $message = \sprintf( + 'Method %s::%s() does not invoke parent::%s() after all other statements.', + $classReflection->getName(), + $methodName, + $hookMethod->methodName()->toString(), + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::invokeParentHookMethod()->toString()) + ->build(), + ]; + } + + throw new ShouldNotHappenException(); + } + + private function findMatchingHookMethod( + Analyser\Scope $scope, + Reflection\ClassReflection $parentClassReflection, + Reflection\ClassReflection $classReflection, + string $methodName + ): ?HookMethod { + foreach ($this->hookMethods as $hookMethod) { + if (!$classReflection->isSubclassOfClass($this->reflectionProvider->getClass($hookMethod->className()->toString()))) { + continue; + } + + if (\mb_strtolower($hookMethod->methodName()->toString()) !== \mb_strtolower($methodName)) { + continue; + } + + $parentMethodReflection = $parentClassReflection->getMethod( + $methodName, + $scope, + ); + + $declaringClassReflection = $parentMethodReflection->getDeclaringClass(); + + if (\mb_strtolower($hookMethod->className()->toString()) !== \mb_strtolower($declaringClassReflection->getName())) { + return HookMethod::create( + ClassName::fromString($declaringClassReflection->getName()), + MethodName::fromString($methodName), + $hookMethod->invocation(), + HasContent::maybe(), + ); + } + + return $hookMethod; + } + + return null; + } + + /** + * @param list $statements + */ + private static function findParentHookMethodInvocation( + array $statements, + HookMethod $hookMethod + ): Invocation { + $statementsWithOperations = \array_filter($statements, static function (Node $statement): bool { + if ($statement instanceof Node\Stmt\Nop) { + return false; + } + + return true; + }); + + $statementCount = \count($statementsWithOperations); + + foreach ($statementsWithOperations as $index => $statement) { + if (!$statement instanceof Node\Stmt\Expression) { + continue; + } + + if (!$statement->expr instanceof Node\Expr\StaticCall) { + continue; + } + + if (!$statement->expr->class instanceof Node\Name) { + continue; + } + + $className = (string) $statement->expr->class; + + if (\mb_strtolower($className) !== 'parent') { + continue; + } + + if (!$statement->expr->name instanceof Node\Identifier) { + continue; + } + + if (\mb_strtolower($statement->expr->name->toString()) === \mb_strtolower($hookMethod->methodName()->toString())) { + if (1 === $statementCount) { + return $hookMethod->invocation(); + } + + if (0 === $index) { + return Invocation::first(); + } + + if ($statementCount - 1 === $index) { + return Invocation::last(); + } + + return Invocation::any(); + } + } + + return Invocation::never(); + } + + /** + * @return list + */ + private static function filter( + Reflection\ReflectionProvider $reflectionProvider, + HookMethod ...$hookMethods + ): array { + return \array_values(\array_filter($hookMethods, static function (HookMethod $hookMethod) use ($reflectionProvider): bool { + return $reflectionProvider->hasClass($hookMethod->className()->toString()); + })); + } + + /** + * @return list + */ + private static function sort( + Reflection\ReflectionProvider $reflectionProvider, + HookMethod ...$hookMethods + ): array { + \usort($hookMethods, static function (HookMethod $a, HookMethod $b) use ($reflectionProvider): int { + if (\mb_strtolower($a->className()->toString()) === \mb_strtolower($b->className()->toString())) { + return 0; + } + + if ($reflectionProvider->getClass($a->className()->toString())->isSubclassOfClass($reflectionProvider->getClass($b->className()->toString()))) { + return -1; + } + + return 1; + }); + + return $hookMethods; + } + + /** + * @return list + */ + private static function defaultHookMethods(): array + { + return [ + /** + * @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2083-L2085 + */ + HookMethod::create( + ClassName::fromString(Framework\TestCase::class), + MethodName::fromString('assertPostConditions'), + Invocation::last(), + HasContent::no(), + ), + /** + * @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2073-L2075 + */ + HookMethod::create( + ClassName::fromString(Framework\TestCase::class), + MethodName::fromString('assertPreConditions'), + Invocation::first(), + HasContent::no(), + ), + /** + * @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2063-L2065 + */ + HookMethod::create( + ClassName::fromString(Framework\TestCase::class), + MethodName::fromString('setUp'), + Invocation::first(), + HasContent::no(), + ), + /** + * @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2055-L2057 + */ + HookMethod::create( + ClassName::fromString(Framework\TestCase::class), + MethodName::fromString('setUpBeforeClass'), + Invocation::first(), + HasContent::no(), + ), + /** + * @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2091-L2093 + */ + HookMethod::create( + ClassName::fromString(Framework\TestCase::class), + MethodName::fromString('tearDown'), + Invocation::last(), + HasContent::no(), + ), + /** + * @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2098-L2100 + */ + HookMethod::create( + ClassName::fromString(Framework\TestCase::class), + MethodName::fromString('tearDownAfterClass'), + Invocation::last(), + HasContent::no(), + ), + /** + * @see https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L11-L13 + */ + HookMethod::create( + ClassName::fromString(PHPUnit\TestCase::class), + MethodName::fromString('_setUp'), + Invocation::first(), + HasContent::no(), + ), + /** + * @see https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L25-L27 + */ + HookMethod::create( + ClassName::fromString(PHPUnit\TestCase::class), + MethodName::fromString('_setUpBeforeClass'), + Invocation::first(), + HasContent::no(), + ), + /** + * @see https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L18-L20 + */ + HookMethod::create( + ClassName::fromString(PHPUnit\TestCase::class), + MethodName::fromString('_tearDown'), + Invocation::last(), + HasContent::no(), + ), + /** + * @see https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L32-L34 + */ + HookMethod::create( + ClassName::fromString(PHPUnit\TestCase::class), + MethodName::fromString('_tearDownAfterClass'), + Invocation::last(), + HasContent::no(), + ), + /** + * @see https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L75-L77 + */ + HookMethod::create( + ClassName::fromString(Test\Unit::class), + MethodName::fromString('_after'), + Invocation::last(), + HasContent::no(), + ), + /** + * @see https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L63-L65 + */ + HookMethod::create( + ClassName::fromString(Test\Unit::class), + MethodName::fromString('_before'), + Invocation::first(), + HasContent::no(), + ), + /** + * @see https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L34-L58 + */ + HookMethod::create( + ClassName::fromString(Test\Unit::class), + MethodName::fromString('_setUp'), + Invocation::first(), + HasContent::yes(), + ), + /** + * @see https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L67-L70 + */ + HookMethod::create( + ClassName::fromString(Test\Unit::class), + MethodName::fromString('_tearDown'), + Invocation::last(), + HasContent::yes(), + ), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoConstructorParameterWithDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoConstructorParameterWithDefaultValueRule.php new file mode 100644 index 000000000..e8b7e34a3 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoConstructorParameterWithDefaultValueRule.php @@ -0,0 +1,99 @@ + + */ +final class NoConstructorParameterWithDefaultValueRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if ('__construct' !== $node->name->toLowerString()) { + return []; + } + + if (0 === \count($node->params)) { + return []; + } + + $parametersWithDefaultValue = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool { + return self::hasDefaultValue($parameter); + })); + + if (0 === \count($parametersWithDefaultValue)) { + return []; + } + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + return \array_map(static function (Node\Param $parameterWithDefaultValue): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Constructor in anonymous class has parameter $%s with default value.', + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noConstructorParameterWithDefaultValue()->toString()) + ->build(); + }, $parametersWithDefaultValue); + } + + $className = $classReflection->getName(); + + return \array_map(static function (Node\Param $parameterWithDefaultValue) use ($className): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Constructor in %s has parameter $%s with default value.', + $className, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noConstructorParameterWithDefaultValue()->toString()) + ->build(); + }, $parametersWithDefaultValue); + } + + private static function hasDefaultValue(Node\Param $parameter): bool + { + return null !== $parameter->default; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoNullableReturnTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoNullableReturnTypeDeclarationRule.php new file mode 100644 index 000000000..e0e84c947 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoNullableReturnTypeDeclarationRule.php @@ -0,0 +1,76 @@ + + */ +final class NoNullableReturnTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!$this->analyzer->isNullableTypeDeclaration($node->getReturnType())) { + return []; + } + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + $message = \sprintf( + 'Method %s() in anonymous class has a nullable return type declaration.', + $node->name->name, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString()) + ->build(), + ]; + } + + $message = \sprintf( + 'Method %s::%s() has a nullable return type declaration.', + $classReflection->getName(), + $node->name->name, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterPassedByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterPassedByReferenceRule.php new file mode 100644 index 000000000..4c90f696d --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterPassedByReferenceRule.php @@ -0,0 +1,94 @@ + + */ +final class NoParameterPassedByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersExplicitlyPassedByReference = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool { + return $parameter->byRef; + })); + + if (0 === \count($parametersExplicitlyPassedByReference)) { + return []; + } + + $methodName = $node->name->toString(); + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + return \array_map(static function (Node\Param $parameterExplicitlyPassedByReference) use ($methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterExplicitlyPassedByReference->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s() in anonymous class has parameter $%s that is passed by reference.', + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterPassedByReference()->toString()) + ->build(); + }, $parametersExplicitlyPassedByReference); + } + + $className = $classReflection->getName(); + + return \array_map(static function (Node\Param $parameterExplicitlyPassedByReference) use ($className, $methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterExplicitlyPassedByReference->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s::%s() has parameter $%s that is passed by reference.', + $className, + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterPassedByReference()->toString()) + ->build(); + }, $parametersExplicitlyPassedByReference); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithContainerTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithContainerTypeDeclarationRule.php new file mode 100644 index 000000000..f64a8312c --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithContainerTypeDeclarationRule.php @@ -0,0 +1,179 @@ + + */ +final class NoParameterWithContainerTypeDeclarationRule implements Rules\Rule +{ + private Reflection\ReflectionProvider $reflectionProvider; + + /** + * @var list + */ + private array $interfacesImplementedByContainers; + + /** + * @var list + */ + private array $methodsAllowedToUseContainerTypeDeclarations; + + /** + * @param list $interfacesImplementedByContainers + * @param list $methodsAllowedToUseContainerTypeDeclarations + */ + public function __construct( + Reflection\ReflectionProvider $reflectionProvider, + array $interfacesImplementedByContainers, + array $methodsAllowedToUseContainerTypeDeclarations + ) { + $this->reflectionProvider = $reflectionProvider; + $this->interfacesImplementedByContainers = \array_values(\array_filter( + \array_map(static function (string $interfaceImplementedByContainers): string { + return $interfaceImplementedByContainers; + }, $interfacesImplementedByContainers), + static function (string $interfaceImplementedByContainer): bool { + return \interface_exists($interfaceImplementedByContainer); + }, + )); + $this->methodsAllowedToUseContainerTypeDeclarations = $methodsAllowedToUseContainerTypeDeclarations; + } + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($this->interfacesImplementedByContainers)) { + return []; + } + + if (0 === \count($node->params)) { + return []; + } + + $methodName = $node->name->toString(); + + if (\in_array($methodName, $this->methodsAllowedToUseContainerTypeDeclarations, true)) { + return []; + } + + /** @var Reflection\ClassReflection $containingClass */ + $containingClass = $scope->getClassReflection(); + + return \array_values(\array_reduce( + $node->params, + function (array $errors, Node\Param $node) use ($scope, $containingClass, $methodName): array { + $type = $node->type; + + if (!$type instanceof Node\Name) { + return $errors; + } + + /** @var Node\Expr\Variable $variable */ + $variable = $node->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $classUsedInTypeDeclaration = $this->reflectionProvider->getClass($scope->resolveName($type)); + + if ($classUsedInTypeDeclaration->isInterface()) { + foreach ($this->interfacesImplementedByContainers as $interfaceImplementedByContainer) { + if ($classUsedInTypeDeclaration->getName() === $interfaceImplementedByContainer) { + $errors[] = self::createError( + $containingClass, + $methodName, + $parameterName, + $classUsedInTypeDeclaration, + ); + + return $errors; + } + + if ($classUsedInTypeDeclaration->getNativeReflection()->isSubclassOf($interfaceImplementedByContainer)) { + $errors[] = self::createError( + $containingClass, + $methodName, + $parameterName, + $classUsedInTypeDeclaration, + ); + + return $errors; + } + } + } + + foreach ($this->interfacesImplementedByContainers as $interfaceImplementedByContainer) { + if ($classUsedInTypeDeclaration->getNativeReflection()->implementsInterface($interfaceImplementedByContainer)) { + $errors[] = self::createError( + $containingClass, + $methodName, + $parameterName, + $classUsedInTypeDeclaration, + ); + + return $errors; + } + } + + return $errors; + }, + [], + )); + } + + private static function createError( + Reflection\ClassReflection $classReflection, + string $methodName, + string $parameterName, + Reflection\ClassReflection $classUsedInTypeDeclaration + ): Rules\RuleError { + if ($classReflection->isAnonymous()) { + $message = \sprintf( + 'Method %s() in anonymous class has a parameter $%s with a type declaration of %s, but containers should not be injected.', + $methodName, + $parameterName, + $classUsedInTypeDeclaration->getName(), + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithContainerTypeDeclaration()->toString()) + ->build(); + } + + $message = \sprintf( + 'Method %s::%s() has a parameter $%s with a type declaration of %s, but containers should not be injected.', + $classReflection->getName(), + $methodName, + $parameterName, + $classUsedInTypeDeclaration->getName(), + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithContainerTypeDeclaration()->toString()) + ->build(); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullDefaultValueRule.php new file mode 100644 index 000000000..9888974c6 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullDefaultValueRule.php @@ -0,0 +1,102 @@ + + */ +final class NoParameterWithNullDefaultValueRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullDefaultValue = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->hasNullDefaultValue($parameter); + })); + + if (0 === \count($parametersWithNullDefaultValue)) { + return []; + } + + $methodName = $node->name->toString(); + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + return \array_map(static function (Node\Param $parameterWithNullDefaultValue) use ($methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s() in anonymous class has parameter $%s with null as default value.', + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString()) + ->build(); + }, $parametersWithNullDefaultValue); + } + + $className = $classReflection->getName(); + + return \array_map(static function (Node\Param $parameterWithNullDefaultValue) use ($className, $methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s::%s() has parameter $%s with null as default value.', + $className, + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString()) + ->build(); + }, $parametersWithNullDefaultValue); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullableTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullableTypeDeclarationRule.php new file mode 100644 index 000000000..632cd7bea --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullableTypeDeclarationRule.php @@ -0,0 +1,102 @@ + + */ +final class NoParameterWithNullableTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullableTypeDeclaration = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->isNullableTypeDeclaration($parameter->type); + })); + + if (0 === \count($parametersWithNullableTypeDeclaration)) { + return []; + } + + $methodName = $node->name->toString(); + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration) use ($methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullableTypeDeclaration->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s() in anonymous class has parameter $%s with a nullable type declaration.', + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString()) + ->build(); + }, $parametersWithNullableTypeDeclaration); + } + + $className = $classReflection->getName(); + + return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration) use ($className, $methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullableTypeDeclaration->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s::%s() has parameter $%s with a nullable type declaration.', + $className, + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString()) + ->build(); + }, $parametersWithNullableTypeDeclaration); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoReturnByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoReturnByReferenceRule.php new file mode 100644 index 000000000..a1bf87ff6 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoReturnByReferenceRule.php @@ -0,0 +1,72 @@ + + */ +final class NoReturnByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (false === $node->byRef) { + return []; + } + + $methodName = $node->name->toString(); + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + $message = \sprintf( + 'Method %s() in anonymous class returns by reference.', + $methodName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noReturnByReference()->toString()) + ->build(), + ]; + } + + $className = $classReflection->getName(); + + $message = \sprintf( + 'Method %s::%s() returns by reference.', + $className, + $methodName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noReturnByReference()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/PrivateInFinalClassRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/PrivateInFinalClassRule.php new file mode 100644 index 000000000..9aa2c3cc2 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/PrivateInFinalClassRule.php @@ -0,0 +1,198 @@ + + */ +final class PrivateInFinalClassRule implements Rules\Rule +{ + /** + * @var list + */ + private static array $whitelistedAnnotations = [ + '@after', + '@before', + '@postCondition', + '@preCondition', + ]; + + /** + * @var list + */ + private static array $whitelistedAttributes = [ + Framework\Attributes\After::class, + Framework\Attributes\Before::class, + Framework\Attributes\PostCondition::class, + Framework\Attributes\PreCondition::class, + ]; + private Type\FileTypeMapper $fileTypeMapper; + + public function __construct(Type\FileTypeMapper $fileTypeMapper) + { + $this->fileTypeMapper = $fileTypeMapper; + } + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + /** @var Reflection\ClassReflection $containingClass */ + $containingClass = $scope->getClassReflection(); + + if (!$containingClass->isFinal()) { + return []; + } + + if ($node->isPublic()) { + return []; + } + + if ($node->isPrivate()) { + return []; + } + + if ($this->hasWhitelistedAnnotation($node, $containingClass)) { + return []; + } + + if (self::hasWhitelistedAttribute($node)) { + return []; + } + + $methodName = $node->name->toString(); + + if (self::isDeclaredByParentClass($containingClass, $methodName)) { + return []; + } + + if (self::isDeclaredByTrait($containingClass, $methodName)) { + return []; + } + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + $message = \sprintf( + 'Method %s() in anonymous class is protected, but since the containing class is final, it can be private.', + $node->name->name, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::privateInFinalClass()->toString()) + ->build(), + ]; + } + + $message = \sprintf( + 'Method %s::%s() is protected, but since the containing class is final, it can be private.', + $containingClass->getName(), + $methodName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::privateInFinalClass()->toString()) + ->build(), + ]; + } + + private function hasWhitelistedAnnotation( + Node\Stmt\ClassMethod $node, + Reflection\ClassReflection $containingClass + ): bool { + $docComment = $node->getDocComment(); + + if (!$docComment instanceof Comment\Doc) { + return false; + } + + $resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc( + null, + $containingClass->getName(), + null, + null, + $docComment->getText(), + ); + + foreach ($resolvedPhpDoc->getPhpDocNodes() as $phpDocNode) { + foreach ($phpDocNode->getTags() as $tag) { + if (\in_array($tag->name, self::$whitelistedAnnotations, true)) { + return true; + } + } + } + + return false; + } + + private static function hasWhitelistedAttribute(Node\Stmt\ClassMethod $node): bool + { + foreach ($node->attrGroups as $attributeGroup) { + foreach ($attributeGroup->attrs as $attribute) { + if (\in_array($attribute->name->toString(), self::$whitelistedAttributes, true)) { + return true; + } + } + } + + return false; + } + + private static function isDeclaredByParentClass( + Reflection\ClassReflection $containingClass, + string $methodName + ): bool { + $parentClass = $containingClass->getNativeReflection()->getParentClass(); + + if (!$parentClass instanceof \ReflectionClass) { + return false; + } + + if (!$parentClass->hasMethod($methodName)) { + return false; + } + + return true; + } + + private static function isDeclaredByTrait( + Reflection\ClassReflection $containingClass, + string $methodName + ): bool { + foreach ($containingClass->getTraits() as $trait) { + if ($trait->hasMethod($methodName)) { + return true; + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Statements/NoSwitchRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Statements/NoSwitchRule.php new file mode 100644 index 000000000..127e5eed3 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Statements/NoSwitchRule.php @@ -0,0 +1,41 @@ + + */ +final class NoSwitchRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\Switch_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + return [ + Rules\RuleErrorBuilder::message('Control structures using switch should not be used.') + ->identifier(ErrorIdentifier::noSwitch()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/.phpstorm.meta.php b/tools/.phpstan/vendor/nette/utils/.phpstorm.meta.php new file mode 100644 index 000000000..25851af66 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/.phpstorm.meta.php @@ -0,0 +1,13 @@ + +✅ [Callback](https://doc.nette.org/utils/callback) - PHP callbacks
    +✅ [Filesystem](https://doc.nette.org/utils/filesystem) - copying, renaming, …
    +✅ [Finder](https://doc.nette.org/utils/finder) - finds files and directories
    +✅ [Floats](https://doc.nette.org/utils/floats) - floating point numbers
    +✅ [Helper Functions](https://doc.nette.org/utils/helpers)
    +✅ [HTML elements](https://doc.nette.org/utils/html-elements) - generate HTML
    +✅ [Images](https://doc.nette.org/utils/images) - crop, resize, rotate images
    +✅ [Iterables](https://doc.nette.org/utils/iterables)
    +✅ [JSON](https://doc.nette.org/utils/json) - encoding and decoding
    +✅ [Generating Random Strings](https://doc.nette.org/utils/random)
    +✅ [Paginator](https://doc.nette.org/utils/paginator) - pagination math
    +✅ [PHP Reflection](https://doc.nette.org/utils/reflection)
    +✅ [Strings](https://doc.nette.org/utils/strings) - useful text functions
    +✅ [SmartObject](https://doc.nette.org/utils/smartobject) - PHP object enhancements
    +✅ [Type](https://doc.nette.org/utils/type) - PHP data type
    +✅ [Validation](https://doc.nette.org/utils/validators) - validate inputs
    + +  + +Installation +------------ + +The recommended way to install is via Composer: + +``` +composer require nette/utils +``` + +Nette Utils 4.0 is compatible with PHP 8.0 to 8.4. + +  + +[Support Me](https://github.com/sponsors/dg) +-------------------------------------------- + +Do you like Nette Utils? Are you looking forward to the new features? + +[![Buy me a coffee](https://files.nette.org/icons/donation-3.svg)](https://github.com/sponsors/dg) + +Thank you! diff --git a/tools/.phpstan/vendor/nette/utils/src/HtmlStringable.php b/tools/.phpstan/vendor/nette/utils/src/HtmlStringable.php new file mode 100644 index 000000000..d749d4ee8 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/HtmlStringable.php @@ -0,0 +1,22 @@ +counter === 1 || ($gridWidth && $this->counter !== 0 && (($this->counter - 1) % $gridWidth) === 0); + } + + + /** + * Is the current element the last one? + */ + public function isLast(?int $gridWidth = null): bool + { + return !$this->hasNext() || ($gridWidth && ($this->counter % $gridWidth) === 0); + } + + + /** + * Is the iterator empty? + */ + public function isEmpty(): bool + { + return $this->counter === 0; + } + + + /** + * Is the counter odd? + */ + public function isOdd(): bool + { + return $this->counter % 2 === 1; + } + + + /** + * Is the counter even? + */ + public function isEven(): bool + { + return $this->counter % 2 === 0; + } + + + /** + * Returns the counter. + */ + public function getCounter(): int + { + return $this->counter; + } + + + /** + * Returns the count of elements. + */ + public function count(): int + { + $inner = $this->getInnerIterator(); + if ($inner instanceof \Countable) { + return $inner->count(); + + } else { + throw new Nette\NotSupportedException('Iterator is not countable.'); + } + } + + + /** + * Forwards to the next element. + */ + public function next(): void + { + parent::next(); + if (parent::valid()) { + $this->counter++; + } + } + + + /** + * Rewinds the Iterator. + */ + public function rewind(): void + { + parent::rewind(); + $this->counter = parent::valid() ? 1 : 0; + } + + + /** + * Returns the next key. + */ + public function getNextKey(): mixed + { + return $this->getInnerIterator()->key(); + } + + + /** + * Returns the next element. + */ + public function getNextValue(): mixed + { + return $this->getInnerIterator()->current(); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Iterators/Mapper.php b/tools/.phpstan/vendor/nette/utils/src/Iterators/Mapper.php new file mode 100644 index 000000000..284da29da --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Iterators/Mapper.php @@ -0,0 +1,33 @@ +callback = $callback; + } + + + public function current(): mixed + { + return ($this->callback)(parent::current(), parent::key()); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/SmartObject.php b/tools/.phpstan/vendor/nette/utils/src/SmartObject.php new file mode 100644 index 000000000..3b2203f1f --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/SmartObject.php @@ -0,0 +1,140 @@ +$name ?? null; + if (is_iterable($handlers)) { + foreach ($handlers as $handler) { + $handler(...$args); + } + } elseif ($handlers !== null) { + throw new UnexpectedValueException("Property $class::$$name must be iterable or null, " . get_debug_type($handlers) . ' given.'); + } + + return null; + } + + ObjectHelpers::strictCall($class, $name); + } + + + /** + * @throws MemberAccessException + */ + public static function __callStatic(string $name, array $args) + { + ObjectHelpers::strictStaticCall(static::class, $name); + } + + + /** + * @return mixed + * @throws MemberAccessException if the property is not defined. + */ + public function &__get(string $name) + { + $class = static::class; + + if ($prop = ObjectHelpers::getMagicProperties($class)[$name] ?? null) { // property getter + if (!($prop & 0b0001)) { + throw new MemberAccessException("Cannot read a write-only property $class::\$$name."); + } + + $m = ($prop & 0b0010 ? 'get' : 'is') . ucfirst($name); + if ($prop & 0b10000) { + $trace = debug_backtrace(0, 1)[0]; // suppose this method is called from __call() + $loc = isset($trace['file'], $trace['line']) + ? " in $trace[file] on line $trace[line]" + : ''; + trigger_error("Property $class::\$$name is deprecated, use $class::$m() method$loc.", E_USER_DEPRECATED); + } + + if ($prop & 0b0100) { // return by reference + return $this->$m(); + } else { + $val = $this->$m(); + return $val; + } + } else { + ObjectHelpers::strictGet($class, $name); + } + } + + + /** + * @throws MemberAccessException if the property is not defined or is read-only + */ + public function __set(string $name, mixed $value): void + { + $class = static::class; + + if (ObjectHelpers::hasProperty($class, $name)) { // unsetted property + $this->$name = $value; + + } elseif ($prop = ObjectHelpers::getMagicProperties($class)[$name] ?? null) { // property setter + if (!($prop & 0b1000)) { + throw new MemberAccessException("Cannot write to a read-only property $class::\$$name."); + } + + $m = 'set' . ucfirst($name); + if ($prop & 0b10000) { + $trace = debug_backtrace(0, 1)[0]; // suppose this method is called from __call() + $loc = isset($trace['file'], $trace['line']) + ? " in $trace[file] on line $trace[line]" + : ''; + trigger_error("Property $class::\$$name is deprecated, use $class::$m() method$loc.", E_USER_DEPRECATED); + } + + $this->$m($value); + + } else { + ObjectHelpers::strictSet($class, $name); + } + } + + + /** + * @throws MemberAccessException + */ + public function __unset(string $name): void + { + $class = static::class; + if (!ObjectHelpers::hasProperty($class, $name)) { + throw new MemberAccessException("Cannot unset the property $class::\$$name."); + } + } + + + public function __isset(string $name): bool + { + return isset(ObjectHelpers::getMagicProperties(static::class)[$name]); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/StaticClass.php b/tools/.phpstan/vendor/nette/utils/src/StaticClass.php new file mode 100644 index 000000000..b1d84862e --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/StaticClass.php @@ -0,0 +1,34 @@ + + * @implements \ArrayAccess + */ +class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \IteratorAggregate +{ + /** + * Transforms array to ArrayHash. + * @param array $array + */ + public static function from(array $array, bool $recursive = true): static + { + $obj = new static; + foreach ($array as $key => $value) { + $obj->$key = $recursive && is_array($value) + ? static::from($value) + : $value; + } + + return $obj; + } + + + /** + * Returns an iterator over all items. + * @return \Iterator + */ + public function &getIterator(): \Iterator + { + foreach ((array) $this as $key => $foo) { + yield $key => $this->$key; + } + } + + + /** + * Returns items count. + */ + public function count(): int + { + return count((array) $this); + } + + + /** + * Replaces or appends a item. + * @param array-key $key + * @param T $value + */ + public function offsetSet($key, $value): void + { + if (!is_scalar($key)) { // prevents null + throw new Nette\InvalidArgumentException(sprintf('Key must be either a string or an integer, %s given.', get_debug_type($key))); + } + + $this->$key = $value; + } + + + /** + * Returns a item. + * @param array-key $key + * @return T + */ + #[\ReturnTypeWillChange] + public function offsetGet($key) + { + return $this->$key; + } + + + /** + * Determines whether a item exists. + * @param array-key $key + */ + public function offsetExists($key): bool + { + return isset($this->$key); + } + + + /** + * Removes the element from this list. + * @param array-key $key + */ + public function offsetUnset($key): void + { + unset($this->$key); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php new file mode 100644 index 000000000..a402f9bf6 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php @@ -0,0 +1,136 @@ + + * @implements \ArrayAccess + */ +class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate +{ + use Nette\SmartObject; + + private array $list = []; + + + /** + * Transforms array to ArrayList. + * @param list $array + */ + public static function from(array $array): static + { + if (!Arrays::isList($array)) { + throw new Nette\InvalidArgumentException('Array is not valid list.'); + } + + $obj = new static; + $obj->list = $array; + return $obj; + } + + + /** + * Returns an iterator over all items. + * @return \Iterator + */ + public function &getIterator(): \Iterator + { + foreach ($this->list as &$item) { + yield $item; + } + } + + + /** + * Returns items count. + */ + public function count(): int + { + return count($this->list); + } + + + /** + * Replaces or appends a item. + * @param int|null $index + * @param T $value + * @throws Nette\OutOfRangeException + */ + public function offsetSet($index, $value): void + { + if ($index === null) { + $this->list[] = $value; + + } elseif (!is_int($index) || $index < 0 || $index >= count($this->list)) { + throw new Nette\OutOfRangeException('Offset invalid or out of range'); + + } else { + $this->list[$index] = $value; + } + } + + + /** + * Returns a item. + * @param int $index + * @return T + * @throws Nette\OutOfRangeException + */ + public function offsetGet($index): mixed + { + if (!is_int($index) || $index < 0 || $index >= count($this->list)) { + throw new Nette\OutOfRangeException('Offset invalid or out of range'); + } + + return $this->list[$index]; + } + + + /** + * Determines whether a item exists. + * @param int $index + */ + public function offsetExists($index): bool + { + return is_int($index) && $index >= 0 && $index < count($this->list); + } + + + /** + * Removes the element at the specified position in this list. + * @param int $index + * @throws Nette\OutOfRangeException + */ + public function offsetUnset($index): void + { + if (!is_int($index) || $index < 0 || $index >= count($this->list)) { + throw new Nette\OutOfRangeException('Offset invalid or out of range'); + } + + array_splice($this->list, $index, 1); + } + + + /** + * Prepends a item. + * @param T $value + */ + public function prepend(mixed $value): void + { + $first = array_slice($this->list, 0, 1); + $this->offsetSet(0, $value); + array_splice($this->list, 1, 0, $first); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php new file mode 100644 index 000000000..02522ca21 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php @@ -0,0 +1,554 @@ + $array + * @param array-key|array-key[] $key + * @param ?T $default + * @return ?T + * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided + */ + public static function get(array $array, string|int|array $key, mixed $default = null): mixed + { + foreach (is_array($key) ? $key : [$key] as $k) { + if (is_array($array) && array_key_exists($k, $array)) { + $array = $array[$k]; + } else { + if (func_num_args() < 3) { + throw new Nette\InvalidArgumentException("Missing item '$k'."); + } + + return $default; + } + } + + return $array; + } + + + /** + * Returns reference to array item. If the index does not exist, new one is created with value null. + * @template T + * @param array $array + * @param array-key|array-key[] $key + * @return ?T + * @throws Nette\InvalidArgumentException if traversed item is not an array + */ + public static function &getRef(array &$array, string|int|array $key): mixed + { + foreach (is_array($key) ? $key : [$key] as $k) { + if (is_array($array) || $array === null) { + $array = &$array[$k]; + } else { + throw new Nette\InvalidArgumentException('Traversed item is not an array.'); + } + } + + return $array; + } + + + /** + * Recursively merges two fields. It is useful, for example, for merging tree structures. It behaves as + * the + operator for array, ie. it adds a key/value pair from the second array to the first one and retains + * the value from the first array in the case of a key collision. + * @template T1 + * @template T2 + * @param array $array1 + * @param array $array2 + * @return array + */ + public static function mergeTree(array $array1, array $array2): array + { + $res = $array1 + $array2; + foreach (array_intersect_key($array1, $array2) as $k => $v) { + if (is_array($v) && is_array($array2[$k])) { + $res[$k] = self::mergeTree($v, $array2[$k]); + } + } + + return $res; + } + + + /** + * Returns zero-indexed position of given array key. Returns null if key is not found. + */ + public static function getKeyOffset(array $array, string|int $key): ?int + { + return Helpers::falseToNull(array_search(self::toKey($key), array_keys($array), strict: true)); + } + + + /** + * @deprecated use getKeyOffset() + */ + public static function searchKey(array $array, $key): ?int + { + return self::getKeyOffset($array, $key); + } + + + /** + * Tests an array for the presence of value. + */ + public static function contains(array $array, mixed $value): bool + { + return in_array($value, $array, true); + } + + + /** + * Returns the first item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null. + * @template K of int|string + * @template V + * @param array $array + * @param ?callable(V, K, array): bool $predicate + * @return ?V + */ + public static function first(array $array, ?callable $predicate = null, ?callable $else = null): mixed + { + $key = self::firstKey($array, $predicate); + return $key === null + ? ($else ? $else() : null) + : $array[$key]; + } + + + /** + * Returns the last item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null. + * @template K of int|string + * @template V + * @param array $array + * @param ?callable(V, K, array): bool $predicate + * @return ?V + */ + public static function last(array $array, ?callable $predicate = null, ?callable $else = null): mixed + { + $key = self::lastKey($array, $predicate); + return $key === null + ? ($else ? $else() : null) + : $array[$key]; + } + + + /** + * Returns the key of first item (matching the specified predicate if given) or null if there is no such item. + * @template K of int|string + * @template V + * @param array $array + * @param ?callable(V, K, array): bool $predicate + * @return ?K + */ + public static function firstKey(array $array, ?callable $predicate = null): int|string|null + { + if (!$predicate) { + return array_key_first($array); + } + foreach ($array as $k => $v) { + if ($predicate($v, $k, $array)) { + return $k; + } + } + return null; + } + + + /** + * Returns the key of last item (matching the specified predicate if given) or null if there is no such item. + * @template K of int|string + * @template V + * @param array $array + * @param ?callable(V, K, array): bool $predicate + * @return ?K + */ + public static function lastKey(array $array, ?callable $predicate = null): int|string|null + { + return $predicate + ? self::firstKey(array_reverse($array, preserve_keys: true), $predicate) + : array_key_last($array); + } + + + /** + * Inserts the contents of the $inserted array into the $array immediately after the $key. + * If $key is null (or does not exist), it is inserted at the beginning. + */ + public static function insertBefore(array &$array, string|int|null $key, array $inserted): void + { + $offset = $key === null ? 0 : (int) self::getKeyOffset($array, $key); + $array = array_slice($array, 0, $offset, preserve_keys: true) + + $inserted + + array_slice($array, $offset, count($array), preserve_keys: true); + } + + + /** + * Inserts the contents of the $inserted array into the $array before the $key. + * If $key is null (or does not exist), it is inserted at the end. + */ + public static function insertAfter(array &$array, string|int|null $key, array $inserted): void + { + if ($key === null || ($offset = self::getKeyOffset($array, $key)) === null) { + $offset = count($array) - 1; + } + + $array = array_slice($array, 0, $offset + 1, preserve_keys: true) + + $inserted + + array_slice($array, $offset + 1, count($array), preserve_keys: true); + } + + + /** + * Renames key in array. + */ + public static function renameKey(array &$array, string|int $oldKey, string|int $newKey): bool + { + $offset = self::getKeyOffset($array, $oldKey); + if ($offset === null) { + return false; + } + + $val = &$array[$oldKey]; + $keys = array_keys($array); + $keys[$offset] = $newKey; + $array = array_combine($keys, $array); + $array[$newKey] = &$val; + return true; + } + + + /** + * Returns only those array items, which matches a regular expression $pattern. + * @param string[] $array + * @return string[] + */ + public static function grep( + array $array, + #[Language('RegExp')] + string $pattern, + bool|int $invert = false, + ): array + { + $flags = $invert ? PREG_GREP_INVERT : 0; + return Strings::pcre('preg_grep', [$pattern, $array, $flags]); + } + + + /** + * Transforms multidimensional array to flat array. + */ + public static function flatten(array $array, bool $preserveKeys = false): array + { + $res = []; + $cb = $preserveKeys + ? function ($v, $k) use (&$res): void { $res[$k] = $v; } + : function ($v) use (&$res): void { $res[] = $v; }; + array_walk_recursive($array, $cb); + return $res; + } + + + /** + * Checks if the array is indexed in ascending order of numeric keys from zero, a.k.a list. + * @return ($value is list ? true : false) + */ + public static function isList(mixed $value): bool + { + return is_array($value) && ( + PHP_VERSION_ID < 80100 + ? !$value || array_keys($value) === range(0, count($value) - 1) + : array_is_list($value) + ); + } + + + /** + * Reformats table to associative tree. Path looks like 'field|field[]field->field=field'. + * @param string|string[] $path + */ + public static function associate(array $array, $path): array|\stdClass + { + $parts = is_array($path) + ? $path + : preg_split('#(\[\]|->|=|\|)#', $path, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + + if (!$parts || $parts === ['->'] || $parts[0] === '=' || $parts[0] === '|') { + throw new Nette\InvalidArgumentException("Invalid path '$path'."); + } + + $res = $parts[0] === '->' ? new \stdClass : []; + + foreach ($array as $rowOrig) { + $row = (array) $rowOrig; + $x = &$res; + + for ($i = 0; $i < count($parts); $i++) { + $part = $parts[$i]; + if ($part === '[]') { + $x = &$x[]; + + } elseif ($part === '=') { + if (isset($parts[++$i])) { + $x = $row[$parts[$i]]; + $row = null; + } + } elseif ($part === '->') { + if (isset($parts[++$i])) { + if ($x === null) { + $x = new \stdClass; + } + + $x = &$x->{$row[$parts[$i]]}; + } else { + $row = is_object($rowOrig) ? $rowOrig : (object) $row; + } + } elseif ($part !== '|') { + $x = &$x[(string) $row[$part]]; + } + } + + if ($x === null) { + $x = $row; + } + } + + return $res; + } + + + /** + * Normalizes array to associative array. Replace numeric keys with their values, the new value will be $filling. + */ + public static function normalize(array $array, mixed $filling = null): array + { + $res = []; + foreach ($array as $k => $v) { + $res[is_int($k) ? $v : $k] = is_int($k) ? $filling : $v; + } + + return $res; + } + + + /** + * Returns and removes the value of an item from an array. If it does not exist, it throws an exception, + * or returns $default, if provided. + * @template T + * @param array $array + * @param ?T $default + * @return ?T + * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided + */ + public static function pick(array &$array, string|int $key, mixed $default = null): mixed + { + if (array_key_exists($key, $array)) { + $value = $array[$key]; + unset($array[$key]); + return $value; + + } elseif (func_num_args() < 3) { + throw new Nette\InvalidArgumentException("Missing item '$key'."); + + } else { + return $default; + } + } + + + /** + * Tests whether at least one element in the array passes the test implemented by the provided function. + * @template K of int|string + * @template V + * @param array $array + * @param callable(V, K, array): bool $predicate + */ + public static function some(iterable $array, callable $predicate): bool + { + foreach ($array as $k => $v) { + if ($predicate($v, $k, $array)) { + return true; + } + } + + return false; + } + + + /** + * Tests whether all elements in the array pass the test implemented by the provided function. + * @template K of int|string + * @template V + * @param array $array + * @param callable(V, K, array): bool $predicate + */ + public static function every(iterable $array, callable $predicate): bool + { + foreach ($array as $k => $v) { + if (!$predicate($v, $k, $array)) { + return false; + } + } + + return true; + } + + + /** + * Returns a new array containing all key-value pairs matching the given $predicate. + * @template K of int|string + * @template V + * @param array $array + * @param callable(V, K, array): bool $predicate + * @return array + */ + public static function filter(array $array, callable $predicate): array + { + $res = []; + foreach ($array as $k => $v) { + if ($predicate($v, $k, $array)) { + $res[$k] = $v; + } + } + return $res; + } + + + /** + * Returns an array containing the original keys and results of applying the given transform function to each element. + * @template K of int|string + * @template V + * @template R + * @param array $array + * @param callable(V, K, array): R $transformer + * @return array + */ + public static function map(iterable $array, callable $transformer): array + { + $res = []; + foreach ($array as $k => $v) { + $res[$k] = $transformer($v, $k, $array); + } + + return $res; + } + + + /** + * Returns an array containing new keys and values generated by applying the given transform function to each element. + * If the function returns null, the element is skipped. + * @template K of int|string + * @template V + * @template ResK of int|string + * @template ResV + * @param array $array + * @param callable(V, K, array): ?array{ResK, ResV} $transformer + * @return array + */ + public static function mapWithKeys(array $array, callable $transformer): array + { + $res = []; + foreach ($array as $k => $v) { + $pair = $transformer($v, $k, $array); + if ($pair) { + $res[$pair[0]] = $pair[1]; + } + } + + return $res; + } + + + /** + * Invokes all callbacks and returns array of results. + * @param callable[] $callbacks + */ + public static function invoke(iterable $callbacks, ...$args): array + { + $res = []; + foreach ($callbacks as $k => $cb) { + $res[$k] = $cb(...$args); + } + + return $res; + } + + + /** + * Invokes method on every object in an array and returns array of results. + * @param object[] $objects + */ + public static function invokeMethod(iterable $objects, string $method, ...$args): array + { + $res = []; + foreach ($objects as $k => $obj) { + $res[$k] = $obj->$method(...$args); + } + + return $res; + } + + + /** + * Copies the elements of the $array array to the $object object and then returns it. + * @template T of object + * @param T $object + * @return T + */ + public static function toObject(iterable $array, object $object): object + { + foreach ($array as $k => $v) { + $object->$k = $v; + } + + return $object; + } + + + /** + * Converts value to array key. + */ + public static function toKey(mixed $value): int|string + { + return key([$value => null]); + } + + + /** + * Returns copy of the $array where every item is converted to string + * and prefixed by $prefix and suffixed by $suffix. + * @param string[] $array + * @return string[] + */ + public static function wrap(array $array, string $prefix = '', string $suffix = ''): array + { + $res = []; + foreach ($array as $k => $v) { + $res[$k] = $prefix . $v . $suffix; + } + + return $res; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php new file mode 100644 index 000000000..1777428fd --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php @@ -0,0 +1,137 @@ +getClosureScopeClass()?->name; + if (str_ends_with($r->name, '}')) { + return $closure; + + } elseif (($obj = $r->getClosureThis()) && $obj::class === $class) { + return [$obj, $r->name]; + + } elseif ($class) { + return [$class, $r->name]; + + } else { + return $r->name; + } + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php b/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php new file mode 100644 index 000000000..811f68dbe --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php @@ -0,0 +1,221 @@ +format('Y-m-d H:i:s.u'), $time->getTimezone()); + + } elseif (is_numeric($time)) { + if ($time <= self::YEAR) { + $time += time(); + } + + return (new static)->setTimestamp((int) $time); + + } else { // textual or null + return new static((string) $time); + } + } + + + /** + * Creates DateTime object. + * @throws Nette\InvalidArgumentException if the date and time are not valid. + */ + public static function fromParts( + int $year, + int $month, + int $day, + int $hour = 0, + int $minute = 0, + float $second = 0.0, + ): static + { + $s = sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second); + if ( + !checkdate($month, $day, $year) + || $hour < 0 || $hour > 23 + || $minute < 0 || $minute > 59 + || $second < 0 || $second >= 60 + ) { + throw new Nette\InvalidArgumentException("Invalid date '$s'"); + } + + return new static($s); + } + + + /** + * Returns a new DateTime object formatted according to the specified format. + */ + public static function createFromFormat( + string $format, + string $datetime, + string|\DateTimeZone|null $timezone = null, + ): static|false + { + if ($timezone === null) { + $timezone = new \DateTimeZone(date_default_timezone_get()); + + } elseif (is_string($timezone)) { + $timezone = new \DateTimeZone($timezone); + } + + $date = parent::createFromFormat($format, $datetime, $timezone); + return $date ? static::from($date) : false; + } + + + public function __construct(string $datetime = 'now', ?\DateTimeZone $timezone = null) + { + $this->apply($datetime, $timezone, true); + } + + + public function modify(string $modifier): static + { + $this->apply($modifier); + return $this; + } + + + public function setDate(int $year, int $month, int $day): static + { + if (!checkdate($month, $day, $year)) { + trigger_error(sprintf(self::class . ': The date %04d-%02d-%02d is not valid.', $year, $month, $day), E_USER_WARNING); + } + return parent::setDate($year, $month, $day); + } + + + public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): static + { + if ( + $hour < 0 || $hour > 23 + || $minute < 0 || $minute > 59 + || $second < 0 || $second >= 60 + || $microsecond < 0 || $microsecond >= 1_000_000 + ) { + trigger_error(sprintf(self::class . ': The time %02d:%02d:%08.5F is not valid.', $hour, $minute, $second + $microsecond / 1_000_000), E_USER_WARNING); + } + return parent::setTime($hour, $minute, $second, $microsecond); + } + + + /** + * Converts a relative time string (e.g. '10 minut') to seconds. + */ + public static function relativeToSeconds(string $relativeTime): int + { + return (new \DateTimeImmutable('1970-01-01 ' . $relativeTime, new \DateTimeZone('UTC'))) + ->getTimestamp(); + } + + + private function apply(string $datetime, $timezone = null, bool $ctr = false): void + { + $relPart = ''; + $absPart = preg_replace_callback( + '/[+-]?\s*\d+\s+((microsecond|millisecond|[mµu]sec)s?|[mµ]s|sec(ond)?s?|min(ute)?s?|hours?)\b/iu', + function ($m) use (&$relPart) { + $relPart .= $m[0] . ' '; + return ''; + }, + $datetime, + ); + + if ($ctr) { + parent::__construct($absPart, $timezone); + $this->handleErrors($datetime); + } elseif (trim($absPart)) { + parent::modify($absPart) && $this->handleErrors($datetime); + } + + if ($relPart) { + $timezone ??= $this->getTimezone(); + $this->setTimezone(new \DateTimeZone('UTC')); + parent::modify($relPart) && $this->handleErrors($datetime); + $this->setTimezone($timezone); + } + } + + + /** + * Returns JSON representation in ISO 8601 (used by JavaScript). + */ + public function jsonSerialize(): string + { + return $this->format('c'); + } + + + /** + * Returns the date and time in the format 'Y-m-d H:i:s'. + */ + public function __toString(): string + { + return $this->format('Y-m-d H:i:s'); + } + + + /** + * You'd better use: (clone $dt)->modify(...) + */ + public function modifyClone(string $modify = ''): static + { + $dolly = clone $this; + return $modify ? $dolly->modify($modify) : $dolly; + } + + + private function handleErrors(string $value): void + { + $errors = self::getLastErrors(); + $errors = array_merge($errors['errors'] ?? [], $errors['warnings'] ?? []); + if ($errors) { + trigger_error(self::class . ': ' . implode(', ', $errors) . " '$value'", E_USER_WARNING); + } + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php b/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php new file mode 100644 index 000000000..fb92d1191 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php @@ -0,0 +1,69 @@ +setInfoClass(static::class); + $this->relativePath = $relativePath; + } + + + /** + * Returns the relative directory path. + */ + public function getRelativePath(): string + { + return $this->relativePath; + } + + + /** + * Returns the relative path including file name. + */ + public function getRelativePathname(): string + { + return ($this->relativePath === '' ? '' : $this->relativePath . DIRECTORY_SEPARATOR) + . $this->getBasename(); + } + + + /** + * Returns the contents of the file. + * @throws Nette\IOException + */ + public function read(): string + { + return FileSystem::read($this->getPathname()); + } + + + /** + * Writes the contents to the file. + * @throws Nette\IOException + */ + public function write(string $content): void + { + FileSystem::write($this->getPathname(), $content); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php b/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php new file mode 100644 index 000000000..6328fb856 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php @@ -0,0 +1,339 @@ +getPathname()); + } + + foreach ($iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($origin, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST) as $item) { + if ($item->isDir()) { + static::createDir($target . '/' . $iterator->getSubPathName()); + } else { + static::copy($item->getPathname(), $target . '/' . $iterator->getSubPathName()); + } + } + } else { + static::createDir(dirname($target)); + if (@stream_copy_to_stream(static::open($origin, 'rb'), static::open($target, 'wb')) === false) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to copy file '%s' to '%s'. %s", + self::normalizePath($origin), + self::normalizePath($target), + Helpers::getLastError(), + )); + } + } + } + + + /** + * Opens file and returns resource. + * @return resource + * @throws Nette\IOException on error occurred + */ + public static function open(string $path, string $mode) + { + $f = @fopen($path, $mode); // @ is escalated to exception + if (!$f) { + throw new Nette\IOException(sprintf( + "Unable to open file '%s'. %s", + self::normalizePath($path), + Helpers::getLastError(), + )); + } + return $f; + } + + + /** + * Deletes a file or an entire directory if exists. If the directory is not empty, it deletes its contents first. + * @throws Nette\IOException on error occurred + */ + public static function delete(string $path): void + { + if (is_file($path) || is_link($path)) { + $func = DIRECTORY_SEPARATOR === '\\' && is_dir($path) ? 'rmdir' : 'unlink'; + if (!@$func($path)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to delete '%s'. %s", + self::normalizePath($path), + Helpers::getLastError(), + )); + } + } elseif (is_dir($path)) { + foreach (new \FilesystemIterator($path) as $item) { + static::delete($item->getPathname()); + } + + if (!@rmdir($path)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to delete directory '%s'. %s", + self::normalizePath($path), + Helpers::getLastError(), + )); + } + } + } + + + /** + * Renames or moves a file or a directory. Overwrites existing files and directories by default. + * @throws Nette\IOException on error occurred + * @throws Nette\InvalidStateException if $overwrite is set to false and destination already exists + */ + public static function rename(string $origin, string $target, bool $overwrite = true): void + { + if (!$overwrite && file_exists($target)) { + throw new Nette\InvalidStateException(sprintf("File or directory '%s' already exists.", self::normalizePath($target))); + + } elseif (!file_exists($origin)) { + throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($origin))); + + } else { + static::createDir(dirname($target)); + if (realpath($origin) !== realpath($target)) { + static::delete($target); + } + + if (!@rename($origin, $target)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to rename file or directory '%s' to '%s'. %s", + self::normalizePath($origin), + self::normalizePath($target), + Helpers::getLastError(), + )); + } + } + } + + + /** + * Reads the content of a file. + * @throws Nette\IOException on error occurred + */ + public static function read(string $file): string + { + $content = @file_get_contents($file); // @ is escalated to exception + if ($content === false) { + throw new Nette\IOException(sprintf( + "Unable to read file '%s'. %s", + self::normalizePath($file), + Helpers::getLastError(), + )); + } + + return $content; + } + + + /** + * Reads the file content line by line. Because it reads continuously as we iterate over the lines, + * it is possible to read files larger than the available memory. + * @return \Generator + * @throws Nette\IOException on error occurred + */ + public static function readLines(string $file, bool $stripNewLines = true): \Generator + { + return (function ($f) use ($file, $stripNewLines) { + $counter = 0; + do { + $line = Callback::invokeSafe('fgets', [$f], fn($error) => throw new Nette\IOException(sprintf( + "Unable to read file '%s'. %s", + self::normalizePath($file), + $error, + ))); + if ($line === false) { + fclose($f); + break; + } + if ($stripNewLines) { + $line = rtrim($line, "\r\n"); + } + + yield $counter++ => $line; + + } while (true); + })(static::open($file, 'r')); + } + + + /** + * Writes the string to a file. + * @throws Nette\IOException on error occurred + */ + public static function write(string $file, string $content, ?int $mode = 0666): void + { + static::createDir(dirname($file)); + if (@file_put_contents($file, $content) === false) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to write file '%s'. %s", + self::normalizePath($file), + Helpers::getLastError(), + )); + } + + if ($mode !== null && !@chmod($file, $mode)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to chmod file '%s' to mode %s. %s", + self::normalizePath($file), + decoct($mode), + Helpers::getLastError(), + )); + } + } + + + /** + * Sets file permissions to `$fileMode` or directory permissions to `$dirMode`. + * Recursively traverses and sets permissions on the entire contents of the directory as well. + * @throws Nette\IOException on error occurred + */ + public static function makeWritable(string $path, int $dirMode = 0777, int $fileMode = 0666): void + { + if (is_file($path)) { + if (!@chmod($path, $fileMode)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to chmod file '%s' to mode %s. %s", + self::normalizePath($path), + decoct($fileMode), + Helpers::getLastError(), + )); + } + } elseif (is_dir($path)) { + foreach (new \FilesystemIterator($path) as $item) { + static::makeWritable($item->getPathname(), $dirMode, $fileMode); + } + + if (!@chmod($path, $dirMode)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to chmod directory '%s' to mode %s. %s", + self::normalizePath($path), + decoct($dirMode), + Helpers::getLastError(), + )); + } + } else { + throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($path))); + } + } + + + /** + * Determines if the path is absolute. + */ + public static function isAbsolute(string $path): bool + { + return (bool) preg_match('#([a-z]:)?[/\\\]|[a-z][a-z0-9+.-]*://#Ai', $path); + } + + + /** + * Normalizes `..` and `.` and directory separators in path. + */ + public static function normalizePath(string $path): string + { + $parts = $path === '' ? [] : preg_split('~[/\\\]+~', $path); + $res = []; + foreach ($parts as $part) { + if ($part === '..' && $res && end($res) !== '..' && end($res) !== '') { + array_pop($res); + } elseif ($part !== '.') { + $res[] = $part; + } + } + + return $res === [''] + ? DIRECTORY_SEPARATOR + : implode(DIRECTORY_SEPARATOR, $res); + } + + + /** + * Joins all segments of the path and normalizes the result. + */ + public static function joinPaths(string ...$paths): string + { + return self::normalizePath(implode('/', $paths)); + } + + + /** + * Resolves a path against a base path. If the path is absolute, returns it directly, if it's relative, joins it with the base path. + */ + public static function resolvePath(string $basePath, string $path): string + { + return match (true) { + self::isAbsolute($path) => self::platformSlashes($path), + $path === '' => self::platformSlashes($basePath), + default => self::joinPaths($basePath, $path), + }; + } + + + /** + * Converts backslashes to slashes. + */ + public static function unixSlashes(string $path): string + { + return strtr($path, '\\', '/'); + } + + + /** + * Converts slashes to platform-specific directory separators. + */ + public static function platformSlashes(string $path): string + { + return DIRECTORY_SEPARATOR === '/' + ? strtr($path, '\\', '/') + : str_replace(':\\\\', '://', strtr($path, '/', '\\')); // protocol:// + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php new file mode 100644 index 000000000..587b35972 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php @@ -0,0 +1,510 @@ +size('> 10kB') + * ->from('.') + * ->exclude('temp'); + * + * @implements \IteratorAggregate + */ +class Finder implements \IteratorAggregate +{ + use Nette\SmartObject; + + /** @var array */ + private array $find = []; + + /** @var string[] */ + private array $in = []; + + /** @var \Closure[] */ + private array $filters = []; + + /** @var \Closure[] */ + private array $descentFilters = []; + + /** @var array */ + private array $appends = []; + private bool $childFirst = false; + + /** @var ?callable */ + private $sort; + private int $maxDepth = -1; + private bool $ignoreUnreadableDirs = true; + + + /** + * Begins search for files and directories matching mask. + */ + public static function find(string|array $masks = ['*']): static + { + $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic + return (new static)->addMask($masks, 'dir')->addMask($masks, 'file'); + } + + + /** + * Begins search for files matching mask. + */ + public static function findFiles(string|array $masks = ['*']): static + { + $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic + return (new static)->addMask($masks, 'file'); + } + + + /** + * Begins search for directories matching mask. + */ + public static function findDirectories(string|array $masks = ['*']): static + { + $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic + return (new static)->addMask($masks, 'dir'); + } + + + /** + * Finds files matching the specified masks. + */ + public function files(string|array $masks = ['*']): static + { + return $this->addMask((array) $masks, 'file'); + } + + + /** + * Finds directories matching the specified masks. + */ + public function directories(string|array $masks = ['*']): static + { + return $this->addMask((array) $masks, 'dir'); + } + + + private function addMask(array $masks, string $mode): static + { + foreach ($masks as $mask) { + $mask = FileSystem::unixSlashes($mask); + if ($mode === 'dir') { + $mask = rtrim($mask, '/'); + } + if ($mask === '' || ($mode === 'file' && str_ends_with($mask, '/'))) { + throw new Nette\InvalidArgumentException("Invalid mask '$mask'"); + } + if (str_starts_with($mask, '**/')) { + $mask = substr($mask, 3); + } + $this->find[] = [$mask, $mode]; + } + return $this; + } + + + /** + * Searches in the given directories. Wildcards are allowed. + */ + public function in(string|array $paths): static + { + $paths = is_array($paths) ? $paths : func_get_args(); // compatibility with variadic + $this->addLocation($paths, ''); + return $this; + } + + + /** + * Searches recursively from the given directories. Wildcards are allowed. + */ + public function from(string|array $paths): static + { + $paths = is_array($paths) ? $paths : func_get_args(); // compatibility with variadic + $this->addLocation($paths, '/**'); + return $this; + } + + + private function addLocation(array $paths, string $ext): void + { + foreach ($paths as $path) { + if ($path === '') { + throw new Nette\InvalidArgumentException("Invalid directory '$path'"); + } + $path = rtrim(FileSystem::unixSlashes($path), '/'); + $this->in[] = $path . $ext; + } + } + + + /** + * Lists directory's contents before the directory itself. By default, this is disabled. + */ + public function childFirst(bool $state = true): static + { + $this->childFirst = $state; + return $this; + } + + + /** + * Ignores unreadable directories. By default, this is enabled. + */ + public function ignoreUnreadableDirs(bool $state = true): static + { + $this->ignoreUnreadableDirs = $state; + return $this; + } + + + /** + * Set a compare function for sorting directory entries. The function will be called to sort entries from the same directory. + * @param callable(FileInfo, FileInfo): int $callback + */ + public function sortBy(callable $callback): static + { + $this->sort = $callback; + return $this; + } + + + /** + * Sorts files in each directory naturally by name. + */ + public function sortByName(): static + { + $this->sort = fn(FileInfo $a, FileInfo $b): int => strnatcmp($a->getBasename(), $b->getBasename()); + return $this; + } + + + /** + * Adds the specified paths or appends a new finder that returns. + */ + public function append(string|array|null $paths = null): static + { + if ($paths === null) { + return $this->appends[] = new static; + } + + $this->appends = array_merge($this->appends, (array) $paths); + return $this; + } + + + /********************* filtering ****************d*g**/ + + + /** + * Skips entries that matches the given masks relative to the ones defined with the in() or from() methods. + */ + public function exclude(string|array $masks): static + { + $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic + foreach ($masks as $mask) { + $mask = FileSystem::unixSlashes($mask); + if (!preg_match('~^/?(\*\*/)?(.+)(/\*\*|/\*|/|)$~D', $mask, $m)) { + throw new Nette\InvalidArgumentException("Invalid mask '$mask'"); + } + $end = $m[3]; + $re = $this->buildPattern($m[2]); + $filter = fn(FileInfo $file): bool => ($end && !$file->isDir()) + || !preg_match($re, FileSystem::unixSlashes($file->getRelativePathname())); + + $this->descentFilter($filter); + if ($end !== '/*') { + $this->filter($filter); + } + } + + return $this; + } + + + /** + * Yields only entries which satisfy the given filter. + * @param callable(FileInfo): bool $callback + */ + public function filter(callable $callback): static + { + $this->filters[] = \Closure::fromCallable($callback); + return $this; + } + + + /** + * It descends only to directories that match the specified filter. + * @param callable(FileInfo): bool $callback + */ + public function descentFilter(callable $callback): static + { + $this->descentFilters[] = \Closure::fromCallable($callback); + return $this; + } + + + /** + * Sets the maximum depth of entries. + */ + public function limitDepth(?int $depth): static + { + $this->maxDepth = $depth ?? -1; + return $this; + } + + + /** + * Restricts the search by size. $operator accepts "[operator] [size] [unit]" example: >=10kB + */ + public function size(string $operator, ?int $size = null): static + { + if (func_num_args() === 1) { // in $operator is predicate + if (!preg_match('#^(?:([=<>!]=?|<>)\s*)?((?:\d*\.)?\d+)\s*(K|M|G|)B?$#Di', $operator, $matches)) { + throw new Nette\InvalidArgumentException('Invalid size predicate format.'); + } + + [, $operator, $size, $unit] = $matches; + $units = ['' => 1, 'k' => 1e3, 'm' => 1e6, 'g' => 1e9]; + $size *= $units[strtolower($unit)]; + $operator = $operator ?: '='; + } + + return $this->filter(fn(FileInfo $file): bool => !$file->isFile() || Helpers::compare($file->getSize(), $operator, $size)); + } + + + /** + * Restricts the search by modified time. $operator accepts "[operator] [date]" example: >1978-01-23 + */ + public function date(string $operator, string|int|\DateTimeInterface|null $date = null): static + { + if (func_num_args() === 1) { // in $operator is predicate + if (!preg_match('#^(?:([=<>!]=?|<>)\s*)?(.+)$#Di', $operator, $matches)) { + throw new Nette\InvalidArgumentException('Invalid date predicate format.'); + } + + [, $operator, $date] = $matches; + $operator = $operator ?: '='; + } + + $date = DateTime::from($date)->getTimestamp(); + return $this->filter(fn(FileInfo $file): bool => !$file->isFile() || Helpers::compare($file->getMTime(), $operator, $date)); + } + + + /********************* iterator generator ****************d*g**/ + + + /** + * Returns an array with all found files and directories. + * @return list + */ + public function collect(): array + { + return iterator_to_array($this->getIterator(), preserve_keys: false); + } + + + /** @return \Generator */ + public function getIterator(): \Generator + { + $plan = $this->buildPlan(); + foreach ($plan as $dir => $searches) { + yield from $this->traverseDir($dir, $searches); + } + + foreach ($this->appends as $item) { + if ($item instanceof self) { + yield from $item->getIterator(); + } else { + $item = FileSystem::platformSlashes($item); + yield $item => new FileInfo($item); + } + } + } + + + /** + * @param array $searches + * @param string[] $subdirs + * @return \Generator + */ + private function traverseDir(string $dir, array $searches, array $subdirs = []): \Generator + { + if ($this->maxDepth >= 0 && count($subdirs) > $this->maxDepth) { + return; + } elseif (!is_dir($dir)) { + throw new Nette\InvalidStateException(sprintf("Directory '%s' does not exist.", rtrim($dir, '/\\'))); + } + + try { + $pathNames = new \FilesystemIterator($dir, \FilesystemIterator::FOLLOW_SYMLINKS | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::UNIX_PATHS); + } catch (\UnexpectedValueException $e) { + if ($this->ignoreUnreadableDirs) { + return; + } else { + throw new Nette\InvalidStateException($e->getMessage()); + } + } + + $files = $this->convertToFiles($pathNames, implode('/', $subdirs), FileSystem::isAbsolute($dir)); + + if ($this->sort) { + $files = iterator_to_array($files); + usort($files, $this->sort); + } + + foreach ($files as $file) { + $pathName = $file->getPathname(); + $cache = $subSearch = []; + + if ($file->isDir()) { + foreach ($searches as $search) { + if ($search->recursive && $this->proveFilters($this->descentFilters, $file, $cache)) { + $subSearch[] = $search; + } + } + } + + if ($this->childFirst && $subSearch) { + yield from $this->traverseDir($pathName, $subSearch, array_merge($subdirs, [$file->getBasename()])); + } + + $relativePathname = FileSystem::unixSlashes($file->getRelativePathname()); + foreach ($searches as $search) { + if ( + $file->{'is' . $search->mode}() + && preg_match($search->pattern, $relativePathname) + && $this->proveFilters($this->filters, $file, $cache) + ) { + yield $pathName => $file; + break; + } + } + + if (!$this->childFirst && $subSearch) { + yield from $this->traverseDir($pathName, $subSearch, array_merge($subdirs, [$file->getBasename()])); + } + } + } + + + private function convertToFiles(iterable $pathNames, string $relativePath, bool $absolute): \Generator + { + foreach ($pathNames as $pathName) { + if (!$absolute) { + $pathName = preg_replace('~\.?/~A', '', $pathName); + } + $pathName = FileSystem::platformSlashes($pathName); + yield new FileInfo($pathName, $relativePath); + } + } + + + private function proveFilters(array $filters, FileInfo $file, array &$cache): bool + { + foreach ($filters as $filter) { + $res = &$cache[spl_object_id($filter)]; + $res ??= $filter($file); + if (!$res) { + return false; + } + } + + return true; + } + + + /** @return array> */ + private function buildPlan(): array + { + $plan = $dirCache = []; + foreach ($this->find as [$mask, $mode]) { + $splits = []; + if (FileSystem::isAbsolute($mask)) { + if ($this->in) { + throw new Nette\InvalidStateException("You cannot combine the absolute path in the mask '$mask' and the directory to search '{$this->in[0]}'."); + } + $splits[] = self::splitRecursivePart($mask); + } else { + foreach ($this->in ?: ['.'] as $in) { + $in = strtr($in, ['[' => '[[]', ']' => '[]]']); // in path, do not treat [ and ] as a pattern by glob() + $splits[] = self::splitRecursivePart($in . '/' . $mask); + } + } + + foreach ($splits as [$base, $rest, $recursive]) { + $base = $base === '' ? '.' : $base; + $dirs = $dirCache[$base] ??= strpbrk($base, '*?[') + ? glob($base, GLOB_NOSORT | GLOB_ONLYDIR | GLOB_NOESCAPE) + : [strtr($base, ['[[]' => '[', '[]]' => ']'])]; // unescape [ and ] + + if (!$dirs) { + throw new Nette\InvalidStateException(sprintf("Directory '%s' does not exist.", rtrim($base, '/\\'))); + } + + $search = (object) ['pattern' => $this->buildPattern($rest), 'mode' => $mode, 'recursive' => $recursive]; + foreach ($dirs as $dir) { + $plan[$dir][] = $search; + } + } + } + + return $plan; + } + + + /** + * Since glob() does not know ** wildcard, we divide the path into a part for glob and a part for manual traversal. + */ + private static function splitRecursivePart(string $path): array + { + $a = strrpos($path, '/'); + $parts = preg_split('~(?<=^|/)\*\*($|/)~', substr($path, 0, $a + 1), 2); + return isset($parts[1]) + ? [$parts[0], $parts[1] . substr($path, $a + 1), true] + : [$parts[0], substr($path, $a + 1), false]; + } + + + /** + * Converts wildcards to regular expression. + */ + private function buildPattern(string $mask): string + { + if ($mask === '*') { + return '##'; + } elseif (str_starts_with($mask, './')) { + $anchor = '^'; + $mask = substr($mask, 2); + } else { + $anchor = '(?:^|/)'; + } + + $pattern = strtr( + preg_quote($mask, '#'), + [ + '\*\*/' => '(.+/)?', + '\*' => '[^/]*', + '\?' => '[^/]', + '\[\!' => '[^', + '\[' => '[', + '\]' => ']', + '\-' => '-', + ], + ); + return '#' . $anchor . $pattern . '$#D' . (Helpers::IsWindows ? 'i' : ''); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Floats.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Floats.php new file mode 100644 index 000000000..cc2781d71 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Floats.php @@ -0,0 +1,107 @@ + $b it returns 1 + * @throws \LogicException if one of parameters is NAN + */ + public static function compare(float $a, float $b): int + { + if (is_nan($a) || is_nan($b)) { + throw new \LogicException('Trying to compare NAN'); + + } elseif (!is_finite($a) && !is_finite($b) && $a === $b) { + return 0; + } + + $diff = abs($a - $b); + if (($diff < self::Epsilon || ($diff / max(abs($a), abs($b)) < self::Epsilon))) { + return 0; + } + + return $a < $b ? -1 : 1; + } + + + /** + * Returns true if $a = $b + * @throws \LogicException if one of parameters is NAN + */ + public static function areEqual(float $a, float $b): bool + { + return self::compare($a, $b) === 0; + } + + + /** + * Returns true if $a < $b + * @throws \LogicException if one of parameters is NAN + */ + public static function isLessThan(float $a, float $b): bool + { + return self::compare($a, $b) < 0; + } + + + /** + * Returns true if $a <= $b + * @throws \LogicException if one of parameters is NAN + */ + public static function isLessThanOrEqualTo(float $a, float $b): bool + { + return self::compare($a, $b) <= 0; + } + + + /** + * Returns true if $a > $b + * @throws \LogicException if one of parameters is NAN + */ + public static function isGreaterThan(float $a, float $b): bool + { + return self::compare($a, $b) > 0; + } + + + /** + * Returns true if $a >= $b + * @throws \LogicException if one of parameters is NAN + */ + public static function isGreaterThanOrEqualTo(float $a, float $b): bool + { + return self::compare($a, $b) >= 0; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Helpers.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Helpers.php new file mode 100644 index 000000000..21efb2ac7 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Helpers.php @@ -0,0 +1,107 @@ + $max) { + throw new Nette\InvalidArgumentException("Minimum ($min) is not less than maximum ($max)."); + } + + return min(max($value, $min), $max); + } + + + /** + * Looks for a string from possibilities that is most similar to value, but not the same (for 8-bit encoding). + * @param string[] $possibilities + */ + public static function getSuggestion(array $possibilities, string $value): ?string + { + $best = null; + $min = (strlen($value) / 4 + 1) * 10 + .1; + foreach (array_unique($possibilities) as $item) { + if ($item !== $value && ($len = levenshtein($item, $value, 10, 11, 10)) < $min) { + $min = $len; + $best = $item; + } + } + + return $best; + } + + + /** + * Compares two values in the same way that PHP does. Recognizes operators: >, >=, <, <=, =, ==, ===, !=, !==, <> + */ + public static function compare(mixed $left, string $operator, mixed $right): bool + { + return match ($operator) { + '>' => $left > $right, + '>=' => $left >= $right, + '<' => $left < $right, + '<=' => $left <= $right, + '=', '==' => $left == $right, + '===' => $left === $right, + '!=', '<>' => $left != $right, + '!==' => $left !== $right, + default => throw new Nette\InvalidArgumentException("Unknown operator '$operator'"), + }; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Html.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Html.php new file mode 100644 index 000000000..fc0e3ef2a --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Html.php @@ -0,0 +1,839 @@ + element's attributes */ + public $attrs = []; + + /** void elements */ + public static $emptyElements = [ + 'img' => 1, 'hr' => 1, 'br' => 1, 'input' => 1, 'meta' => 1, 'area' => 1, 'embed' => 1, 'keygen' => 1, + 'source' => 1, 'base' => 1, 'col' => 1, 'link' => 1, 'param' => 1, 'basefont' => 1, 'frame' => 1, + 'isindex' => 1, 'wbr' => 1, 'command' => 1, 'track' => 1, + ]; + + /** @var array nodes */ + protected $children = []; + + /** element's name */ + private string $name = ''; + + private bool $isEmpty = false; + + + /** + * Constructs new HTML element. + * @param array|string $attrs element's attributes or plain text content + */ + public static function el(?string $name = null, array|string|null $attrs = null): static + { + $el = new static; + $parts = explode(' ', (string) $name, 2); + $el->setName($parts[0]); + + if (is_array($attrs)) { + $el->attrs = $attrs; + + } elseif ($attrs !== null) { + $el->setText($attrs); + } + + if (isset($parts[1])) { + foreach (Strings::matchAll($parts[1] . ' ', '#([a-z0-9:-]+)(?:=(["\'])?(.*?)(?(2)\2|\s))?#i') as $m) { + $el->attrs[$m[1]] = $m[3] ?? true; + } + } + + return $el; + } + + + /** + * Returns an object representing HTML text. + */ + public static function fromHtml(string $html): static + { + return (new static)->setHtml($html); + } + + + /** + * Returns an object representing plain text. + */ + public static function fromText(string $text): static + { + return (new static)->setText($text); + } + + + /** + * Converts to HTML. + */ + final public function toHtml(): string + { + return $this->render(); + } + + + /** + * Converts to plain text. + */ + final public function toText(): string + { + return $this->getText(); + } + + + /** + * Converts given HTML code to plain text. + */ + public static function htmlToText(string $html): string + { + return html_entity_decode(strip_tags($html), ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } + + + /** + * Changes element's name. + */ + final public function setName(string $name, ?bool $isEmpty = null): static + { + $this->name = $name; + $this->isEmpty = $isEmpty ?? isset(static::$emptyElements[$name]); + return $this; + } + + + /** + * Returns element's name. + */ + final public function getName(): string + { + return $this->name; + } + + + /** + * Is element empty? + */ + final public function isEmpty(): bool + { + return $this->isEmpty; + } + + + /** + * Sets multiple attributes. + */ + public function addAttributes(array $attrs): static + { + $this->attrs = array_merge($this->attrs, $attrs); + return $this; + } + + + /** + * Appends value to element's attribute. + */ + public function appendAttribute(string $name, mixed $value, mixed $option = true): static + { + if (is_array($value)) { + $prev = isset($this->attrs[$name]) ? (array) $this->attrs[$name] : []; + $this->attrs[$name] = $value + $prev; + + } elseif ((string) $value === '') { + $tmp = &$this->attrs[$name]; // appending empty value? -> ignore, but ensure it exists + + } elseif (!isset($this->attrs[$name]) || is_array($this->attrs[$name])) { // needs array + $this->attrs[$name][$value] = $option; + + } else { + $this->attrs[$name] = [$this->attrs[$name] => true, $value => $option]; + } + + return $this; + } + + + /** + * Sets element's attribute. + */ + public function setAttribute(string $name, mixed $value): static + { + $this->attrs[$name] = $value; + return $this; + } + + + /** + * Returns element's attribute. + */ + public function getAttribute(string $name): mixed + { + return $this->attrs[$name] ?? null; + } + + + /** + * Unsets element's attribute. + */ + public function removeAttribute(string $name): static + { + unset($this->attrs[$name]); + return $this; + } + + + /** + * Unsets element's attributes. + */ + public function removeAttributes(array $attributes): static + { + foreach ($attributes as $name) { + unset($this->attrs[$name]); + } + + return $this; + } + + + /** + * Overloaded setter for element's attribute. + */ + final public function __set(string $name, mixed $value): void + { + $this->attrs[$name] = $value; + } + + + /** + * Overloaded getter for element's attribute. + */ + final public function &__get(string $name): mixed + { + return $this->attrs[$name]; + } + + + /** + * Overloaded tester for element's attribute. + */ + final public function __isset(string $name): bool + { + return isset($this->attrs[$name]); + } + + + /** + * Overloaded unsetter for element's attribute. + */ + final public function __unset(string $name): void + { + unset($this->attrs[$name]); + } + + + /** + * Overloaded setter for element's attribute. + */ + final public function __call(string $m, array $args): mixed + { + $p = substr($m, 0, 3); + if ($p === 'get' || $p === 'set' || $p === 'add') { + $m = substr($m, 3); + $m[0] = $m[0] | "\x20"; + if ($p === 'get') { + return $this->attrs[$m] ?? null; + + } elseif ($p === 'add') { + $args[] = true; + } + } + + if (count($args) === 0) { // invalid + + } elseif (count($args) === 1) { // set + $this->attrs[$m] = $args[0]; + + } else { // add + $this->appendAttribute($m, $args[0], $args[1]); + } + + return $this; + } + + + /** + * Special setter for element's attribute. + */ + final public function href(string $path, array $query = []): static + { + if ($query) { + $query = http_build_query($query, '', '&'); + if ($query !== '') { + $path .= '?' . $query; + } + } + + $this->attrs['href'] = $path; + return $this; + } + + + /** + * Setter for data-* attributes. Booleans are converted to 'true' resp. 'false'. + */ + public function data(string $name, mixed $value = null): static + { + if (func_num_args() === 1) { + $this->attrs['data'] = $name; + } else { + $this->attrs["data-$name"] = is_bool($value) + ? json_encode($value) + : $value; + } + + return $this; + } + + + /** + * Sets element's HTML content. + */ + final public function setHtml(mixed $html): static + { + $this->children = [(string) $html]; + return $this; + } + + + /** + * Returns element's HTML content. + */ + final public function getHtml(): string + { + return implode('', $this->children); + } + + + /** + * Sets element's textual content. + */ + final public function setText(mixed $text): static + { + if (!$text instanceof HtmlStringable) { + $text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8'); + } + + $this->children = [(string) $text]; + return $this; + } + + + /** + * Returns element's textual content. + */ + final public function getText(): string + { + return self::htmlToText($this->getHtml()); + } + + + /** + * Adds new element's child. + */ + final public function addHtml(mixed $child): static + { + return $this->insert(null, $child); + } + + + /** + * Appends plain-text string to element content. + */ + public function addText(mixed $text): static + { + if (!$text instanceof HtmlStringable) { + $text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8'); + } + + return $this->insert(null, $text); + } + + + /** + * Creates and adds a new Html child. + */ + final public function create(string $name, array|string|null $attrs = null): static + { + $this->insert(null, $child = static::el($name, $attrs)); + return $child; + } + + + /** + * Inserts child node. + */ + public function insert(?int $index, HtmlStringable|string $child, bool $replace = false): static + { + $child = $child instanceof self ? $child : (string) $child; + if ($index === null) { // append + $this->children[] = $child; + + } else { // insert or replace + array_splice($this->children, $index, $replace ? 1 : 0, [$child]); + } + + return $this; + } + + + /** + * Inserts (replaces) child node (\ArrayAccess implementation). + * @param int|null $index position or null for appending + * @param Html|string $child Html node or raw HTML string + */ + final public function offsetSet($index, $child): void + { + $this->insert($index, $child, replace: true); + } + + + /** + * Returns child node (\ArrayAccess implementation). + * @param int $index + */ + final public function offsetGet($index): HtmlStringable|string + { + return $this->children[$index]; + } + + + /** + * Exists child node? (\ArrayAccess implementation). + * @param int $index + */ + final public function offsetExists($index): bool + { + return isset($this->children[$index]); + } + + + /** + * Removes child node (\ArrayAccess implementation). + * @param int $index + */ + public function offsetUnset($index): void + { + if (isset($this->children[$index])) { + array_splice($this->children, $index, 1); + } + } + + + /** + * Returns children count. + */ + final public function count(): int + { + return count($this->children); + } + + + /** + * Removes all children. + */ + public function removeChildren(): void + { + $this->children = []; + } + + + /** + * Iterates over elements. + * @return \ArrayIterator + */ + final public function getIterator(): \ArrayIterator + { + return new \ArrayIterator($this->children); + } + + + /** + * Returns all children. + */ + final public function getChildren(): array + { + return $this->children; + } + + + /** + * Renders element's start tag, content and end tag. + */ + final public function render(?int $indent = null): string + { + $s = $this->startTag(); + + if (!$this->isEmpty) { + // add content + if ($indent !== null) { + $indent++; + } + + foreach ($this->children as $child) { + if ($child instanceof self) { + $s .= $child->render($indent); + } else { + $s .= $child; + } + } + + // add end tag + $s .= $this->endTag(); + } + + if ($indent !== null) { + return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2)); + } + + return $s; + } + + + final public function __toString(): string + { + return $this->render(); + } + + + /** + * Returns element's start tag. + */ + final public function startTag(): string + { + return $this->name + ? '<' . $this->name . $this->attributes() . '>' + : ''; + } + + + /** + * Returns element's end tag. + */ + final public function endTag(): string + { + return $this->name && !$this->isEmpty ? 'name . '>' : ''; + } + + + /** + * Returns element's attributes. + * @internal + */ + final public function attributes(): string + { + if (!is_array($this->attrs)) { + return ''; + } + + $s = ''; + $attrs = $this->attrs; + foreach ($attrs as $key => $value) { + if ($value === null || $value === false) { + continue; + + } elseif ($value === true) { + $s .= ' ' . $key; + + continue; + + } elseif (is_array($value)) { + if (strncmp($key, 'data-', 5) === 0) { + $value = Json::encode($value); + + } else { + $tmp = null; + foreach ($value as $k => $v) { + if ($v != null) { // intentionally ==, skip nulls & empty string + // composite 'style' vs. 'others' + $tmp[] = $v === true + ? $k + : (is_string($k) ? $k . ':' . $v : $v); + } + } + + if ($tmp === null) { + continue; + } + + $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp); + } + } elseif (is_float($value)) { + $value = rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.'); + + } else { + $value = (string) $value; + } + + $q = str_contains($value, '"') ? "'" : '"'; + $s .= ' ' . $key . '=' . $q + . str_replace( + ['&', $q, '<'], + ['&', $q === '"' ? '"' : ''', '<'], + $value, + ) + . (str_contains($value, '`') && strpbrk($value, ' <>"\'') === false ? ' ' : '') + . $q; + } + + $s = str_replace('@', '@', $s); + return $s; + } + + + /** + * Clones all children too. + */ + public function __clone() + { + foreach ($this->children as $key => $value) { + if (is_object($value)) { + $this->children[$key] = clone $value; + } + } + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Image.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Image.php new file mode 100644 index 000000000..19ce7b4f4 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Image.php @@ -0,0 +1,816 @@ + + * $image = Image::fromFile('nette.jpg'); + * $image->resize(150, 100); + * $image->sharpen(); + * $image->send(); + * + * + * @method Image affine(array $affine, ?array $clip = null) + * @method void alphaBlending(bool $enable) + * @method void antialias(bool $enable) + * @method void arc(int $centerX, int $centerY, int $width, int $height, int $startAngle, int $endAngle, ImageColor $color) + * @method int colorAllocate(int $red, int $green, int $blue) + * @method int colorAllocateAlpha(int $red, int $green, int $blue, int $alpha) + * @method int colorAt(int $x, int $y) + * @method int colorClosest(int $red, int $green, int $blue) + * @method int colorClosestAlpha(int $red, int $green, int $blue, int $alpha) + * @method int colorClosestHWB(int $red, int $green, int $blue) + * @method void colorDeallocate(int $color) + * @method int colorExact(int $red, int $green, int $blue) + * @method int colorExactAlpha(int $red, int $green, int $blue, int $alpha) + * @method void colorMatch(Image $image2) + * @method int colorResolve(int $red, int $green, int $blue) + * @method int colorResolveAlpha(int $red, int $green, int $blue, int $alpha) + * @method void colorSet(int $index, int $red, int $green, int $blue, int $alpha = 0) + * @method array colorsForIndex(int $color) + * @method int colorsTotal() + * @method int colorTransparent(?int $color = null) + * @method void convolution(array $matrix, float $div, float $offset) + * @method void copy(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH) + * @method void copyMerge(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH, int $pct) + * @method void copyMergeGray(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH, int $pct) + * @method void copyResampled(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $dstW, int $dstH, int $srcW, int $srcH) + * @method void copyResized(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $dstW, int $dstH, int $srcW, int $srcH) + * @method Image cropAuto(int $mode = IMG_CROP_DEFAULT, float $threshold = .5, ?ImageColor $color = null) + * @method void ellipse(int $centerX, int $centerY, int $width, int $height, ImageColor $color) + * @method void fill(int $x, int $y, ImageColor $color) + * @method void filledArc(int $centerX, int $centerY, int $width, int $height, int $startAngle, int $endAngle, ImageColor $color, int $style) + * @method void filledEllipse(int $centerX, int $centerY, int $width, int $height, ImageColor $color) + * @method void filledPolygon(array $points, ImageColor $color) + * @method void filledRectangle(int $x1, int $y1, int $x2, int $y2, ImageColor $color) + * @method void fillToBorder(int $x, int $y, ImageColor $borderColor, ImageColor $color) + * @method void filter(int $filter, ...$args) + * @method void flip(int $mode) + * @method array ftText(float $size, float $angle, int $x, int $y, ImageColor $color, string $fontFile, string $text, array $options = []) + * @method void gammaCorrect(float $inputgamma, float $outputgamma) + * @method array getClip() + * @method int getInterpolation() + * @method int interlace(?bool $enable = null) + * @method bool isTrueColor() + * @method void layerEffect(int $effect) + * @method void line(int $x1, int $y1, int $x2, int $y2, ImageColor $color) + * @method void openPolygon(array $points, ImageColor $color) + * @method void paletteCopy(Image $source) + * @method void paletteToTrueColor() + * @method void polygon(array $points, ImageColor $color) + * @method void rectangle(int $x1, int $y1, int $x2, int $y2, ImageColor $color) + * @method mixed resolution(?int $resolutionX = null, ?int $resolutionY = null) + * @method Image rotate(float $angle, ImageColor $backgroundColor) + * @method void saveAlpha(bool $enable) + * @method Image scale(int $newWidth, int $newHeight = -1, int $mode = IMG_BILINEAR_FIXED) + * @method void setBrush(Image $brush) + * @method void setClip(int $x1, int $y1, int $x2, int $y2) + * @method void setInterpolation(int $method = IMG_BILINEAR_FIXED) + * @method void setPixel(int $x, int $y, ImageColor $color) + * @method void setStyle(array $style) + * @method void setThickness(int $thickness) + * @method void setTile(Image $tile) + * @method void trueColorToPalette(bool $dither, int $ncolors) + * @method array ttfText(float $size, float $angle, int $x, int $y, ImageColor $color, string $fontfile, string $text, array $options = []) + * @property-read positive-int $width + * @property-read positive-int $height + * @property-read \GdImage $imageResource + */ +class Image +{ + use Nette\SmartObject; + + /** Prevent from getting resized to a bigger size than the original */ + public const ShrinkOnly = 0b0001; + + /** Resizes to a specified width and height without keeping aspect ratio */ + public const Stretch = 0b0010; + + /** Resizes to fit into a specified width and height and preserves aspect ratio */ + public const OrSmaller = 0b0000; + + /** Resizes while bounding the smaller dimension to the specified width or height and preserves aspect ratio */ + public const OrBigger = 0b0100; + + /** Resizes to the smallest possible size to completely cover specified width and height and reserves aspect ratio */ + public const Cover = 0b1000; + + /** @deprecated use Image::ShrinkOnly */ + public const SHRINK_ONLY = self::ShrinkOnly; + + /** @deprecated use Image::Stretch */ + public const STRETCH = self::Stretch; + + /** @deprecated use Image::OrSmaller */ + public const FIT = self::OrSmaller; + + /** @deprecated use Image::OrBigger */ + public const FILL = self::OrBigger; + + /** @deprecated use Image::Cover */ + public const EXACT = self::Cover; + + /** @deprecated use Image::EmptyGIF */ + public const EMPTY_GIF = self::EmptyGIF; + + /** image types */ + public const + JPEG = ImageType::JPEG, + PNG = ImageType::PNG, + GIF = ImageType::GIF, + WEBP = ImageType::WEBP, + AVIF = ImageType::AVIF, + BMP = ImageType::BMP; + + public const EmptyGIF = "GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;"; + + private const Formats = [ImageType::JPEG => 'jpeg', ImageType::PNG => 'png', ImageType::GIF => 'gif', ImageType::WEBP => 'webp', ImageType::AVIF => 'avif', ImageType::BMP => 'bmp']; + + private \GdImage $image; + + + /** + * Returns RGB color (0..255) and transparency (0..127). + * @deprecated use ImageColor::rgb() + */ + public static function rgb(int $red, int $green, int $blue, int $transparency = 0): array + { + return [ + 'red' => max(0, min(255, $red)), + 'green' => max(0, min(255, $green)), + 'blue' => max(0, min(255, $blue)), + 'alpha' => max(0, min(127, $transparency)), + ]; + } + + + /** + * Reads an image from a file and returns its type in $type. + * @throws Nette\NotSupportedException if gd extension is not loaded + * @throws UnknownImageFileException if file not found or file type is not known + */ + public static function fromFile(string $file, ?int &$type = null): static + { + self::ensureExtension(); + $type = self::detectTypeFromFile($file); + if (!$type) { + throw new UnknownImageFileException(is_file($file) ? "Unknown type of file '$file'." : "File '$file' not found."); + } + + return self::invokeSafe('imagecreatefrom' . self::Formats[$type], $file, "Unable to open file '$file'.", __METHOD__); + } + + + /** + * Reads an image from a string and returns its type in $type. + * @throws Nette\NotSupportedException if gd extension is not loaded + * @throws ImageException + */ + public static function fromString(string $s, ?int &$type = null): static + { + self::ensureExtension(); + $type = self::detectTypeFromString($s); + if (!$type) { + throw new UnknownImageFileException('Unknown type of image.'); + } + + return self::invokeSafe('imagecreatefromstring', $s, 'Unable to open image from string.', __METHOD__); + } + + + private static function invokeSafe(string $func, string $arg, string $message, string $callee): static + { + $errors = []; + $res = Callback::invokeSafe($func, [$arg], function (string $message) use (&$errors): void { + $errors[] = $message; + }); + + if (!$res) { + throw new ImageException($message . ' Errors: ' . implode(', ', $errors)); + } elseif ($errors) { + trigger_error($callee . '(): ' . implode(', ', $errors), E_USER_WARNING); + } + + return new static($res); + } + + + /** + * Creates a new true color image of the given dimensions. The default color is black. + * @param positive-int $width + * @param positive-int $height + * @throws Nette\NotSupportedException if gd extension is not loaded + */ + public static function fromBlank(int $width, int $height, ImageColor|array|null $color = null): static + { + self::ensureExtension(); + if ($width < 1 || $height < 1) { + throw new Nette\InvalidArgumentException('Image width and height must be greater than zero.'); + } + + $image = new static(imagecreatetruecolor($width, $height)); + if ($color) { + $image->alphablending(false); + $image->filledrectangle(0, 0, $width - 1, $height - 1, $color); + $image->alphablending(true); + } + + return $image; + } + + + /** + * Returns the type of image from file. + * @return ImageType::*|null + */ + public static function detectTypeFromFile(string $file, &$width = null, &$height = null): ?int + { + [$width, $height, $type] = @getimagesize($file); // @ - files smaller than 12 bytes causes read error + return isset(self::Formats[$type]) ? $type : null; + } + + + /** + * Returns the type of image from string. + * @return ImageType::*|null + */ + public static function detectTypeFromString(string $s, &$width = null, &$height = null): ?int + { + [$width, $height, $type] = @getimagesizefromstring($s); // @ - strings smaller than 12 bytes causes read error + return isset(self::Formats[$type]) ? $type : null; + } + + + /** + * Returns the file extension for the given image type. + * @param ImageType::* $type + * @return value-of + */ + public static function typeToExtension(int $type): string + { + if (!isset(self::Formats[$type])) { + throw new Nette\InvalidArgumentException("Unsupported image type '$type'."); + } + + return self::Formats[$type]; + } + + + /** + * Returns the image type for given file extension. + * @return ImageType::* + */ + public static function extensionToType(string $extension): int + { + $extensions = array_flip(self::Formats) + ['jpg' => ImageType::JPEG]; + $extension = strtolower($extension); + if (!isset($extensions[$extension])) { + throw new Nette\InvalidArgumentException("Unsupported file extension '$extension'."); + } + + return $extensions[$extension]; + } + + + /** + * Returns the mime type for the given image type. + * @param ImageType::* $type + */ + public static function typeToMimeType(int $type): string + { + return 'image/' . self::typeToExtension($type); + } + + + /** + * @param ImageType::* $type + */ + public static function isTypeSupported(int $type): bool + { + self::ensureExtension(); + return (bool) (imagetypes() & match ($type) { + ImageType::JPEG => IMG_JPG, + ImageType::PNG => IMG_PNG, + ImageType::GIF => IMG_GIF, + ImageType::WEBP => IMG_WEBP, + ImageType::AVIF => 256, // IMG_AVIF, + ImageType::BMP => IMG_BMP, + default => 0, + }); + } + + + /** @return ImageType[] */ + public static function getSupportedTypes(): array + { + self::ensureExtension(); + $flag = imagetypes(); + return array_filter([ + $flag & IMG_GIF ? ImageType::GIF : null, + $flag & IMG_JPG ? ImageType::JPEG : null, + $flag & IMG_PNG ? ImageType::PNG : null, + $flag & IMG_WEBP ? ImageType::WEBP : null, + $flag & 256 ? ImageType::AVIF : null, // IMG_AVIF + $flag & IMG_BMP ? ImageType::BMP : null, + ]); + } + + + /** + * Wraps GD image. + */ + public function __construct(\GdImage $image) + { + $this->setImageResource($image); + imagesavealpha($image, true); + } + + + /** + * Returns image width. + * @return positive-int + */ + public function getWidth(): int + { + return imagesx($this->image); + } + + + /** + * Returns image height. + * @return positive-int + */ + public function getHeight(): int + { + return imagesy($this->image); + } + + + /** + * Sets image resource. + */ + protected function setImageResource(\GdImage $image): static + { + $this->image = $image; + return $this; + } + + + /** + * Returns image GD resource. + */ + public function getImageResource(): \GdImage + { + return $this->image; + } + + + /** + * Scales an image. Width and height accept pixels or percent. + * @param int-mask-of $mode + */ + public function resize(int|string|null $width, int|string|null $height, int $mode = self::OrSmaller): static + { + if ($mode & self::Cover) { + return $this->resize($width, $height, self::OrBigger)->crop('50%', '50%', $width, $height); + } + + [$newWidth, $newHeight] = static::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $mode); + + if ($newWidth !== $this->getWidth() || $newHeight !== $this->getHeight()) { // resize + $newImage = static::fromBlank($newWidth, $newHeight, ImageColor::rgb(0, 0, 0, 0))->getImageResource(); + imagecopyresampled( + $newImage, + $this->image, + 0, + 0, + 0, + 0, + $newWidth, + $newHeight, + $this->getWidth(), + $this->getHeight(), + ); + $this->image = $newImage; + } + + if ($width < 0 || $height < 0) { + imageflip($this->image, $width < 0 ? ($height < 0 ? IMG_FLIP_BOTH : IMG_FLIP_HORIZONTAL) : IMG_FLIP_VERTICAL); + } + + return $this; + } + + + /** + * Calculates dimensions of resized image. Width and height accept pixels or percent. + * @param int-mask-of $mode + */ + public static function calculateSize( + int $srcWidth, + int $srcHeight, + $newWidth, + $newHeight, + int $mode = self::OrSmaller, + ): array + { + if ($newWidth === null) { + } elseif (self::isPercent($newWidth)) { + $newWidth = (int) round($srcWidth / 100 * abs($newWidth)); + $percents = true; + } else { + $newWidth = abs($newWidth); + } + + if ($newHeight === null) { + } elseif (self::isPercent($newHeight)) { + $newHeight = (int) round($srcHeight / 100 * abs($newHeight)); + $mode |= empty($percents) ? 0 : self::Stretch; + } else { + $newHeight = abs($newHeight); + } + + if ($mode & self::Stretch) { // non-proportional + if (!$newWidth || !$newHeight) { + throw new Nette\InvalidArgumentException('For stretching must be both width and height specified.'); + } + + if ($mode & self::ShrinkOnly) { + $newWidth = min($srcWidth, $newWidth); + $newHeight = min($srcHeight, $newHeight); + } + } else { // proportional + if (!$newWidth && !$newHeight) { + throw new Nette\InvalidArgumentException('At least width or height must be specified.'); + } + + $scale = []; + if ($newWidth > 0) { // fit width + $scale[] = $newWidth / $srcWidth; + } + + if ($newHeight > 0) { // fit height + $scale[] = $newHeight / $srcHeight; + } + + if ($mode & self::OrBigger) { + $scale = [max($scale)]; + } + + if ($mode & self::ShrinkOnly) { + $scale[] = 1; + } + + $scale = min($scale); + $newWidth = (int) round($srcWidth * $scale); + $newHeight = (int) round($srcHeight * $scale); + } + + return [max($newWidth, 1), max($newHeight, 1)]; + } + + + /** + * Crops image. Arguments accepts pixels or percent. + */ + public function crop(int|string $left, int|string $top, int|string $width, int|string $height): static + { + [$r['x'], $r['y'], $r['width'], $r['height']] + = static::calculateCutout($this->getWidth(), $this->getHeight(), $left, $top, $width, $height); + if (gd_info()['GD Version'] === 'bundled (2.1.0 compatible)') { + $this->image = imagecrop($this->image, $r); + imagesavealpha($this->image, true); + } else { + $newImage = static::fromBlank($r['width'], $r['height'], ImageColor::rgb(0, 0, 0, 0))->getImageResource(); + imagecopy($newImage, $this->image, 0, 0, $r['x'], $r['y'], $r['width'], $r['height']); + $this->image = $newImage; + } + + return $this; + } + + + /** + * Calculates dimensions of cutout in image. Arguments accepts pixels or percent. + */ + public static function calculateCutout( + int $srcWidth, + int $srcHeight, + int|string $left, + int|string $top, + int|string $newWidth, + int|string $newHeight, + ): array + { + if (self::isPercent($newWidth)) { + $newWidth = (int) round($srcWidth / 100 * $newWidth); + } + + if (self::isPercent($newHeight)) { + $newHeight = (int) round($srcHeight / 100 * $newHeight); + } + + if (self::isPercent($left)) { + $left = (int) round(($srcWidth - $newWidth) / 100 * $left); + } + + if (self::isPercent($top)) { + $top = (int) round(($srcHeight - $newHeight) / 100 * $top); + } + + if ($left < 0) { + $newWidth += $left; + $left = 0; + } + + if ($top < 0) { + $newHeight += $top; + $top = 0; + } + + $newWidth = min($newWidth, $srcWidth - $left); + $newHeight = min($newHeight, $srcHeight - $top); + return [$left, $top, $newWidth, $newHeight]; + } + + + /** + * Sharpens image a little bit. + */ + public function sharpen(): static + { + imageconvolution($this->image, [ // my magic numbers ;) + [-1, -1, -1], + [-1, 24, -1], + [-1, -1, -1], + ], 16, 0); + return $this; + } + + + /** + * Puts another image into this image. Left and top accepts pixels or percent. + * @param int<0, 100> $opacity 0..100 + */ + public function place(self $image, int|string $left = 0, int|string $top = 0, int $opacity = 100): static + { + $opacity = max(0, min(100, $opacity)); + if ($opacity === 0) { + return $this; + } + + $width = $image->getWidth(); + $height = $image->getHeight(); + + if (self::isPercent($left)) { + $left = (int) round(($this->getWidth() - $width) / 100 * $left); + } + + if (self::isPercent($top)) { + $top = (int) round(($this->getHeight() - $height) / 100 * $top); + } + + $output = $input = $image->image; + if ($opacity < 100) { + $tbl = []; + for ($i = 0; $i < 128; $i++) { + $tbl[$i] = round(127 - (127 - $i) * $opacity / 100); + } + + $output = imagecreatetruecolor($width, $height); + imagealphablending($output, false); + if (!$image->isTrueColor()) { + $input = $output; + imagefilledrectangle($output, 0, 0, $width, $height, imagecolorallocatealpha($output, 0, 0, 0, 127)); + imagecopy($output, $image->image, 0, 0, 0, 0, $width, $height); + } + + for ($x = 0; $x < $width; $x++) { + for ($y = 0; $y < $height; $y++) { + $c = \imagecolorat($input, $x, $y); + $c = ($c & 0xFFFFFF) + ($tbl[$c >> 24] << 24); + \imagesetpixel($output, $x, $y, $c); + } + } + + imagealphablending($output, true); + } + + imagecopy( + $this->image, + $output, + $left, + $top, + 0, + 0, + $width, + $height, + ); + return $this; + } + + + /** + * Calculates the bounding box for a TrueType text. Returns keys left, top, width and height. + */ + public static function calculateTextBox( + string $text, + string $fontFile, + float $size, + float $angle = 0, + array $options = [], + ): array + { + self::ensureExtension(); + $box = imagettfbbox($size, $angle, $fontFile, $text, $options); + return [ + 'left' => $minX = min([$box[0], $box[2], $box[4], $box[6]]), + 'top' => $minY = min([$box[1], $box[3], $box[5], $box[7]]), + 'width' => max([$box[0], $box[2], $box[4], $box[6]]) - $minX + 1, + 'height' => max([$box[1], $box[3], $box[5], $box[7]]) - $minY + 1, + ]; + } + + + /** + * Draw a rectangle. + */ + public function rectangleWH(int $x, int $y, int $width, int $height, ImageColor $color): void + { + if ($width !== 0 && $height !== 0) { + $this->rectangle($x, $y, $x + $width + ($width > 0 ? -1 : 1), $y + $height + ($height > 0 ? -1 : 1), $color); + } + } + + + /** + * Draw a filled rectangle. + */ + public function filledRectangleWH(int $x, int $y, int $width, int $height, ImageColor $color): void + { + if ($width !== 0 && $height !== 0) { + $this->filledRectangle($x, $y, $x + $width + ($width > 0 ? -1 : 1), $y + $height + ($height > 0 ? -1 : 1), $color); + } + } + + + /** + * Saves image to the file. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9). + * @param ImageType::*|null $type + * @throws ImageException + */ + public function save(string $file, ?int $quality = null, ?int $type = null): void + { + $type ??= self::extensionToType(pathinfo($file, PATHINFO_EXTENSION)); + $this->output($type, $quality, $file); + } + + + /** + * Outputs image to string. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9). + * @param ImageType::* $type + */ + public function toString(int $type = ImageType::JPEG, ?int $quality = null): string + { + return Helpers::capture(function () use ($type, $quality): void { + $this->output($type, $quality); + }); + } + + + /** + * Outputs image to string. + */ + public function __toString(): string + { + return $this->toString(); + } + + + /** + * Outputs image to browser. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9). + * @param ImageType::* $type + * @throws ImageException + */ + public function send(int $type = ImageType::JPEG, ?int $quality = null): void + { + header('Content-Type: ' . self::typeToMimeType($type)); + $this->output($type, $quality); + } + + + /** + * Outputs image to browser or file. + * @param ImageType::* $type + * @throws ImageException + */ + private function output(int $type, ?int $quality, ?string $file = null): void + { + [$defQuality, $min, $max] = match ($type) { + ImageType::JPEG => [85, 0, 100], + ImageType::PNG => [9, 0, 9], + ImageType::GIF => [null, null, null], + ImageType::WEBP => [80, 0, 100], + ImageType::AVIF => [30, 0, 100], + ImageType::BMP => [null, null, null], + default => throw new Nette\InvalidArgumentException("Unsupported image type '$type'."), + }; + + $args = [$this->image, $file]; + if ($defQuality !== null) { + $args[] = $quality === null ? $defQuality : max($min, min($max, $quality)); + } + + Callback::invokeSafe('image' . self::Formats[$type], $args, function (string $message) use ($file): void { + if ($file !== null) { + @unlink($file); + } + throw new ImageException($message); + }); + } + + + /** + * Call to undefined method. + * @throws Nette\MemberAccessException + */ + public function __call(string $name, array $args): mixed + { + $function = 'image' . $name; + if (!function_exists($function)) { + ObjectHelpers::strictCall(static::class, $name); + } + + foreach ($args as $key => $value) { + if ($value instanceof self) { + $args[$key] = $value->getImageResource(); + + } elseif ($value instanceof ImageColor || (is_array($value) && isset($value['red']))) { + $args[$key] = $this->resolveColor($value); + } + } + + $res = $function($this->image, ...$args); + return $res instanceof \GdImage + ? $this->setImageResource($res) + : $res; + } + + + public function __clone() + { + ob_start(function () {}); + imagepng($this->image, null, 0); + $this->setImageResource(imagecreatefromstring(ob_get_clean())); + } + + + private static function isPercent(int|string &$num): bool + { + if (is_string($num) && str_ends_with($num, '%')) { + $num = (float) substr($num, 0, -1); + return true; + } elseif (is_int($num) || $num === (string) (int) $num) { + $num = (int) $num; + return false; + } + + throw new Nette\InvalidArgumentException("Expected dimension in int|string, '$num' given."); + } + + + /** + * Prevents serialization. + */ + public function __sleep(): array + { + throw new Nette\NotSupportedException('You cannot serialize or unserialize ' . self::class . ' instances.'); + } + + + public function resolveColor(ImageColor|array $color): int + { + $color = $color instanceof ImageColor ? $color->toRGBA() : array_values($color); + return imagecolorallocatealpha($this->image, ...$color) ?: imagecolorresolvealpha($this->image, ...$color); + } + + + private static function ensureExtension(): void + { + if (!extension_loaded('gd')) { + throw new Nette\NotSupportedException('PHP extension GD is not loaded.'); + } + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ImageColor.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageColor.php new file mode 100644 index 000000000..013adbd6e --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageColor.php @@ -0,0 +1,75 @@ +red = max(0, min(255, $red)); + $this->green = max(0, min(255, $green)); + $this->blue = max(0, min(255, $blue)); + $this->opacity = max(0, min(1, $opacity)); + } + + + public function toRGBA(): array + { + return [ + max(0, min(255, $this->red)), + max(0, min(255, $this->green)), + max(0, min(255, $this->blue)), + max(0, min(127, (int) round(127 - $this->opacity * 127))), + ]; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ImageType.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageType.php new file mode 100644 index 000000000..3092c8f02 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageType.php @@ -0,0 +1,25 @@ + $v) { + if ($k === $key) { + return true; + } + } + return false; + } + + + /** + * Returns the first item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null. + * @template K + * @template V + * @param iterable $iterable + * @param ?callable(V, K, iterable): bool $predicate + * @return ?V + */ + public static function first(iterable $iterable, ?callable $predicate = null, ?callable $else = null): mixed + { + foreach ($iterable as $k => $v) { + if (!$predicate || $predicate($v, $k, $iterable)) { + return $v; + } + } + return $else ? $else() : null; + } + + + /** + * Returns the key of first item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null. + * @template K + * @template V + * @param iterable $iterable + * @param ?callable(V, K, iterable): bool $predicate + * @return ?K + */ + public static function firstKey(iterable $iterable, ?callable $predicate = null, ?callable $else = null): mixed + { + foreach ($iterable as $k => $v) { + if (!$predicate || $predicate($v, $k, $iterable)) { + return $k; + } + } + return $else ? $else() : null; + } + + + /** + * Tests whether at least one element in the iterator passes the test implemented by the provided function. + * @template K + * @template V + * @param iterable $iterable + * @param callable(V, K, iterable): bool $predicate + */ + public static function some(iterable $iterable, callable $predicate): bool + { + foreach ($iterable as $k => $v) { + if ($predicate($v, $k, $iterable)) { + return true; + } + } + return false; + } + + + /** + * Tests whether all elements in the iterator pass the test implemented by the provided function. + * @template K + * @template V + * @param iterable $iterable + * @param callable(V, K, iterable): bool $predicate + */ + public static function every(iterable $iterable, callable $predicate): bool + { + foreach ($iterable as $k => $v) { + if (!$predicate($v, $k, $iterable)) { + return false; + } + } + return true; + } + + + /** + * Iterator that filters elements according to a given $predicate. Maintains original keys. + * @template K + * @template V + * @param iterable $iterable + * @param callable(V, K, iterable): bool $predicate + * @return \Generator + */ + public static function filter(iterable $iterable, callable $predicate): \Generator + { + foreach ($iterable as $k => $v) { + if ($predicate($v, $k, $iterable)) { + yield $k => $v; + } + } + } + + + /** + * Iterator that transforms values by calling $transformer. Maintains original keys. + * @template K + * @template V + * @template R + * @param iterable $iterable + * @param callable(V, K, iterable): R $transformer + * @return \Generator + */ + public static function map(iterable $iterable, callable $transformer): \Generator + { + foreach ($iterable as $k => $v) { + yield $k => $transformer($v, $k, $iterable); + } + } + + + /** + * Iterator that transforms keys and values by calling $transformer. If it returns null, the element is skipped. + * @template K + * @template V + * @template ResV + * @template ResK + * @param iterable $iterable + * @param callable(V, K, iterable): ?array{ResV, ResK} $transformer + * @return \Generator + */ + public static function mapWithKeys(iterable $iterable, callable $transformer): \Generator + { + foreach ($iterable as $k => $v) { + $pair = $transformer($v, $k, $iterable); + if ($pair) { + yield $pair[0] => $pair[1]; + } + } + } + + + /** + * Wraps around iterator and caches its keys and values during iteration. + * This allows the data to be re-iterated multiple times. + * @template K + * @template V + * @param iterable $iterable + * @return \IteratorAggregate + */ + public static function memoize(iterable $iterable): iterable + { + return new class (self::toIterator($iterable)) implements \IteratorAggregate { + public function __construct( + private \Iterator $iterator, + private array $cache = [], + ) { + } + + + public function getIterator(): \Generator + { + if (!$this->cache) { + $this->iterator->rewind(); + } + $i = 0; + while (true) { + if (isset($this->cache[$i])) { + [$k, $v] = $this->cache[$i]; + } elseif ($this->iterator->valid()) { + $k = $this->iterator->key(); + $v = $this->iterator->current(); + $this->iterator->next(); + $this->cache[$i] = [$k, $v]; + } else { + break; + } + yield $k => $v; + $i++; + } + } + }; + } + + + /** + * Creates an iterator from anything that is iterable. + * @template K + * @template V + * @param iterable $iterable + * @return \Iterator + */ + public static function toIterator(iterable $iterable): \Iterator + { + return match (true) { + $iterable instanceof \Iterator => $iterable, + $iterable instanceof \IteratorAggregate => self::toIterator($iterable->getIterator()), + is_array($iterable) => new \ArrayIterator($iterable), + default => throw new Nette\ShouldNotHappenException, + }; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Json.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Json.php new file mode 100644 index 000000000..b87917b2a --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Json.php @@ -0,0 +1,84 @@ +getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()), + self::parseFullDoc($rc, '~^[ \t*]*@property(?:-read)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'), + ), $name); + throw new MemberAccessException("Cannot read an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.')); + } + + + /** + * @return never + * @throws MemberAccessException + */ + public static function strictSet(string $class, string $name): void + { + $rc = new \ReflectionClass($class); + $hint = self::getSuggestion(array_merge( + array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()), + self::parseFullDoc($rc, '~^[ \t*]*@property(?:-write)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'), + ), $name); + throw new MemberAccessException("Cannot write to an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.')); + } + + + /** + * @return never + * @throws MemberAccessException + */ + public static function strictCall(string $class, string $method, array $additionalMethods = []): void + { + $trace = debug_backtrace(0, 3); // suppose this method is called from __call() + $context = ($trace[1]['function'] ?? null) === '__call' + ? ($trace[2]['class'] ?? null) + : null; + + if ($context && is_a($class, $context, true) && method_exists($context, $method)) { // called parent::$method() + $class = get_parent_class($context); + } + + if (method_exists($class, $method)) { // insufficient visibility + $rm = new \ReflectionMethod($class, $method); + $visibility = $rm->isPrivate() + ? 'private ' + : ($rm->isProtected() ? 'protected ' : ''); + throw new MemberAccessException("Call to {$visibility}method $class::$method() from " . ($context ? "scope $context." : 'global scope.')); + + } else { + $hint = self::getSuggestion(array_merge( + get_class_methods($class), + self::parseFullDoc(new \ReflectionClass($class), '~^[ \t*]*@method[ \t]+(?:static[ \t]+)?(?:\S+[ \t]+)??(\w+)\(~m'), + $additionalMethods, + ), $method); + throw new MemberAccessException("Call to undefined method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.')); + } + } + + + /** + * @return never + * @throws MemberAccessException + */ + public static function strictStaticCall(string $class, string $method): void + { + $trace = debug_backtrace(0, 3); // suppose this method is called from __callStatic() + $context = ($trace[1]['function'] ?? null) === '__callStatic' + ? ($trace[2]['class'] ?? null) + : null; + + if ($context && is_a($class, $context, true) && method_exists($context, $method)) { // called parent::$method() + $class = get_parent_class($context); + } + + if (method_exists($class, $method)) { // insufficient visibility + $rm = new \ReflectionMethod($class, $method); + $visibility = $rm->isPrivate() + ? 'private ' + : ($rm->isProtected() ? 'protected ' : ''); + throw new MemberAccessException("Call to {$visibility}method $class::$method() from " . ($context ? "scope $context." : 'global scope.')); + + } else { + $hint = self::getSuggestion( + array_filter((new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC), fn($m) => $m->isStatic()), + $method, + ); + throw new MemberAccessException("Call to undefined static method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.')); + } + } + + + /** + * Returns array of magic properties defined by annotation @property. + * @return array of [name => bit mask] + * @internal + */ + public static function getMagicProperties(string $class): array + { + static $cache; + $props = &$cache[$class]; + if ($props !== null) { + return $props; + } + + $rc = new \ReflectionClass($class); + preg_match_all( + '~^ [ \t*]* @property(|-read|-write|-deprecated) [ \t]+ [^\s$]+ [ \t]+ \$ (\w+) ()~mx', + (string) $rc->getDocComment(), + $matches, + PREG_SET_ORDER, + ); + + $props = []; + foreach ($matches as [, $type, $name]) { + $uname = ucfirst($name); + $write = $type !== '-read' + && $rc->hasMethod($nm = 'set' . $uname) + && ($rm = $rc->getMethod($nm))->name === $nm && !$rm->isPrivate() && !$rm->isStatic(); + $read = $type !== '-write' + && ($rc->hasMethod($nm = 'get' . $uname) || $rc->hasMethod($nm = 'is' . $uname)) + && ($rm = $rc->getMethod($nm))->name === $nm && !$rm->isPrivate() && !$rm->isStatic(); + + if ($read || $write) { + $props[$name] = $read << 0 | ($nm[0] === 'g') << 1 | $rm->returnsReference() << 2 | $write << 3 | ($type === '-deprecated') << 4; + } + } + + foreach ($rc->getTraits() as $trait) { + $props += self::getMagicProperties($trait->name); + } + + if ($parent = get_parent_class($class)) { + $props += self::getMagicProperties($parent); + } + + return $props; + } + + + /** + * Finds the best suggestion (for 8-bit encoding). + * @param (\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionClass|\ReflectionProperty|string)[] $possibilities + * @internal + */ + public static function getSuggestion(array $possibilities, string $value): ?string + { + $norm = preg_replace($re = '#^(get|set|has|is|add)(?=[A-Z])#', '+', $value); + $best = null; + $min = (strlen($value) / 4 + 1) * 10 + .1; + foreach (array_unique($possibilities, SORT_REGULAR) as $item) { + $item = $item instanceof \Reflector ? $item->name : $item; + if ($item !== $value && ( + ($len = levenshtein($item, $value, 10, 11, 10)) < $min + || ($len = levenshtein(preg_replace($re, '*', $item), $norm, 10, 11, 10)) < $min + )) { + $min = $len; + $best = $item; + } + } + + return $best; + } + + + private static function parseFullDoc(\ReflectionClass $rc, string $pattern): array + { + do { + $doc[] = $rc->getDocComment(); + $traits = $rc->getTraits(); + while ($trait = array_pop($traits)) { + $doc[] = $trait->getDocComment(); + $traits += $trait->getTraits(); + } + } while ($rc = $rc->getParentClass()); + + return preg_match_all($pattern, implode('', $doc), $m) ? $m[1] : []; + } + + + /** + * Checks if the public non-static property exists. + * Returns 'event' if the property exists and has event like name + * @internal + */ + public static function hasProperty(string $class, string $name): bool|string + { + static $cache; + $prop = &$cache[$class][$name]; + if ($prop === null) { + $prop = false; + try { + $rp = new \ReflectionProperty($class, $name); + if ($rp->isPublic() && !$rp->isStatic()) { + $prop = $name >= 'onA' && $name < 'on_' ? 'event' : true; + } + } catch (\ReflectionException $e) { + } + } + + return $prop; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Paginator.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Paginator.php new file mode 100644 index 000000000..aa4812c0a --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Paginator.php @@ -0,0 +1,245 @@ + $firstItemOnPage + * @property-read int<0,max> $lastItemOnPage + * @property int $base + * @property-read bool $first + * @property-read bool $last + * @property-read int<0,max>|null $pageCount + * @property positive-int $itemsPerPage + * @property int<0,max>|null $itemCount + * @property-read int<0,max> $offset + * @property-read int<0,max>|null $countdownOffset + * @property-read int<0,max> $length + */ +class Paginator +{ + use Nette\SmartObject; + + private int $base = 1; + + /** @var positive-int */ + private int $itemsPerPage = 1; + + private int $page = 1; + + /** @var int<0, max>|null */ + private ?int $itemCount = null; + + + /** + * Sets current page number. + */ + public function setPage(int $page): static + { + $this->page = $page; + return $this; + } + + + /** + * Returns current page number. + */ + public function getPage(): int + { + return $this->base + $this->getPageIndex(); + } + + + /** + * Returns first page number. + */ + public function getFirstPage(): int + { + return $this->base; + } + + + /** + * Returns last page number. + */ + public function getLastPage(): ?int + { + return $this->itemCount === null + ? null + : $this->base + max(0, $this->getPageCount() - 1); + } + + + /** + * Returns the sequence number of the first element on the page + * @return int<0, max> + */ + public function getFirstItemOnPage(): int + { + return $this->itemCount !== 0 + ? $this->offset + 1 + : 0; + } + + + /** + * Returns the sequence number of the last element on the page + * @return int<0, max> + */ + public function getLastItemOnPage(): int + { + return $this->offset + $this->length; + } + + + /** + * Sets first page (base) number. + */ + public function setBase(int $base): static + { + $this->base = $base; + return $this; + } + + + /** + * Returns first page (base) number. + */ + public function getBase(): int + { + return $this->base; + } + + + /** + * Returns zero-based page number. + * @return int<0, max> + */ + protected function getPageIndex(): int + { + $index = max(0, $this->page - $this->base); + return $this->itemCount === null + ? $index + : min($index, max(0, $this->getPageCount() - 1)); + } + + + /** + * Is the current page the first one? + */ + public function isFirst(): bool + { + return $this->getPageIndex() === 0; + } + + + /** + * Is the current page the last one? + */ + public function isLast(): bool + { + return $this->itemCount === null + ? false + : $this->getPageIndex() >= $this->getPageCount() - 1; + } + + + /** + * Returns the total number of pages. + * @return int<0, max>|null + */ + public function getPageCount(): ?int + { + return $this->itemCount === null + ? null + : (int) ceil($this->itemCount / $this->itemsPerPage); + } + + + /** + * Sets the number of items to display on a single page. + */ + public function setItemsPerPage(int $itemsPerPage): static + { + $this->itemsPerPage = max(1, $itemsPerPage); + return $this; + } + + + /** + * Returns the number of items to display on a single page. + * @return positive-int + */ + public function getItemsPerPage(): int + { + return $this->itemsPerPage; + } + + + /** + * Sets the total number of items. + */ + public function setItemCount(?int $itemCount = null): static + { + $this->itemCount = $itemCount === null ? null : max(0, $itemCount); + return $this; + } + + + /** + * Returns the total number of items. + * @return int<0, max>|null + */ + public function getItemCount(): ?int + { + return $this->itemCount; + } + + + /** + * Returns the absolute index of the first item on current page. + * @return int<0, max> + */ + public function getOffset(): int + { + return $this->getPageIndex() * $this->itemsPerPage; + } + + + /** + * Returns the absolute index of the first item on current page in countdown paging. + * @return int<0, max>|null + */ + public function getCountdownOffset(): ?int + { + return $this->itemCount === null + ? null + : max(0, $this->itemCount - ($this->getPageIndex() + 1) * $this->itemsPerPage); + } + + + /** + * Returns the number of items on current page. + * @return int<0, max> + */ + public function getLength(): int + { + return $this->itemCount === null + ? $this->itemsPerPage + : min($this->itemsPerPage, $this->itemCount - $this->getPageIndex() * $this->itemsPerPage); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Random.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Random.php new file mode 100644 index 000000000..b14fbd55e --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Random.php @@ -0,0 +1,52 @@ + implode('', range($m[0][0], $m[0][2])), + $charlist, + ); + $charlist = count_chars($charlist, mode: 3); + $chLen = strlen($charlist); + + if ($length < 1) { + throw new Nette\InvalidArgumentException('Length must be greater than zero.'); + } elseif ($chLen < 2) { + throw new Nette\InvalidArgumentException('Character list must contain at least two chars.'); + } elseif (PHP_VERSION_ID >= 80300) { + return (new Randomizer)->getBytesFromString($charlist, $length); + } + + $res = ''; + for ($i = 0; $i < $length; $i++) { + $res .= $charlist[random_int(0, $chLen - 1)]; + } + + return $res; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Reflection.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Reflection.php new file mode 100644 index 000000000..215c56860 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Reflection.php @@ -0,0 +1,320 @@ +isDefaultValueConstant()) { + $const = $orig = $param->getDefaultValueConstantName(); + $pair = explode('::', $const); + if (isset($pair[1])) { + $pair[0] = Type::resolve($pair[0], $param); + try { + $rcc = new \ReflectionClassConstant($pair[0], $pair[1]); + } catch (\ReflectionException $e) { + $name = self::toString($param); + throw new \ReflectionException("Unable to resolve constant $orig used as default value of $name.", 0, $e); + } + + return $rcc->getValue(); + + } elseif (!defined($const)) { + $const = substr((string) strrchr($const, '\\'), 1); + if (!defined($const)) { + $name = self::toString($param); + throw new \ReflectionException("Unable to resolve constant $orig used as default value of $name."); + } + } + + return constant($const); + } + + return $param->getDefaultValue(); + } + + + /** + * Returns a reflection of a class or trait that contains a declaration of given property. Property can also be declared in the trait. + */ + public static function getPropertyDeclaringClass(\ReflectionProperty $prop): \ReflectionClass + { + foreach ($prop->getDeclaringClass()->getTraits() as $trait) { + if ($trait->hasProperty($prop->name) + // doc-comment guessing as workaround for insufficient PHP reflection + && $trait->getProperty($prop->name)->getDocComment() === $prop->getDocComment() + ) { + return self::getPropertyDeclaringClass($trait->getProperty($prop->name)); + } + } + + return $prop->getDeclaringClass(); + } + + + /** + * Returns a reflection of a method that contains a declaration of $method. + * Usually, each method is its own declaration, but the body of the method can also be in the trait and under a different name. + */ + public static function getMethodDeclaringMethod(\ReflectionMethod $method): \ReflectionMethod + { + // file & line guessing as workaround for insufficient PHP reflection + $decl = $method->getDeclaringClass(); + if ($decl->getFileName() === $method->getFileName() + && $decl->getStartLine() <= $method->getStartLine() + && $decl->getEndLine() >= $method->getEndLine() + ) { + return $method; + } + + $hash = [$method->getFileName(), $method->getStartLine(), $method->getEndLine()]; + if (($alias = $decl->getTraitAliases()[$method->name] ?? null) + && ($m = new \ReflectionMethod(...explode('::', $alias, 2))) + && $hash === [$m->getFileName(), $m->getStartLine(), $m->getEndLine()] + ) { + return self::getMethodDeclaringMethod($m); + } + + foreach ($decl->getTraits() as $trait) { + if ($trait->hasMethod($method->name) + && ($m = $trait->getMethod($method->name)) + && $hash === [$m->getFileName(), $m->getStartLine(), $m->getEndLine()] + ) { + return self::getMethodDeclaringMethod($m); + } + } + + return $method; + } + + + /** + * Finds out if reflection has access to PHPdoc comments. Comments may not be available due to the opcode cache. + */ + public static function areCommentsAvailable(): bool + { + static $res; + return $res ?? $res = (bool) (new \ReflectionMethod(self::class, __FUNCTION__))->getDocComment(); + } + + + public static function toString(\Reflector $ref): string + { + if ($ref instanceof \ReflectionClass) { + return $ref->name; + } elseif ($ref instanceof \ReflectionMethod) { + return $ref->getDeclaringClass()->name . '::' . $ref->name . '()'; + } elseif ($ref instanceof \ReflectionFunction) { + return PHP_VERSION_ID >= 80200 && $ref->isAnonymous() + ? '{closure}()' + : $ref->name . '()'; + } elseif ($ref instanceof \ReflectionProperty) { + return self::getPropertyDeclaringClass($ref)->name . '::$' . $ref->name; + } elseif ($ref instanceof \ReflectionParameter) { + return '$' . $ref->name . ' in ' . self::toString($ref->getDeclaringFunction()); + } else { + throw new Nette\InvalidArgumentException; + } + } + + + /** + * Expands the name of the class to full name in the given context of given class. + * Thus, it returns how the PHP parser would understand $name if it were written in the body of the class $context. + * @throws Nette\InvalidArgumentException + */ + public static function expandClassName(string $name, \ReflectionClass $context): string + { + $lower = strtolower($name); + if (empty($name)) { + throw new Nette\InvalidArgumentException('Class name must not be empty.'); + + } elseif (Validators::isBuiltinType($lower)) { + return $lower; + + } elseif ($lower === 'self' || $lower === 'static') { + return $context->name; + + } elseif ($lower === 'parent') { + return $context->getParentClass() + ? $context->getParentClass()->name + : 'parent'; + + } elseif ($name[0] === '\\') { // fully qualified name + return ltrim($name, '\\'); + } + + $uses = self::getUseStatements($context); + $parts = explode('\\', $name, 2); + if (isset($uses[$parts[0]])) { + $parts[0] = $uses[$parts[0]]; + return implode('\\', $parts); + + } elseif ($context->inNamespace()) { + return $context->getNamespaceName() . '\\' . $name; + + } else { + return $name; + } + } + + + /** @return array of [alias => class] */ + public static function getUseStatements(\ReflectionClass $class): array + { + if ($class->isAnonymous()) { + throw new Nette\NotImplementedException('Anonymous classes are not supported.'); + } + + static $cache = []; + if (!isset($cache[$name = $class->name])) { + if ($class->isInternal()) { + $cache[$name] = []; + } else { + $code = file_get_contents($class->getFileName()); + $cache = self::parseUseStatements($code, $name) + $cache; + } + } + + return $cache[$name]; + } + + + /** + * Parses PHP code to [class => [alias => class, ...]] + */ + private static function parseUseStatements(string $code, ?string $forClass = null): array + { + try { + $tokens = \PhpToken::tokenize($code, TOKEN_PARSE); + } catch (\ParseError $e) { + trigger_error($e->getMessage(), E_USER_NOTICE); + $tokens = []; + } + + $namespace = $class = null; + $classLevel = $level = 0; + $res = $uses = []; + + $nameTokens = [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED]; + + while ($token = current($tokens)) { + next($tokens); + switch ($token->id) { + case T_NAMESPACE: + $namespace = ltrim(self::fetch($tokens, $nameTokens) . '\\', '\\'); + $uses = []; + break; + + case T_CLASS: + case T_INTERFACE: + case T_TRAIT: + case PHP_VERSION_ID < 80100 ? T_CLASS : T_ENUM: + if ($name = self::fetch($tokens, T_STRING)) { + $class = $namespace . $name; + $classLevel = $level + 1; + $res[$class] = $uses; + if ($class === $forClass) { + return $res; + } + } + + break; + + case T_USE: + while (!$class && ($name = self::fetch($tokens, $nameTokens))) { + $name = ltrim($name, '\\'); + if (self::fetch($tokens, '{')) { + while ($suffix = self::fetch($tokens, $nameTokens)) { + if (self::fetch($tokens, T_AS)) { + $uses[self::fetch($tokens, T_STRING)] = $name . $suffix; + } else { + $tmp = explode('\\', $suffix); + $uses[end($tmp)] = $name . $suffix; + } + + if (!self::fetch($tokens, ',')) { + break; + } + } + } elseif (self::fetch($tokens, T_AS)) { + $uses[self::fetch($tokens, T_STRING)] = $name; + + } else { + $tmp = explode('\\', $name); + $uses[end($tmp)] = $name; + } + + if (!self::fetch($tokens, ',')) { + break; + } + } + + break; + + case T_CURLY_OPEN: + case T_DOLLAR_OPEN_CURLY_BRACES: + case ord('{'): + $level++; + break; + + case ord('}'): + if ($level === $classLevel) { + $class = $classLevel = 0; + } + + $level--; + } + } + + return $res; + } + + + private static function fetch(array &$tokens, string|int|array $take): ?string + { + $res = null; + while ($token = current($tokens)) { + if ($token->is($take)) { + $res .= $token->text; + } elseif (!$token->is([T_DOC_COMMENT, T_WHITESPACE, T_COMMENT])) { + break; + } + + next($tokens); + } + + return $res; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ReflectionMethod.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ReflectionMethod.php new file mode 100644 index 000000000..b003fcbd1 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ReflectionMethod.php @@ -0,0 +1,36 @@ +originalClass = new \ReflectionClass($objectOrMethod); + } + + + public function getOriginalClass(): \ReflectionClass + { + return $this->originalClass; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Strings.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Strings.php new file mode 100644 index 000000000..79fa46bb0 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Strings.php @@ -0,0 +1,727 @@ += 0xD800 && $code <= 0xDFFF) || $code > 0x10FFFF) { + throw new Nette\InvalidArgumentException('Code point must be in range 0x0 to 0xD7FF or 0xE000 to 0x10FFFF.'); + } elseif (!extension_loaded('iconv')) { + throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.'); + } + + return iconv('UTF-32BE', 'UTF-8//IGNORE', pack('N', $code)); + } + + + /** + * Returns a code point of specific character in UTF-8 (number in range 0x0000..D7FF or 0xE000..10FFFF). + */ + public static function ord(string $c): int + { + if (!extension_loaded('iconv')) { + throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.'); + } + + $tmp = iconv('UTF-8', 'UTF-32BE//IGNORE', $c); + if (!$tmp) { + throw new Nette\InvalidArgumentException('Invalid UTF-8 character "' . ($c === '' ? '' : '\x' . strtoupper(bin2hex($c))) . '".'); + } + + return unpack('N', $tmp)[1]; + } + + + /** + * @deprecated use str_starts_with() + */ + public static function startsWith(string $haystack, string $needle): bool + { + return str_starts_with($haystack, $needle); + } + + + /** + * @deprecated use str_ends_with() + */ + public static function endsWith(string $haystack, string $needle): bool + { + return str_ends_with($haystack, $needle); + } + + + /** + * @deprecated use str_contains() + */ + public static function contains(string $haystack, string $needle): bool + { + return str_contains($haystack, $needle); + } + + + /** + * Returns a part of UTF-8 string specified by starting position and length. If start is negative, + * the returned string will start at the start'th character from the end of string. + */ + public static function substring(string $s, int $start, ?int $length = null): string + { + if (function_exists('mb_substr')) { + return mb_substr($s, $start, $length, 'UTF-8'); // MB is much faster + } elseif (!extension_loaded('iconv')) { + throw new Nette\NotSupportedException(__METHOD__ . '() requires extension ICONV or MBSTRING, neither is loaded.'); + } elseif ($length === null) { + $length = self::length($s); + } elseif ($start < 0 && $length < 0) { + $start += self::length($s); // unifies iconv_substr behavior with mb_substr + } + + return iconv_substr($s, $start, $length, 'UTF-8'); + } + + + /** + * Removes control characters, normalizes line breaks to `\n`, removes leading and trailing blank lines, + * trims end spaces on lines, normalizes UTF-8 to the normal form of NFC. + */ + public static function normalize(string $s): string + { + // convert to compressed normal form (NFC) + if (class_exists('Normalizer', false) && ($n = \Normalizer::normalize($s, \Normalizer::FORM_C)) !== false) { + $s = $n; + } + + $s = self::unixNewLines($s); + + // remove control characters; leave \t + \n + $s = self::pcre('preg_replace', ['#[\x00-\x08\x0B-\x1F\x7F-\x9F]+#u', '', $s]); + + // right trim + $s = self::pcre('preg_replace', ['#[\t ]+$#m', '', $s]); + + // leading and trailing blank lines + $s = trim($s, "\n"); + + return $s; + } + + + /** @deprecated use Strings::unixNewLines() */ + public static function normalizeNewLines(string $s): string + { + return self::unixNewLines($s); + } + + + /** + * Converts line endings to \n used on Unix-like systems. + * Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator. + */ + public static function unixNewLines(string $s): string + { + return preg_replace("~\r\n?|\u{2028}|\u{2029}~", "\n", $s); + } + + + /** + * Converts line endings to platform-specific, i.e. \r\n on Windows and \n elsewhere. + * Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator. + */ + public static function platformNewLines(string $s): string + { + return preg_replace("~\r\n?|\n|\u{2028}|\u{2029}~", PHP_EOL, $s); + } + + + /** + * Converts UTF-8 string to ASCII, ie removes diacritics etc. + */ + public static function toAscii(string $s): string + { + $iconv = defined('ICONV_IMPL') ? trim(ICONV_IMPL, '"\'') : null; + static $transliterator = null; + if ($transliterator === null) { + if (class_exists('Transliterator', false)) { + $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII'); + } else { + trigger_error(__METHOD__ . "(): it is recommended to enable PHP extensions 'intl'.", E_USER_NOTICE); + $transliterator = false; + } + } + + // remove control characters and check UTF-8 validity + $s = self::pcre('preg_replace', ['#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s]); + + // transliteration (by Transliterator and iconv) is not optimal, replace some characters directly + $s = strtr($s, ["\u{201E}" => '"', "\u{201C}" => '"', "\u{201D}" => '"', "\u{201A}" => "'", "\u{2018}" => "'", "\u{2019}" => "'", "\u{B0}" => '^', "\u{42F}" => 'Ya', "\u{44F}" => 'ya', "\u{42E}" => 'Yu', "\u{44E}" => 'yu', "\u{c4}" => 'Ae', "\u{d6}" => 'Oe', "\u{dc}" => 'Ue', "\u{1e9e}" => 'Ss', "\u{e4}" => 'ae', "\u{f6}" => 'oe', "\u{fc}" => 'ue', "\u{df}" => 'ss']); // „ “ ” ‚ ‘ ’ ° Я я Ю ю Ä Ö Ü ẞ ä ö ü ß + if ($iconv !== 'libiconv') { + $s = strtr($s, ["\u{AE}" => '(R)', "\u{A9}" => '(c)', "\u{2026}" => '...', "\u{AB}" => '<<', "\u{BB}" => '>>', "\u{A3}" => 'lb', "\u{A5}" => 'yen', "\u{B2}" => '^2', "\u{B3}" => '^3', "\u{B5}" => 'u', "\u{B9}" => '^1', "\u{BA}" => 'o', "\u{BF}" => '?', "\u{2CA}" => "'", "\u{2CD}" => '_', "\u{2DD}" => '"', "\u{1FEF}" => '', "\u{20AC}" => 'EUR', "\u{2122}" => 'TM', "\u{212E}" => 'e', "\u{2190}" => '<-', "\u{2191}" => '^', "\u{2192}" => '->', "\u{2193}" => 'V', "\u{2194}" => '<->']); // ® © … « » £ ¥ ² ³ µ ¹ º ¿ ˊ ˍ ˝ ` € ™ ℮ ← ↑ → ↓ ↔ + } + + if ($transliterator) { + $s = $transliterator->transliterate($s); + // use iconv because The transliterator leaves some characters out of ASCII, eg → ʾ + if ($iconv === 'glibc') { + $s = strtr($s, '?', "\x01"); // temporarily hide ? to distinguish them from the garbage that iconv creates + $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); + $s = str_replace(['?', "\x01"], ['', '?'], $s); // remove garbage and restore ? characters + } elseif ($iconv === 'libiconv') { + $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); + } else { // null or 'unknown' (#216) + $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars + } + } elseif ($iconv === 'glibc' || $iconv === 'libiconv') { + // temporarily hide these characters to distinguish them from the garbage that iconv creates + $s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06"); + if ($iconv === 'glibc') { + // glibc implementation is very limited. transliterate into Windows-1250 and then into ASCII, so most Eastern European characters are preserved + $s = iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s); + $s = strtr( + $s, + "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\x96\xa0\x8b\x97\x9b\xa6\xad\xb7", + 'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.', + ); + $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); + } else { + $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); + } + + // remove garbage that iconv creates during transliteration (eg Ý -> Y') + $s = str_replace(['`', "'", '"', '^', '~', '?'], '', $s); + // restore temporarily hidden characters + $s = strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?'); + } else { + $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars + } + + return $s; + } + + + /** + * Modifies the UTF-8 string to the form used in the URL, ie removes diacritics and replaces all characters + * except letters of the English alphabet and numbers with a hyphens. + */ + public static function webalize(string $s, ?string $charlist = null, bool $lower = true): string + { + $s = self::toAscii($s); + if ($lower) { + $s = strtolower($s); + } + + $s = self::pcre('preg_replace', ['#[^a-z0-9' . ($charlist !== null ? preg_quote($charlist, '#') : '') . ']+#i', '-', $s]); + $s = trim($s, '-'); + return $s; + } + + + /** + * Truncates a UTF-8 string to given maximal length, while trying not to split whole words. Only if the string is truncated, + * an ellipsis (or something else set with third argument) is appended to the string. + */ + public static function truncate(string $s, int $maxLen, string $append = "\u{2026}"): string + { + if (self::length($s) > $maxLen) { + $maxLen -= self::length($append); + if ($maxLen < 1) { + return $append; + + } elseif ($matches = self::match($s, '#^.{1,' . $maxLen . '}(?=[\s\x00-/:-@\[-`{-~])#us')) { + return $matches[0] . $append; + + } else { + return self::substring($s, 0, $maxLen) . $append; + } + } + + return $s; + } + + + /** + * Indents a multiline text from the left. Second argument sets how many indentation chars should be used, + * while the indent itself is the third argument (*tab* by default). + */ + public static function indent(string $s, int $level = 1, string $chars = "\t"): string + { + if ($level > 0) { + $s = self::replace($s, '#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level)); + } + + return $s; + } + + + /** + * Converts all characters of UTF-8 string to lower case. + */ + public static function lower(string $s): string + { + return mb_strtolower($s, 'UTF-8'); + } + + + /** + * Converts the first character of a UTF-8 string to lower case and leaves the other characters unchanged. + */ + public static function firstLower(string $s): string + { + return self::lower(self::substring($s, 0, 1)) . self::substring($s, 1); + } + + + /** + * Converts all characters of a UTF-8 string to upper case. + */ + public static function upper(string $s): string + { + return mb_strtoupper($s, 'UTF-8'); + } + + + /** + * Converts the first character of a UTF-8 string to upper case and leaves the other characters unchanged. + */ + public static function firstUpper(string $s): string + { + return self::upper(self::substring($s, 0, 1)) . self::substring($s, 1); + } + + + /** + * Converts the first character of every word of a UTF-8 string to upper case and the others to lower case. + */ + public static function capitalize(string $s): string + { + return mb_convert_case($s, MB_CASE_TITLE, 'UTF-8'); + } + + + /** + * Compares two UTF-8 strings or their parts, without taking character case into account. If length is null, whole strings are compared, + * if it is negative, the corresponding number of characters from the end of the strings is compared, + * otherwise the appropriate number of characters from the beginning is compared. + */ + public static function compare(string $left, string $right, ?int $length = null): bool + { + if (class_exists('Normalizer', false)) { + $left = \Normalizer::normalize($left, \Normalizer::FORM_D); // form NFD is faster + $right = \Normalizer::normalize($right, \Normalizer::FORM_D); // form NFD is faster + } + + if ($length < 0) { + $left = self::substring($left, $length, -$length); + $right = self::substring($right, $length, -$length); + } elseif ($length !== null) { + $left = self::substring($left, 0, $length); + $right = self::substring($right, 0, $length); + } + + return self::lower($left) === self::lower($right); + } + + + /** + * Finds the common prefix of strings or returns empty string if the prefix was not found. + * @param string[] $strings + */ + public static function findPrefix(array $strings): string + { + $first = array_shift($strings); + for ($i = 0; $i < strlen($first); $i++) { + foreach ($strings as $s) { + if (!isset($s[$i]) || $first[$i] !== $s[$i]) { + while ($i && $first[$i - 1] >= "\x80" && $first[$i] >= "\x80" && $first[$i] < "\xC0") { + $i--; + } + + return substr($first, 0, $i); + } + } + } + + return $first; + } + + + /** + * Returns number of characters (not bytes) in UTF-8 string. + * That is the number of Unicode code points which may differ from the number of graphemes. + */ + public static function length(string $s): int + { + return match (true) { + extension_loaded('mbstring') => mb_strlen($s, 'UTF-8'), + extension_loaded('iconv') => iconv_strlen($s, 'UTF-8'), + default => strlen(@utf8_decode($s)), // deprecated + }; + } + + + /** + * Removes all left and right side spaces (or the characters passed as second argument) from a UTF-8 encoded string. + */ + public static function trim(string $s, string $charlist = self::TrimCharacters): string + { + $charlist = preg_quote($charlist, '#'); + return self::replace($s, '#^[' . $charlist . ']+|[' . $charlist . ']+$#Du', ''); + } + + + /** + * Pads a UTF-8 string to given length by prepending the $pad string to the beginning. + * @param non-empty-string $pad + */ + public static function padLeft(string $s, int $length, string $pad = ' '): string + { + $length = max(0, $length - self::length($s)); + $padLen = self::length($pad); + return str_repeat($pad, (int) ($length / $padLen)) . self::substring($pad, 0, $length % $padLen) . $s; + } + + + /** + * Pads UTF-8 string to given length by appending the $pad string to the end. + * @param non-empty-string $pad + */ + public static function padRight(string $s, int $length, string $pad = ' '): string + { + $length = max(0, $length - self::length($s)); + $padLen = self::length($pad); + return $s . str_repeat($pad, (int) ($length / $padLen)) . self::substring($pad, 0, $length % $padLen); + } + + + /** + * Reverses UTF-8 string. + */ + public static function reverse(string $s): string + { + if (!extension_loaded('iconv')) { + throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.'); + } + + return iconv('UTF-32LE', 'UTF-8', strrev(iconv('UTF-8', 'UTF-32BE', $s))); + } + + + /** + * Returns part of $haystack before $nth occurence of $needle or returns null if the needle was not found. + * Negative value means searching from the end. + */ + public static function before(string $haystack, string $needle, int $nth = 1): ?string + { + $pos = self::pos($haystack, $needle, $nth); + return $pos === null + ? null + : substr($haystack, 0, $pos); + } + + + /** + * Returns part of $haystack after $nth occurence of $needle or returns null if the needle was not found. + * Negative value means searching from the end. + */ + public static function after(string $haystack, string $needle, int $nth = 1): ?string + { + $pos = self::pos($haystack, $needle, $nth); + return $pos === null + ? null + : substr($haystack, $pos + strlen($needle)); + } + + + /** + * Returns position in characters of $nth occurence of $needle in $haystack or null if the $needle was not found. + * Negative value of `$nth` means searching from the end. + */ + public static function indexOf(string $haystack, string $needle, int $nth = 1): ?int + { + $pos = self::pos($haystack, $needle, $nth); + return $pos === null + ? null + : self::length(substr($haystack, 0, $pos)); + } + + + /** + * Returns position in characters of $nth occurence of $needle in $haystack or null if the needle was not found. + */ + private static function pos(string $haystack, string $needle, int $nth = 1): ?int + { + if (!$nth) { + return null; + } elseif ($nth > 0) { + if ($needle === '') { + return 0; + } + + $pos = 0; + while (($pos = strpos($haystack, $needle, $pos)) !== false && --$nth) { + $pos++; + } + } else { + $len = strlen($haystack); + if ($needle === '') { + return $len; + } elseif ($len === 0) { + return null; + } + + $pos = $len - 1; + while (($pos = strrpos($haystack, $needle, $pos - $len)) !== false && ++$nth) { + $pos--; + } + } + + return Helpers::falseToNull($pos); + } + + + /** + * Divides the string into arrays according to the regular expression. Expressions in parentheses will be captured and returned as well. + */ + public static function split( + string $subject, + #[Language('RegExp')] + string $pattern, + bool|int $captureOffset = false, + bool $skipEmpty = false, + int $limit = -1, + bool $utf8 = false, + ): array + { + $flags = is_int($captureOffset) // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_SPLIT_OFFSET_CAPTURE : 0) | ($skipEmpty ? PREG_SPLIT_NO_EMPTY : 0); + + $pattern .= $utf8 ? 'u' : ''; + $m = self::pcre('preg_split', [$pattern, $subject, $limit, $flags | PREG_SPLIT_DELIM_CAPTURE]); + return $utf8 && $captureOffset + ? self::bytesToChars($subject, [$m])[0] + : $m; + } + + + /** + * Searches the string for the part matching the regular expression and returns + * an array with the found expression and individual subexpressions, or `null`. + */ + public static function match( + string $subject, + #[Language('RegExp')] + string $pattern, + bool|int $captureOffset = false, + int $offset = 0, + bool $unmatchedAsNull = false, + bool $utf8 = false, + ): ?array + { + $flags = is_int($captureOffset) // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0); + + if ($utf8) { + $offset = strlen(self::substring($subject, 0, $offset)); + $pattern .= 'u'; + } + + if ($offset > strlen($subject)) { + return null; + } elseif (!self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])) { + return null; + } elseif ($utf8 && $captureOffset) { + return self::bytesToChars($subject, [$m])[0]; + } else { + return $m; + } + } + + + /** + * Searches the string for all occurrences matching the regular expression and + * returns an array of arrays containing the found expression and each subexpression. + * @return ($lazy is true ? \Generator : array[]) + */ + public static function matchAll( + string $subject, + #[Language('RegExp')] + string $pattern, + bool|int $captureOffset = false, + int $offset = 0, + bool $unmatchedAsNull = false, + bool $patternOrder = false, + bool $utf8 = false, + bool $lazy = false, + ): array|\Generator + { + if ($utf8) { + $offset = strlen(self::substring($subject, 0, $offset)); + $pattern .= 'u'; + } + + if ($lazy) { + $flags = PREG_OFFSET_CAPTURE | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0); + return (function () use ($utf8, $captureOffset, $flags, $subject, $pattern, $offset) { + $counter = 0; + while ( + $offset <= strlen($subject) - ($counter ? 1 : 0) + && self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset]) + ) { + $offset = $m[0][1] + max(1, strlen($m[0][0])); + if (!$captureOffset) { + $m = array_map(fn($item) => $item[0], $m); + } elseif ($utf8) { + $m = self::bytesToChars($subject, [$m])[0]; + } + yield $counter++ => $m; + } + })(); + } + + if ($offset > strlen($subject)) { + return []; + } + + $flags = is_int($captureOffset) // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0) | ($patternOrder ? PREG_PATTERN_ORDER : 0); + + self::pcre('preg_match_all', [ + $pattern, $subject, &$m, + ($flags & PREG_PATTERN_ORDER) ? $flags : ($flags | PREG_SET_ORDER), + $offset, + ]); + return $utf8 && $captureOffset + ? self::bytesToChars($subject, $m) + : $m; + } + + + /** + * Replaces all occurrences matching regular expression $pattern which can be string or array in the form `pattern => replacement`. + */ + public static function replace( + string $subject, + #[Language('RegExp')] + string|array $pattern, + string|callable $replacement = '', + int $limit = -1, + bool $captureOffset = false, + bool $unmatchedAsNull = false, + bool $utf8 = false, + ): string + { + if (is_object($replacement) || is_array($replacement)) { + if (!is_callable($replacement, false, $textual)) { + throw new Nette\InvalidStateException("Callback '$textual' is not callable."); + } + + $flags = ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0); + if ($utf8) { + $pattern .= 'u'; + if ($captureOffset) { + $replacement = fn($m) => $replacement(self::bytesToChars($subject, [$m])[0]); + } + } + + return self::pcre('preg_replace_callback', [$pattern, $replacement, $subject, $limit, 0, $flags]); + + } elseif (is_array($pattern) && is_string(key($pattern))) { + $replacement = array_values($pattern); + $pattern = array_keys($pattern); + } + + if ($utf8) { + $pattern = array_map(fn($item) => $item . 'u', (array) $pattern); + } + + return self::pcre('preg_replace', [$pattern, $replacement, $subject, $limit]); + } + + + private static function bytesToChars(string $s, array $groups): array + { + $lastBytes = $lastChars = 0; + foreach ($groups as &$matches) { + foreach ($matches as &$match) { + if ($match[1] > $lastBytes) { + $lastChars += self::length(substr($s, $lastBytes, $match[1] - $lastBytes)); + } elseif ($match[1] < $lastBytes) { + $lastChars -= self::length(substr($s, $match[1], $lastBytes - $match[1])); + } + + $lastBytes = $match[1]; + $match[1] = $lastChars; + } + } + + return $groups; + } + + + /** @internal */ + public static function pcre(string $func, array $args) + { + $res = Callback::invokeSafe($func, $args, function (string $message) use ($args): void { + // compile-time error, not detectable by preg_last_error + throw new RegexpException($message . ' in pattern: ' . implode(' or ', (array) $args[0])); + }); + + if (($code = preg_last_error()) // run-time error, but preg_last_error & return code are liars + && ($res === null || !in_array($func, ['preg_filter', 'preg_replace_callback', 'preg_replace'], true)) + ) { + throw new RegexpException(preg_last_error_msg() + . ' (pattern: ' . implode(' or ', (array) $args[0]) . ')', $code); + } + + return $res; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Type.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Type.php new file mode 100644 index 000000000..3444a8f17 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Type.php @@ -0,0 +1,267 @@ + */ + private array $types; + private bool $simple; + private string $kind; // | & + + + /** + * Creates a Type object based on reflection. Resolves self, static and parent to the actual class name. + * If the subject has no type, it returns null. + */ + public static function fromReflection( + \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection, + ): ?self + { + $type = $reflection instanceof \ReflectionFunctionAbstract + ? $reflection->getReturnType() ?? (PHP_VERSION_ID >= 80100 && $reflection instanceof \ReflectionMethod ? $reflection->getTentativeReturnType() : null) + : $reflection->getType(); + + return $type ? self::fromReflectionType($type, $reflection, asObject: true) : null; + } + + + private static function fromReflectionType(\ReflectionType $type, $of, bool $asObject): self|string + { + if ($type instanceof \ReflectionNamedType) { + $name = self::resolve($type->getName(), $of); + return $asObject + ? new self($type->allowsNull() && $name !== 'mixed' ? [$name, 'null'] : [$name]) + : $name; + + } elseif ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) { + return new self( + array_map(fn($t) => self::fromReflectionType($t, $of, asObject: false), $type->getTypes()), + $type instanceof \ReflectionUnionType ? '|' : '&', + ); + + } else { + throw new Nette\InvalidStateException('Unexpected type of ' . Reflection::toString($of)); + } + } + + + /** + * Creates the Type object according to the text notation. + */ + public static function fromString(string $type): self + { + if (!Validators::isTypeDeclaration($type)) { + throw new Nette\InvalidArgumentException("Invalid type '$type'."); + } + + if ($type[0] === '?') { + return new self([substr($type, 1), 'null']); + } + + $unions = []; + foreach (explode('|', $type) as $part) { + $part = explode('&', trim($part, '()')); + $unions[] = count($part) === 1 ? $part[0] : new self($part, '&'); + } + + return count($unions) === 1 && $unions[0] instanceof self + ? $unions[0] + : new self($unions); + } + + + /** + * Resolves 'self', 'static' and 'parent' to the actual class name. + */ + public static function resolve( + string $type, + \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $of, + ): string + { + $lower = strtolower($type); + if ($of instanceof \ReflectionFunction) { + return $type; + } elseif ($lower === 'self') { + return $of->getDeclaringClass()->name; + } elseif ($lower === 'static') { + return ($of instanceof ReflectionMethod ? $of->getOriginalClass() : $of->getDeclaringClass())->name; + } elseif ($lower === 'parent' && $of->getDeclaringClass()->getParentClass()) { + return $of->getDeclaringClass()->getParentClass()->name; + } else { + return $type; + } + } + + + private function __construct(array $types, string $kind = '|') + { + $o = array_search('null', $types, strict: true); + if ($o !== false) { // null as last + array_splice($types, $o, 1); + $types[] = 'null'; + } + + $this->types = $types; + $this->simple = is_string($types[0]) && ($types[1] ?? 'null') === 'null'; + $this->kind = count($types) > 1 ? $kind : ''; + } + + + public function __toString(): string + { + $multi = count($this->types) > 1; + if ($this->simple) { + return ($multi ? '?' : '') . $this->types[0]; + } + + $res = []; + foreach ($this->types as $type) { + $res[] = $type instanceof self && $multi ? "($type)" : $type; + } + return implode($this->kind, $res); + } + + + /** + * Returns the array of subtypes that make up the compound type as strings. + * @return array + */ + public function getNames(): array + { + return array_map(fn($t) => $t instanceof self ? $t->getNames() : $t, $this->types); + } + + + /** + * Returns the array of subtypes that make up the compound type as Type objects: + * @return self[] + */ + public function getTypes(): array + { + return array_map(fn($t) => $t instanceof self ? $t : new self([$t]), $this->types); + } + + + /** + * Returns the type name for simple types, otherwise null. + */ + public function getSingleName(): ?string + { + return $this->simple + ? $this->types[0] + : null; + } + + + /** + * Returns true whether it is a union type. + */ + public function isUnion(): bool + { + return $this->kind === '|'; + } + + + /** + * Returns true whether it is an intersection type. + */ + public function isIntersection(): bool + { + return $this->kind === '&'; + } + + + /** + * Returns true whether it is a simple type. Single nullable types are also considered to be simple types. + */ + public function isSimple(): bool + { + return $this->simple; + } + + + /** @deprecated use isSimple() */ + public function isSingle(): bool + { + return $this->simple; + } + + + /** + * Returns true whether the type is both a simple and a PHP built-in type. + */ + public function isBuiltin(): bool + { + return $this->simple && Validators::isBuiltinType($this->types[0]); + } + + + /** + * Returns true whether the type is both a simple and a class name. + */ + public function isClass(): bool + { + return $this->simple && !Validators::isBuiltinType($this->types[0]); + } + + + /** + * Determines if type is special class name self/parent/static. + */ + public function isClassKeyword(): bool + { + return $this->simple && Validators::isClassKeyword($this->types[0]); + } + + + /** + * Verifies type compatibility. For example, it checks if a value of a certain type could be passed as a parameter. + */ + public function allows(string $subtype): bool + { + if ($this->types === ['mixed']) { + return true; + } + + $subtype = self::fromString($subtype); + return $subtype->isUnion() + ? Arrays::every($subtype->types, fn($t) => $this->allows2($t instanceof self ? $t->types : [$t])) + : $this->allows2($subtype->types); + } + + + private function allows2(array $subtypes): bool + { + return $this->isUnion() + ? Arrays::some($this->types, fn($t) => $this->allows3($t instanceof self ? $t->types : [$t], $subtypes)) + : $this->allows3($this->types, $subtypes); + } + + + private function allows3(array $types, array $subtypes): bool + { + return Arrays::every( + $types, + fn($type) => Arrays::some( + $subtypes, + fn($subtype) => Validators::isBuiltinType($type) + ? strcasecmp($type, $subtype) === 0 + : is_a($subtype, $type, allow_string: true), + ), + ); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Validators.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Validators.php new file mode 100644 index 000000000..61ccf091a --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Validators.php @@ -0,0 +1,416 @@ + 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1, + 'callable' => 1, 'iterable' => 1, 'void' => 1, 'null' => 1, 'mixed' => 1, 'false' => 1, + 'never' => 1, 'true' => 1, + ]; + + /** @var array */ + protected static $validators = [ + // PHP types + 'array' => 'is_array', + 'bool' => 'is_bool', + 'boolean' => 'is_bool', + 'float' => 'is_float', + 'int' => 'is_int', + 'integer' => 'is_int', + 'null' => 'is_null', + 'object' => 'is_object', + 'resource' => 'is_resource', + 'scalar' => 'is_scalar', + 'string' => 'is_string', + + // pseudo-types + 'callable' => [self::class, 'isCallable'], + 'iterable' => 'is_iterable', + 'list' => [Arrays::class, 'isList'], + 'mixed' => [self::class, 'isMixed'], + 'none' => [self::class, 'isNone'], + 'number' => [self::class, 'isNumber'], + 'numeric' => [self::class, 'isNumeric'], + 'numericint' => [self::class, 'isNumericInt'], + + // string patterns + 'alnum' => 'ctype_alnum', + 'alpha' => 'ctype_alpha', + 'digit' => 'ctype_digit', + 'lower' => 'ctype_lower', + 'pattern' => null, + 'space' => 'ctype_space', + 'unicode' => [self::class, 'isUnicode'], + 'upper' => 'ctype_upper', + 'xdigit' => 'ctype_xdigit', + + // syntax validation + 'email' => [self::class, 'isEmail'], + 'identifier' => [self::class, 'isPhpIdentifier'], + 'uri' => [self::class, 'isUri'], + 'url' => [self::class, 'isUrl'], + + // environment validation + 'class' => 'class_exists', + 'interface' => 'interface_exists', + 'directory' => 'is_dir', + 'file' => 'is_file', + 'type' => [self::class, 'isType'], + ]; + + /** @var array */ + protected static $counters = [ + 'string' => 'strlen', + 'unicode' => [Strings::class, 'length'], + 'array' => 'count', + 'list' => 'count', + 'alnum' => 'strlen', + 'alpha' => 'strlen', + 'digit' => 'strlen', + 'lower' => 'strlen', + 'space' => 'strlen', + 'upper' => 'strlen', + 'xdigit' => 'strlen', + ]; + + + /** + * Verifies that the value is of expected types separated by pipe. + * @throws AssertionException + */ + public static function assert(mixed $value, string $expected, string $label = 'variable'): void + { + if (!static::is($value, $expected)) { + $expected = str_replace(['|', ':'], [' or ', ' in range '], $expected); + $translate = ['boolean' => 'bool', 'integer' => 'int', 'double' => 'float', 'NULL' => 'null']; + $type = $translate[gettype($value)] ?? gettype($value); + if (is_int($value) || is_float($value) || (is_string($value) && strlen($value) < 40)) { + $type .= ' ' . var_export($value, return: true); + } elseif (is_object($value)) { + $type .= ' ' . $value::class; + } + + throw new AssertionException("The $label expects to be $expected, $type given."); + } + } + + + /** + * Verifies that element $key in array is of expected types separated by pipe. + * @param mixed[] $array + * @throws AssertionException + */ + public static function assertField( + array $array, + $key, + ?string $expected = null, + string $label = "item '%' in array", + ): void + { + if (!array_key_exists($key, $array)) { + throw new AssertionException('Missing ' . str_replace('%', $key, $label) . '.'); + + } elseif ($expected) { + static::assert($array[$key], $expected, str_replace('%', $key, $label)); + } + } + + + /** + * Verifies that the value is of expected types separated by pipe. + */ + public static function is(mixed $value, string $expected): bool + { + foreach (explode('|', $expected) as $item) { + if (str_ends_with($item, '[]')) { + if (is_iterable($value) && self::everyIs($value, substr($item, 0, -2))) { + return true; + } + + continue; + } elseif (str_starts_with($item, '?')) { + $item = substr($item, 1); + if ($value === null) { + return true; + } + } + + [$type] = $item = explode(':', $item, 2); + if (isset(static::$validators[$type])) { + try { + if (!static::$validators[$type]($value)) { + continue; + } + } catch (\TypeError $e) { + continue; + } + } elseif ($type === 'pattern') { + if (Strings::match($value, '|^' . ($item[1] ?? '') . '$|D')) { + return true; + } + + continue; + } elseif (!$value instanceof $type) { + continue; + } + + if (isset($item[1])) { + $length = $value; + if (isset(static::$counters[$type])) { + $length = static::$counters[$type]($value); + } + + $range = explode('..', $item[1]); + if (!isset($range[1])) { + $range[1] = $range[0]; + } + + if (($range[0] !== '' && $length < $range[0]) || ($range[1] !== '' && $length > $range[1])) { + continue; + } + } + + return true; + } + + return false; + } + + + /** + * Finds whether all values are of expected types separated by pipe. + * @param mixed[] $values + */ + public static function everyIs(iterable $values, string $expected): bool + { + foreach ($values as $value) { + if (!static::is($value, $expected)) { + return false; + } + } + + return true; + } + + + /** + * Checks if the value is an integer or a float. + * @return ($value is int|float ? true : false) + */ + public static function isNumber(mixed $value): bool + { + return is_int($value) || is_float($value); + } + + + /** + * Checks if the value is an integer or a integer written in a string. + * @return ($value is non-empty-string ? bool : ($value is int ? true : false)) + */ + public static function isNumericInt(mixed $value): bool + { + return is_int($value) || (is_string($value) && preg_match('#^[+-]?[0-9]+$#D', $value)); + } + + + /** + * Checks if the value is a number or a number written in a string. + * @return ($value is non-empty-string ? bool : ($value is int|float ? true : false)) + */ + public static function isNumeric(mixed $value): bool + { + return is_float($value) || is_int($value) || (is_string($value) && preg_match('#^[+-]?([0-9]++\.?[0-9]*|\.[0-9]+)$#D', $value)); + } + + + /** + * Checks if the value is a syntactically correct callback. + */ + public static function isCallable(mixed $value): bool + { + return $value && is_callable($value, syntax_only: true); + } + + + /** + * Checks if the value is a valid UTF-8 string. + */ + public static function isUnicode(mixed $value): bool + { + return is_string($value) && preg_match('##u', $value); + } + + + /** + * Checks if the value is 0, '', false or null. + * @return ($value is 0|''|false|null ? true : false) + */ + public static function isNone(mixed $value): bool + { + return $value == null; // intentionally == + } + + + /** @internal */ + public static function isMixed(): bool + { + return true; + } + + + /** + * Checks if a variable is a zero-based integer indexed array. + * @deprecated use Nette\Utils\Arrays::isList + * @return ($value is list ? true : false) + */ + public static function isList(mixed $value): bool + { + return Arrays::isList($value); + } + + + /** + * Checks if the value is in the given range [min, max], where the upper or lower limit can be omitted (null). + * Numbers, strings and DateTime objects can be compared. + */ + public static function isInRange(mixed $value, array $range): bool + { + if ($value === null || !(isset($range[0]) || isset($range[1]))) { + return false; + } + + $limit = $range[0] ?? $range[1]; + if (is_string($limit)) { + $value = (string) $value; + } elseif ($limit instanceof \DateTimeInterface) { + if (!$value instanceof \DateTimeInterface) { + return false; + } + } elseif (is_numeric($value)) { + $value *= 1; + } else { + return false; + } + + return (!isset($range[0]) || ($value >= $range[0])) && (!isset($range[1]) || ($value <= $range[1])); + } + + + /** + * Checks if the value is a valid email address. It does not verify that the domain actually exists, only the syntax is verified. + */ + public static function isEmail(string $value): bool + { + $atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part + $alpha = "a-z\x80-\xFF"; // superset of IDN + return (bool) preg_match(<< \\? (? [a-zA-Z_\x7f-\xff][\w\x7f-\xff]*) (\\ (?&name))* ) | + (? (?&type) (& (?&type))+ ) | + (? (?&type) | \( (?&intersection) \) ) (\| (?&upart))+ + )$~xAD + XX, $type); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/exceptions.php b/tools/.phpstan/vendor/nette/utils/src/Utils/exceptions.php new file mode 100644 index 000000000..30805ea3c --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/exceptions.php @@ -0,0 +1,50 @@ + + array ( + 'install_path' => '/usr/local/src/php-code-coverage/tools/.phpstan/vendor/ergebnis/phpstan-rules', + 'relative_install_path' => '../../../ergebnis/phpstan-rules', + 'extra' => + array ( + 'includes' => + array ( + 0 => 'rules.neon', + ), + ), + 'version' => '2.10.5', + 'phpstanVersionConstraint' => '>=2.1.8.0-dev, <3.0.0.0-dev', + ), + 'phpstan/phpstan-strict-rules' => + array ( + 'install_path' => '/usr/local/src/php-code-coverage/tools/.phpstan/vendor/phpstan/phpstan-strict-rules', + 'relative_install_path' => '../../phpstan-strict-rules', + 'extra' => + array ( + 'includes' => + array ( + 0 => 'rules.neon', + ), + ), + 'version' => '2.0.6', + 'phpstanVersionConstraint' => '>=2.0.4.0-dev, <3.0.0.0-dev', + ), + 'tomasvotruba/type-coverage' => + array ( + 'install_path' => '/usr/local/src/php-code-coverage/tools/.phpstan/vendor/tomasvotruba/type-coverage', + 'relative_install_path' => '../../../tomasvotruba/type-coverage', + 'extra' => + array ( + 'includes' => + array ( + 0 => 'config/extension.neon', + ), + ), + 'version' => '2.0.2', + 'phpstanVersionConstraint' => '>=2.0.0.0-dev, <3.0.0.0-dev', + ), +); + + public const NOT_INSTALLED = array ( +); + + /** @var string|null */ + public const PHPSTAN_VERSION_CONSTRAINT = '>=2.1.8.0-dev, <3.0.0.0-dev'; + + private function __construct() + { + } + +} diff --git a/tools/.phpstan/vendor/phpstan/extension-installer/src/Plugin.php b/tools/.phpstan/vendor/phpstan/extension-installer/src/Plugin.php new file mode 100644 index 000000000..ec757351f --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/extension-installer/src/Plugin.php @@ -0,0 +1,228 @@ + + */ + public static function getSubscribedEvents(): array + { + return [ + ScriptEvents::POST_INSTALL_CMD => 'process', + ScriptEvents::POST_UPDATE_CMD => 'process', + ]; + } + + public function process(Event $event): void + { + $io = $event->getIO(); + + if (!file_exists(__DIR__)) { + $io->write('phpstan/extension-installer: Package not found (probably scheduled for removal); extensions installation skipped.'); + return; + } + + $composer = $event->getComposer(); + $installationManager = $composer->getInstallationManager(); + + $generatedConfigFilePath = __DIR__ . '/GeneratedConfig.php'; + $oldGeneratedConfigFileHash = null; + if (is_file($generatedConfigFilePath)) { + $oldGeneratedConfigFileHash = md5_file($generatedConfigFilePath); + } + $notInstalledPackages = []; + $installedPackages = []; + $ignoredPackages = []; + + $data = []; + $fs = new Filesystem(); + $ignore = []; + + $packageExtra = $composer->getPackage()->getExtra(); + + if (isset($packageExtra['phpstan/extension-installer']['ignore'])) { + $ignore = $packageExtra['phpstan/extension-installer']['ignore']; + } + + $phpstanVersionConstraints = []; + + foreach ($composer->getRepositoryManager()->getLocalRepository()->getPackages() as $package) { + if ( + $package->getType() !== 'phpstan-extension' + && !isset($package->getExtra()['phpstan']) + ) { + if ( + strpos($package->getName(), 'phpstan') !== false + && !in_array($package->getName(), [ + 'phpstan/phpstan', + 'phpstan/phpstan-shim', + 'phpstan/phpdoc-parser', + 'phpstan/extension-installer', + ], true) + ) { + $notInstalledPackages[$package->getName()] = $package->getFullPrettyVersion(); + } + continue; + } + + if (in_array($package->getName(), $ignore, true)) { + $ignoredPackages[] = $package->getName(); + continue; + } + + $installPath = $installationManager->getInstallPath($package); + if ($installPath === null) { + continue; + } + + $absoluteInstallPath = $fs->isAbsolutePath($installPath) + ? $installPath + : getcwd() . DIRECTORY_SEPARATOR . $installPath; + + $packageRequires = $package->getRequires(); + $phpstanConstraint = null; + if (array_key_exists('phpstan/phpstan', $packageRequires)) { + $phpstanConstraint = $packageRequires['phpstan/phpstan']->getConstraint(); + if ($phpstanConstraint->getLowerBound()->isZero()) { + continue; + } + if ($phpstanConstraint->getUpperBound()->isPositiveInfinity()) { + continue; + } + $phpstanVersionConstraints[] = $phpstanConstraint; + } + + $data[$package->getName()] = [ + 'install_path' => $absoluteInstallPath, + 'relative_install_path' => $fs->findShortestPath(dirname($generatedConfigFilePath), $absoluteInstallPath, true), + 'extra' => $package->getExtra()['phpstan'] ?? null, + 'version' => $package->getFullPrettyVersion(), + 'phpstanVersionConstraint' => $phpstanConstraint !== null ? $this->constraintIntoString($phpstanConstraint) : null, + ]; + + $installedPackages[$package->getName()] = true; + } + + $phpstanVersionConstraint = null; + if (count($phpstanVersionConstraints) > 0 && class_exists(Intervals::class)) { + if (count($phpstanVersionConstraints) === 1) { + $multiConstraint = $phpstanVersionConstraints[0]; + } else { + $multiConstraint = new MultiConstraint($phpstanVersionConstraints); + } + $phpstanVersionConstraint = $this->constraintIntoString(Intervals::compactConstraint($multiConstraint)); + } + + ksort($data); + ksort($installedPackages); + ksort($notInstalledPackages); + sort($ignoredPackages); + + $generatedConfigFileContents = sprintf(self::$generatedFileTemplate, var_export($data, true), var_export($notInstalledPackages, true), var_export($phpstanVersionConstraint, true)); + file_put_contents($generatedConfigFilePath, $generatedConfigFileContents); + $io->write('phpstan/extension-installer: Extensions installed'); + + if ($oldGeneratedConfigFileHash === md5($generatedConfigFileContents)) { + return; + } + + foreach (array_keys($installedPackages) as $name) { + $io->write(sprintf('> %s: installed', $name)); + } + + foreach (array_keys($notInstalledPackages) as $name) { + $io->write(sprintf('> %s: not supported', $name)); + } + + foreach ($ignoredPackages as $name) { + $io->write(sprintf('> %s: ignored', $name)); + } + } + + private function constraintIntoString(ConstraintInterface $constraint): string + { + return sprintf( + '%s%s, %s%s', + $constraint->getLowerBound()->isInclusive() ? '>=' : '>', + $constraint->getLowerBound()->getVersion(), + $constraint->getUpperBound()->isInclusive() ? '<=' : '<', + $constraint->getUpperBound()->getVersion() + ); + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/.editorconfig b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/.editorconfig new file mode 100644 index 000000000..5d66bc427 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/.editorconfig @@ -0,0 +1,27 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true + +[*.{php,phpt}] +indent_style = tab +indent_size = 4 + +[*.xml] +indent_style = tab +indent_size = 4 + +[*.neon] +indent_style = tab +indent_size = 4 + +[*.{yaml,yml}] +indent_style = space +indent_size = 2 + +[composer.json] +indent_style = tab +indent_size = 4 diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/LICENSE b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/LICENSE new file mode 100644 index 000000000..52fba1e23 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2016 Ondřej Mirtes +Copyright (c) 2025 PHPStan s.r.o. + +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. + diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/README.md b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/README.md new file mode 100644 index 000000000..e56aa34d7 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/README.md @@ -0,0 +1,108 @@ +# Extra strict and opinionated rules for PHPStan + +[![Build](https://github.com/phpstan/phpstan-strict-rules/workflows/Build/badge.svg)](https://github.com/phpstan/phpstan-strict-rules/actions) +[![Latest Stable Version](https://poser.pugx.org/phpstan/phpstan-strict-rules/v/stable)](https://packagist.org/packages/phpstan/phpstan-strict-rules) +[![License](https://poser.pugx.org/phpstan/phpstan-strict-rules/license)](https://packagist.org/packages/phpstan/phpstan-strict-rules) + +[PHPStan](https://phpstan.org/) focuses on finding bugs in your code. But in PHP there's a lot of leeway in how stuff can be written. This repository contains additional rules that revolve around strictly and strongly typed code with no loose casting for those who want additional safety in extremely defensive programming: + +| Configuration Parameters | Rule Description | +|:---------------------------------------|:--------------------------------------------------------------------------------------------------------| +| `booleansInConditions` | Require booleans in `if`, `elseif`, ternary operator, after `!`, and on both sides of `&&` and `\|\|`. | +| `booleansInLoopConditions` | Require booleans in `while` and `do while` loop conditions. | +| `numericOperandsInArithmeticOperators` | Require numeric operand in `+$var`, `-$var`, `$var++`, `$var--`, `++$var` and `--$var`. | +| `numericOperandsInArithmeticOperators` | Require numeric operand in `$var++`, `$var--`, `++$var`and `--$var`. | +| `strictFunctionCalls` | These functions contain a `$strict` parameter for better type safety, it must be set to `true`:
    * `in_array` (3rd parameter)
    * `array_search` (3rd parameter)
    * `array_keys` (3rd parameter; only if the 2nd parameter `$search_value` is provided)
    * `base64_decode` (2nd parameter). | +| `overwriteVariablesWithLoop` | Variables assigned in `while` loop condition and `for` loop initial assignment cannot be used after the loop. | +| `overwriteVariablesWithLoop` | Variables set in foreach that's always looped thanks to non-empty arrays cannot be used after the loop. | +| `switchConditionsMatchingType` | Types in `switch` condition and `case` value must match. PHP compares them loosely by default and that can lead to unexpected results. | +| `dynamicCallOnStaticMethod` | Check that statically declared methods are called statically. | +| `disallowedEmpty` | Disallow `empty()` - it's a very loose comparison (see [manual](https://php.net/empty)), it's recommended to use more strict one. | +| `disallowedShortTernary` | Disallow short ternary operator (`?:`) - implies weak comparison, it's recommended to use null coalesce operator (`??`) or ternary operator with strict condition. | +| `noVariableVariables` | Disallow variable variables (`$$foo`, `$this->$method()` etc.). | +| `overwriteVariablesWithLoop` | Disallow overwriting variables with foreach key and value variables. | +| `checkAlwaysTrueInstanceof`, `checkAlwaysTrueCheckTypeFunctionCall`, `checkAlwaysTrueStrictComparison` | Always true `instanceof`, type-checking `is_*` functions and strict comparisons `===`/`!==`. These checks can be turned off by setting `checkAlwaysTrueInstanceof`, `checkAlwaysTrueCheckTypeFunctionCall` and `checkAlwaysTrueStrictComparison` to false. | +| | Correct case for referenced and called function names. | +| `matchingInheritedMethodNames` | Correct case for inherited and implemented method names. | +| | Contravariance for parameter types and covariance for return types in inherited methods (also known as Liskov substitution principle - LSP).| +| | Check LSP even for static methods. | +| `requireParentConstructorCall` | Require calling parent constructor. | +| `disallowedBacktick` | Disallow usage of backtick operator (`` $ls = `ls -la` ``). | +| `closureUsesThis` | Closure should use `$this` directly instead of using `$this` variable indirectly. | + +Additional rules are coming in subsequent releases! + + +## Installation + +To use this extension, require it in [Composer](https://getcomposer.org/): + +``` +composer require --dev phpstan/phpstan-strict-rules +``` + +If you also install [phpstan/extension-installer](https://github.com/phpstan/extension-installer) then you're all set! + +
    + Manual installation + +If you don't want to use `phpstan/extension-installer`, include rules.neon in your project's PHPStan config: + +``` +includes: + - vendor/phpstan/phpstan-strict-rules/rules.neon +``` +
    + +## Disabling rules + +You can disable rules using configuration parameters: + +```neon +parameters: + strictRules: + disallowedLooseComparison: false + booleansInConditions: false + booleansInLoopConditions: false + uselessCast: false + requireParentConstructorCall: false + disallowedBacktick: false + disallowedEmpty: false + disallowedImplicitArrayCreation: false + disallowedShortTernary: false + overwriteVariablesWithLoop: false + closureUsesThis: false + matchingInheritedMethodNames: false + numericOperandsInArithmeticOperators: false + strictFunctionCalls: false + dynamicCallOnStaticMethod: false + switchConditionsMatchingType: false + noVariableVariables: false + strictArrayFilter: false + illegalConstructorMethodCall: false +``` + +Aside from introducing new custom rules, phpstan-strict-rules also [change the default values of some configuration parameters](./rules.neon#L1) that are present in PHPStan itself. These parameters are [documented on phpstan.org](https://phpstan.org/config-reference#stricter-analysis). + +## Enabling rules one-by-one + +If you don't want to start using all the available strict rules at once but only one or two, you can! + +You can disable all rules from the included `rules.neon` with: + +```neon +parameters: + strictRules: + allRules: false +``` + +Then you can re-enable individual rules with configuration parameters: + +```neon +parameters: + strictRules: + allRules: false + booleansInConditions: true +``` + +Even with `strictRules.allRules` set to `false`, part of this package is still in effect. That's because phpstan-strict-rules also [change the default values of some configuration parameters](./rules.neon#L1) that are present in PHPStan itself. These parameters are [documented on phpstan.org](https://phpstan.org/config-reference#stricter-analysis). diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/composer.json b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/composer.json new file mode 100644 index 000000000..2bbc44d69 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/composer.json @@ -0,0 +1,43 @@ +{ + "name": "phpstan/phpstan-strict-rules", + "type": "phpstan-extension", + "description": "Extra strict and opinionated rules for PHPStan", + "license": [ + "MIT" + ], + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0.4" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" + }, + "config": { + "platform": { + "php": "7.4.6" + }, + "sort-packages": true + }, + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "autoload-dev": { + "classmap": [ + "tests/" + ] + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/rules.neon b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/rules.neon new file mode 100644 index 000000000..7a63c4ec3 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/rules.neon @@ -0,0 +1,301 @@ +parameters: + strictRulesInstalled: true + polluteScopeWithLoopInitialAssignments: false + polluteScopeWithAlwaysIterableForeach: false + polluteScopeWithBlock: false + checkDynamicProperties: true + checkExplicitMixedMissingReturn: true + checkFunctionNameCase: true + checkInternalClassCaseSensitivity: true + reportMaybesInMethodSignatures: true + reportStaticMethodSignatures: true + reportMaybesInPropertyPhpDocTypes: true + reportWrongPhpDocTypeInVarTag: true + strictRules: + allRules: true + disallowedLooseComparison: %strictRules.allRules% + booleansInConditions: %strictRules.allRules% + booleansInLoopConditions: [%strictRules.allRules%, %featureToggles.bleedingEdge%] + uselessCast: %strictRules.allRules% + requireParentConstructorCall: %strictRules.allRules% + disallowedBacktick: %strictRules.allRules% + disallowedEmpty: %strictRules.allRules% + disallowedImplicitArrayCreation: %strictRules.allRules% + disallowedShortTernary: %strictRules.allRules% + overwriteVariablesWithLoop: %strictRules.allRules% + closureUsesThis: %strictRules.allRules% + matchingInheritedMethodNames: %strictRules.allRules% + numericOperandsInArithmeticOperators: %strictRules.allRules% + strictFunctionCalls: %strictRules.allRules% + dynamicCallOnStaticMethod: %strictRules.allRules% + switchConditionsMatchingType: %strictRules.allRules% + noVariableVariables: %strictRules.allRules% + strictArrayFilter: %strictRules.allRules% + illegalConstructorMethodCall: %strictRules.allRules% + +parametersSchema: + strictRules: structure([ + allRules: anyOf(bool(), arrayOf(bool())), + disallowedLooseComparison: anyOf(bool(), arrayOf(bool())), + booleansInConditions: anyOf(bool(), arrayOf(bool())) + booleansInLoopConditions: anyOf(bool(), arrayOf(bool())) + uselessCast: anyOf(bool(), arrayOf(bool())) + requireParentConstructorCall: anyOf(bool(), arrayOf(bool())) + disallowedBacktick: anyOf(bool(), arrayOf(bool())) + disallowedEmpty: anyOf(bool(), arrayOf(bool())) + disallowedImplicitArrayCreation: anyOf(bool(), arrayOf(bool())) + disallowedShortTernary: anyOf(bool(), arrayOf(bool())) + overwriteVariablesWithLoop: anyOf(bool(), arrayOf(bool())) + closureUsesThis: anyOf(bool(), arrayOf(bool())) + matchingInheritedMethodNames: anyOf(bool(), arrayOf(bool())) + numericOperandsInArithmeticOperators: anyOf(bool(), arrayOf(bool())) + strictFunctionCalls: anyOf(bool(), arrayOf(bool())) + dynamicCallOnStaticMethod: anyOf(bool(), arrayOf(bool())) + switchConditionsMatchingType: anyOf(bool(), arrayOf(bool())) + noVariableVariables: anyOf(bool(), arrayOf(bool())) + strictArrayFilter: anyOf(bool(), arrayOf(bool())) + illegalConstructorMethodCall: anyOf(bool(), arrayOf(bool())) + ]) + +conditionalTags: + PHPStan\Rules\DisallowedConstructs\DisallowedLooseComparisonRule: + phpstan.rules.rule: %strictRules.disallowedLooseComparison% + PHPStan\Rules\BooleansInConditions\BooleanInBooleanAndRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInBooleanNotRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInBooleanOrRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInDoWhileConditionRule: + phpstan.rules.rule: %strictRules.booleansInLoopConditions% + PHPStan\Rules\BooleansInConditions\BooleanInElseIfConditionRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInIfConditionRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInTernaryOperatorRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInWhileConditionRule: + phpstan.rules.rule: %strictRules.booleansInLoopConditions% + PHPStan\Rules\Cast\UselessCastRule: + phpstan.rules.rule: %strictRules.uselessCast% + PHPStan\Rules\Classes\RequireParentConstructCallRule: + phpstan.rules.rule: %strictRules.requireParentConstructorCall% + PHPStan\Rules\DisallowedConstructs\DisallowedBacktickRule: + phpstan.rules.rule: %strictRules.disallowedBacktick% + PHPStan\Rules\DisallowedConstructs\DisallowedEmptyRule: + phpstan.rules.rule: %strictRules.disallowedEmpty% + PHPStan\Rules\DisallowedConstructs\DisallowedImplicitArrayCreationRule: + phpstan.rules.rule: %strictRules.disallowedImplicitArrayCreation% + PHPStan\Rules\DisallowedConstructs\DisallowedShortTernaryRule: + phpstan.rules.rule: %strictRules.disallowedShortTernary% + PHPStan\Rules\ForeachLoop\OverwriteVariablesWithForeachRule: + phpstan.rules.rule: %strictRules.overwriteVariablesWithLoop% + PHPStan\Rules\ForLoop\OverwriteVariablesWithForLoopInitRule: + phpstan.rules.rule: %strictRules.overwriteVariablesWithLoop% + PHPStan\Rules\Functions\ArrayFilterStrictRule: + phpstan.rules.rule: %strictRules.strictArrayFilter% + PHPStan\Rules\Functions\ClosureUsesThisRule: + phpstan.rules.rule: %strictRules.closureUsesThis% + PHPStan\Rules\Methods\WrongCaseOfInheritedMethodRule: + phpstan.rules.rule: %strictRules.matchingInheritedMethodNames% + PHPStan\Rules\Operators\OperandInArithmeticPostDecrementRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandInArithmeticPostIncrementRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandInArithmeticPreDecrementRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandInArithmeticPreIncrementRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandInArithmeticUnaryMinusRule: + phpstan.rules.rule: [%strictRules.numericOperandsInArithmeticOperators%, %featureToggles.bleedingEdge%] + PHPStan\Rules\Operators\OperandInArithmeticUnaryPlusRule: + phpstan.rules.rule: [%strictRules.numericOperandsInArithmeticOperators%, %featureToggles.bleedingEdge%] + PHPStan\Rules\Operators\OperandsInArithmeticAdditionRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandsInArithmeticDivisionRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandsInArithmeticExponentiationRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandsInArithmeticModuloRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandsInArithmeticMultiplicationRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandsInArithmeticSubtractionRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsRule: + phpstan.rules.rule: %strictRules.dynamicCallOnStaticMethod% + PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsCallableRule: + phpstan.rules.rule: %strictRules.dynamicCallOnStaticMethod% + PHPStan\Rules\StrictCalls\StrictFunctionCallsRule: + phpstan.rules.rule: %strictRules.strictFunctionCalls% + PHPStan\Rules\SwitchConditions\MatchingTypeInSwitchCaseConditionRule: + phpstan.rules.rule: %strictRules.switchConditionsMatchingType% + PHPStan\Rules\VariableVariables\VariableMethodCallRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariableMethodCallableRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariableStaticMethodCallRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariableStaticMethodCallableRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariableStaticPropertyFetchRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariableVariablesRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariablePropertyFetchRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\Methods\IllegalConstructorMethodCallRule: + phpstan.rules.rule: %strictRules.illegalConstructorMethodCall% + PHPStan\Rules\Methods\IllegalConstructorStaticCallRule: + phpstan.rules.rule: %strictRules.illegalConstructorMethodCall% + +services: + - + class: PHPStan\Rules\BooleansInConditions\BooleanRuleHelper + + - + class: PHPStan\Rules\Operators\OperatorRuleHelper + + - + class: PHPStan\Rules\VariableVariables\VariablePropertyFetchRule + arguments: + universalObjectCratesClasses: %universalObjectCratesClasses% + + - + class: PHPStan\Rules\DisallowedConstructs\DisallowedLooseComparisonRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanAndRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanNotRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanOrRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInDoWhileConditionRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInElseIfConditionRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInIfConditionRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInTernaryOperatorRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInWhileConditionRule + + - + class: PHPStan\Rules\Cast\UselessCastRule + arguments: + treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain% + treatPhpDocTypesAsCertainTip: %tips.treatPhpDocTypesAsCertain% + + - + class: PHPStan\Rules\Classes\RequireParentConstructCallRule + + - + class: PHPStan\Rules\DisallowedConstructs\DisallowedBacktickRule + + - + class: PHPStan\Rules\DisallowedConstructs\DisallowedEmptyRule + + - + class: PHPStan\Rules\DisallowedConstructs\DisallowedImplicitArrayCreationRule + + - + class: PHPStan\Rules\DisallowedConstructs\DisallowedShortTernaryRule + + - + class: PHPStan\Rules\ForeachLoop\OverwriteVariablesWithForeachRule + + - + class: PHPStan\Rules\ForLoop\OverwriteVariablesWithForLoopInitRule + + - + class: PHPStan\Rules\Functions\ArrayFilterStrictRule + arguments: + treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain% + checkNullables: %checkNullables% + treatPhpDocTypesAsCertainTip: %tips.treatPhpDocTypesAsCertain% + + - + class: PHPStan\Rules\Functions\ClosureUsesThisRule + + - + class: PHPStan\Rules\Methods\WrongCaseOfInheritedMethodRule + + - + class: PHPStan\Rules\Methods\IllegalConstructorMethodCallRule + + - + class: PHPStan\Rules\Methods\IllegalConstructorStaticCallRule + + - + class: PHPStan\Rules\Operators\OperandInArithmeticPostDecrementRule + + - + class: PHPStan\Rules\Operators\OperandInArithmeticPostIncrementRule + + - + class: PHPStan\Rules\Operators\OperandInArithmeticPreDecrementRule + + - + class: PHPStan\Rules\Operators\OperandInArithmeticPreIncrementRule + + - + class: PHPStan\Rules\Operators\OperandInArithmeticUnaryMinusRule + + - + class: PHPStan\Rules\Operators\OperandInArithmeticUnaryPlusRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticAdditionRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticDivisionRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticExponentiationRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticModuloRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticMultiplicationRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticSubtractionRule + + - + class: PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsRule + + - + class: PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsCallableRule + + - + class: PHPStan\Rules\StrictCalls\StrictFunctionCallsRule + + - + class: PHPStan\Rules\SwitchConditions\MatchingTypeInSwitchCaseConditionRule + + - + class: PHPStan\Rules\VariableVariables\VariableMethodCallRule + + - + class: PHPStan\Rules\VariableVariables\VariableMethodCallableRule + + - + class: PHPStan\Rules\VariableVariables\VariableStaticMethodCallRule + + - + class: PHPStan\Rules\VariableVariables\VariableStaticMethodCallableRule + + - + class: PHPStan\Rules\VariableVariables\VariableStaticPropertyFetchRule + + - + class: PHPStan\Rules\VariableVariables\VariableVariablesRule diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanAndRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanAndRule.php new file mode 100644 index 000000000..eed19aeac --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanAndRule.php @@ -0,0 +1,59 @@ + + */ +class BooleanInBooleanAndRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return BooleanAndNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $originalNode = $node->getOriginalNode(); + $messages = []; + $nodeText = $originalNode->getOperatorSigil(); + $identifierType = $originalNode instanceof Node\Expr\BinaryOp\BooleanAnd ? 'booleanAnd' : 'logicalAnd'; + if (!$this->helper->passesAsBoolean($scope, $originalNode->left)) { + $leftType = $scope->getType($originalNode->left); + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in %s, %s given on the left side.', + $nodeText, + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier(sprintf('%s.leftNotBoolean', $identifierType))->build(); + } + + $rightScope = $node->getRightScope(); + if (!$this->helper->passesAsBoolean($rightScope, $originalNode->right)) { + $rightType = $rightScope->getType($originalNode->right); + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in %s, %s given on the right side.', + $nodeText, + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier(sprintf('%s.rightNotBoolean', $identifierType))->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanNotRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanNotRule.php new file mode 100644 index 000000000..5187cf57b --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanNotRule.php @@ -0,0 +1,47 @@ + + */ +class BooleanInBooleanNotRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return BooleanNot::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($this->helper->passesAsBoolean($scope, $node->expr)) { + return []; + } + + $expressionType = $scope->getType($node->expr); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in a negated boolean, %s given.', + $expressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('booleanNot.exprNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanOrRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanOrRule.php new file mode 100644 index 000000000..cb06a3416 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanOrRule.php @@ -0,0 +1,59 @@ + + */ +class BooleanInBooleanOrRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return BooleanOrNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $originalNode = $node->getOriginalNode(); + $messages = []; + $nodeText = $originalNode->getOperatorSigil(); + $identifierType = $originalNode instanceof Node\Expr\BinaryOp\BooleanOr ? 'booleanOr' : 'logicalOr'; + if (!$this->helper->passesAsBoolean($scope, $originalNode->left)) { + $leftType = $scope->getType($originalNode->left); + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in %s, %s given on the left side.', + $nodeText, + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier(sprintf('%s.leftNotBoolean', $identifierType))->build(); + } + + $rightScope = $node->getRightScope(); + if (!$this->helper->passesAsBoolean($rightScope, $originalNode->right)) { + $rightType = $rightScope->getType($originalNode->right); + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in %s, %s given on the right side.', + $nodeText, + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier(sprintf('%s.rightNotBoolean', $identifierType))->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInDoWhileConditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInDoWhileConditionRule.php new file mode 100644 index 000000000..d0db29629 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInDoWhileConditionRule.php @@ -0,0 +1,46 @@ + + */ +class BooleanInDoWhileConditionRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Node\Stmt\Do_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($this->helper->passesAsBoolean($scope, $node->cond)) { + return []; + } + + $conditionExpressionType = $scope->getType($node->cond); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in a do-while condition, %s given.', + $conditionExpressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('doWhile.condNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInElseIfConditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInElseIfConditionRule.php new file mode 100644 index 000000000..550e9857d --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInElseIfConditionRule.php @@ -0,0 +1,47 @@ + + */ +class BooleanInElseIfConditionRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return ElseIf_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($this->helper->passesAsBoolean($scope, $node->cond)) { + return []; + } + + $conditionExpressionType = $scope->getType($node->cond); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in an elseif condition, %s given.', + $conditionExpressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('elseif.condNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInIfConditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInIfConditionRule.php new file mode 100644 index 000000000..5c08894b4 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInIfConditionRule.php @@ -0,0 +1,47 @@ + + */ +class BooleanInIfConditionRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return If_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($this->helper->passesAsBoolean($scope, $node->cond)) { + return []; + } + + $conditionExpressionType = $scope->getType($node->cond); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in an if condition, %s given.', + $conditionExpressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('if.condNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInTernaryOperatorRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInTernaryOperatorRule.php new file mode 100644 index 000000000..4fe855a5c --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInTernaryOperatorRule.php @@ -0,0 +1,51 @@ + + */ +class BooleanInTernaryOperatorRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Ternary::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->if === null) { + return []; // elvis ?: + } + + if ($this->helper->passesAsBoolean($scope, $node->cond)) { + return []; + } + + $conditionExpressionType = $scope->getType($node->cond); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in a ternary operator condition, %s given.', + $conditionExpressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('ternary.condNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInWhileConditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInWhileConditionRule.php new file mode 100644 index 000000000..2f1661a63 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInWhileConditionRule.php @@ -0,0 +1,46 @@ + + */ +class BooleanInWhileConditionRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Node\Stmt\While_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($this->helper->passesAsBoolean($scope, $node->cond)) { + return []; + } + + $conditionExpressionType = $scope->getType($node->cond); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in a while condition, %s given.', + $conditionExpressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('while.condNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanRuleHelper.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanRuleHelper.php new file mode 100644 index 000000000..4ecba3299 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanRuleHelper.php @@ -0,0 +1,42 @@ +ruleLevelHelper = $ruleLevelHelper; + } + + public function passesAsBoolean(Scope $scope, Expr $expr): bool + { + $type = $scope->getType($expr); + if ($type instanceof MixedType) { + return !$type->isExplicitMixed(); + } + $typeToCheck = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $expr, + '', + static fn (Type $type): bool => $type->isBoolean()->yes(), + ); + $foundType = $typeToCheck->getType(); + if ($foundType instanceof ErrorType) { + return true; + } + + return $foundType->isBoolean()->yes(); + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Cast/UselessCastRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Cast/UselessCastRule.php new file mode 100644 index 000000000..662975055 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Cast/UselessCastRule.php @@ -0,0 +1,81 @@ + + */ +class UselessCastRule implements Rule +{ + + private bool $treatPhpDocTypesAsCertain; + + private bool $treatPhpDocTypesAsCertainTip; + + public function __construct( + bool $treatPhpDocTypesAsCertain, + bool $treatPhpDocTypesAsCertainTip + ) + { + $this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain; + $this->treatPhpDocTypesAsCertainTip = $treatPhpDocTypesAsCertainTip; + } + + public function getNodeType(): string + { + return Cast::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $castType = $scope->getType($node); + if ($castType instanceof ErrorType) { + return []; + } + $castType = $castType->generalize(GeneralizePrecision::lessSpecific()); + + if ($this->treatPhpDocTypesAsCertain) { + $expressionType = $scope->getType($node->expr); + } else { + $expressionType = $scope->getNativeType($node->expr); + } + if ($castType->isSuperTypeOf($expressionType)->yes()) { + $addTip = function (RuleErrorBuilder $ruleErrorBuilder) use ($scope, $node, $castType): RuleErrorBuilder { + if (!$this->treatPhpDocTypesAsCertain) { + return $ruleErrorBuilder; + } + + if (!$this->treatPhpDocTypesAsCertainTip) { + return $ruleErrorBuilder; + } + + $expressionTypeWithoutPhpDoc = $scope->getNativeType($node->expr); + if ($castType->isSuperTypeOf($expressionTypeWithoutPhpDoc)->yes()) { + return $ruleErrorBuilder; + } + + return $ruleErrorBuilder->treatPhpDocTypesAsCertainTip(); + }; + return [ + $addTip(RuleErrorBuilder::message(sprintf( + 'Casting to %s something that\'s already %s.', + $castType->describe(VerbosityLevel::typeOnly()), + $expressionType->describe(VerbosityLevel::typeOnly()), + )))->identifier('cast.useless')->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Classes/RequireParentConstructCallRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Classes/RequireParentConstructCallRule.php new file mode 100644 index 000000000..77595810a --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Classes/RequireParentConstructCallRule.php @@ -0,0 +1,142 @@ + + */ +class RequireParentConstructCallRule implements Rule +{ + + public function getNodeType(): string + { + return ClassMethod::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$scope->isInClass()) { + throw new ShouldNotHappenException(); + } + + if ($scope->isInTrait()) { + return []; + } + + if ($node->name->name !== '__construct') { + return []; + } + + if ($node->isAbstract()) { + return []; + } + + $classReflection = $scope->getClassReflection()->getNativeReflection(); + if ($classReflection->isInterface() || $classReflection->isAnonymous()) { + return []; + } + + if ($this->callsParentConstruct($node)) { + return []; + } + + $parentClass = $this->getParentConstructorClass($classReflection); + if ($parentClass !== false) { + return [ + RuleErrorBuilder::message(sprintf( + '%s::__construct() does not call parent constructor from %s.', + $classReflection->getName(), + $parentClass->getName(), + ))->identifier('constructor.missingParentCall')->build(), + ]; + } + + return []; + } + + private function callsParentConstruct(Node $parserNode): bool + { + if (!property_exists($parserNode, 'stmts')) { + return false; + } + + foreach ($parserNode->stmts as $statement) { + if ($statement instanceof Node\Stmt\Expression) { + $statement = $statement->expr; + } + + $statement = $this->ignoreErrorSuppression($statement); + if ($statement instanceof StaticCall) { + if ( + $statement->class instanceof Name + && ((string) $statement->class === 'parent') + && $statement->name instanceof Node\Identifier + && $statement->name->name === '__construct' + ) { + return true; + } + } else { + if ($this->callsParentConstruct($statement)) { + return true; + } + } + } + + return false; + } + + /** + * @param ReflectionClass|ReflectionEnum $classReflection + * @return ReflectionClass|false + */ + private function getParentConstructorClass($classReflection) + { + while ($classReflection->getParentClass() !== false) { + $constructor = $classReflection->getParentClass()->hasMethod('__construct') ? $classReflection->getParentClass()->getMethod('__construct') : null; + $constructorWithClassName = $classReflection->getParentClass()->hasMethod($classReflection->getParentClass()->getName()) ? $classReflection->getParentClass()->getMethod($classReflection->getParentClass()->getName()) : null; + if ( + ( + $constructor !== null + && $constructor->getDeclaringClass()->getName() === $classReflection->getParentClass()->getName() + && !$constructor->isAbstract() + && !$constructor->isPrivate() + && !$constructor->isDeprecated() + ) || ( + $constructorWithClassName !== null + && $constructorWithClassName->getDeclaringClass()->getName() === $classReflection->getParentClass()->getName() + && !$constructorWithClassName->isAbstract() + ) + ) { + return $classReflection->getParentClass(); + } + + $classReflection = $classReflection->getParentClass(); + } + + return false; + } + + private function ignoreErrorSuppression(Node $statement): Node + { + if ($statement instanceof Node\Expr\ErrorSuppress) { + + return $statement->expr; + } + + return $statement; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedBacktickRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedBacktickRule.php new file mode 100644 index 000000000..76e401cee --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedBacktickRule.php @@ -0,0 +1,31 @@ + + */ +class DisallowedBacktickRule implements Rule +{ + + public function getNodeType(): string + { + return ShellExec::class; + } + + public function processNode(Node $node, Scope $scope): array + { + return [ + RuleErrorBuilder::message('Backtick operator is not allowed. Use shell_exec() instead.') + ->identifier('backtick.notAllowed') + ->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedEmptyRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedEmptyRule.php new file mode 100644 index 000000000..d19f5ea20 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedEmptyRule.php @@ -0,0 +1,31 @@ + + */ +class DisallowedEmptyRule implements Rule +{ + + public function getNodeType(): string + { + return Empty_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + return [ + RuleErrorBuilder::message('Construct empty() is not allowed. Use more strict comparison.') + ->identifier('empty.notAllowed') + ->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedImplicitArrayCreationRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedImplicitArrayCreationRule.php new file mode 100644 index 000000000..cee777ce9 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedImplicitArrayCreationRule.php @@ -0,0 +1,65 @@ + + */ +class DisallowedImplicitArrayCreationRule implements Rule +{ + + public function getNodeType(): string + { + return Assign::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->var instanceof ArrayDimFetch) { + return []; + } + + $node = $node->var; + while ($node instanceof ArrayDimFetch) { + $node = $node->var; + } + + if (!$node instanceof Variable) { + return []; + } + + if (!is_string($node->name)) { + return []; + } + + $certainty = $scope->hasVariableType($node->name); + if ($certainty->no()) { + return [ + RuleErrorBuilder::message(sprintf('Implicit array creation is not allowed - variable $%s does not exist.', $node->name)) + ->identifier('variable.implicitArray') + ->build(), + ]; + } + + if ($certainty->maybe()) { + return [ + RuleErrorBuilder::message(sprintf('Implicit array creation is not allowed - variable $%s might not exist.', $node->name)) + ->identifier('variable.implicitArray') + ->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedLooseComparisonRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedLooseComparisonRule.php new file mode 100644 index 000000000..70b8514f1 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedLooseComparisonRule.php @@ -0,0 +1,48 @@ + + */ +class DisallowedLooseComparisonRule implements Rule +{ + + public function getNodeType(): string + { + return BinaryOp::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof Equal) { + return [ + RuleErrorBuilder::message( + 'Loose comparison via "==" is not allowed.', + )->tip('Use strict comparison via "===" instead.') + ->identifier('equal.notAllowed') + ->build(), + ]; + } + if ($node instanceof NotEqual) { + return [ + RuleErrorBuilder::message( + 'Loose comparison via "!=" is not allowed.', + )->tip('Use strict comparison via "!==" instead.') + ->identifier('notEqual.notAllowed') + ->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedShortTernaryRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedShortTernaryRule.php new file mode 100644 index 000000000..fac42790d --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedShortTernaryRule.php @@ -0,0 +1,35 @@ + + */ +class DisallowedShortTernaryRule implements Rule +{ + + public function getNodeType(): string + { + return Ternary::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->if !== null) { + return []; + } + + return [ + RuleErrorBuilder::message('Short ternary operator is not allowed. Use null coalesce operator if applicable or consider using long ternary.') + ->identifier('ternary.shortNotAllowed') + ->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForLoop/OverwriteVariablesWithForLoopInitRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForLoop/OverwriteVariablesWithForLoopInitRule.php new file mode 100644 index 000000000..f710474e2 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForLoop/OverwriteVariablesWithForLoopInitRule.php @@ -0,0 +1,77 @@ + + */ +class OverwriteVariablesWithForLoopInitRule implements Rule +{ + + public function getNodeType(): string + { + return For_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $errors = []; + foreach ($node->init as $expr) { + if (!($expr instanceof Assign)) { + continue; + } + + foreach ($this->checkValueVar($scope, $expr->var) as $error) { + $errors[] = $error; + } + } + + return $errors; + } + + /** + * @return list + */ + private function checkValueVar(Scope $scope, Expr $expr): array + { + $errors = []; + if ( + $expr instanceof Node\Expr\Variable + && is_string($expr->name) + && $scope->hasVariableType($expr->name)->yes() + ) { + $errors[] = RuleErrorBuilder::message(sprintf('For loop initial assignment overwrites variable $%s.', $expr->name)) + ->identifier('for.variableOverwrite') + ->build(); + } + + if ( + $expr instanceof Node\Expr\List_ + || $expr instanceof Node\Expr\Array_ + ) { + foreach ($expr->items as $item) { + if ($item === null) { + continue; + } + + foreach ($this->checkValueVar($scope, $item->value) as $error) { + $errors[] = $error; + } + } + } + + return $errors; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForeachLoop/OverwriteVariablesWithForeachRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForeachLoop/OverwriteVariablesWithForeachRule.php new file mode 100644 index 000000000..0cf620c37 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForeachLoop/OverwriteVariablesWithForeachRule.php @@ -0,0 +1,80 @@ + + */ +class OverwriteVariablesWithForeachRule implements Rule +{ + + public function getNodeType(): string + { + return Foreach_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $errors = []; + if ( + $node->keyVar instanceof Node\Expr\Variable + && is_string($node->keyVar->name) + && $scope->hasVariableType($node->keyVar->name)->yes() + ) { + $errors[] = RuleErrorBuilder::message(sprintf('Foreach overwrites $%s with its key variable.', $node->keyVar->name)) + ->identifier('foreach.keyOverwrite') + ->build(); + } + + foreach ($this->checkValueVar($scope, $node->valueVar) as $error) { + $errors[] = $error; + } + + return $errors; + } + + /** + * @return list + */ + private function checkValueVar(Scope $scope, Expr $expr): array + { + $errors = []; + if ( + $expr instanceof Node\Expr\Variable + && is_string($expr->name) + && $scope->hasVariableType($expr->name)->yes() + ) { + $errors[] = RuleErrorBuilder::message(sprintf('Foreach overwrites $%s with its value variable.', $expr->name)) + ->identifier('foreach.valueOverwrite') + ->build(); + } + + if ( + $expr instanceof Node\Expr\List_ + || $expr instanceof Node\Expr\Array_ + ) { + foreach ($expr->items as $item) { + if ($item === null) { + continue; + } + + foreach ($this->checkValueVar($scope, $item->value) as $error) { + $errors[] = $error; + } + } + } + + return $errors; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ArrayFilterStrictRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ArrayFilterStrictRule.php new file mode 100644 index 000000000..6760c7d56 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ArrayFilterStrictRule.php @@ -0,0 +1,162 @@ + + */ +class ArrayFilterStrictRule implements Rule +{ + + private ReflectionProvider $reflectionProvider; + + private bool $treatPhpDocTypesAsCertain; + + private bool $checkNullables; + + private bool $treatPhpDocTypesAsCertainTip; + + public function __construct( + ReflectionProvider $reflectionProvider, + bool $treatPhpDocTypesAsCertain, + bool $checkNullables, + bool $treatPhpDocTypesAsCertainTip + ) + { + $this->reflectionProvider = $reflectionProvider; + $this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain; + $this->checkNullables = $checkNullables; + $this->treatPhpDocTypesAsCertainTip = $treatPhpDocTypesAsCertainTip; + } + + public function getNodeType(): string + { + return FuncCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Name) { + return []; + } + + if (!$this->reflectionProvider->hasFunction($node->name, $scope)) { + return []; + } + + $functionReflection = $this->reflectionProvider->getFunction($node->name, $scope); + + if ($functionReflection->getName() !== 'array_filter') { + return []; + } + + $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( + $scope, + $node->getArgs(), + $functionReflection->getVariants(), + $functionReflection->getNamedArgumentsVariants(), + ); + + $normalizedFuncCall = ArgumentsNormalizer::reorderFuncArguments($parametersAcceptor, $node); + + if ($normalizedFuncCall === null) { + return []; + } + + $args = $normalizedFuncCall->getArgs(); + if (count($args) === 0) { + return []; + } + + if (count($args) === 1) { + $arrayType = $scope->getType($args[0]->value); + $itemType = $arrayType->getIterableValueType(); + if ($itemType instanceof UnionType) { + $hasTruthy = false; + $hasFalsey = false; + foreach ($itemType->getTypes() as $innerType) { + $booleanType = $innerType->toBoolean(); + if ($booleanType->isTrue()->yes()) { + $hasTruthy = true; + continue; + } + if ($booleanType->isFalse()->yes()) { + $hasFalsey = true; + continue; + } + + $hasTruthy = false; + $hasFalsey = false; + break; + } + + if ($hasTruthy && $hasFalsey) { + return []; + } + } elseif ($itemType->isBoolean()->yes()) { + return []; + } elseif ($itemType->isArray()->yes()) { + return []; + } + + return [ + RuleErrorBuilder::message('Call to function array_filter() requires parameter #2 to be passed to avoid loose comparison semantics.') + ->identifier('arrayFilter.strict') + ->build(), + ]; + } + + $nativeCallbackType = $scope->getNativeType($args[1]->value); + + if ($this->treatPhpDocTypesAsCertain) { + $callbackType = $scope->getType($args[1]->value); + } else { + $callbackType = $nativeCallbackType; + } + + if ($this->isCallbackTypeNull($callbackType)) { + $message = 'Parameter #2 of array_filter() cannot be null to avoid loose comparison semantics (%s given).'; + $errorBuilder = RuleErrorBuilder::message(sprintf( + $message, + $callbackType->describe(VerbosityLevel::typeOnly()), + ))->identifier('arrayFilter.strict'); + + if ($this->treatPhpDocTypesAsCertainTip && !$this->isCallbackTypeNull($nativeCallbackType) && $this->treatPhpDocTypesAsCertain) { + $errorBuilder->treatPhpDocTypesAsCertainTip(); + } + + return [$errorBuilder->build()]; + } + + return []; + } + + private function isCallbackTypeNull(Type $callbackType): bool + { + if ($callbackType->isNull()->yes()) { + return true; + } + + if ($callbackType->isNull()->no()) { + return false; + } + + return $this->checkNullables; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ClosureUsesThisRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ClosureUsesThisRule.php new file mode 100644 index 000000000..4f41d26b4 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ClosureUsesThisRule.php @@ -0,0 +1,52 @@ + + */ +class ClosureUsesThisRule implements Rule +{ + + public function getNodeType(): string + { + return Node\Expr\Closure::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->static) { + return []; + } + + if ($scope->isInClosureBind()) { + return []; + } + + $messages = []; + foreach ($node->uses as $closureUse) { + $varType = $scope->getType($closureUse->var); + if (!is_string($closureUse->var->name)) { + continue; + } + if (!$varType instanceof ThisType) { + continue; + } + + $messages[] = RuleErrorBuilder::message(sprintf('Anonymous function uses $this assigned to variable $%s. Use $this directly in the function body.', $closureUse->var->name)) + ->line($closureUse->getStartLine()) + ->identifier('closure.useThis') + ->build(); + } + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorMethodCallRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorMethodCallRule.php new file mode 100644 index 000000000..1dba6ed6d --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorMethodCallRule.php @@ -0,0 +1,34 @@ + + */ +final class IllegalConstructorMethodCallRule implements Rule +{ + + public function getNodeType(): string + { + return Node\Expr\MethodCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Node\Identifier || $node->name->toLowerString() !== '__construct') { + return []; + } + + return [ + RuleErrorBuilder::message('Call to __construct() on an existing object is not allowed.') + ->identifier('constructor.call') + ->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorStaticCallRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorStaticCallRule.php new file mode 100644 index 000000000..fa747d6a2 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorStaticCallRule.php @@ -0,0 +1,92 @@ + + */ +final class IllegalConstructorStaticCallRule implements Rule +{ + + public function getNodeType(): string + { + return Node\Expr\StaticCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Node\Identifier || $node->name->toLowerString() !== '__construct') { + return []; + } + + if ($this->isCollectCallingConstructor($node, $scope)) { + return []; + } + + return [ + RuleErrorBuilder::message('Static call to __construct() is only allowed on a parent class in the constructor.') + ->identifier('constructor.call') + ->build(), + ]; + } + + private function isCollectCallingConstructor(Node\Expr\StaticCall $node, Scope $scope): bool + { + // __construct should be called from inside constructor + if ($scope->getFunction() === null) { + return false; + } + + if ($scope->getFunction()->getName() !== '__construct') { + if (!$this->isInRenamedTraitConstructor($scope)) { + return false; + } + } + + if (!$scope->isInClass()) { + return false; + } + + if (!$node->class instanceof Node\Name) { + return false; + } + + $parentClasses = array_map(static fn (string $name) => strtolower($name), $scope->getClassReflection()->getParentClassesNames()); + + return in_array(strtolower($scope->resolveName($node->class)), $parentClasses, true); + } + + private function isInRenamedTraitConstructor(Scope $scope): bool + { + if (!$scope->isInClass()) { + return false; + } + + if (!$scope->isInTrait()) { + return false; + } + + if ($scope->getFunction() === null) { + return false; + } + + $traitAliases = $scope->getClassReflection()->getNativeReflection()->getTraitAliases(); + $functionName = $scope->getFunction()->getName(); + if (!array_key_exists($functionName, $traitAliases)) { + return false; + } + + return $traitAliases[$functionName] === sprintf('%s::%s', $scope->getTraitReflection()->getName(), '__construct'); + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/WrongCaseOfInheritedMethodRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/WrongCaseOfInheritedMethodRule.php new file mode 100644 index 000000000..5f800e502 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/WrongCaseOfInheritedMethodRule.php @@ -0,0 +1,86 @@ + + */ +class WrongCaseOfInheritedMethodRule implements Rule +{ + + public function getNodeType(): string + { + return InClassMethodNode::class; + } + + public function processNode( + Node $node, + Scope $scope + ): array + { + $methodReflection = $node->getMethodReflection(); + $declaringClass = $methodReflection->getDeclaringClass(); + + $messages = []; + if ($declaringClass->getParentClass() !== null) { + $parentMessage = $this->findMethod( + $declaringClass, + $declaringClass->getParentClass(), + $methodReflection->getName(), + ); + if ($parentMessage !== null) { + $messages[] = $parentMessage; + } + } + + foreach ($declaringClass->getInterfaces() as $interface) { + $interfaceMessage = $this->findMethod( + $declaringClass, + $interface, + $methodReflection->getName(), + ); + if ($interfaceMessage === null) { + continue; + } + + $messages[] = $interfaceMessage; + } + + return $messages; + } + + private function findMethod( + ClassReflection $declaringClass, + ClassReflection $classReflection, + string $methodName + ): ?IdentifierRuleError + { + if (!$classReflection->hasNativeMethod($methodName)) { + return null; + } + + $parentMethod = $classReflection->getNativeMethod($methodName); + if ($parentMethod->getName() === $methodName) { + return null; + } + + return RuleErrorBuilder::message(sprintf( + 'Method %s::%s() does not match %s method name: %s::%s().', + $declaringClass->getDisplayName(), + $methodName, + $classReflection->isInterface() ? 'interface' : 'parent', + $classReflection->getDisplayName(), + $parentMethod->getName(), + ))->identifier('method.nameCase')->build(); + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticIncrementOrDecrementRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticIncrementOrDecrementRule.php new file mode 100644 index 000000000..4e87a8858 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticIncrementOrDecrementRule.php @@ -0,0 +1,61 @@ + + */ +abstract class OperandInArithmeticIncrementOrDecrementRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + /** + * @param TNodeType $node + */ + public function processNode(Node $node, Scope $scope): array + { + $messages = []; + $varType = $scope->getType($node->var); + + if ( + ($node instanceof PreInc || $node instanceof PostInc) + && !$this->helper->isValidForIncrement($scope, $node->var) + || ($node instanceof PreDec || $node instanceof PostDec) + && !$this->helper->isValidForDecrement($scope, $node->var) + ) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in %s, %s given.', + $this->describeOperation(), + $varType->describe(VerbosityLevel::typeOnly()), + ))->identifier(sprintf('%s.nonNumeric', $this->getIdentifier()))->build(); + } + + return $messages; + } + + abstract protected function describeOperation(): string; + + /** + * @return 'preInc'|'postInc'|'preDec'|'postDec' + */ + abstract protected function getIdentifier(): string; + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostDecrementRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostDecrementRule.php new file mode 100644 index 000000000..d0e08099f --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostDecrementRule.php @@ -0,0 +1,28 @@ + + */ +class OperandInArithmeticPostDecrementRule extends OperandInArithmeticIncrementOrDecrementRule +{ + + public function getNodeType(): string + { + return PostDec::class; + } + + protected function describeOperation(): string + { + return 'post-decrement'; + } + + protected function getIdentifier(): string + { + return 'postDec'; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostIncrementRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostIncrementRule.php new file mode 100644 index 000000000..400d82889 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostIncrementRule.php @@ -0,0 +1,28 @@ + + */ +class OperandInArithmeticPostIncrementRule extends OperandInArithmeticIncrementOrDecrementRule +{ + + public function getNodeType(): string + { + return PostInc::class; + } + + protected function describeOperation(): string + { + return 'post-increment'; + } + + protected function getIdentifier(): string + { + return 'postInc'; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreDecrementRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreDecrementRule.php new file mode 100644 index 000000000..9d5835600 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreDecrementRule.php @@ -0,0 +1,28 @@ + + */ +class OperandInArithmeticPreDecrementRule extends OperandInArithmeticIncrementOrDecrementRule +{ + + public function getNodeType(): string + { + return PreDec::class; + } + + protected function describeOperation(): string + { + return 'pre-decrement'; + } + + protected function getIdentifier(): string + { + return 'preDec'; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreIncrementRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreIncrementRule.php new file mode 100644 index 000000000..d5d81f2a5 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreIncrementRule.php @@ -0,0 +1,28 @@ + + */ +class OperandInArithmeticPreIncrementRule extends OperandInArithmeticIncrementOrDecrementRule +{ + + public function getNodeType(): string + { + return PreInc::class; + } + + protected function describeOperation(): string + { + return 'pre-increment'; + } + + protected function getIdentifier(): string + { + return 'preInc'; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticUnaryMinusRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticUnaryMinusRule.php new file mode 100644 index 000000000..d3db7df51 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticUnaryMinusRule.php @@ -0,0 +1,47 @@ + + */ +class OperandInArithmeticUnaryMinusRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return UnaryMinus::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $messages = []; + + if (!$this->helper->isValidForArithmeticOperation($scope, $node->expr)) { + $varType = $scope->getType($node->expr); + + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in unary -, %s given.', + $varType->describe(VerbosityLevel::typeOnly()), + ))->identifier('unaryMinus.nonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticUnaryPlusRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticUnaryPlusRule.php new file mode 100644 index 000000000..78313d8c1 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticUnaryPlusRule.php @@ -0,0 +1,47 @@ + + */ +class OperandInArithmeticUnaryPlusRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return UnaryPlus::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $messages = []; + + if (!$this->helper->isValidForArithmeticOperation($scope, $node->expr)) { + $varType = $scope->getType($node->expr); + + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in unary +, %s given.', + $varType->describe(VerbosityLevel::typeOnly()), + ))->identifier('unaryPlus.nonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticAdditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticAdditionRule.php new file mode 100644 index 000000000..80de1463b --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticAdditionRule.php @@ -0,0 +1,69 @@ + + */ +class OperandsInArithmeticAdditionRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpPlus) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpPlus) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $leftType = $scope->getType($left); + $rightType = $scope->getType($right); + if (count($leftType->getArrays()) > 0 && count($rightType->getArrays()) > 0) { + return []; + } + + $messages = []; + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in +, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('plus.leftNonNumeric')->build(); + } + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in +, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('plus.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticDivisionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticDivisionRule.php new file mode 100644 index 000000000..e95b3d624 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticDivisionRule.php @@ -0,0 +1,65 @@ + + */ +class OperandsInArithmeticDivisionRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpDiv) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpDiv) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $messages = []; + $leftType = $scope->getType($left); + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in /, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('div.leftNonNumeric')->build(); + } + + $rightType = $scope->getType($right); + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in /, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('div.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticExponentiationRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticExponentiationRule.php new file mode 100644 index 000000000..1992b84a7 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticExponentiationRule.php @@ -0,0 +1,65 @@ + + */ +class OperandsInArithmeticExponentiationRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpPow) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpPow) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $messages = []; + $leftType = $scope->getType($left); + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in **, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('pow.leftNonNumeric')->build(); + } + + $rightType = $scope->getType($right); + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in **, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('pow.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticModuloRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticModuloRule.php new file mode 100644 index 000000000..5b5f3c326 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticModuloRule.php @@ -0,0 +1,65 @@ + + */ +class OperandsInArithmeticModuloRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpMod) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpMod) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $messages = []; + $leftType = $scope->getType($left); + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in %%, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('mod.leftNonNumeric')->build(); + } + + $rightType = $scope->getType($right); + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in %%, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('mod.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticMultiplicationRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticMultiplicationRule.php new file mode 100644 index 000000000..353df4c67 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticMultiplicationRule.php @@ -0,0 +1,65 @@ + + */ +class OperandsInArithmeticMultiplicationRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpMul) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpMul) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $messages = []; + $leftType = $scope->getType($left); + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in *, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('mul.leftNonNumeric')->build(); + } + + $rightType = $scope->getType($right); + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in *, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('mul.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticSubtractionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticSubtractionRule.php new file mode 100644 index 000000000..5559d60fe --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticSubtractionRule.php @@ -0,0 +1,65 @@ + + */ +class OperandsInArithmeticSubtractionRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpMinus) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpMinus) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $messages = []; + $leftType = $scope->getType($left); + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in -, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('minus.leftNonNumeric')->build(); + } + + $rightType = $scope->getType($right); + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in -, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('minus.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperatorRuleHelper.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperatorRuleHelper.php new file mode 100644 index 000000000..6de54cab1 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperatorRuleHelper.php @@ -0,0 +1,92 @@ +ruleLevelHelper = $ruleLevelHelper; + } + + public function isValidForArithmeticOperation(Scope $scope, Expr $expr): bool + { + $type = $scope->getType($expr); + if ($type instanceof MixedType) { + return true; + } + + // already reported by PHPStan core + if ($type->toNumber() instanceof ErrorType) { + return true; + } + + return $this->isSubtypeOfNumber($scope, $expr); + } + + public function isValidForIncrement(Scope $scope, Expr $expr): bool + { + $type = $scope->getType($expr); + if ($type instanceof MixedType) { + return true; + } + + if ($type->isString()->yes()) { + // Because `$a = 'a'; $a++;` is valid + return true; + } + + return $this->isSubtypeOfNumber($scope, $expr); + } + + public function isValidForDecrement(Scope $scope, Expr $expr): bool + { + $type = $scope->getType($expr); + if ($type instanceof MixedType) { + return true; + } + + return $this->isSubtypeOfNumber($scope, $expr); + } + + private function isSubtypeOfNumber(Scope $scope, Expr $expr): bool + { + $acceptedType = new UnionType([new IntegerType(), new FloatType(), new IntersectionType([new StringType(), new AccessoryNumericStringType()])]); + + $type = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $expr, + '', + static fn (Type $type): bool => $acceptedType->isSuperTypeOf($type)->yes(), + )->getType(); + + if ($type instanceof ErrorType) { + return true; + } + + $isSuperType = $acceptedType->isSuperTypeOf($type); + if ($type instanceof BenevolentUnionType) { + return !$isSuperType->no(); + } + + return $isSuperType->yes(); + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsCallableRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsCallableRule.php new file mode 100644 index 000000000..492aa604c --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsCallableRule.php @@ -0,0 +1,65 @@ + + */ +class DynamicCallOnStaticMethodsCallableRule implements Rule +{ + + private RuleLevelHelper $ruleLevelHelper; + + public function __construct(RuleLevelHelper $ruleLevelHelper) + { + $this->ruleLevelHelper = $ruleLevelHelper; + } + + public function getNodeType(): string + { + return MethodCallableNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->getName() instanceof Node\Identifier) { + return []; + } + + $name = $node->getName()->name; + $type = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $node->getVar(), + '', + static fn (Type $type): bool => $type->canCallMethods()->yes() && $type->hasMethod($name)->yes(), + )->getType(); + + if ($type instanceof ErrorType || !$type->canCallMethods()->yes() || !$type->hasMethod($name)->yes()) { + return []; + } + + $methodReflection = $type->getMethod($name, $scope); + if ($methodReflection->isStatic()) { + return [ + RuleErrorBuilder::message(sprintf( + 'Dynamic call to static method %s::%s().', + $methodReflection->getDeclaringClass()->getDisplayName(), + $methodReflection->getName(), + ))->identifier('staticMethod.dynamicCall')->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsRule.php new file mode 100644 index 000000000..c0ae18b34 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsRule.php @@ -0,0 +1,76 @@ + + */ +class DynamicCallOnStaticMethodsRule implements Rule +{ + + private RuleLevelHelper $ruleLevelHelper; + + public function __construct(RuleLevelHelper $ruleLevelHelper) + { + $this->ruleLevelHelper = $ruleLevelHelper; + } + + public function getNodeType(): string + { + return MethodCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Node\Identifier) { + return []; + } + + $name = $node->name->name; + $type = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $node->var, + '', + static fn (Type $type): bool => $type->canCallMethods()->yes() && $type->hasMethod($name)->yes(), + )->getType(); + + if ($type instanceof ErrorType || !$type->canCallMethods()->yes() || !$type->hasMethod($name)->yes()) { + return []; + } + + $methodReflection = $type->getMethod($name, $scope); + if ($methodReflection->isStatic()) { + $prototype = $methodReflection->getPrototype(); + if (in_array($prototype->getDeclaringClass()->getName(), [ + TypeInferenceTestCase::class, + PHPStanTestCase::class, + ], true)) { + return []; + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Dynamic call to static method %s::%s().', + $methodReflection->getDeclaringClass()->getDisplayName(), + $methodReflection->getName(), + ))->identifier('staticMethod.dynamicCall')->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/StrictFunctionCallsRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/StrictFunctionCallsRule.php new file mode 100644 index 000000000..f959fc913 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/StrictFunctionCallsRule.php @@ -0,0 +1,96 @@ + + */ +class StrictFunctionCallsRule implements Rule +{ + + /** @var int[] */ + private array $functionArguments = [ + 'in_array' => 2, + 'array_search' => 2, + 'base64_decode' => 1, + 'array_keys' => 2, + ]; + + private ReflectionProvider $reflectionProvider; + + public function __construct(ReflectionProvider $reflectionProvider) + { + $this->reflectionProvider = $reflectionProvider; + } + + public function getNodeType(): string + { + return FuncCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Name) { + return []; + } + + if (!$this->reflectionProvider->hasFunction($node->name, $scope)) { + return []; + } + + $function = $this->reflectionProvider->getFunction($node->name, $scope); + $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs($scope, $node->getArgs(), $function->getVariants()); + $node = ArgumentsNormalizer::reorderFuncArguments($parametersAcceptor, $node); + if ($node === null) { + return []; + } + $functionName = strtolower($function->getName()); + if (!array_key_exists($functionName, $this->functionArguments)) { + return []; + } + + if ($functionName === 'array_keys' && !array_key_exists(1, $node->getArgs())) { + return []; + } + + $argumentPosition = $this->functionArguments[$functionName]; + if (!array_key_exists($argumentPosition, $node->getArgs())) { + return [ + RuleErrorBuilder::message(sprintf( + 'Call to function %s() requires parameter #%d to be set.', + $functionName, + $argumentPosition + 1, + ))->identifier('function.strict')->build(), + ]; + } + + $argumentType = $scope->getType($node->getArgs()[$argumentPosition]->value); + $trueType = new ConstantBooleanType(true); + if (!$trueType->isSuperTypeOf($argumentType)->yes()) { + return [ + RuleErrorBuilder::message(sprintf( + 'Call to function %s() requires parameter #%d to be true.', + $functionName, + $argumentPosition + 1, + ))->identifier('function.strict')->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/SwitchConditions/MatchingTypeInSwitchCaseConditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/SwitchConditions/MatchingTypeInSwitchCaseConditionRule.php new file mode 100644 index 000000000..ba7c92f3a --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/SwitchConditions/MatchingTypeInSwitchCaseConditionRule.php @@ -0,0 +1,60 @@ + + */ +class MatchingTypeInSwitchCaseConditionRule implements Rule +{ + + private Printer $printer; + + public function __construct(Printer $printer) + { + $this->printer = $printer; + } + + public function getNodeType(): string + { + return Switch_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $messages = []; + $conditionType = $scope->getType($node->cond); + foreach ($node->cases as $case) { + if ($case->cond === null) { + continue; + } + + $caseType = $scope->getType($case->cond); + if (!$conditionType->isSuperTypeOf($caseType)->no()) { + continue; + } + + $messages[] = RuleErrorBuilder::message(sprintf( + 'Switch condition type (%s) does not match case condition %s (%s).', + $conditionType->describe(VerbosityLevel::value()), + $this->printer->prettyPrintExpr($case->cond), + $caseType->describe(VerbosityLevel::typeOnly()), + )) + ->line($case->getStartLine()) + ->identifier('switch.type') + ->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallRule.php new file mode 100644 index 000000000..d55fc7894 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallRule.php @@ -0,0 +1,38 @@ + + */ +class VariableMethodCallRule implements Rule +{ + + public function getNodeType(): string + { + return MethodCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->name instanceof Node\Identifier) { + return []; + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable method call on %s.', + $scope->getType($node->var)->describe(VerbosityLevel::typeOnly()), + ))->identifier('method.dynamicName')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallableRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallableRule.php new file mode 100644 index 000000000..dd891a9e8 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallableRule.php @@ -0,0 +1,38 @@ + + */ +class VariableMethodCallableRule implements Rule +{ + + public function getNodeType(): string + { + return MethodCallableNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->getName() instanceof Node\Identifier) { + return []; + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable method call on %s.', + $scope->getType($node->getVar())->describe(VerbosityLevel::typeOnly()), + ))->identifier('method.dynamicName')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariablePropertyFetchRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariablePropertyFetchRule.php new file mode 100644 index 000000000..760bff697 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariablePropertyFetchRule.php @@ -0,0 +1,94 @@ + + */ +class VariablePropertyFetchRule implements Rule +{ + + private ReflectionProvider $reflectionProvider; + + /** @var string[] */ + private array $universalObjectCratesClasses; + + /** + * @param string[] $universalObjectCratesClasses + */ + public function __construct(ReflectionProvider $reflectionProvider, array $universalObjectCratesClasses) + { + $this->reflectionProvider = $reflectionProvider; + $this->universalObjectCratesClasses = $universalObjectCratesClasses; + } + + public function getNodeType(): string + { + return PropertyFetch::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->name instanceof Node\Identifier) { + return []; + } + + $fetchedOnType = $scope->getType($node->var); + foreach ($fetchedOnType->getObjectClassNames() as $referencedClass) { + if (!$this->reflectionProvider->hasClass($referencedClass)) { + continue; + } + + $classReflection = $this->reflectionProvider->getClass($referencedClass); + if ( + $this->isUniversalObjectCrate($classReflection) + || $this->isSimpleXMLElement($classReflection) + ) { + return []; + } + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable property access on %s.', + $fetchedOnType->describe(VerbosityLevel::typeOnly()), + ))->identifier('property.dynamicName')->build(), + ]; + } + + private function isSimpleXMLElement( + ClassReflection $classReflection + ): bool + { + return $classReflection->is(SimpleXMLElement::class); + } + + private function isUniversalObjectCrate( + ClassReflection $classReflection + ): bool + { + foreach ($this->universalObjectCratesClasses as $className) { + if (!$this->reflectionProvider->hasClass($className)) { + continue; + } + + if ($classReflection->is($className)) { + return true; + } + } + + return false; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallRule.php new file mode 100644 index 000000000..963f01d09 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallRule.php @@ -0,0 +1,44 @@ + + */ +class VariableStaticMethodCallRule implements Rule +{ + + public function getNodeType(): string + { + return StaticCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->name instanceof Node\Identifier) { + return []; + } + + if ($node->class instanceof Node\Name) { + $methodCalledOn = $scope->resolveName($node->class); + } else { + $methodCalledOn = $scope->getType($node->class)->describe(VerbosityLevel::typeOnly()); + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable static method call on %s.', + $methodCalledOn, + ))->identifier('staticMethod.dynamicName')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallableRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallableRule.php new file mode 100644 index 000000000..2cfebaca8 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallableRule.php @@ -0,0 +1,44 @@ + + */ +class VariableStaticMethodCallableRule implements Rule +{ + + public function getNodeType(): string + { + return StaticMethodCallableNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->getName() instanceof Node\Identifier) { + return []; + } + + if ($node->getClass() instanceof Node\Name) { + $methodCalledOn = $scope->resolveName($node->getClass()); + } else { + $methodCalledOn = $scope->getType($node->getClass())->describe(VerbosityLevel::typeOnly()); + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable static method call on %s.', + $methodCalledOn, + ))->identifier('staticMethod.dynamicName')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticPropertyFetchRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticPropertyFetchRule.php new file mode 100644 index 000000000..bc4759928 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticPropertyFetchRule.php @@ -0,0 +1,44 @@ + + */ +class VariableStaticPropertyFetchRule implements Rule +{ + + public function getNodeType(): string + { + return StaticPropertyFetch::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->name instanceof Node\Identifier) { + return []; + } + + if ($node->class instanceof Node\Name) { + $propertyAccessedOn = $scope->resolveName($node->class); + } else { + $propertyAccessedOn = $scope->getType($node->class)->describe(VerbosityLevel::typeOnly()); + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable static property access on %s.', + $propertyAccessedOn, + ))->identifier('staticProperty.dynamicName')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableVariablesRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableVariablesRule.php new file mode 100644 index 000000000..f78e4ef42 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableVariablesRule.php @@ -0,0 +1,36 @@ + + */ +class VariableVariablesRule implements Rule +{ + + public function getNodeType(): string + { + return Variable::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (is_string($node->name)) { + return []; + } + + return [ + RuleErrorBuilder::message('Variable variables are not allowed.') + ->identifier('variable.dynamicName') + ->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan/LICENSE b/tools/.phpstan/vendor/phpstan/phpstan/LICENSE new file mode 100644 index 000000000..e5f34e607 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2016 Ondřej Mirtes +Copyright (c) 2025 PHPStan s.r.o. + +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. diff --git a/tools/.phpstan/vendor/phpstan/phpstan/README.md b/tools/.phpstan/vendor/phpstan/phpstan/README.md new file mode 100644 index 000000000..f4af4753a --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/README.md @@ -0,0 +1,120 @@ +

    PHPStan - PHP Static Analysis Tool

    + +

    + PHPStan +

    + +

    + Build Status + Latest Stable Version + Total Downloads + License + PHPStan Enabled +

    + +------ + +PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs +even before you write tests for the code. It moves PHP closer to compiled languages in the sense that the correctness of each line of the code +can be checked before you run the actual line. + +**[Read more about PHPStan »](https://phpstan.org/)** + +**[Try out PHPStan on the on-line playground! »](https://phpstan.org/try)** + +## Sponsors + +Want your logo here? [Learn more »](https://phpstan.org/sponsor) + +### Gold Sponsors + +Matt Mullenweg +Mojam + +

    + +### Silver Sponsors + +ShipMonk +Shopware + +

    + +### Bronze Sponsors + +TheCodingMachine +    +Private Packagist +
    +CDN77 +    +Blackfire.io +
    +iO +    +Fame Helsinki +
    +Belsimpel +    +Togetter +
    +RightCapital +    +ContentKing +
    +ZOL +    +EdgeNext +
    +Route4Me: Route Optimizer and Route Planner Software +    +Craft CMS +
    +TicketSwap +    +campoint AG +
    +Crisp.nl +    +Inviqa +
    +Shoptet +    + + + +[**You can sponsor my open-source work on PHPStan through GitHub Sponsors and also directly.**](https://phpstan.org/sponsor) + +One-time donations [through Revolut.me](https://revolut.me/ondrejmirtes) are also accepted. To request an invoice, [contact me](mailto:ondrej@mirtes.cz) through e-mail. + +## Documentation + +All the documentation lives on the [phpstan.org website](https://phpstan.org/): + +* [Getting Started & User Guide](https://phpstan.org/user-guide/getting-started) +* [Config Reference](https://phpstan.org/config-reference) +* [PHPDocs Basics](https://phpstan.org/writing-php-code/phpdocs-basics) & [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types) +* [Extension Library](https://phpstan.org/user-guide/extension-library) +* [Developing Extensions](https://phpstan.org/developing-extensions/extension-types) +* [API Reference](https://apiref.phpstan.org/) + +## PHPStan Pro + +PHPStan Pro is a paid add-on on top of open-source PHPStan Static Analysis Tool with these premium features: + +* Web UI for browsing found errors, you can click and open your editor of choice on the offending line. +* Continuous analysis (watch mode): scans changed files in the background, refreshes the UI automatically. + +Try it on PHPStan 0.12.45 or later by running it with the `--pro` option. You can create an account either by following the on-screen instructions, or by visiting [account.phpstan.com](https://account.phpstan.com/). + +After 30-day free trial period it costs 7 EUR for individuals monthly, 70 EUR for teams (up to 25 members). By paying for PHPStan Pro, you're supporting the development of open-source PHPStan. + +You can read more about it on [PHPStan's website](https://phpstan.org/blog/introducing-phpstan-pro). + +## Code of Conduct + +This project adheres to a [Contributor Code of Conduct](https://github.com/phpstan/phpstan/blob/master/CODE_OF_CONDUCT.md). By participating in this project and its community, you are expected to uphold this code. + +## Contributing + +Any contributions are welcome. PHPStan's source code open to pull requests lives at [`phpstan/phpstan-src`](https://github.com/phpstan/phpstan-src). diff --git a/tools/.phpstan/vendor/phpstan/phpstan/UPGRADING.md b/tools/.phpstan/vendor/phpstan/phpstan/UPGRADING.md new file mode 100644 index 000000000..522949096 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/UPGRADING.md @@ -0,0 +1,338 @@ +Upgrading from PHPStan 1.x to 2.0 +================================= + +## PHP version requirements + +PHPStan now requires PHP 7.4 or newer to run. + +## Upgrading guide for end users + +The best way to get ready for upgrade to PHPStan 2.0 is to update to the **latest PHPStan 1.12 release** +and enable [**Bleeding Edge**](https://phpstan.org/blog/what-is-bleeding-edge). This will enable the new rules and behaviours that 2.0 turns on for all users. + +Also make sure to install and enable [`phpstan/phpstan-deprecation-rules`](https://github.com/phpstan/phpstan-deprecation-rules). + +Once you get to a green build with no deprecations showed on latest PHPStan 1.12.x with Bleeding Edge enabled, you can update all your related PHPStan dependencies to 2.0 in `composer.json`: + +```json +"require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-doctrine": "^2.0", + "phpstan/phpstan-nette": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpstan/phpstan-symfony": "^2.0", + "phpstan/phpstan-webmozart-assert": "^2.0", + ... +} +``` + +Don't forget to update [3rd party PHPStan extensions](https://phpstan.org/user-guide/extension-library) as well. + +After changing your `composer.json`, run `composer update 'phpstan/*' -W`. + +It's up to you whether you go through the new reported errors or if you just put them all to the [baseline](https://phpstan.org/user-guide/baseline) ;) Everyone who's on PHPStan 1.12 should be able to upgrade to PHPStan 2.0. + +### Noteworthy changes to code analysis + +* [**Enhancements in handling parameters passed by reference**](https://phpstan.org/blog/enhancements-in-handling-parameters-passed-by-reference) +* [**Validate inline PHPDoc `@var` tag type**](https://phpstan.org/blog/phpstan-1-10-comes-with-lie-detector#validate-inline-phpdoc-%40var-tag-type) +* [**List type enforced**](https://phpstan.org/blog/phpstan-1-9-0-with-phpdoc-asserts-list-type#list-type) +* **Always `true` conditions always reported**: previously reported only with phpstan-strict-rules, this is now always reported. + +### Removed option `checkMissingIterableValueType` + +It's strongly recommended to add the missing array typehints. + +If you want to continue ignoring missing typehints from arrays, add `missingType.iterableValue` error identifier to your `ignoreErrors`: + +```neon +parameters: + ignoreErrors: + - + identifier: missingType.iterableValue +``` + +### Removed option `checkGenericClassInNonGenericObjectType` + +It's strongly recommended to add the missing generic typehints. + +If you want to continue ignoring missing typehints from generics, add `missingType.generics` error identifier to your `ignoreErrors`: + +```neon +parameters: + ignoreErrors: + - + identifier: missingType.generics +``` + +### Removed `checkAlwaysTrue*` options + +These options have been removed because PHPStan now always behaves as if these were set to `true`: + +* `checkAlwaysTrueCheckTypeFunctionCall` +* `checkAlwaysTrueInstanceof` +* `checkAlwaysTrueStrictComparison` +* `checkAlwaysTrueLooseComparison` + +### Removed option `excludes_analyse` + +It has been replaced with [`excludePaths`](https://phpstan.org/user-guide/ignoring-errors#excluding-whole-files). + +### Paths in `excludePaths` and `ignoreErrors` have to be a valid file path or a fnmatch pattern + +If you are excluding a file path that might not exist but you still want to have it in `excludePaths`, append `(?)`: + +```neon +parameters: + excludePaths: + - tests/*/data/* + - src/broken + - node_modules (?) # optional path, might not exist +``` + +If you have the same situation in `ignoreErrors` (ignoring an error in a path that might not exist), use `reportUnmatchedIgnoredErrors: false`. + +```neon +parameters: + reportUnmatchedIgnoredErrors: false +``` + +Appending `(?)` in `ignoreErrors` is not supported. + +### Changes in 1st party PHPStan extensions + +* [phpstan-doctrine](https://github.com/phpstan/phpstan-doctrine) + * Removed config parameter `searchOtherMethodsForQueryBuilderBeginning` (extension now behaves as when this was set to `true`) + * Removed config parameter `queryBuilderFastAlgorithm` (extension now behaves as when this was set to `false`) +* [phpstan-symfony](https://github.com/phpstan/phpstan-symfony) + * Removed legacy options with `_` in the name + * `container_xml_path` -> use `containerXmlPath` + * `constant_hassers` -> use `constantHassers` + * `console_application_loader` -> use `consoleApplicationLoader` + +### Minor backward compatibility breaks + +* Removed unused config parameter `cache.nodesByFileCountMax` +* Removed unused config parameter `memoryLimitFile` +* Removed unused feature toggle `disableRuntimeReflectionProvider` +* Removed unused config parameter `staticReflectionClassNamePatterns` +* Remove `fixerTmpDir` config parameter, use `pro.tmpDir` instead +* Remove `tempResultCachePath` config parameter, use `resultCachePath` instead +* `additionalConfigFiles` config parameter must be a list + +## Upgrading guide for extension developers + +> [!NOTE] +> Please switch to PHPStan 2.0 in a new major version of your extension. It's not feasible to try to support both PHPStan 1.x and PHPStan 2.x with the same extension code. +> +> You can definitely get closer to supporting PHPStan 2.0 without increasing major version by solving reported deprecations and other issues by analysing your extension code with PHPStan & phpstan-deprecation-rules & Bleeding Edge, but the final leap and solving backward incompatibilities should be done by requiring `"phpstan/phpstan": "^2.0"` in your `composer.json`, and releasing a new major version. + +### PHPStan now uses nikic/php-parser v5 + +See [UPGRADING](https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md) guide for PHP-Parser. + +The most notable change is how `throw` statement is represented. Previously, `throw` statements like `throw $e;` were represented using the `Stmt\Throw_` class, while uses inside other expressions (such as `$x ?? throw $e`) used the `Expr\Throw_` class. + +Now, `throw $e;` is represented as a `Stmt\Expression` that contains an `Expr\Throw_`. The +`Stmt\Throw_` class has been removed. + +### PHPStan now uses phpstan/phpdoc-parser v2 + +See [UPGRADING](https://github.com/phpstan/phpdoc-parser/blob/2.0.x/UPGRADING.md) guide for phpstan/phpdoc-parser. + +### Returning plain strings as errors no longer supported, use RuleErrorBuilder + +Identifiers are also required in custom rules. + +Learn more: [Using RuleErrorBuilder to enrich reported errors in custom rules](https://phpstan.org/blog/using-rule-error-builder) + +**Before**: + +```php +return ['My error']; +``` + +**After**: + +```php +return [ + RuleErrorBuilder::message('My error') + ->identifier('my.error') + ->build(), +]; +``` + +### Deprecate various `instanceof *Type` in favour of new methods on `Type` interface + +Learn more: [Why Is instanceof *Type Wrong and Getting Deprecated?](https://phpstan.org/blog/why-is-instanceof-type-wrong-and-getting-deprecated) + +### Removed deprecated `ParametersAcceptorSelector::selectSingle()` + +Use [`ParametersAcceptorSelector::selectFromArgs()`](https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ParametersAcceptorSelector.html#_selectFromArgs) instead. It should be used in most places where `selectSingle()` was previously used, like dynamic return type extensions. + +**Before**: + +```php +$defaultReturnType = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType(); +``` + +**After**: + +```php +$defaultReturnType = ParametersAcceptorSelector::selectFromArgs( + $scope, + $functionCall->getArgs(), + $functionReflection->getVariants() +)->getReturnType(); +``` + +If you're analysing function or method body itself and you're using one of the following methods, ask for `getParameters()` and `getReturnType()` directly on the reflection object: + +* [InClassMethodNode::getMethodReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.InClassMethodNode.html) +* [InFunctionNode::getFunctionReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.InFunctionNode.html) +* [FunctionReturnStatementsNode::getFunctionReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.FunctionReturnStatementsNode.html) +* [MethodReturnStatementsNode::getMethodReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.MethodReturnStatementsNode.html) +* [Scope::getFunction()](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.Scope.html#_getFunction) + +**Before**: + +```php +$function = $node->getFunctionReflection(); +$returnType = ParametersAcceptorSelector::selectSingle($function->getVariants())->getReturnType(); +``` + +**After**: + +```php +$returnType = $node->getFunctionReflection()->getReturnType(); +``` + +### Changed `TypeSpecifier::create()` and `SpecifiedTypes` constructor parameters + +[`PHPStan\Analyser\TypeSpecifier::create()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.TypeSpecifier.html#_create) now accepts (all parameters are required): + +* `Expr $expr` +* `Type $type` +* `TypeSpecifierContext $context` +* `Scope $scope` + +If you want to change `$overwrite` or `$rootExpr` (previous parameters also used to be accepted by this method), call `setAlwaysOverwriteTypes()` and `setRootExpr()` on [`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) (object returned by `TypeSpecifier::create()`). These methods return a new object (SpecifiedTypes is immutable). + +[`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) constructor now accepts: + +* `array $sureTypes` +* `array $sureNotTypes` + +If you want to change `$overwrite` or `$rootExpr` (previous parameters also used to be accepted by the constructor), call `setAlwaysOverwriteTypes()` and `setRootExpr()`. These methods return a new object (SpecifiedTypes is immutable). + +### `ConstantArrayType` no longer extends `ArrayType` + +`Type::getArrays()` now returns `list`. + +Using `$type instanceof ArrayType` is [being deprecated anyway](https://phpstan.org/blog/why-is-instanceof-type-wrong-and-getting-deprecated) so the impact of this change should be minimal. + +### Changed `TypeSpecifier::specifyTypesInCondition()` + +This method no longer accepts `Expr $rootExpr`. If you want to change it, call `setRootExpr()` on [`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) (object returned by `TypeSpecifier::specifyTypesInCondition()`). `setRootExpr()` method returns a new object (SpecifiedTypes is immutable). + +### Node attributes `parent`, `previous`, `next` are no longer available + +Learn more: https://phpstan.org/blog/preprocessing-ast-for-custom-rules + +### Removed config parameter `scopeClass` + +As a replacement you can implement [`PHPStan\Type\ExpressionTypeResolverExtension`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.ExpressionTypeResolverExtension.html) interface instead and register it as a service. + +### Removed `PHPStan\Broker\Broker` + +Use [`PHPStan\Reflection\ReflectionProvider`](https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ReflectionProvider.html) instead. + +`BrokerAwareExtension` was also removed. Ask for `ReflectionProvider` in the extension constructor instead. + +Instead of `PHPStanTestCase::createBroker()`, call `PHPStanTestCase::createReflectionProvider()`. + +### List type is enabled for everyone + +Removed static methods from `AccessoryArrayListType` class: + +* `isListTypeEnabled()` +* `setListTypeEnabled()` +* `intersectWith()` + +Instead of `AccessoryArrayListType::intersectWith($type)`, do `TypeCombinator::intersect($type, new AccessoryArrayListType())`. + +### Minor backward compatibility breaks + +* Classes that were previously `@final` were made `final` +* Parameter `$callableParameters` of [`MutatingScope::enterAnonymousFunction()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.MutatingScope.html#_enterAnonymousFunction) and [`enterArrowFunction()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.MutatingScope.html#_enterArrowFunction) made required +* Parameter `StatementContext $context` of [`NodeScopeResolver::processStmtNodes()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.NodeScopeResolver.html#_processStmtNodes) made required +* ClassPropertiesNode - remove `$extensions` parameter from [`getUninitializedProperties()`](https://apiref.phpstan.org/2.0.x/PHPStan.Node.ClassPropertiesNode.html#_getUninitializedProperties) +* `Type::getSmallerType()`, `Type::getSmallerOrEqualType()`, `Type::getGreaterType()`, `Type::getGreaterOrEqualType()`, `Type::isSmallerThan()`, `Type::isSmallerThanOrEqual()` now require [`PhpVersion`](https://apiref.phpstan.org/2.0.x/PHPStan.Php.PhpVersion.html) as argument. +* `CompoundType::isGreaterThan()`, `CompoundType::isGreaterThanOrEqual()` now require [`PhpVersion`](https://apiref.phpstan.org/2.0.x/PHPStan.Php.PhpVersion.html) as argument. +* Removed `ReflectionProvider::supportsAnonymousClasses()` (all reflection providers support anonymous classes) +* Remove `ArrayType::generalizeKeys()` +* Remove `ArrayType::count()`, use `Type::getArraySize()` instead +* Remove `ArrayType::castToArrayKeyType()`, `Type::toArrayKey()` instead +* Remove `UnionType::pickTypes()`, use `pickFromTypes()` instead +* Remove `RegexArrayShapeMatcher::matchType()`, use `matchExpr()` instead +* Remove unused `PHPStanTestCase::$useStaticReflectionProvider` +* Remove `PHPStanTestCase::getReflectors()`, use `getReflector()` instead +* Remove `ClassReflection::getFileNameWithPhpDocs()`, use `getFileName()` instead +* Remove `AnalysisResult::getInternalErrors()`, use `getInternalErrorObjects()` instead +* Remove `ConstantReflection::getValue()`, use `getValueExpr()` instead. To get `Type` from `Expr`, use `Scope::getType()` or `InitializerExprTypeResolver::getType()` +* Remove `PropertyTag::getType()`, use `getReadableType()` / `getWritableType()` instead +* Remove `GenericTypeVariableResolver`, use [`Type::getTemplateType()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getTemplateType) instead +* Rename `Type::isClassStringType()` to `Type::isClassString()` +* Remove `Scope::isSpecified()`, use `hasExpressionType()` instead +* Remove `ConstantArrayType::isEmpty()`, use `isIterableAtLeastOnce()->no()` instead +* Remove `ConstantArrayType::getNextAutoIndex()` +* Removed methods from `ConstantArrayType` - `getFirst*Type` and `getLast*Type` + * Use `getFirstIterable*Type` and `getLastIterable*Type` instead +* Remove `ConstantArrayType::generalizeToArray()` +* Remove `ConstantArrayType::findTypeAndMethodName()`, use `findTypeAndMethodNames()` instead +* Remove `ConstantArrayType::removeLast()`, use [`Type::popArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_popArray) instead +* Remove `ConstantArrayType::removeFirst()`, use [`Type::shiftArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_shiftArray) instead +* Remove `ConstantArrayType::reverse()`, use [`Type::reverseArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_reverseArray) instead +* Remove `ConstantArrayType::chunk()`, use [`Type::chunkArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_chunkArray) instead +* Remove `ConstantArrayType::slice()`, use [`Type::sliceArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_sliceArray) instead +* Made `TypeUtils` thinner by removing methods: + * Remove `TypeUtils::getArrays()` and `getAnyArrays()`, use [`Type::getArrays()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getArrays) instead + * Remove `TypeUtils::getConstantArrays()` and `getOldConstantArrays()`, use [`Type::getConstantArrays()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantArrays) instead + * Remove `TypeUtils::getConstantStrings()`, use [`Type::getConstantStrings()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantStrings) instead + * Remove `TypeUtils::getConstantTypes()` and `getAnyConstantTypes()`, use [`Type::isConstantValue()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantValue) or [`Type::generalize()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_generalize) + * Remove `TypeUtils::generalizeType()`, use [`Type::generalize()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_generalize) instead + * Remove `TypeUtils::getDirectClassNames()`, use [`Type::getObjectClassNames()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getObjectClassNames) instead + * Remove `TypeUtils::getConstantScalars()`, use [`Type::isConstantScalarValue()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantScalarValue) or [`Type::getConstantScalarTypes()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantScalarTypes) instead + * Remove `TypeUtils::getEnumCaseObjects()`, use [`Type::getEnumCases()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getEnumCases) instead + * Remove `TypeUtils::containsCallable()`, use [`Type::isCallable()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isCallable) instead +* Removed `Scope::doNotTreatPhpDocTypesAsCertain()`, use `getNativeType()` instead +* Parameter `$isList` in `ConstantArrayType` constructor can only be `TrinaryLogic`, no longer `bool` +* Parameter `$nextAutoIndexes` in `ConstantArrayType` constructor can only be `non-empty-list`, no longer `int` +* Remove `ConstantType` interface, use [`Type::isConstantValue()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantValue) instead +* `acceptsNamedArguments()` in `FunctionReflection`, `ExtendedMethodReflection` and `CallableParametersAcceptor` interfaces returns `TrinaryLogic` instead of `bool` +* Remove `FunctionReflection::isFinal()` +* [`Type::getProperty()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getProperty) now returns [`ExtendedPropertyReflection`](https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ExtendedPropertyReflection.html) +* Remove `__set_state()` on objects that should not be serialized in cache +* Parameter `$selfClass` of [`TypehintHelper::decideTypeFromReflection()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.TypehintHelper.html#_decideTypeFromReflection) no longer accepts `string` +* `LevelsTestCase::dataTopics()` data provider made static +* `PHPStan\Node\Printer\Printer` no longer autowired as `PhpParser\PrettyPrinter\Standard`, use `PHPStan\Node\Printer\Printer` in the typehint +* Remove `Type::acceptsWithReason()`, `Type:accepts()` return type changed from `TrinaryLogic` to [`AcceptsResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html) +* Remove `CompoundType::isAcceptedWithReasonBy()`, `CompoundType::isAcceptedBy()` return type changed from `TrinaryLogic` to [`AcceptsResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html) +Remove `Type::isSuperTypeOfWithReason()`, `Type:isSuperTypeOf()` return type changed from `TrinaryLogic` to [`IsSuperTypeOfResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html) +* Remove `CompoundType::isSubTypeOfWithReasonBy()`, `CompoundType::isSubTypeOf()` return type changed from `TrinaryLogic` to [`IsSuperTypeOfResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html) +* Remove `TemplateType::isValidVarianceWithReason()`, changed `TemplateType::isValidVariance()` return type to [`IsSuperTypeOfResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html) +* `RuleLevelHelper::accepts()` return type changed from `bool` to [`RuleLevelHelperAcceptsResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html) +* Changes around `ClassConstantReflection` + * Class `ClassConstantReflection` removed from BC promise, renamed to `RealClassConstantReflection` + * Interface `ConstantReflection` renamed to `ClassConstantReflection` + * Added more methods around PHPDoc types and native types to the (new) `ClassConstantReflection` + * Interface `GlobalConstantReflection` renamed to `ConstantReflection` +* Renamed interfaces and classes from `*WithPhpDocs` to `Extended*` + * `ParametersAcceptorWithPhpDocs` -> `ExtendedParametersAcceptor` + * `ParameterReflectionWithPhpDocs` -> `ExtendedParameterReflection` + * `FunctionVariantWithPhpDocs` -> `ExtendedFunctionVariant` +* `ClassPropertyNode::getNativeType()` return type changed from AST node to `Type|null` +* Class `PHPStan\Node\ClassMethod` (accessible from `ClassMethodsNode`) is no longer an AST node + * Call `PHPStan\Node\ClassMethod::getNode()` to access the original AST node diff --git a/tools/.phpstan/vendor/phpstan/phpstan/bootstrap.php b/tools/.phpstan/vendor/phpstan/phpstan/bootstrap.php new file mode 100644 index 000000000..a5d341bfd --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/bootstrap.php @@ -0,0 +1,114 @@ +loadClass($class); + + return; + } + if (strpos($class, 'PHPStan\\') !== 0 || strpos($class, 'PHPStan\\PhpDocParser\\') === 0) { + return; + } + + if (!in_array('phar', stream_get_wrappers(), true)) { + throw new \Exception('Phar wrapper is not registered. Please review your php.ini settings.'); + } + + if (!self::$polyfillsLoaded) { + self::$polyfillsLoaded = true; + + if ( + PHP_VERSION_ID < 80000 + && empty($GLOBALS['__composer_autoload_files']['a4a119a56e50fbb293281d9a48007e0e']) + && !class_exists(\Symfony\Polyfill\Php80\Php80::class, false) + ) { + $GLOBALS['__composer_autoload_files']['a4a119a56e50fbb293281d9a48007e0e'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php80/Php80.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php80/bootstrap.php'; + } + + if ( + empty($GLOBALS['__composer_autoload_files']['0e6d7bf4a5811bfa5cf40c5ccd6fae6a']) + && !class_exists(\Symfony\Polyfill\Mbstring\Mbstring::class, false) + ) { + $GLOBALS['__composer_autoload_files']['0e6d7bf4a5811bfa5cf40c5ccd6fae6a'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-mbstring/Mbstring.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-mbstring/bootstrap.php'; + } + + if ( + empty($GLOBALS['__composer_autoload_files']['e69f7f6ee287b969198c3c9d6777bd38']) + && !class_exists(\Symfony\Polyfill\Intl\Normalizer\Normalizer::class, false) + ) { + $GLOBALS['__composer_autoload_files']['e69f7f6ee287b969198c3c9d6777bd38'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-normalizer/Normalizer.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-normalizer/bootstrap.php'; + } + + if ( + !extension_loaded('intl') + && empty($GLOBALS['__composer_autoload_files']['8825ede83f2f289127722d4e842cf7e8']) + && !class_exists(\Symfony\Polyfill\Intl\Grapheme\Grapheme::class, false) + ) { + $GLOBALS['__composer_autoload_files']['8825ede83f2f289127722d4e842cf7e8'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-grapheme/Grapheme.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-grapheme/bootstrap.php'; + } + + if ( + PHP_VERSION_ID < 80100 + && empty ($GLOBALS['__composer_autoload_files']['23c18046f52bef3eea034657bafda50f']) + && !class_exists(\Symfony\Polyfill\Php81\Php81::class, false) + ) { + $GLOBALS['__composer_autoload_files']['23c18046f52bef3eea034657bafda50f'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php81/Php81.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php81/bootstrap.php'; + } + } + + $filename = str_replace('\\', DIRECTORY_SEPARATOR, $class); + if (strpos($class, 'PHPStan\\BetterReflection\\') === 0) { + $filename = substr($filename, strlen('PHPStan\\BetterReflection\\')); + $filepath = 'phar://' . __DIR__ . '/phpstan.phar/vendor/ondrejmirtes/better-reflection/src/' . $filename . '.php'; + } else { + $filename = substr($filename, strlen('PHPStan\\')); + $filepath = 'phar://' . __DIR__ . '/phpstan.phar/src/' . $filename . '.php'; + } + + if (!file_exists($filepath)) { + return; + } + + require $filepath; + } +} + +spl_autoload_register([PharAutoloader::class, 'loadClass']); diff --git a/tools/.phpstan/vendor/phpstan/phpstan/composer.json b/tools/.phpstan/vendor/phpstan/phpstan/composer.json new file mode 100644 index 000000000..dc62c19ce --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/composer.json @@ -0,0 +1,26 @@ +{ + "name": "phpstan/phpstan", + "description": "PHPStan - PHP Static Analysis Tool", + "license": ["MIT"], + "keywords": ["dev", "static analysis"], + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "autoload": { + "files": ["bootstrap.php"] + }, + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "forum": "https://github.com/phpstan/phpstan/discussions", + "source": "https://github.com/phpstan/phpstan-src", + "docs": "https://phpstan.org/user-guide/getting-started", + "security": "https://github.com/phpstan/phpstan/security/policy" + } +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan/conf/bleedingEdge.neon b/tools/.phpstan/vendor/phpstan/phpstan/conf/bleedingEdge.neon new file mode 100644 index 000000000..01fee972d --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/conf/bleedingEdge.neon @@ -0,0 +1,2 @@ +includes: + - phar://phpstan.phar/conf/bleedingEdge.neon diff --git a/tools/.phpstan/vendor/phpstan/phpstan/phpstan b/tools/.phpstan/vendor/phpstan/phpstan/phpstan new file mode 100755 index 000000000..7a08ef485 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/phpstan @@ -0,0 +1,8 @@ +#!/usr/bin/env php + + +
    + AI abilities sea level rising... as way to rise type coverage for class elements +
    + +
    + +PHPStan uses type declarations to determine the type of variables, properties and other expression. Sometimes it's hard to see what PHPStan errors are the important ones among thousands of others. + +Instead of fixing all PHPStan errors at once, we can start with minimal require type coverage. + +
    + +What is the type coverage you ask? We have 4 type possible declarations in total here: + +```php +final class ConferenceFactory +{ + const SPEAKER_TAG = 'speaker'; + + private $talkFactory; + + public function createConference(array $data) + { + $talks = $this->talkFactory->create($data); + + return new Conference($talks); + } +} +``` + +*Note: Class constant types require PHP 8.3 to run.* + +The param type is defined. But the property, return and constant types are missing. + +* 1 out of 4 = 25 % coverage + +Our code quality is only at one-quarter of its potential. Let's get to 100 %! + +```diff + final class ConferenceFactory + { +- public const SPEAKER_TAG = 'speaker'; ++ public const string SPEAKER_TAG = 'speaker'; + +- private $talkFactory; ++ private TalkFactory $talkFactory; + +- public function createConference(array $data) ++ public function createConference(array $data): Conference + { + $talks = $this->talkFactory->create($data); + + return new Conference($talks); + } + } +``` + +This technique is very simple to start even on legacy project. Also, you're now aware exactly how high coverage your project has. + +
    + +## Install + +```bash +composer require tomasvotruba/type-coverage --dev +``` + +The package is available on PHP 7.2+ version in tagged releases. + +
    + +## Usage + +With [PHPStan extension installer](https://github.com/phpstan/extension-installer), everything is ready to run. + +Enable each item on their own: + +```yaml +# phpstan.neon +parameters: + type_coverage: + return: 50 + param: 35.5 + property: 70 + constant: 85 +``` + +
    + +## Measure Strict Declares coverage + +Once you've reached 100 % type coverage, make sure [your code is strict and uses types](https://tomasvotruba.com/blog/how-adding-type-declarations-makes-your-code-dangerous): + +```php + + +## Full Paths only + +If you run PHPStan only on some subpaths that are different from your setup in `phpstan.neon`, e.g.: + +```bash +vendor/bin/phpstan analyze src/Controller +``` + +This package could show false positives, as classes in the `src/Controller` could be slightly less typed. This would be spamming whole PHPStan output and make hard to see any other errors you look for. + +That's why this package only triggers if there are full paths, e.g.: + +```bash +vendor/bin/phpstan +```` + +
    + +Happy coding! diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json b/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json new file mode 100644 index 000000000..e32de7ffb --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json @@ -0,0 +1,24 @@ +{ + "name": "tomasvotruba/type-coverage", + "type": "phpstan-extension", + "description": "Measure type coverage of your project", + "license": "MIT", + "keywords": ["static analysis", "phpstan-extension"], + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0", + "nette/utils": "^3.2 || ^4.0" + }, + "autoload": { + "psr-4": { + "TomasVotruba\\TypeCoverage\\": "src" + } + }, + "extra": { + "phpstan": { + "includes": [ + "config/extension.neon" + ] + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/config/extension.neon b/tools/.phpstan/vendor/tomasvotruba/type-coverage/config/extension.neon new file mode 100644 index 000000000..b2a58b699 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/config/extension.neon @@ -0,0 +1,79 @@ +parametersSchema: + # see https://doc.nette.org/en/schema for configuration + type_coverage: structure([ + declare: anyOf(float(), int()) + # type declarations + return_type: anyOf(float(), int()) + param_type: anyOf(float(), int()) + property_type: anyOf(float(), int()) + constant_type: anyOf(float(), int()) + print_suggestions: bool() + # aliases to avoid typos + return: anyOf(schema(float(), nullable()), schema(int(), nullable())) + param: anyOf(schema(float(), nullable()), schema(int(), nullable())) + property: anyOf(schema(float(), nullable()), schema(int(), nullable())) + constant: anyOf(schema(float(), nullable()), schema(int(), nullable())) + + # measure + measure: bool() + ]) + +# default parameters +parameters: + type_coverage: + declare: 0 + # type declarations + return_type: 99 + param_type: 99 + property_type: 99 + constant_type: 99 + # default, yet deprecated + print_suggestions: true + # aliases + return: null + param: null + property: null + constant: null + + measure: false + +services: + - TomasVotruba\TypeCoverage\Formatter\TypeCoverageFormatter + - TomasVotruba\TypeCoverage\CollectorDataNormalizer + + - + factory: TomasVotruba\TypeCoverage\Configuration + arguments: + - %type_coverage% + + # collectors + - + class: TomasVotruba\TypeCoverage\Collectors\ReturnTypeDeclarationCollector + tags: + - phpstan.collector + + - + class: TomasVotruba\TypeCoverage\Collectors\ParamTypeDeclarationCollector + tags: + - phpstan.collector + + - + class: TomasVotruba\TypeCoverage\Collectors\PropertyTypeDeclarationCollector + tags: + - phpstan.collector + - + class: TomasVotruba\TypeCoverage\Collectors\ConstantTypeDeclarationCollector + tags: + - phpstan.collector + + - + class: TomasVotruba\TypeCoverage\Collectors\DeclareCollector + tags: + - phpstan.collector + +rules: + - TomasVotruba\TypeCoverage\Rules\ParamTypeCoverageRule + - TomasVotruba\TypeCoverage\Rules\ReturnTypeCoverageRule + - TomasVotruba\TypeCoverage\Rules\PropertyTypeCoverageRule + - TomasVotruba\TypeCoverage\Rules\ConstantTypeCoverageRule + - TomasVotruba\TypeCoverage\Rules\DeclareCoverageRule diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/docs/required_type_level.jpg b/tools/.phpstan/vendor/tomasvotruba/type-coverage/docs/required_type_level.jpg new file mode 100644 index 000000000..cd219abe8 Binary files /dev/null and b/tools/.phpstan/vendor/tomasvotruba/type-coverage/docs/required_type_level.jpg differ diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/rector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/rector.php new file mode 100644 index 000000000..224fbf73e --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/rector.php @@ -0,0 +1,18 @@ +withPaths([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->withPhpSets() + ->withPreparedSets(deadCode: true, codeQuality: true, codingStyle: true, typeDeclarations: true, privatization: true, naming: true) + ->withImportNames(removeUnusedImports: true) + ->withSkip([ + '*/Fixture/*', + '*/Source/*', + ]); diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/CollectorDataNormalizer.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/CollectorDataNormalizer.php new file mode 100644 index 000000000..e05c09d15 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/CollectorDataNormalizer.php @@ -0,0 +1,36 @@ +}>> $collectorDataByPath + */ + public function normalize(array $collectorDataByPath): TypeCountAndMissingTypes + { + $totalCount = 0; + $missingCount = 0; + + $missingTypeLinesByFilePath = []; + + foreach ($collectorDataByPath as $filePath => $typeCoverageData) { + foreach ($typeCoverageData as $nestedData) { + $totalCount += $nestedData[0]; + + $missingCount += count($nestedData[1]); + + $missingTypeLinesByFilePath[$filePath] = array_merge( + $missingTypeLinesByFilePath[$filePath] ?? [], + $nestedData[1] + ); + } + } + + return new TypeCountAndMissingTypes($totalCount, $missingCount, $missingTypeLinesByFilePath); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php new file mode 100644 index 000000000..4296b6135 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php @@ -0,0 +1,77 @@ + + */ + public function getNodeType(): string + { + return ClassConstantsNode::class; + } + + /** + * @param ClassConstantsNode $node + * @return mixed[] + */ + public function processNode(Node $node, Scope $scope): array + { + // enable only on PHP 8.3+ + if (PHP_VERSION_ID < 80300) { + return [0, []]; + } + + $constantCount = count($node->getConstants()); + + $missingTypeLines = []; + + foreach ($node->getConstants() as $classConst) { + // blocked by parent type + if ($this->isGuardedByParentClassConstant($scope, $classConst)) { + continue; + } + + // already typed + if ($classConst->type instanceof Node) { + continue; + } + + // give useful context + $missingTypeLines[] = $classConst->getLine(); + } + + return [$constantCount, $missingTypeLines]; + } + + private function isGuardedByParentClassConstant(Scope $scope, ClassConst $classConst): bool + { + $constName = $classConst->consts[0]->name->toString(); + + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + foreach ($classReflection->getParents() as $parentClassReflection) { + if ($parentClassReflection->hasConstant($constName)) { + return true; + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/DeclareCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/DeclareCollector.php new file mode 100644 index 000000000..ad677eeed --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/DeclareCollector.php @@ -0,0 +1,51 @@ +getNodes() as $node) { + if (! $node instanceof Declare_) { + continue; + } + + foreach ($node->declares as $declare) { + if ( + $declare->key->name !== 'strict_types' + ) { + continue; + } + + if ( + ! $declare->value instanceof LNumber + || $declare->value->value !== 1 + ) { + return false; + } + + return true; + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ParamTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ParamTypeDeclarationCollector.php new file mode 100644 index 000000000..a9d4a4943 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ParamTypeDeclarationCollector.php @@ -0,0 +1,72 @@ +shouldSkipFunctionLike($node)) { + return null; + } + + $missingTypeLines = []; + $paramCount = count($node->getParams()); + + foreach ($node->getParams() as $param) { + if ($param->variadic) { + // skip variadic + --$paramCount; + continue; + } + + if ($param->type === null) { + $missingTypeLines[] = $param->getLine(); + } + } + + return [$paramCount, $missingTypeLines]; + } + + private function shouldSkipFunctionLike(FunctionLike $functionLike): bool + { + // nothing to analyse + if ($functionLike->getParams() === []) { + return true; + } + + return $this->hasFunctionLikeCallableParam($functionLike); + } + + private function hasFunctionLikeCallableParam(FunctionLike $functionLike): bool + { + // skip callable, can be anythings + $docComment = $functionLike->getDocComment(); + if (! $docComment instanceof Doc) { + return false; + } + + $docCommentText = $docComment->getText(); + return strpos($docCommentText, '@param callable') !== false; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php new file mode 100644 index 000000000..893a60ba4 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php @@ -0,0 +1,93 @@ + + */ + public function getNodeType(): string + { + return InClassNode::class; + } + + /** + * @param InClassNode $node + * @return mixed[] + */ + public function processNode(Node $node, Scope $scope): array + { + // return typed properties/all properties + $classLike = $node->getOriginalNode(); + + $propertyCount = count($classLike->getProperties()); + + $missingTypeLines = []; + + foreach ($classLike->getProperties() as $property) { + // blocked by parent type + if ($this->isGuardedByParentClassProperty($scope, $property)) { + continue; + } + + // already typed + if ($property->type instanceof Node) { + continue; + } + + if ($this->isPropertyDocTyped($property)) { + continue; + } + + // give useful context + $missingTypeLines[] = $property->getLine(); + } + + return [$propertyCount, $missingTypeLines]; + } + + private function isPropertyDocTyped(Property $property): bool + { + $docComment = $property->getDocComment(); + if (! $docComment instanceof Doc) { + return false; + } + + $docCommentText = $docComment->getText(); + + // skip as unable to type + return strpos($docCommentText, 'callable') !== false || strpos($docCommentText, 'resource') !== false; + } + + private function isGuardedByParentClassProperty(Scope $scope, Property $property): bool + { + $propertyName = $property->props[0]->name->toString(); + + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + foreach ($classReflection->getParents() as $parentClassReflection) { + if ($parentClassReflection->hasProperty($propertyName)) { + return true; + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ReturnTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ReturnTypeDeclarationCollector.php new file mode 100644 index 000000000..f6f63b841 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ReturnTypeDeclarationCollector.php @@ -0,0 +1,48 @@ +isMagic()) { + return null; + } + + if ($scope->isInTrait()) { + $originalMethodName = $node->getAttribute('originalTraitMethodName'); + if ($originalMethodName === '__construct') { + return null; + } + } + + $missingTypeLines = []; + + if (! $node->returnType instanceof Node) { + $missingTypeLines[] = $node->getLine(); + } + + return [1, $missingTypeLines]; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration.php new file mode 100644 index 000000000..f2fd6f2b9 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration.php @@ -0,0 +1,76 @@ + + * @readonly + */ + private array $parameters; + + /** + * @param array $parameters + */ + public function __construct(array $parameters) + { + $this->parameters = $parameters; + } + + /** + * @return float|int + */ + public function getRequiredPropertyTypeLevel() + { + return $this->parameters['property'] ?? $this->parameters['property_type']; + } + + public function isConstantTypeCoverageEnabled(): bool + { + if (PHP_VERSION_ID < 80300) { + return false; + } + + return $this->getRequiredConstantTypeLevel() > 0; + } + + /** + * @return float|int + */ + public function getRequiredConstantTypeLevel() + { + return $this->parameters['constant'] ?? $this->parameters['constant_type']; + } + + /** + * @return float|int + */ + public function getRequiredParamTypeLevel() + { + return $this->parameters['param'] ?? $this->parameters['param_type']; + } + + /** + * @return float|int + */ + public function getRequiredReturnTypeLevel() + { + return $this->parameters['return'] ?? $this->parameters['return_type']; + } + + /** + * @return float|int + */ + public function getRequiredDeclareLevel() + { + return $this->parameters['declare']; + } + + public function showOnlyMeasure(): bool + { + return $this->parameters['measure']; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration/ScopeConfigurationResolver.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration/ScopeConfigurationResolver.php new file mode 100644 index 000000000..7b4cec24f --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration/ScopeConfigurationResolver.php @@ -0,0 +1,58 @@ +getParameter('analysedPaths'); + $analysedPathsFromConfig = $originalContainer->getParameter('analysedPathsFromConfig'); + + self::$areFullPathsAnalysed = $analysedPathsFromConfig === $analysedPaths; + + return self::$areFullPathsAnalysed; + } + + private static function getPrivateProperty(object $object, string $propertyName): object + { + $reflectionProperty = new ReflectionProperty($object, $propertyName); + $reflectionProperty->setAccessible(true); + + return $reflectionProperty->getValue($object); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Formatter/TypeCoverageFormatter.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Formatter/TypeCoverageFormatter.php new file mode 100644 index 000000000..04d7c3946 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Formatter/TypeCoverageFormatter.php @@ -0,0 +1,56 @@ +getTotalCount() === 0) { + return []; + } + + $typeCoveragePercentage = $typeCountAndMissingTypes->getCoveragePercentage(); + + // has the code met the minimal sea level of types? + if ($typeCoveragePercentage >= $minimalLevel) { + return []; + } + + $ruleErrors = []; + + foreach ($typeCountAndMissingTypes->getMissingTypeLinesByFilePath() as $filePath => $lines) { + $errorMessage = sprintf( + $message, + $typeCountAndMissingTypes->getTotalCount(), + $typeCountAndMissingTypes->getFilledCount(), + $typeCoveragePercentage, + $minimalLevel + ); + + foreach ($lines as $line) { + $ruleErrors[] = RuleErrorBuilder::message($errorMessage) + ->identifier($identifier) + ->file($filePath) + ->line($line) + ->build(); + } + } + + return $ruleErrors; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ConstantTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ConstantTypeCoverageRule.php new file mode 100644 index 000000000..b53da098c --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ConstantTypeCoverageRule.php @@ -0,0 +1,101 @@ + + */ +final class ConstantTypeCoverageRule implements Rule +{ + /** + * @var string + */ + public const ERROR_MESSAGE = 'Out of %d possible constant types, only %d - %.1f %% actually have it. Add more constant types to get over %s %%'; + + /** + * @var string + */ + private const IDENTIFIER = 'typeCoverage.constantTypeCoverage'; + + /** + * @readonly + */ + private TypeCoverageFormatter $typeCoverageFormatter; + + /** + * @readonly + */ + private Configuration $configuration; + + /** + * @readonly + */ + private CollectorDataNormalizer $collectorDataNormalizer; + + public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer) + { + $this->typeCoverageFormatter = $typeCoverageFormatter; + $this->configuration = $configuration; + $this->collectorDataNormalizer = $collectorDataNormalizer; + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + // if only subpaths are analysed, skip as data will be false positive + if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) { + return []; + } + + $constantTypeDeclarationCollector = $node->get(ConstantTypeDeclarationCollector::class); + $typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($constantTypeDeclarationCollector); + + if ($this->configuration->showOnlyMeasure()) { + $errorMessage = sprintf( + 'Class constant type coverage is %.1f %% out of %d possible', + $typeCountAndMissingTypes->getCoveragePercentage(), + $typeCountAndMissingTypes->getTotalCount() + ); + + return [RuleErrorBuilder::message($errorMessage)->build()]; + } + + if (! $this->configuration->isConstantTypeCoverageEnabled()) { + return []; + } + + return $this->typeCoverageFormatter->formatErrors( + self::ERROR_MESSAGE, + self::IDENTIFIER, + $this->configuration->getRequiredConstantTypeLevel(), + $typeCountAndMissingTypes + ); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/DeclareCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/DeclareCoverageRule.php new file mode 100644 index 000000000..314c0b597 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/DeclareCoverageRule.php @@ -0,0 +1,116 @@ + + */ +final class DeclareCoverageRule implements Rule +{ + /** + * @var string + */ + public const ERROR_MESSAGE = 'Out of %d possible declare(strict_types=1), only %d - %.1f %% actually have it. Add more declares to get over %s %%'; + + /** + * @readonly + */ + private Configuration $configuration; + + public function __construct(Configuration $configuration) + { + $this->configuration = $configuration; + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + // if only subpaths are analysed, skip as data will be false positive + if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) { + return []; + } + + $requiredDeclareLevel = $this->configuration->getRequiredDeclareLevel(); + + $declareCollector = $node->get(DeclareCollector::class); + $totalPossibleDeclares = count($declareCollector); + + $coveredDeclares = 0; + $notCoveredDeclareFilePaths = []; + + foreach ($declareCollector as $fileName => $data) { + // has declares + if ($data === [true]) { + ++$coveredDeclares; + } else { + $notCoveredDeclareFilePaths[] = $fileName; + } + } + + $declareCoverage = ($coveredDeclares / $totalPossibleDeclares) * 100; + + if ($this->configuration->showOnlyMeasure()) { + $errorMessage = sprintf( + 'Strict declares coverage is %.1f %% out of %d possible', + $declareCoverage, + $totalPossibleDeclares + ); + return [RuleErrorBuilder::message($errorMessage)->build()]; + } + + // not enabled + if ($requiredDeclareLevel === 0) { + return []; + } + + // nothing to handle + if ($totalPossibleDeclares === 0) { + return []; + } + + // we meet the limit, all good + if ($declareCoverage >= $requiredDeclareLevel) { + return []; + } + + $ruleErrors = []; + foreach ($notCoveredDeclareFilePaths as $notCoveredDeclareFilePath) { + $errorMessage = sprintf( + self::ERROR_MESSAGE, + $totalPossibleDeclares, + $coveredDeclares, + $declareCoverage, + $requiredDeclareLevel, + ); + + $ruleErrors[] = RuleErrorBuilder::message($errorMessage)->file($notCoveredDeclareFilePath)->build(); + } + + return $ruleErrors; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ParamTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ParamTypeCoverageRule.php new file mode 100644 index 000000000..ed300eea6 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ParamTypeCoverageRule.php @@ -0,0 +1,105 @@ + + */ +final class ParamTypeCoverageRule implements Rule +{ + /** + * @var string + */ + public const ERROR_MESSAGE = 'Out of %d possible param types, only %d - %.1f %% actually have it. Add more param types to get over %s %%'; + + /** + * @var string + */ + private const IDENTIFIER = 'typeCoverage.paramTypeCoverage'; + + /** + * @readonly + */ + private TypeCoverageFormatter $typeCoverageFormatter; + + /** + * @readonly + */ + private Configuration $configuration; + + /** + * @readonly + */ + private CollectorDataNormalizer $collectorDataNormalizer; + + public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer) + { + $this->typeCoverageFormatter = $typeCoverageFormatter; + $this->configuration = $configuration; + $this->collectorDataNormalizer = $collectorDataNormalizer; + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + // if only subpaths are analysed, skip as data will be false positive + if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) { + return []; + } + + $paramTypeDeclarationCollector = $node->get(ParamTypeDeclarationCollector::class); + + $typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($paramTypeDeclarationCollector); + + if ($this->configuration->showOnlyMeasure()) { + $errorMessage = sprintf( + 'Param type coverage is %.1f %% out of %d possible', + $typeCountAndMissingTypes->getCoveragePercentage(), + $typeCountAndMissingTypes->getTotalCount() + ); + + $ruleError = RuleErrorBuilder::message($errorMessage) + ->build(); + + return [$ruleError]; + } + + if ($this->configuration->getRequiredParamTypeLevel() === 0) { + return []; + } + + return $this->typeCoverageFormatter->formatErrors( + self::ERROR_MESSAGE, + self::IDENTIFIER, + $this->configuration->getRequiredParamTypeLevel(), + $typeCountAndMissingTypes + ); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/PropertyTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/PropertyTypeCoverageRule.php new file mode 100644 index 000000000..27dcec2cf --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/PropertyTypeCoverageRule.php @@ -0,0 +1,101 @@ + + */ +final class PropertyTypeCoverageRule implements Rule +{ + /** + * @var string + */ + public const ERROR_MESSAGE = 'Out of %d possible property types, only %d - %.1f %% actually have it. Add more property types to get over %s %%'; + + /** + * @var string + */ + private const IDENTIFIER = 'typeCoverage.propertyTypeCoverage'; + + /** + * @readonly + */ + private TypeCoverageFormatter $typeCoverageFormatter; + + /** + * @readonly + */ + private Configuration $configuration; + + /** + * @readonly + */ + private CollectorDataNormalizer $collectorDataNormalizer; + + public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer) + { + $this->typeCoverageFormatter = $typeCoverageFormatter; + $this->configuration = $configuration; + $this->collectorDataNormalizer = $collectorDataNormalizer; + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + // if only subpaths are analysed, skip as data will be false positive + if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) { + return []; + } + + $propertyTypeDeclarationCollector = $node->get(PropertyTypeDeclarationCollector::class); + $typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($propertyTypeDeclarationCollector); + + if ($this->configuration->showOnlyMeasure()) { + $errorMessage = sprintf( + 'Property type coverage is %.1f %% out of %d possible', + $typeCountAndMissingTypes->getCoveragePercentage(), + $typeCountAndMissingTypes->getTotalCount() + ); + + return [RuleErrorBuilder::message($errorMessage)->build()]; + } + + if ($this->configuration->getRequiredPropertyTypeLevel() === 0) { + return []; + } + + return $this->typeCoverageFormatter->formatErrors( + self::ERROR_MESSAGE, + self::IDENTIFIER, + $this->configuration->getRequiredPropertyTypeLevel(), + $typeCountAndMissingTypes + ); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ReturnTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ReturnTypeCoverageRule.php new file mode 100644 index 000000000..c7e7fbaa6 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ReturnTypeCoverageRule.php @@ -0,0 +1,100 @@ + + */ +final class ReturnTypeCoverageRule implements Rule +{ + /** + * @var string + */ + public const ERROR_MESSAGE = 'Out of %d possible return types, only %d - %.1f %% actually have it. Add more return types to get over %s %%'; + + /** + * @var string + */ + private const IDENTIFIER = 'typeCoverage.returnTypeCoverage'; + + /** + * @readonly + */ + private TypeCoverageFormatter $typeCoverageFormatter; + + /** + * @readonly + */ + private Configuration $configuration; + + /** + * @readonly + */ + private CollectorDataNormalizer $collectorDataNormalizer; + + public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer) + { + $this->typeCoverageFormatter = $typeCoverageFormatter; + $this->configuration = $configuration; + $this->collectorDataNormalizer = $collectorDataNormalizer; + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + // if only subpaths are analysed, skip as data will be false positive + if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) { + return []; + } + + $returnSeaLevelDataByFilePath = $node->get(ReturnTypeDeclarationCollector::class); + $typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($returnSeaLevelDataByFilePath); + + if ($this->configuration->showOnlyMeasure()) { + $errorMessage = sprintf( + 'Return type coverage is %.1f %% out of %d possible', + $typeCountAndMissingTypes->getCoveragePercentage(), + $typeCountAndMissingTypes->getTotalCount() + ); + return [RuleErrorBuilder::message($errorMessage)->build()]; + } + + if ($this->configuration->getRequiredReturnTypeLevel() === 0) { + return []; + } + + return $this->typeCoverageFormatter->formatErrors( + self::ERROR_MESSAGE, + self::IDENTIFIER, + $this->configuration->getRequiredReturnTypeLevel(), + $typeCountAndMissingTypes + ); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/ValueObject/TypeCountAndMissingTypes.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/ValueObject/TypeCountAndMissingTypes.php new file mode 100644 index 000000000..c539839de --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/ValueObject/TypeCountAndMissingTypes.php @@ -0,0 +1,75 @@ + + * @readonly + */ + private array $missingTypeLinesByFilePath; + + /** + * @param array $missingTypeLinesByFilePath + */ + public function __construct(int $totalCount, int $missingCount, array $missingTypeLinesByFilePath) + { + $this->totalCount = $totalCount; + $this->missingCount = $missingCount; + $this->missingTypeLinesByFilePath = $missingTypeLinesByFilePath; + } + + public function getTotalCount(): int + { + return $this->totalCount; + } + + public function getFilledCount(): int + { + return $this->totalCount - $this->missingCount; + } + + /** + * @return array + */ + public function getMissingTypeLinesByFilePath(): array + { + return $this->missingTypeLinesByFilePath; + } + + public function getCoveragePercentage(): float + { + if ($this->totalCount === 0) { + return 100.0; + } + + $relative = 100 * ($this->getTypedCount() / $this->totalCount); + + // round down with one decimal, to make error message clear that required value is not reached yet + return floor($relative * 10) / 10; + } + + private function getTypedCount(): int + { + $missingCount = 0; + + foreach ($this->missingTypeLinesByFilePath as $missingTypeLines) { + $missingCount += count($missingTypeLines); + } + + return $this->totalCount - $missingCount; + } +} diff --git a/tools/composer b/tools/composer new file mode 100755 index 000000000..4f53d5aa0 Binary files /dev/null and b/tools/composer differ diff --git a/tools/phive b/tools/phive new file mode 100755 index 000000000..da56a9d34 --- /dev/null +++ b/tools/phive @@ -0,0 +1,1104 @@ +#!/usr/bin/env php + '/vendor/phar-io/executor/src/ExecutorException.php', + 'phario\\executor\\executor' => '/vendor/phar-io/executor/src/Executor.php', + 'phario\\executor\\executorresult' => '/vendor/phar-io/executor/src/ExecutorResult.php', + 'phario\\filesystem\\directory' => '/vendor/phar-io/filesystem/src/Directory.php', + 'phario\\filesystem\\directoryexception' => '/vendor/phar-io/filesystem/src/DirectoryException.php', + 'phario\\filesystem\\exception' => '/vendor/phar-io/filesystem/src/Exception.php', + 'phario\\filesystem\\file' => '/vendor/phar-io/filesystem/src/File.php', + 'phario\\filesystem\\filename' => '/vendor/phar-io/filesystem/src/Filename.php', + 'phario\\filesystem\\filenameexception' => '/vendor/phar-io/filesystem/src/FilenameException.php', + 'phario\\filesystem\\lastmodifieddate' => '/vendor/phar-io/filesystem/src/LastModifiedDate.php', + 'phario\\gnupg\\errorstrings' => '/vendor/phar-io/gnupg/src/ErrorStrings.php', + 'phario\\gnupg\\exception' => '/vendor/phar-io/gnupg/src/Exception.php', + 'phario\\gnupg\\factory' => '/vendor/phar-io/gnupg/src/Factory.php', + 'phario\\gnupg\\gnupg' => '/vendor/phar-io/gnupg/src/GnuPG.php', + 'phario\\manifest\\application' => '/vendor/phar-io/manifest/src/values/Application.php', + 'phario\\manifest\\applicationname' => '/vendor/phar-io/manifest/src/values/ApplicationName.php', + 'phario\\manifest\\author' => '/vendor/phar-io/manifest/src/values/Author.php', + 'phario\\manifest\\authorcollection' => '/vendor/phar-io/manifest/src/values/AuthorCollection.php', + 'phario\\manifest\\authorcollectioniterator' => '/vendor/phar-io/manifest/src/values/AuthorCollectionIterator.php', + 'phario\\manifest\\authorelement' => '/vendor/phar-io/manifest/src/xml/AuthorElement.php', + 'phario\\manifest\\authorelementcollection' => '/vendor/phar-io/manifest/src/xml/AuthorElementCollection.php', + 'phario\\manifest\\bundledcomponent' => '/vendor/phar-io/manifest/src/values/BundledComponent.php', + 'phario\\manifest\\bundledcomponentcollection' => '/vendor/phar-io/manifest/src/values/BundledComponentCollection.php', + 'phario\\manifest\\bundledcomponentcollectioniterator' => '/vendor/phar-io/manifest/src/values/BundledComponentCollectionIterator.php', + 'phario\\manifest\\bundleselement' => '/vendor/phar-io/manifest/src/xml/BundlesElement.php', + 'phario\\manifest\\componentelement' => '/vendor/phar-io/manifest/src/xml/ComponentElement.php', + 'phario\\manifest\\componentelementcollection' => '/vendor/phar-io/manifest/src/xml/ComponentElementCollection.php', + 'phario\\manifest\\containselement' => '/vendor/phar-io/manifest/src/xml/ContainsElement.php', + 'phario\\manifest\\copyrightelement' => '/vendor/phar-io/manifest/src/xml/CopyrightElement.php', + 'phario\\manifest\\copyrightinformation' => '/vendor/phar-io/manifest/src/values/CopyrightInformation.php', + 'phario\\manifest\\elementcollection' => '/vendor/phar-io/manifest/src/xml/ElementCollection.php', + 'phario\\manifest\\elementcollectionexception' => '/vendor/phar-io/manifest/src/exceptions/ElementCollectionException.php', + 'phario\\manifest\\email' => '/vendor/phar-io/manifest/src/values/Email.php', + 'phario\\manifest\\exception' => '/vendor/phar-io/manifest/src/exceptions/Exception.php', + 'phario\\manifest\\extelement' => '/vendor/phar-io/manifest/src/xml/ExtElement.php', + 'phario\\manifest\\extelementcollection' => '/vendor/phar-io/manifest/src/xml/ExtElementCollection.php', + 'phario\\manifest\\extension' => '/vendor/phar-io/manifest/src/values/Extension.php', + 'phario\\manifest\\extensionelement' => '/vendor/phar-io/manifest/src/xml/ExtensionElement.php', + 'phario\\manifest\\invalidapplicationnameexception' => '/vendor/phar-io/manifest/src/exceptions/InvalidApplicationNameException.php', + 'phario\\manifest\\invalidemailexception' => '/vendor/phar-io/manifest/src/exceptions/InvalidEmailException.php', + 'phario\\manifest\\invalidurlexception' => '/vendor/phar-io/manifest/src/exceptions/InvalidUrlException.php', + 'phario\\manifest\\library' => '/vendor/phar-io/manifest/src/values/Library.php', + 'phario\\manifest\\license' => '/vendor/phar-io/manifest/src/values/License.php', + 'phario\\manifest\\licenseelement' => '/vendor/phar-io/manifest/src/xml/LicenseElement.php', + 'phario\\manifest\\manifest' => '/vendor/phar-io/manifest/src/values/Manifest.php', + 'phario\\manifest\\manifestdocument' => '/vendor/phar-io/manifest/src/xml/ManifestDocument.php', + 'phario\\manifest\\manifestdocumentexception' => '/vendor/phar-io/manifest/src/exceptions/ManifestDocumentException.php', + 'phario\\manifest\\manifestdocumentloadingexception' => '/vendor/phar-io/manifest/src/exceptions/ManifestDocumentLoadingException.php', + 'phario\\manifest\\manifestdocumentmapper' => '/vendor/phar-io/manifest/src/ManifestDocumentMapper.php', + 'phario\\manifest\\manifestdocumentmapperexception' => '/vendor/phar-io/manifest/src/exceptions/ManifestDocumentMapperException.php', + 'phario\\manifest\\manifestelement' => '/vendor/phar-io/manifest/src/xml/ManifestElement.php', + 'phario\\manifest\\manifestelementexception' => '/vendor/phar-io/manifest/src/exceptions/ManifestElementException.php', + 'phario\\manifest\\manifestloader' => '/vendor/phar-io/manifest/src/ManifestLoader.php', + 'phario\\manifest\\manifestloaderexception' => '/vendor/phar-io/manifest/src/exceptions/ManifestLoaderException.php', + 'phario\\manifest\\manifestserializer' => '/vendor/phar-io/manifest/src/ManifestSerializer.php', + 'phario\\manifest\\noemailaddressexception' => '/vendor/phar-io/manifest/src/exceptions/NoEmailAddressException.php', + 'phario\\manifest\\phpelement' => '/vendor/phar-io/manifest/src/xml/PhpElement.php', + 'phario\\manifest\\phpextensionrequirement' => '/vendor/phar-io/manifest/src/values/PhpExtensionRequirement.php', + 'phario\\manifest\\phpversionrequirement' => '/vendor/phar-io/manifest/src/values/PhpVersionRequirement.php', + 'phario\\manifest\\requirement' => '/vendor/phar-io/manifest/src/values/Requirement.php', + 'phario\\manifest\\requirementcollection' => '/vendor/phar-io/manifest/src/values/RequirementCollection.php', + 'phario\\manifest\\requirementcollectioniterator' => '/vendor/phar-io/manifest/src/values/RequirementCollectionIterator.php', + 'phario\\manifest\\requireselement' => '/vendor/phar-io/manifest/src/xml/RequiresElement.php', + 'phario\\manifest\\type' => '/vendor/phar-io/manifest/src/values/Type.php', + 'phario\\manifest\\url' => '/vendor/phar-io/manifest/src/values/Url.php', + 'phario\\phive\\abstractrequestedpharresolver' => '/src/services/resolver/AbstractRequestedPharResolver.php', + 'phario\\phive\\abstractresolvingstrategy' => '/src/services/resolver/strategy/AbstractResolvingStrategy.php', + 'phario\\phive\\authconfig' => '/src/shared/config/AuthConfig.php', + 'phario\\phive\\authentication' => '/src/shared/http/Authentication.php', + 'phario\\phive\\authexception' => '/src/shared/exceptions/AuthException.php', + 'phario\\phive\\authxmlconfig' => '/src/shared/config/AuthXmlConfig.php', + 'phario\\phive\\authxmlconfigfilelocator' => '/src/shared/config/AuthXmlConfigFileLocator.php', + 'phario\\phive\\basehash' => '/src/shared/hash/BaseHash.php', + 'phario\\phive\\basicauthentication' => '/src/shared/http/authentication/BasicAuthentication.php', + 'phario\\phive\\bearerauthentication' => '/src/shared/http/authentication/BearerAuthentication.php', + 'phario\\phive\\cachebackend' => '/src/shared/http/CacheBackend.php', + 'phario\\phive\\checksumservice' => '/src/services/checksum/ChecksumService.php', + 'phario\\phive\\cli\\coloredconsoleoutput' => '/src/shared/cli/output/ColoredConsoleOutput.php', + 'phario\\phive\\cli\\command' => '/src/shared/cli/Command.php', + 'phario\\phive\\cli\\commandlocator' => '/src/shared/cli/CommandLocator.php', + 'phario\\phive\\cli\\commandlocatorexception' => '/src/shared/cli/CommandLocatorException.php', + 'phario\\phive\\cli\\commandoptionsexception' => '/src/shared/cli/CommandOptionsException.php', + 'phario\\phive\\cli\\consoleinput' => '/src/shared/cli/input/ConsoleInput.php', + 'phario\\phive\\cli\\consoleoutput' => '/src/shared/cli/output/ConsoleOutput.php', + 'phario\\phive\\cli\\consoletable' => '/src/shared/cli/output/ConsoleTable.php', + 'phario\\phive\\cli\\context' => '/src/shared/cli/Context.php', + 'phario\\phive\\cli\\contextexception' => '/src/shared/cli/ContextException.php', + 'phario\\phive\\cli\\generalcontext' => '/src/shared/cli/GeneralContext.php', + 'phario\\phive\\cli\\input' => '/src/shared/cli/input/Input.php', + 'phario\\phive\\cli\\options' => '/src/shared/cli/Options.php', + 'phario\\phive\\cli\\output' => '/src/shared/cli/output/Output.php', + 'phario\\phive\\cli\\outputfactory' => '/src/shared/cli/output/OutputFactory.php', + 'phario\\phive\\cli\\outputlocator' => '/src/shared/cli/output/OutputLocator.php', + 'phario\\phive\\cli\\request' => '/src/shared/cli/Request.php', + 'phario\\phive\\cli\\requestexception' => '/src/shared/cli/RequestException.php', + 'phario\\phive\\cli\\runner' => '/src/shared/cli/Runner.php', + 'phario\\phive\\cli\\runnerexception' => '/src/shared/cli/RunnerException.php', + 'phario\\phive\\commandlocator' => '/src/commands/CommandLocator.php', + 'phario\\phive\\compatibilityservice' => '/src/services/phar/CompatibilityService.php', + 'phario\\phive\\composeralias' => '/src/shared/ComposerAlias.php', + 'phario\\phive\\composercommand' => '/src/commands/composer/ComposerCommand.php', + 'phario\\phive\\composercommandconfig' => '/src/commands/composer/ComposerCommandConfig.php', + 'phario\\phive\\composercontext' => '/src/commands/composer/ComposerContext.php', + 'phario\\phive\\composerservice' => '/src/commands/composer/ComposerService.php', + 'phario\\phive\\compositeauthconfig' => '/src/shared/config/CompositeAuthConfig.php', + 'phario\\phive\\config' => '/src/shared/config/Config.php', + 'phario\\phive\\configexception' => '/src/shared/exceptions/ConfigException.php', + 'phario\\phive\\configuredphar' => '/src/shared/phar/ConfiguredPhar.php', + 'phario\\phive\\configuredpharexception' => '/src/shared/phar/ConfiguredPharException.php', + 'phario\\phive\\curl' => '/src/shared/http/Curl.php', + 'phario\\phive\\curlconfig' => '/src/shared/http/CurlConfig.php', + 'phario\\phive\\curlconfigbuilder' => '/src/shared/http/CurlConfigBuilder.php', + 'phario\\phive\\curlconfigexception' => '/src/shared/exceptions/CurlConfigException.php', + 'phario\\phive\\curlexception' => '/src/shared/exceptions/CurlException.php', + 'phario\\phive\\curlhttpclient' => '/src/shared/http/CurlHttpClient.php', + 'phario\\phive\\defaultcommand' => '/src/commands/default/DefaultCommand.php', + 'phario\\phive\\defaultcommandconfig' => '/src/commands/default/DefaultCommandConfig.php', + 'phario\\phive\\directurlresolver' => '/src/services/resolver/DirectUrlResolver.php', + 'phario\\phive\\downloadfailedexception' => '/src/shared/exceptions/DownloadFailedException.php', + 'phario\\phive\\environment' => '/src/shared/environment/Environment.php', + 'phario\\phive\\environmentauthconfig' => '/src/shared/config/EnvironmentAuthConfig.php', + 'phario\\phive\\environmentexception' => '/src/shared/exceptions/EnvironmentException.php', + 'phario\\phive\\environmentlocator' => '/src/shared/environment/EnvironmentLocator.php', + 'phario\\phive\\errorexception' => '/src/shared/exceptions/ErrorException.php', + 'phario\\phive\\etag' => '/src/shared/http/ETag.php', + 'phario\\phive\\exception' => '/src/shared/exceptions/Exception.php', + 'phario\\phive\\executor' => '/src/shared/executor/Executor.php', + 'phario\\phive\\executorexception' => '/src/shared/exceptions/ExecutorException.php', + 'phario\\phive\\executorresult' => '/src/shared/executor/ExecutorResult.php', + 'phario\\phive\\factory' => '/src/Factory.php', + 'phario\\phive\\featuremissingexception' => '/src/shared/exceptions/FeatureMissingException.php', + 'phario\\phive\\filedownloader' => '/src/shared/download/FileDownloader.php', + 'phario\\phive\\filedownloaderexception' => '/src/shared/FileDownloaderException.php', + 'phario\\phive\\filemigration' => '/src/services/migration/FileMigration.php', + 'phario\\phive\\filenotwritableexception' => '/src/shared/exceptions/FileNotWritableException.php', + 'phario\\phive\\filestoragecachebackend' => '/src/shared/http/FileStorageCacheBackend.php', + 'phario\\phive\\git' => '/src/shared/Git.php', + 'phario\\phive\\gitawarephiveversion' => '/src/shared/version/GitAwarePhiveVersion.php', + 'phario\\phive\\gitexception' => '/src/shared/exceptions/GitException.php', + 'phario\\phive\\githubaliasresolver' => '/src/services/resolver/GithubAliasResolver.php', + 'phario\\phive\\githubaliasresolverexception' => '/src/GithubAliasResolverException.php', + 'phario\\phive\\githubrepository' => '/src/shared/repository/GithubRepository.php', + 'phario\\phive\\gitlabaliasresolver' => '/src/services/resolver/GitlabAliasResolver.php', + 'phario\\phive\\gitlabrepository' => '/src/shared/repository/GitlabRepository.php', + 'phario\\phive\\globalphivexmlconfig' => '/src/shared/config/GlobalPhiveXmlConfig.php', + 'phario\\phive\\gnupg' => '/src/shared/GnuPG.php', + 'phario\\phive\\gnupgkeydownloader' => '/src/services/key/gpg/GnupgKeyDownloader.php', + 'phario\\phive\\gnupgkeydownloaderexception' => '/src/shared/exceptions/GnupgKeyDownloaderException.php', + 'phario\\phive\\gnupgkeyimporter' => '/src/services/key/gpg/GnupgKeyImporter.php', + 'phario\\phive\\gnupgsignatureverifier' => '/src/services/signature/gpg/GnupgSignatureVerifier.php', + 'phario\\phive\\gnupgverificationresult' => '/src/services/signature/gpg/GnupgVerificationResult.php', + 'phario\\phive\\hash' => '/src/shared/hash/Hash.php', + 'phario\\phive\\helpcommand' => '/src/commands/help/HelpCommand.php', + 'phario\\phive\\homepharsxmlmigration' => '/src/services/migration/HomePharsXmlMigration.php', + 'phario\\phive\\homephivexmlmigration' => '/src/services/migration/HomePhiveXmlMigration.php', + 'phario\\phive\\httpclient' => '/src/shared/http/HttpClient.php', + 'phario\\phive\\httpexception' => '/src/shared/http/HttpException.php', + 'phario\\phive\\httpprogresshandler' => '/src/shared/http/HttpProgressHandler.php', + 'phario\\phive\\httpprogressrenderer' => '/src/shared/http/HttpProgressRenderer.php', + 'phario\\phive\\httpprogressupdate' => '/src/shared/http/HttpProgressUpdate.php', + 'phario\\phive\\httpresponse' => '/src/shared/http/HttpResponse.php', + 'phario\\phive\\httpresponseexception' => '/src/shared/http/HttpResponseException.php', + 'phario\\phive\\installationfailedexception' => '/src/shared/exceptions/InstallationFailedException.php', + 'phario\\phive\\installcommand' => '/src/commands/install/InstallCommand.php', + 'phario\\phive\\installcommandconfig' => '/src/commands/install/InstallCommandConfig.php', + 'phario\\phive\\installcommandconfigexception' => '/src/commands/install/InstallCommandConfigException.php', + 'phario\\phive\\installcontext' => '/src/commands/install/InstallContext.php', + 'phario\\phive\\installedphar' => '/src/shared/phar/InstalledPhar.php', + 'phario\\phive\\installservice' => '/src/services/phar/InstallService.php', + 'phario\\phive\\internalfilemigration' => '/src/services/migration/InternalFileMigration.php', + 'phario\\phive\\invalidhashexception' => '/src/shared/exceptions/InvalidHashException.php', + 'phario\\phive\\invalidxmlexception' => '/src/shared/exceptions/InvalidXmlException.php', + 'phario\\phive\\ioexception' => '/src/shared/exceptions/IOException.php', + 'phario\\phive\\jsondata' => '/src/shared/JsonData.php', + 'phario\\phive\\keydownloader' => '/src/services/key/KeyDownloader.php', + 'phario\\phive\\keyimporter' => '/src/services/key/KeyImporter.php', + 'phario\\phive\\keyimportresult' => '/src/services/key/KeyImportResult.php', + 'phario\\phive\\keyservice' => '/src/services/key/KeyService.php', + 'phario\\phive\\linkcreationfailedexception' => '/src/shared/exceptions/LinkCreationFailedException.php', + 'phario\\phive\\listcommand' => '/src/commands/list/ListCommand.php', + 'phario\\phive\\localaliasresolver' => '/src/services/resolver/LocalAliasResolver.php', + 'phario\\phive\\localfirstresolvingstrategy' => '/src/services/resolver/strategy/LocalFirstResolvingStrategy.php', + 'phario\\phive\\localphivexmlconfig' => '/src/shared/config/LocalPhiveXmlConfig.php', + 'phario\\phive\\localrepository' => '/src/shared/repository/LocalRepository.php', + 'phario\\phive\\localsourceslistfileloader' => '/src/shared/sources/LocalSourcesListFileLoader.php', + 'phario\\phive\\localsslcertificate' => '/src/shared/http/LocalSslCertificate.php', + 'phario\\phive\\migratecommand' => '/src/commands/migrate/MigrateCommand.php', + 'phario\\phive\\migratecommandconfig' => '/src/commands/migrate/MigrateCommandConfig.php', + 'phario\\phive\\migratecontext' => '/src/commands/migrate/MigrateContext.php', + 'phario\\phive\\migration' => '/src/services/migration/Migration.php', + 'phario\\phive\\migrationexception' => '/src/shared/exceptions/MigrationException.php', + 'phario\\phive\\migrationfactory' => '/src/services/migration/MigrationFactory.php', + 'phario\\phive\\migrationservice' => '/src/services/migration/MigrationService.php', + 'phario\\phive\\migrationsfailedexception' => '/src/shared/exceptions/MigrationsFailedException.php', + 'phario\\phive\\nogpgbinaryfoundexception' => '/src/shared/exceptions/NoGPGBinaryFoundException.php', + 'phario\\phive\\notfoundexception' => '/src/shared/exceptions/NotFoundException.php', + 'phario\\phive\\outdatedcommand' => '/src/commands/outdated/OutdatedCommand.php', + 'phario\\phive\\outdatedconfig' => '/src/commands/outdated/OutdatedConfig.php', + 'phario\\phive\\outdatedconfigexception' => '/src/commands/outdated/OutdatedConfigException.php', + 'phario\\phive\\outdatedcontext' => '/src/commands/outdated/OutdatedContext.php', + 'phario\\phive\\phar' => '/src/shared/phar/Phar.php', + 'phario\\phive\\pharalias' => '/src/shared/phar/PharAlias.php', + 'phario\\phive\\phardownloader' => '/src/services/phar/PharDownloader.php', + 'phario\\phive\\pharexception' => '/src/shared/exceptions/PharException.php', + 'phario\\phive\\pharidentifier' => '/src/shared/phar/PharIdentifier.php', + 'phario\\phive\\pharinstaller' => '/src/services/phar/PharInstaller.php', + 'phario\\phive\\pharinstallerexception' => '/src/shared/exceptions/PharInstallerException.php', + 'phario\\phive\\pharinstallerfactory' => '/src/services/phar/PharInstallerFactory.php', + 'phario\\phive\\pharinstallerlocator' => '/src/services/phar/PharInstallerLocator.php', + 'phario\\phive\\pharioaliasresolver' => '/src/services/resolver/PharIoAliasResolver.php', + 'phario\\phive\\phariorepository' => '/src/shared/repository/PharIoRepository.php', + 'phario\\phive\\pharregistry' => '/src/shared/PharRegistry.php', + 'phario\\phive\\pharregistryexception' => '/src/shared/exceptions/PharRegistryException.php', + 'phario\\phive\\pharservice' => '/src/services/phar/PharService.php', + 'phario\\phive\\pharurl' => '/src/shared/phar/PharUrl.php', + 'phario\\phive\\phivecontext' => '/src/PhiveContext.php', + 'phario\\phive\\phiveversion' => '/src/shared/version/PhiveVersion.php', + 'phario\\phive\\phivexmlconfig' => '/src/shared/config/PhiveXmlConfig.php', + 'phario\\phive\\phivexmlconfigfilelocator' => '/src/shared/config/PhiveXmlConfigFileLocator.php', + 'phario\\phive\\projectphivexmlmigration' => '/src/services/migration/ProjectPhiveXmlMigration.php', + 'phario\\phive\\publickey' => '/src/services/key/PublicKey.php', + 'phario\\phive\\publickeyexception' => '/src/shared/exceptions/PublicKeyException.php', + 'phario\\phive\\publickeyreader' => '/src/services/key/gpg/PublicKeyReader.php', + 'phario\\phive\\purgecommand' => '/src/commands/purge/PurgeCommand.php', + 'phario\\phive\\purgecontext' => '/src/commands/purge/PurgeContext.php', + 'phario\\phive\\ratelimit' => '/src/shared/http/RateLimit.php', + 'phario\\phive\\release' => '/src/shared/phar/Release.php', + 'phario\\phive\\releasecollection' => '/src/shared/phar/ReleaseCollection.php', + 'phario\\phive\\releaseexception' => '/src/shared/exceptions/ReleaseException.php', + 'phario\\phive\\releaseselector' => '/src/services/phar/ReleaseSelector.php', + 'phario\\phive\\remotefirstresolvingstrategy' => '/src/services/resolver/strategy/RemoteFirstResolvingStrategy.php', + 'phario\\phive\\remotesourceslistfileloader' => '/src/shared/sources/RemoteSourcesListFileLoader.php', + 'phario\\phive\\removalservice' => '/src/services/phar/RemovalService.php', + 'phario\\phive\\removecommand' => '/src/commands/remove/RemoveCommand.php', + 'phario\\phive\\removecommandconfig' => '/src/commands/remove/RemoveCommandConfig.php', + 'phario\\phive\\removecontext' => '/src/commands/remove/RemoveContext.php', + 'phario\\phive\\requestedphar' => '/src/shared/phar/RequestedPhar.php', + 'phario\\phive\\requestedpharresolver' => '/src/services/resolver/RequestedPharResolver.php', + 'phario\\phive\\requestedpharresolverfactory' => '/src/services/resolver/RequestedPharResolverFactory.php', + 'phario\\phive\\requestedpharresolverservice' => '/src/services/resolver/RequestedPharResolverService.php', + 'phario\\phive\\requestedpharresolverservicebuilder' => '/src/services/resolver/RequestedPharResolverServiceBuilder.php', + 'phario\\phive\\resetcommand' => '/src/commands/reset/ResetCommand.php', + 'phario\\phive\\resetcommandconfig' => '/src/commands/reset/ResetCommandConfig.php', + 'phario\\phive\\resetcontext' => '/src/commands/reset/ResetContext.php', + 'phario\\phive\\resolveexception' => '/src/shared/exceptions/ResolveException.php', + 'phario\\phive\\resolvingstrategy' => '/src/services/resolver/strategy/ResolvingStrategy.php', + 'phario\\phive\\retryinghttpclient' => '/src/shared/http/RetryingHttpClient.php', + 'phario\\phive\\ringdowncurlhttpclient' => '/src/shared/http/RingdownCurlHttpClient.php', + 'phario\\phive\\selfupdatecommand' => '/src/commands/selfupdate/SelfupdateCommand.php', + 'phario\\phive\\sha1hash' => '/src/shared/hash/sha/Sha1Hash.php', + 'phario\\phive\\sha256hash' => '/src/shared/hash/sha/Sha256Hash.php', + 'phario\\phive\\sha384hash' => '/src/shared/hash/sha/Sha384Hash.php', + 'phario\\phive\\sha512hash' => '/src/shared/hash/sha/Sha512Hash.php', + 'phario\\phive\\signatureverifier' => '/src/services/signature/SignatureVerifier.php', + 'phario\\phive\\skelcommand' => '/src/commands/skel/SkelCommand.php', + 'phario\\phive\\skelcommandconfig' => '/src/commands/skel/SkelCommandConfig.php', + 'phario\\phive\\skelcontext' => '/src/commands/skel/SkelContext.php', + 'phario\\phive\\source' => '/src/shared/sources/Source.php', + 'phario\\phive\\sourcerepository' => '/src/shared/repository/SourceRepository.php', + 'phario\\phive\\sourceslist' => '/src/shared/sources/SourcesList.php', + 'phario\\phive\\sourceslistexception' => '/src/shared/exceptions/SourcesListException.php', + 'phario\\phive\\sourceslistfileloader' => '/src/shared/sources/SourcesListFileLoader.php', + 'phario\\phive\\staticphiveversion' => '/src/shared/version/StaticPhiveVersion.php', + 'phario\\phive\\statuscommand' => '/src/commands/status/StatusCommand.php', + 'phario\\phive\\statuscommandconfig' => '/src/commands/status/StatusCommandConfig.php', + 'phario\\phive\\statuscontext' => '/src/commands/status/StatusContext.php', + 'phario\\phive\\supportedrelease' => '/src/shared/phar/SupportedRelease.php', + 'phario\\phive\\targetdirectorylocator' => '/src/shared/TargetDirectoryLocator.php', + 'phario\\phive\\tokenauthentication' => '/src/shared/http/authentication/TokenAuthentication.php', + 'phario\\phive\\trustedcollection' => '/src/services/key/TrustedCollection.php', + 'phario\\phive\\unixoidenvironment' => '/src/shared/environment/UnixoidEnvironment.php', + 'phario\\phive\\unixoidpharinstaller' => '/src/services/phar/UnixoidPharInstaller.php', + 'phario\\phive\\unsupportedrelease' => '/src/shared/phar/UnsupportedRelease.php', + 'phario\\phive\\unsupportedversionconstraintexception' => '/src/shared/exceptions/UnsupportedVersionConstraintException.php', + 'phario\\phive\\updatecommand' => '/src/commands/update/UpdateCommand.php', + 'phario\\phive\\updatecommandconfig' => '/src/commands/update/UpdateCommandConfig.php', + 'phario\\phive\\updatecontext' => '/src/commands/update/UpdateContext.php', + 'phario\\phive\\updaterepositorylistcommand' => '/src/commands/update-repository-list/UpdateRepositoryListCommand.php', + 'phario\\phive\\url' => '/src/shared/Url.php', + 'phario\\phive\\urlrepository' => '/src/shared/repository/UrlRepository.php', + 'phario\\phive\\usedphar' => '/src/shared/phar/UsedPhar.php', + 'phario\\phive\\userfilemigration' => '/src/services/migration/UserFileMigration.php', + 'phario\\phive\\verificationfailedexception' => '/src/shared/exceptions/VerificationFailedException.php', + 'phario\\phive\\verificationresult' => '/src/services/signature/VerificationResult.php', + 'phario\\phive\\versioncommand' => '/src/commands/version/VersionCommand.php', + 'phario\\phive\\windowsenvironment' => '/src/shared/environment/WindowsEnvironment.php', + 'phario\\phive\\windowspharinstaller' => '/src/services/phar/WindowsPharInstaller.php', + 'phario\\phive\\xmlfile' => '/src/shared/XmlFile.php', + 'phario\\version\\abstractversionconstraint' => '/vendor/phar-io/version/src/constraints/AbstractVersionConstraint.php', + 'phario\\version\\andversionconstraintgroup' => '/vendor/phar-io/version/src/constraints/AndVersionConstraintGroup.php', + 'phario\\version\\anyversionconstraint' => '/vendor/phar-io/version/src/constraints/AnyVersionConstraint.php', + 'phario\\version\\buildmetadata' => '/vendor/phar-io/version/src/BuildMetaData.php', + 'phario\\version\\exactversionconstraint' => '/vendor/phar-io/version/src/constraints/ExactVersionConstraint.php', + 'phario\\version\\exception' => '/vendor/phar-io/version/src/exceptions/Exception.php', + 'phario\\version\\greaterthanorequaltoversionconstraint' => '/vendor/phar-io/version/src/constraints/GreaterThanOrEqualToVersionConstraint.php', + 'phario\\version\\invalidprereleasesuffixexception' => '/vendor/phar-io/version/src/exceptions/InvalidPreReleaseSuffixException.php', + 'phario\\version\\invalidversionexception' => '/vendor/phar-io/version/src/exceptions/InvalidVersionException.php', + 'phario\\version\\nobuildmetadataexception' => '/vendor/phar-io/version/src/exceptions/NoBuildMetaDataException.php', + 'phario\\version\\noprereleasesuffixexception' => '/vendor/phar-io/version/src/exceptions/NoPreReleaseSuffixException.php', + 'phario\\version\\orversionconstraintgroup' => '/vendor/phar-io/version/src/constraints/OrVersionConstraintGroup.php', + 'phario\\version\\prereleasesuffix' => '/vendor/phar-io/version/src/PreReleaseSuffix.php', + 'phario\\version\\specificmajorandminorversionconstraint' => '/vendor/phar-io/version/src/constraints/SpecificMajorAndMinorVersionConstraint.php', + 'phario\\version\\specificmajorversionconstraint' => '/vendor/phar-io/version/src/constraints/SpecificMajorVersionConstraint.php', + 'phario\\version\\unsupportedversionconstraintexception' => '/vendor/phar-io/version/src/exceptions/UnsupportedVersionConstraintException.php', + 'phario\\version\\version' => '/vendor/phar-io/version/src/Version.php', + 'phario\\version\\versionconstraint' => '/vendor/phar-io/version/src/constraints/VersionConstraint.php', + 'phario\\version\\versionconstraintparser' => '/vendor/phar-io/version/src/VersionConstraintParser.php', + 'phario\\version\\versionconstraintvalue' => '/vendor/phar-io/version/src/VersionConstraintValue.php', + 'phario\\version\\versionnumber' => '/vendor/phar-io/version/src/VersionNumber.php' + ); + } + + $class = strtolower($class); + + if (isset($classes[$class])) { + require 'phar://phive.phar/' . $classes[$class]; + } + } +); + +Phar::mapPhar('phive.phar'); + +$rc = (new Factory(new Cli\Request($_SERVER['argv']), new StaticPhiveVersion('0.16.0')))->getRunner()->run(); +exit($rc); + +__HALT_COMPILER(); ?> +nX= +phive.phar+vendor/phar-io/filesystem/src/Directory.php9?g4vendor/phar-io/filesystem/src/DirectoryException.php9?g=v"+vendor/phar-io/filesystem/src/Exception.phpf9?g^$&vendor/phar-io/filesystem/src/File.phpf9?gC+r*vendor/phar-io/filesystem/src/Filename.php 9?gVs)3vendor/phar-io/filesystem/src/FilenameException.phpm9?gbfo2vendor/phar-io/filesystem/src/LastModifiedDate.php9?gT[(vendor/phar-io/executor/src/Executor.php9?g#j~1vendor/phar-io/executor/src/ExecutorException.php9?gg13,vendor/phar-io/version/src/VersionNumber.php9?gkJ}r6vendor/phar-io/manifest/src/ManifestDocumentMapper.php9?gq.vendor/phar-io/manifest/src/ManifestLoader.php9?gq̤2vendor/phar-io/manifest/src/ManifestSerializer.php9?gMۂEvendor/phar-io/manifest/src/exceptions/ElementCollectionException.php9?g0_Uj4vendor/phar-io/manifest/src/exceptions/Exception.php9?gPJvendor/phar-io/manifest/src/exceptions/InvalidApplicationNameException.php19?gFMV@vendor/phar-io/manifest/src/exceptions/InvalidEmailException.php9?g+ˤ>vendor/phar-io/manifest/src/exceptions/InvalidUrlException.php9?g/YDvendor/phar-io/manifest/src/exceptions/ManifestDocumentException.php9?g' oKvendor/phar-io/manifest/src/exceptions/ManifestDocumentLoadingException.php9?g!գJvendor/phar-io/manifest/src/exceptions/ManifestDocumentMapperException.php9?g0`'iCvendor/phar-io/manifest/src/exceptions/ManifestElementException.php9?g%#mߤBvendor/phar-io/manifest/src/exceptions/ManifestLoaderException.php9?g4oBvendor/phar-io/manifest/src/exceptions/NoEmailAddressException.php9?g4q:2vendor/phar-io/manifest/src/values/Application.php9?g5f=6vendor/phar-io/manifest/src/values/ApplicationName.php9?g-vendor/phar-io/manifest/src/values/Author.php9?gvendor/phar-io/manifest/src/values/PhpExtensionRequirement.php9?gw(<vendor/phar-io/manifest/src/values/PhpVersionRequirement.php$9?g"&2vendor/phar-io/manifest/src/values/Requirement.php9?g 1q<vendor/phar-io/manifest/src/values/RequirementCollection.php\9?gX?Dvendor/phar-io/manifest/src/values/RequirementCollectionIterator.php9?g +vendor/phar-io/manifest/src/values/Type.php9?g<2*vendor/phar-io/manifest/src/values/Url.php9?g#lͣ1vendor/phar-io/manifest/src/xml/AuthorElement.php9?gn2;vendor/phar-io/manifest/src/xml/AuthorElementCollection.phpS9?gS(72vendor/phar-io/manifest/src/xml/BundlesElement.phpz9?gf}4vendor/phar-io/manifest/src/xml/ComponentElement.php9?g`$Sg>vendor/phar-io/manifest/src/xml/ComponentElementCollection.php\9?gT6 3vendor/phar-io/manifest/src/xml/ContainsElement.php9?gaΤ4vendor/phar-io/manifest/src/xml/CopyrightElement.php 9?g}t)T5vendor/phar-io/manifest/src/xml/ElementCollection.php9?g砲L.vendor/phar-io/manifest/src/xml/ExtElement.php9?gK8vendor/phar-io/manifest/src/xml/ExtElementCollection.phpJ9?gQ4vendor/phar-io/manifest/src/xml/ExtensionElement.php9?gazj2vendor/phar-io/manifest/src/xml/LicenseElement.php|9?g\tTX4vendor/phar-io/manifest/src/xml/ManifestDocument.php 9?g9S3vendor/phar-io/manifest/src/xml/ManifestElement.phpn9?gp.vendor/phar-io/manifest/src/xml/PhpElement.php9?gwO3vendor/phar-io/manifest/src/xml/RequiresElement.phpK9?g^ܗ)src/commands/composer/ComposerContext.php9?gx)src/commands/composer/ComposerCommand.php9?g`^/src/commands/composer/ComposerCommandConfig.php9?gl Ť)src/commands/composer/ComposerService.php 9?g,'src/commands/default/DefaultCommand.php,9?ge-src/commands/default/DefaultCommandConfig.php9?gt@ksrc/commands/help/help.md +9?g$4w!src/commands/help/HelpCommand.php9?gJD6src/commands/install/InstallCommandConfigException.php9?g2='src/commands/install/InstallContext.phpN9?g|S-src/commands/install/InstallCommandConfig.php{9?gk'src/commands/install/InstallCommand.php +9?g;!src/commands/list/ListCommand.php9?gD|'src/commands/migrate/MigrateContext.php9?gPX'e'src/commands/migrate/MigrateCommand.php9?g "-src/commands/migrate/MigrateCommandConfig.phpt9?gtW¤1src/commands/outdated/OutdatedConfigException.php9?g-4)src/commands/outdated/OutdatedContext.php49?g#>(src/commands/outdated/OutdatedConfig.php99?g )src/commands/outdated/OutdatedCommand.phpQ9?gPK5#src/commands/purge/PurgeContext.php9?gfP#src/commands/purge/PurgeCommand.phps9?g(Ҥ%src/commands/remove/RemoveContext.php9?gQ'%src/commands/remove/RemoveCommand.php 9?g0M+src/commands/remove/RemoveCommandConfig.php9?g/@#src/commands/reset/ResetContext.php9?gEŴ#src/commands/reset/ResetCommand.php09?gr#\)src/commands/reset/ResetCommandConfig.php9?gIŤ-src/commands/selfupdate/SelfupdateCommand.php9?ggYU!src/commands/skel/SkelContext.php29?gX_Mʹ'src/commands/skel/SkelCommandConfig.php9?g!E!src/commands/skel/SkelCommand.phph 9?gV%src/commands/status/StatusContext.php9?gt&%src/commands/status/StatusCommand.php 9?g+src/commands/status/StatusCommandConfig.phpI +9?g<U氝Csrc/commands/update-repository-list/UpdateRepositoryListCommand.php9?g8Hj%src/commands/update/UpdateContext.php9?gzf%src/commands/update/UpdateCommand.php9?gk+src/commands/update/UpdateCommandConfig.php 9?g@$'src/commands/version/VersionCommand.php 9?gOsrc/commands/CommandLocator.phpq +9?gX@)src/services/checksum/ChecksumService.php89?g 2w+src/services/key/gpg/GnupgKeyDownloader.phpt +9?gݴ)src/services/key/gpg/GnupgKeyImporter.php9?g~(src/services/key/gpg/PublicKeyReader.php9?g5W"src/services/key/KeyDownloader.php9?g}.e src/services/key/KeyImporter.php9?gj$src/services/key/KeyImportResult.phpa9?gAGsrc/services/key/KeyService.php 9?gUsrc/services/key/PublicKey.php9?gr:F?&src/services/key/TrustedCollection.php9?ga~ (src/services/migration/FileMigration.php9?gt ;Y0src/services/migration/HomePhiveXmlMigration.php>9?g?겴0src/services/migration/InternalFileMigration.phpY9?gm@$src/services/migration/Migration.php9?gX'mY3src/services/migration/ProjectPhiveXmlMigration.php9?g;9̴,src/services/migration/UserFileMigration.php9?g`p0src/services/migration/HomePharsXmlMigration.phpl9?g]]&+src/services/migration/MigrationFactory.php9?grA+src/services/migration/MigrationService.php9?gH4$src/services/phar/PharDownloader.phpV9?gx^#src/services/phar/PharInstaller.php 9?g 5JԴ*src/services/phar/PharInstallerFactory.php9?g *src/services/phar/PharInstallerLocator.php-9?gV!src/services/phar/PharService.php9?gs%src/services/phar/ReleaseSelector.php 9?gm*src/services/phar/UnixoidPharInstaller.php79?gn0ޱ*src/services/phar/WindowsPharInstaller.php9?gG$src/services/phar/InstallService.phpv 9?g*src/services/phar/CompatibilityService.php 9?g>~^$src/services/phar/RemovalService.phpu9?g%<src/services/resolver/strategy/AbstractResolvingStrategy.php 9?g+ז >src/services/resolver/strategy/LocalFirstResolvingStrategy.php]9?gqM;B?src/services/resolver/strategy/RemoteFirstResolvingStrategy.php^9?grٴ4src/services/resolver/strategy/ResolvingStrategy.php9?g-W$7src/services/resolver/AbstractRequestedPharResolver.php09?g82Q+src/services/resolver/DirectUrlResolver.php9?g7k-src/services/resolver/GitlabAliasResolver.phpG9?gv㵴,src/services/resolver/LocalAliasResolver.php9?g_-src/services/resolver/PharIoAliasResolver.php 9?gIDX/src/services/resolver/RequestedPharResolver.php49?gWR/6src/services/resolver/RequestedPharResolverFactory.php49?g.?L6src/services/resolver/RequestedPharResolverService.phpW9?gzN=src/services/resolver/RequestedPharResolverServiceBuilder.php9?g+&-src/services/resolver/GithubAliasResolver.phpt 9?g@Zt6src/services/signature/gpg/GnupgVerificationResult.php9?gkL5src/services/signature/gpg/GnupgSignatureVerifier.php89?ge '8,src/services/signature/SignatureVerifier.php9?g>P-src/services/signature/VerificationResult.php49?gJOsrc/shared/cli/input/Input.php9?g()DU%src/shared/cli/input/ConsoleInput.php9?gK﨤'src/shared/cli/output/ConsoleOutput.php +9?gyT:|&src/shared/cli/output/ConsoleTable.php9?grd#; src/shared/cli/output/Output.php9?g^J'src/shared/cli/output/OutputFactory.php9?g[Q@.src/shared/cli/output/ColoredConsoleOutput.php[9?g;b'src/shared/cli/output/OutputLocator.phpg9?g Asrc/shared/cli/Command.php9?gǯG*src/shared/cli/CommandLocatorException.php9?g&t*src/shared/cli/CommandOptionsException.php9?g<@src/shared/cli/Context.phpz9?g4nN#src/shared/cli/ContextException.php9?g/U#src/shared/cli/RequestException.php9?gGMꪴ"src/shared/cli/RunnerException.php9?gm;gsrc/shared/cli/error.txtR9?g'!src/shared/cli/CommandLocator.php9?g7o6!src/shared/cli/GeneralContext.phpB 9?gsrc/shared/cli/Options.php9?g6src/shared/cli/Request.php9?gu7Nsrc/shared/cli/Runner.phpV9?gE0N src/shared/config/AuthConfig.php9?g,d#src/shared/config/AuthXmlConfig.php 9?gS঴.src/shared/config/AuthXmlConfigFileLocator.phpx9?gf6ô)src/shared/config/CompositeAuthConfig.php9?g1z*src/shared/config/GlobalPhiveXmlConfig.php@9?g_fK[)src/shared/config/LocalPhiveXmlConfig.php9?gqٴ/src/shared/config/PhiveXmlConfigFileLocator.php)9?gWQwŴ+src/shared/config/EnvironmentAuthConfig.php9?gVo$src/shared/config/PhiveXmlConfig.php#9?gh} Msrc/shared/config/Config.php9?g5&src/shared/download/FileDownloader.phpj 9?gFcz-src/shared/environment/EnvironmentLocator.php9?g-src/shared/environment/WindowsEnvironment.php9?g |-src/shared/environment/UnixoidEnvironment.php +9?gRs&src/shared/environment/Environment.php=9?g'f)'src/shared/exceptions/AuthException.php9?g Z +P)src/shared/exceptions/ConfigException.php9?g .´-src/shared/exceptions/CurlConfigException.php9?g?c'src/shared/exceptions/CurlException.php9?g α1src/shared/exceptions/DownloadFailedException.php9?g˫)$.src/shared/exceptions/EnvironmentException.php9?gbb(src/shared/exceptions/ErrorException.php9?g IW#src/shared/exceptions/Exception.php9?g +<+src/shared/exceptions/ExecutorException.php9?g.1src/shared/exceptions/FeatureMissingException.php9?g2src/shared/exceptions/FileNotWritableException.php9?gg )&src/shared/exceptions/GitException.php9?g +=5src/shared/exceptions/GnupgKeyDownloaderException.php9?gm8%src/shared/exceptions/IOException.php9?g +˸˴5src/shared/exceptions/InstallationFailedException.php9?gu.src/shared/exceptions/InvalidHashException.php9?g`o-src/shared/exceptions/InvalidXmlException.php9?g}5src/shared/exceptions/LinkCreationFailedException.php9?gH@!g,src/shared/exceptions/MigrationException.php9?g g3src/shared/exceptions/MigrationsFailedException.php9?gtshg3src/shared/exceptions/NoGPGBinaryFoundException.php9?g G?+src/shared/exceptions/NotFoundException.php9?gA)'src/shared/exceptions/PharException.php9?g +2eh״0src/shared/exceptions/PharInstallerException.php9?gB/src/shared/exceptions/PharRegistryException.php9?gQz-,src/shared/exceptions/PublicKeyException.php9?gL*src/shared/exceptions/ReleaseException.php9?g |*src/shared/exceptions/ResolveException.php9?g Db.src/shared/exceptions/SourcesListException.php9?g)z/?src/shared/exceptions/UnsupportedVersionConstraintException.php9?gR5src/shared/exceptions/VerificationFailedException.php9?gV& src/shared/executor/Executor.php9?gZ'&/&src/shared/executor/ExecutorResult.php"9?g8 src/shared/hash/sha/Sha1Hash.phpL9?g|"src/shared/hash/sha/Sha256Hash.phpR9?g$U"src/shared/hash/sha/Sha384Hash.phpR9?g-G"src/shared/hash/sha/Sha512Hash.phpS9?gCcsrc/shared/hash/BaseHash.phpN9?gqQsrc/shared/hash/Hash.php9?gB. 6src/shared/http/authentication/BasicAuthentication.php9?gB-7src/shared/http/authentication/BearerAuthentication.php9?g/'I6src/shared/http/authentication/TokenAuthentication.php9?g/E src/shared/http/CacheBackend.phpL9?gS4src/shared/http/Curl.phpu +9?glBi!src/shared/http/HttpException.php9?gfd_'src/shared/http/HttpProgressHandler.php9?g Y_)src/shared/http/HttpResponseException.php9?gmȑA"src/shared/http/Authentication.php9?gsrc/shared/http/CurlConfig.phpG9?g %src/shared/http/CurlConfigBuilder.php9?g src/shared/http/ETag.php29?gPxpr+src/shared/http/FileStorageCacheBackend.php9?g9[i(src/shared/http/HttpProgressRenderer.php +9?gF&src/shared/http/HttpProgressUpdate.php9?g'mKdĤ'src/shared/http/LocalSslCertificate.php9?gۥsrc/shared/http/RateLimit.php(9?g$src/shared/http/HttpClient.php9?g1K e src/shared/http/HttpResponse.php9?g|&src/shared/http/RetryingHttpClient.php9?gp+@*src/shared/http/RingdownCurlHttpClient.phpn 9?g T"src/shared/http/CurlHttpClient.php9?gCDU+src/shared/phar/ConfiguredPharException.php9?g/4@ý"src/shared/phar/PharIdentifier.php9?glIsrc/shared/phar/Release.php9?g8Eȴ%src/shared/phar/ReleaseCollection.phpH9?gcG!src/shared/phar/InstalledPhar.php?9?g)7src/shared/phar/PharAlias.phpv9?gmy&src/shared/phar/UnsupportedRelease.php9?g- "src/shared/phar/ConfiguredPhar.php3 9?gosrc/shared/phar/Phar.php9?g=@j!src/shared/phar/RequestedPhar.php +9?g0$src/shared/phar/SupportedRelease.php9?gO1src/shared/phar/UsedPhar.phpv9?g<src/shared/phar/PharUrl.php9?gb,g*src/shared/repository/SourceRepository.php9?g*dд*src/shared/repository/GitlabRepository.phpI +9?g 2t)src/shared/repository/LocalRepository.php9?g im*src/shared/repository/PharIoRepository.php( +9?g{'src/shared/repository/UrlRepository.php9?gV$t*src/shared/repository/GithubRepository.php +9?gۤ,src/shared/sources/SourcesListFileLoader.php9?g9>K1src/shared/sources/LocalSourcesListFileLoader.phpC9?gnCҤ2src/shared/sources/RemoteSourcesListFileLoader.php9?gsSDsrc/shared/sources/Source.php\9?g [ "src/shared/sources/SourcesList.php 9?g!dN+src/shared/version/GitAwarePhiveVersion.php9?g%BǴ#src/shared/version/PhiveVersion.php9?gL++)src/shared/version/StaticPhiveVersion.phpa9?g`Vkشsrc/shared/ComposerAlias.php^9?gI&src/shared/FileDownloaderException.php9?gfsrc/shared/Git.phpV9?g5<Ӵsrc/shared/JsonData.phpM9?g讛 %src/shared/TargetDirectoryLocator.php9?gyH>Ofsrc/shared/PharRegistry.php~&9?gc src/shared/XmlFile.phpm 9?g8jsrc/shared/GnuPG.phpX9?g0src/shared/Url.php/ 9?g Ф$src/GithubAliasResolverException.php9?gQsrc/PhiveContext.php9?g4ksrc/autoload.phpS9?g1 "w|src/Factory.phpF9?g +(conf/auth.skeleton.xmld9?gR&ٴ conf/auth.xsd9?g:%conf/pharBat.template%9?g'conf/phive.skeleton.xml9?gRconf/pgp-keyservers.phpG9?gAfdWmoF _v$et:s"M)DYHw$h߇;Xd7OD>$RO B դx@sP `)1SOý&L{ aZ[0 KS`|{;S|1 ,_$<(q)`>Фۚ{ڛ_b'(t\x܅Yr[ -݂zrK^"rεx0w`ZoUPlxllIf u1@I6ɨ.KdM S(L.6"113'QWMEW@H1L Sk `6Q?ȟ!KXR!zBZL)v?1ȭjiWDcw%<]”qQ{dYh9׆3d.aIqV;î˴X)BY!L.;魓X'm[fW14$`xN69ca 5h39^7B!i?zuj?? ys?+oVak}H75(­ͣ壖{"yˍ ko +AlنWZ!Mzwb%cQqqV,u8?N4 ҷm= 0~ō::Air4] +/uZ&o2OD#hI,XlZA0=K2xLnqE +Ls I] ȃr@DLw(nhyUb(7yXh?bG7Ͳx Eȱ +@ =O8Mp$<8pɠz[hGMs玟MKE6Uw>)c<=Dŝ߇"qiɺMG}=>'Z.*N6o}kkܟ ajMqg!Yϝ2>C>N30Cviۏ8ΰmiqh Qq'ĢęqeiIXXZ徫9ju*$% Iy* +r1$9MID~FJ4"WMJ/[oJS6"4iKu"ipt6޵v!w|6 /z7;߽Oܧ 4HM|[ |r43쒵_l2g&l9^I$a*}Jf+iH>܃_)*(in&3*FJ3Y KzRJsB.pS՘ /\r5'$CpJZŮtZSs]#8@*AP;f[&£ O}A_?Y[O\OJ]i_%&4BmM)?EZ@xuLY? ExszvU˴ :P 4_*{d:LΚcSzڮTuζxgʧY-9/T5XI5vڴ#rXE>.ۣd,J~gmnW+*Ҹ{4v؏I4ΙWppxs83δcp5k^EgM|;n⩦Q-Ns{98r6MeO +|A[́G[$WmFSӪ7}?O0wJT({ U@0\4:*叓6 %ݽM,G)<\2&R7|Jpb `E <8#Tz__-Ӣ:pp%Lq[;9"6O>i%Vf'(hG% rY=@3Wh4mOm|{-H M,U>0oK]éVԧDYIUPp;PIQ֘tt2޽ҹy׵cB!%`Nž/e#ЧpSU'Y~\m6>,nbImKN.w$9@˴mTHE(b{n>Ib<ݱ#;V7T(^5i=^}BЖ֌z+G݋uC&ojcM!| yO;r`)j؎2.?̣T1cɞtoX7 )o-P{F0,`TcX1ot۰˳pI򜦓H\k{dG fj11D]'&pفX˝լڼeEaJ$ܡ˃+*`®wJ>0)>J h:C:'ipZ&f@.oC+3GD0:*-濌 =RkNC(ڴ'pfi@µӝd'CLz!Q:Ư-"ԛr(/c5\ruO/Q>9#>\ҤUEož&'zKR9)F%`~U!u 8_/Ue믃׳#,W8ю[t#3={kʛO}|]  6xYg{j8W*jw/[,"L{B*@@6[ϋlZeaGYO{3M µ:WxR@ydM;kRCPCױ +[O"esEr0 &Ay$ ;$2% dҐI@gLl¨thM\O}4tȺ^GhxN̐=\cJE :b .<[p /d\ Tݱ2"]4qG [Ƽx̿6_V4 ̦FHX.+I=eFrf\FHyP2D;v)*H.`%j#tgs/Xj]`gT<%J.|d֚$JL=HtȽ{.KM2פ=aȣyTCIT?PPb0ef=B/Q7/C6aۘdCE":]w1#/ۘ 1]W_&e}#!;3pt ?3KϏGNȑs^CYG~չ;)>ϏEm@>똩.ZzG< Ֆv(];$_ΉC:5*#7GBmzkz2!% >+ ?F/ZۻYe+kց4 SLFi(UQCl8 ~Cs’KKNpZ"#ƾKŖ [~o BU1j5InX3l9<=뜲q'w <*%3n>6,;g%31avnmSr2&[uOUp [֫\W>ﮪo +Eu.ܻ!"kbMRG.vq&[4_\]Ty38@>TtjЈFhFk.@[v 7qQJX-\q!4Yh){3;tI +]'3 +l}V'B P3dH r'ܵ[|C@+?V>MEƸ'pQFF-.LlMMQ3Λm ;f pe޼m& Q9*z v;7Fh$χ:eB?2B=U]@"$_?BH]DaC.P*լ7qObVw;Ag0?ZKX34.r|fyl|zWlD9{9WH!CV˦W"q($ cү_̵ɮe[= [JxB?z#({6<7𩦈rGƣRUɞ7A(Waui]Ȩǝs{:s픐NӈB[8[K\iY h|OW:Qh̶W.ѝ:ḍ.#)]o"|:AbY^`edTKt: 6^2*^NywLjLL"oUU!ZHdh>[jv +젱S|^seu,mΞ2?; +SUʏD9ڱ=5_ՠuJv P艶_g9xS-=9 v!Ɛ< ++cO\-)f\K@m8Չvj;z/ya % {W1L֓*[VdT?fWYZFz(6F9@8*CvrMF"K_*y J1\sAV?07RHƟF MRHTM#)SnQ\XYSdͣ1mT*//=fo Fn7h [+Wf]aqӾQq%On^J8㽙 lBxڤY +j~/ݶIw{"+*' S!?Qيxu.QtF*U;%8qO^larHWFW]]F/1z}Bh(^B]FW_F1xF: NX3̰8gęay3-h%Z<# tED-=j_.b?@l\aT÷_Qe(`(7^!WFV*CAb9cwS/Ǟ76U{.v^/Hjh9/5(* yZ|\Ҽ6tcEj-R4'o^ϋyVwl Q@ +b~Q:޶i,A{[[lդ 8L%t]UQ V8Ose5>pm -=@%@&)}س1|Fy'po 7+kG ANR ?IRM JʮDF棞_vFxe,~CM}'>;27dl3>Dd3R/xbs2,PYj63sMV "M2hoH,ܷ/ ؈ hlHB~A{_̼ԬmKY_pgH/9zΧxy/xE1 +0 =Ȩ$Pt*ᝒic_V1l*kt)Ry4E};#Jvp>uT]o0}ϯ@=V(E(*L.R-)>9_@H>{V&T!FDf'Qz5jIcYBDct}4bKqO06B>_q8.q&8y/RhTAsӽvj 4{{ +i/b tVrqjs,/tk_ڨn]T](eo)a"(}cԃ tLtr`Bj0;DzQDbx +qWzǹ 'x{6~|5oP=7-Ɩ2h4Pk@R`'OWZ +Mxn}?`Xj&Tvx6.0&kYSq;!:.6"<-FjM+by)ǸnLFh#xRy|u:"+%gs8Bh7)4 gyg2u ܂IPaB`w,N)Yj7bLB-fI+ Wx>+K:e^,9y9%\dEؕSYa&qr[Rd|=)R`_E܁P64M$Uw*۷8 g@V|!+ߚr=lUcɱ_c5l _]<5h:Yh&2т;{?ks6~ތ;|%n˖'vs58I1 hG|$Gnzb}b͛tB~D8Br˅ܤ(` uEJ|5=|t2QmE? ~\~FE3q#$Ƴ;ї BP?TםNeMP\ /)r 6=HXX1uGF9@.ӄPJzD6] i12A~r?/kF$Md,DȒ,\Ln&pEJ8$rҌ@с~Ǐ]v:SxL8nPx +rL$ VޫTiQ K&t'UQrDJ$@7,lQV ?aB̗ne4[=C&5@2]SsWD&%|slY1.1 C4PMi4j 욈 +Mn}pg?3ឝvNff'+DӢGquuʹ8:&R&9&YQ"O9eQ$<ߪmh)y<|ol#QXk"ֻSl)ܤ꾷J-&˸oUD^̯xۧoe aɑ<5`\ި)VW?1K"$% !c܈[Qn{b,DH.6?Nrr%4nnTSLn'Pyp?ё7&xu(4篏Œ8Vec{c?TnaЀWqrV ļ9 +M\i+6-P;|QdXa:U9fހrp2;=myd-#Hn4o ɋH4jKT( h!GV򑗰RN%zk.Հh=_f]NW~%L8[tmTyVB.I#?'XϿfQz"Jÿ{PtepX90seQ䘭I߫[~p>9n~ݍo9`mNuB:Nai5n;m3SҺz2j<Ë_#*Zn΅ E&Kܾ&˛?h:vv=i&LmY_X_Dp1ܷTo9 Ճ0/-ZT3Z}[Rv_pyM:!d"e׫q/y4n D25jIFaŶex)bq*0]xR[MsB*tF;`>s:Wbj-b!VtwrtC[u.,x5JTWo[cp'-v]=IJ404λǝ{ѫԨ]+m3>ʥ/Suz_EHZE^=R>8\;鶛Vh4;JbXJ0+O-~_T|%2mzgWTx!N_Qu~/QI$.rCi%&4r,9jݑiH";߷z*3R\7 vˈnS*`mIi8#> jŬr"øˆn`"ckx}pũE&ؙT!eWE3/8[0V6i>3h#hS$d\"?]XNo``<b%s^!c-%FO>=OՑby Enw o5et ֩yFn6#ٚG ڟ0Z= ! 4tN9zקںzP~o%}9 &Xyo;k^t#brwzmǾ;!jq(FTI||PmP]kA |_1&$5v|@)>ޞ']$])S=IAͧWrTZ+'Jx\U +?z6t lQ>SyIj\*`.w*76Qn|Kl_h$Nm+xOwz=<~>NVG>ZiF{>beDH) q 1Y@Rfz(bC4O:6Qt9RjfQw/ RQ6*pi=ῖѶ,6woM.V'mAo1+!RwK+Ɇ6%J8!YllؖgiѦI"|G{Mhx\fg>'x[Mns"vf)?ğ?8a\`] +6ܐOsTds^)3Nt1C-뽇9UPh פ.+$$GǛr1X6VK Z7-6N-thEb Ė +n@kD +$c䂂C+k?ΠK`rޡeEQ"׳]Xդx]Zy|>RYU''o.07L{=#9F)I/m-mn@~9pJDT@9EBG]ngШɻWkB{~{9TJ2Q9P)|o *aK +?=D:nYtI4Ѽ8\_4’ +5$0< o+t%X6"B`Ǵop\$UV%~F=kZC`|IQ氦YA7Yf,_PIVӠ]. ;EHFw()B*Af5ڷݦl`8Zڴh-G 3ah(t<>fu08s_t. +I8!m mPݸt:}4rbxa?uQAn0sA6n$HE[ A.i`E&%e(ڊQٙ=JVE;~8& #<:҆;}9)F<6PfH#ak|moOXs] Cƍ&} 7؜+ (d[`iްȘ lk$&hg n˶ SAbbjwŚ!f +!.QıTٴ+$6`[V*v)(hRJSX9VRz/M tkMDȦ +XnDſ + X#I卥#"j-$CSRѓ⋀E ňg>N;gVmqS%Dɿ&l,c>= c'Eqhf*mNeAIqbn%Hfi"KQN*T7%AV̥D.V'T~;:X/Dٲf<43ċ+i'Me_E= 0=F89t+ +N9 G.R\qVŷW(.t:.s){дrD5)ypG619J*`c[MJ&$iDBnF{~Eʱ +0=OqUSA둻JE|w +]?YGXagrF': \atpGy~{nCp͙ +.XYj>x8;Ƃ4H sH4}b=&jCi;F'*ۛzY8F lg3{l5FbJEig2ѫ$ :AM2Rw2k--ؿW<9.ׁA?f ѩfNp|ݿ?}Ao1+!lTqmETR4Z8UU;64|oM#Z6I}^׳j:0]o:6!(B=m%/+ ұVX;L"=3Lb| g İ #_ :[> Ono +jX{R(&+8X - +H +UqVٺ3+'%DŸTBϖS>d9pJ}3tQ.J^(Rk7JM]N=pSeN#}.863l֨ O3Ԫ!ܚD{ w"m~P_kfk"3gZٸU&|&al&T5gj)QiL +MD\xBkc:blڼL=*bLPH DŽ +_k;:MswY󁋺Q8jt/}1v?N']]=mvTXS7fSBeRQ&oQ#'{Ӧ1Sq ) @gs`^;X**+haO! Ƀ_lAp>v#[Hծy KXn^lv_h0Oj*pF_5i ;*@Zz\N&s۞\/mIkτ Nyt>l9)}XmS6_pㅼp\L2%)؛X#{GIJlGg]iQ +!1 +i Szp❶["*`Bc* %\B2dx\Є[9C8 %ٸ78&BRW̐CW($ ٌQDu_2Bdq A>q:$BLdIgD҄B#so?]\\(Sy2"*|ƙ@*TD!HBTHI@V!@> eM8tN$n2k\U/yČWPZ 2UXNjk`M6Ǫ!`r8L$oC"OqLd,P aB,F{w k>~8$k螚ATxiedēW cJHY4]Z3pufAUDH} CSs@ FJg6pgPM 2K޿kukXɮz0)LNrǰ9:)ʯm]/ p\+zתa-+qU"ǎ֍RdF +68*>֍Fׁqĵ}GXW,V7Wϡ y |RR~# %/[/ 9?Sw7- fEnWG L_FjAB7dsmusdJ(EpNVVw̼񠗣TjץLz73ymeA ,M=Bt +yo:#/_v,6Qd ! 7㘜]b*1 |Ψ֦^FTnzlA5L[{C,<8Es|P\wsZw'0Jah\*ZNZ5όS1[vMA q$ YEetް=P:[,cAgxr6:s=zѫÔ>',]IP8dɇbҔv> +D$npv4YZy57&*PU]o6}<裎&q7i_,+Lr$7h>Pe}ر +0xË%HF1jG;op3 KImLG|Fd[]Pq 2.x5[wCgDF8)*W2(dQޔ3c,!Jc0\#0   ׏ף#*clbb`I43K d&cU"U' ԒP tjфh (¸%Jo-gOJ,5|:R(a;JQZ"9=e:K(GW~ IYXnB+@'ǃ7>D$vۥФC۪rr&x>(N[QͫeG9[9IVʽ^ծ /u:ڮΡ~N:KKh<kzpUmLQsoUM0WLI(l8Bӭ^*u+J\6(2fW|*yofx=ŇH"ВJ0|+F .8$D<%V($0h@Sĥq"AH%[nPxƂB ɏH,AƇBb +-}-6   $,>""CǬi'Q%*q}jPe\ew*y'*DHU*4\W-e/ފ/9Ri>AZ|UyEvgeMZ6kܹ;8pfιП3"g+i<1bۺO[o_+X/#\1wTG`܇ȕ~=cg$RC\WOzoX7[9axߣzƺA7tޱdu-r2cy?{xbɰ|baקּTr,.v\#6B[Ή4k(ge۲i`:`#ŕLwd³W26xuWjdl4 c<x}gu/Cw]zk9> H S9lziW_o6ק"NIsM + t$GRq"} EQE;يMu쭨TX6DbC? +Tpͫ ^mMli@"5-D^oׄ-*:˻QһZC^^2ˆ?QjgD2|_Wx1/!JS#(LwE1%ʻa,mՆLKi5K5¶m(%2@ٖє9BrV_zCDÞ(ta{kcD(y;o2Fv)12Z婿To1GeJ?vLiI(7D*m-l^-+b%$ez̲!JAolw5%|Dih9cUWhNo晙X@:PN/PǺACˋ$5s(^숸}{-))Yyl{b\TC5-8ݑ:iY`;/Xf#$ }y{!ZںLv3THl4dYc#OP]֐zQjs6ڸZ| 3Frt+^Y C oWpPC C`S{q,J߬4 >YL,k\VC7p[42uԚ!=staOOP/GLBpimK?9$%G>uQ,|5L 3K0lY۷o,z-H\8Pk|'䴫0:bU RU嘞<8BOD mZ]yKvE `mLЋ]c̭}t!rߧyh]i jU6y9s!Py2 G#1Û0fy9P;'&%FB+xUױ>2; ?lfgbn[I$Y;D&t|K +ozϘy'H!kOV B(Pi%h0yuмID^y"{DQyku%m u +i)?bҎ=V}0]K^o1F,O[_ZlqgYX刎O=R0az/<6ȳ4ɮL uy2e@Hw 縹l`rA5חV8v!5!*,Enй# q|.!'b2juYb:c +\4TWugTaG)}aXE*+v&S4^Q4ܾUu 8.4|*ɱo0깏BgT\2`4>ƆEFc} 6׉;T(pm:M6 +:vjͲr7 `[nWި G:20.-o~k9:6ff{A+T#H*m3&*wϛ6^ uJy&9zZI ^~lvB|lji:g;kv( + uKKA +"]ˊ *dv=馓qˬ/XԗJQ4U1 +q; {{xJbPuwE~!6,*αwq^qHaJUl34|{^tsI ^p]*> mzw\RB :I"1D\{rɺ"]/fhi;rЈ}k>6fysP&! X %ijY}a&!Df'YN"Wgm_H_ >]AKA +JxnZXP(Hv6 Ng$k-VDŽ%ovQCE>Do/8h&x +QĈJb(ɪ/ƪɆwؐ7؈wјFF 9 J=[f! X+%izY}cf!Df;$qH=86"H__ $q4g-tuOKA )QEϭXP(H:g3$kVPKozZc;U?{a1v'p/`ZI 1⾣:OҲAʲs]We\$nRU>[l惆O 2R\Sg+Km'-vPiի,~] +oБNY!ړK}dWw3=S6#NJ /ncc17aP +Eo`R.gofBLdf=I݂ߜ퐾$%ß>uOkA )1 !gmPC[ +io"j"A ߽'`QO'=c:΅OG# uTzHس=lI>ŵ)㦐>cI|98=oCH'6, \qmHG3 +jl/!F?tdVgF +zV(>oW_W}1P`GN/sc^'ˌ\;~i2)2HirZHwmidS6Y V^w`f +ϒMeJA Ew"ߎ($SթvߥGGa\&\t D7̫D†}mNV:1b(TmG2?^J;K4*ϝc#n*$puÔ|54|{jqT¹$^O>*> mzO\RB-:I"1D\{rɺ*X.Og7҆wX |4fys?vvRV(I1uy%&!DfXqg9=7gm_җ#k[~RMO@WD^HShPBU*B8u^qH>3ͼ}o"Bƞ(;}(pq2'Ї_ H V9, _K)GѣYOLBgSY {.GNp #JwXօi,"px,;ʓP0>2-* ,B^9Ehvd z1SFwc0%|^:Uޛۙoe_KASԣ(yK⟜(h1雭ug{ޜ^<c.jc?K#>.Q~ ЧL$C(= n;$`^88oJ\d'67,)ֻcs-IלذpVR3 QTI(D;ĢzUi觜ߊtNjDҾQ<=F#~%nۛ]ql%6E"V%rE'4rYb3/%N#V7|vjgƚ9; O e]KA EWGMEAEGA٬ U.[B}L87Q +*oW ߞN:1b(TmG2?^J{+,*ϝc+n*4puÌb=5|{^zq\¹$O>*> mzO\RB-:I"1D\{rɺ*\.7ʆwX |4fys?v&Ag+y4w<\b"3y6/ 7k[~]QKBA8)b%FE`BsݥT49|3seX6 +_oC鍪~>ޝ>0 SQo<^(E4}2=֩4>r`Ɨao1^:RlI`bWIjadh纊԰d2|oTU&N"2]N9Z&n8~W?/uOK$A )QE=ϸ:ʈ"QLUz:lue<嗗Ǜ׾"qI'l8ÏY8= +8C/N2C t멭oR$`^d;!M77̩)_??qk2R s.j_%@{ +҄XԛG/޳]ݘ3gЉY!ڕ6Kcdgjy{vmxO-/N؊1+cXsJ0cϔ%-fX}Naf!Lf-ˁ$/Rjl8ko C3t=;7pmN1)NMzʏԢJTB&I±]{6 7H̜gg_}Q28<{KgUꈙ6 ) ?K +Y=(AKA: qe>r3 +/i=hM—7/0_{jIR-[ %cVF+̅vo"cn^iT$)qj-%$%]CtGOfYU>| Qp ̆Y ňKV -3wXR@d>% (q u^ &lP)FiGu.xr6VM[V67xX5Tnp?|?5Ṡ1﹵Qؽ c'N[~zgΏC}xk;OKjXqkfb'>m򝶴G\sv:ĩ rP7J#18qRD[ JJO+z0{R1֭q,2OJc(y-ue_rV5zϱ\jXҒi}n5Lꀬ<a0bIut=iŏu~]w}LYY*'BYW˓u{龇({5ʃɞeFڃY`e7Ō$S *v~xe?kXyo$ivz6i~lpwR߂-DH_?}Oo1s 5K(*R[UJoUwvת׶Ƴwh{=}-2C^ExwL& +&2 +c L, Z!oLAQڵ/lJ`ǰbGh$sdGpi1gc>RC 8 +䃥K#qYsIR@NlcDQ_L}y;UN$)(*@;6(Cc +ւ>Mjk4R@0T:G=U@X4{_o մ!ب ;/,`cIY#hW` #UcJi,a.JSI(]$t0U}S{5ȐnT'O[.fZlx{5B@V>>%Zx6k%Wav7$gRi1wǥ5  (G-i܃W qN`|'+"a)Pw>ڙ~hi8K?FLgo "GGf$I]IŵOOBAo1CF\IZB"*AT )rY gllj*v{Yyo:u `FY;Slb0g>|F->vV}KY NuLOwbx#LX_I4cai NH[[;*$XaL]] +W$eZEVۢQr6 +#%3hU3seoG"pt<{{xV6h|E ^;hm,"bC};3vO9YG͍)>= 㞭~WUX7žYg~;ˎi4~5g_gW){6A0s vQ KYVEjJEę$VO@^9 t,U}{o^AQJ~Sx7'IV6:Gp9|-g: w 4ڍ'E)0RcxdK6hKUze3DT"V伡pM\ThmO[[-Q +h3P +ǡ1HIƀj@mBN@ptl>m7O_"(pepRĉW"P.n:bEIR`jhlB( +8AErð~Խ$^ZVLR}N1{3tAү= 3^ײ|VxOrAr~ȿGΪԵ.a^߅Oo@sZR*4BWYϮfgwց{㏾P=(2X`xn ?JK" 7څ{6R an="KbY~<8*Q_0"8_|yMyS)6$EhG&8}v R"_&5) Z;G}U@s_w U!ة ^XH  .FЮC;ÌT+FYR8էu$vdLV L-VH^Ɔ?TX,dU!@YǑ''ݷ[$V UސQaHZCS;w5_veNwK*``AiZNwTNd*:uh[h9s?Dntmx9#U.rtdFjw܊zp|hI[ޮyAk@mܘ^k;ubRjHK!=x5wYPߋd)N&oyZ|uDƑ(X[}M +L u I·d~|!o+Nz]e!>׊V<Αɢ ϫ]~]<>sf"V!xDz?6{ +%L*v5H>֌*;"a}@j":8Z>6MTۆ֤8QBiy8YMc)d1 Jڙ"7/ +(K +~0L:6kdcӞOf-IWi^yrwwx;g ۭ >d7Qӗ>mY7USȻUsw=Y)r1,~ n&_7mn0z= n^#;*(j#ƚZYDiXr^Pb7-?̎lc")u+afy2$0e#RHكW\WԲ&gc?Xn`ƚIC5M5:/Q +0v6Ut >o=u]0J8#6E/U碈R]A;tPIw +v7ccTԷ3J4nYt_$EX;/|Y^ٲXrw@bOj$ +bR`4ELow[-z;la8KV] §%alyI}ٞ|Sώ$/yk5덓Q?>< @p /pg@W ԽscV%C*{p4­;:0N,;_8#f3 1?,liX[tJh 89HQ8vgpP@nEG ]W &` +&s狇ER0l s11oOghQBb3q\}F{hNR aQ`=NmQꆐ)P-#0,n Ӧ +mc|. o0놵@ 4t؉#/ݦV /i}+W98ݡK+"3{3fIL+Ϫ_۠hB$EvbH Z''ǡ! +.G 0c&FIL1w´ ˚ϱS :u(,r)H[1h;Vp\'6 Β1$t1¿-<7IvBsHN @ղmX >{QCV6OvivN7**ڼi].d JRg4gst˨X}b5鼍?I9ge-zhvHN|~uOo1)ޡ$*?FETTQ )rmfƉ*$%{~WosђNh&maOx=l|Yq ";1ܑE$ #?;S\94[-ݜ㑖N]*$}aL):oIVk["[Eb /%ѝI`=+!t`OQ $kg9r vx?xWG iX [hYhQ-[i* >KgD&a~MSD9>OQMNJTYXhM f'8ZU" o6't,{t% ? xJ]g5W7G Oז.VdG.dEM]NA x!Ihbli\fNWBnǶ_?&}Cʽb*m + x RPKÐLjH52=R]8y{M0|LH#Oק!VbBܲbRNiSnnF +>ESY%F6 Ўns, u-8Dn +Kxw{XOEw`vTPIvb+U⣝󹿱sf/ي_}Mo0 <i]4KtXmݲ``d&KE'A:o_>|Lyї2RAX+-{O0M&Frmt,r^"/ݯhuNAh R`ȖɠC,Ȗ]F1hjbܗ7t|".*,[[-1 +h3P +M-C1HIƀ:ƴъl 6w\hg a j5}_o/lCJaLveRčW"P.n;bEIRPڼ.cQ0(q^[Ax$B7(t+o"+0V?o̢2NjG`G%%[X1{oi0=INgĵN&l̙f{'W +^8 ̸ u+^'V#O[+Ђpi}ˡvﷴBG?Mo1+|*R[UMoiwk1U7,%3~Q LZ`ۃɠJ +PA^+Gx~S + rA:aZ-݆Ol`d_8-.TR`Da=L|yke=FKlYaZDq>;)h 藠ImH d Ǖr ޠ ++uu6~{P6Tk 9IJ"krnYUaJYL8Է"Zv&IӁ`Q Vh% J'ueLQ!@u,Ԟ+M}⦪gZ)AjddH]z}\үS Nwmos~7na/( lhu4kyVc΀VPY8gFlpo|$LrtdF+X̀w>g,{yt_mO0sDtQnYV{X5vlCW߽r[^~7|I-:γ>59 %4AEJ ʡaKO¨Oа϶~g<:1“7w64Z zaN+(cQ]dkMz#bQ>ƳZoٵ^,W2h y +i$GP^YCI8VѮ7>O~'QiWc' +^T`|s6$H[PN?1bC˒$2-&x6:^L,k~Ș-{|+8KsXiV0b*q\ +O 0 ++Y6ϛﴗ#m㹋}z$ӒԽ Nõ\`akNl R4=)Fbsm̴d=#-򱉓\.UEUs~fޭ7b8e4Aahw+kvBOk1)ޡ; n'li +aV;JB3J{%[L6g)B4;X1 3 QV_-XN{cvU,*XS|=;u geH6qLljB8ЖJAA,3ۧ1a^@;A. 1O.$$;ߗQ5ԒbKs:кĒ ĞuΚ@K"Çx㪛33I1LHP^wi -;/E*&í0.{Mb|9iՒgS#L+#Y= 8وsP$-ѽE`0FG:@CT5WŸDoŏ(Mâ7lO{zlEؘ&JOt.5iN:WmXdiMl3 j[04MC!IKȚ06oɑ+N@uGnW}jAuXkCb; +1$&`a4[23LFVC4|U]e4Jx_ϫ +x邢 !޹~|sZT8%l;TXF'XLpE7 ̍S N :5rr9dN$؈IJ 9XGCEG:OQ"HJ,$BtJt_WhuH:g9E8 v:ؘx§oVzjvͫG M#{(&n oᜏUr[GX X:Ł<9uG_7i lw4lI=^aEٽخm@ٝ.߉Ǘ+7en1 z9 nf"ڢ@z ZWHnP݋I>G+CAG!y`VH7]_8\}L(^ S~9ve\$,4"LxHb~u_ut{gZyTIC rIt +>G|V6!d6jYtk@kJGb VB>-fDI'f;]x^NR4lWtQw&\%Bhεc?4s!yUy,mh$6Da2kĝRW)wPEm|:SOL%dUL3jv0횬 5ݽj@zP҄^k'Ml\hC!!BF;kJ޽#P|O 8\&ONj^8}g6!PT;77pҳ{v.ڶSuґ W~V)xCI- 4Bcۓ;Ռn1n}vfp ig KbXi|ISǔ7:ڧ)PklvМXC4 k9/zN 7/ +(%(YIk=+K0SOa8k b3Z[깬>"+I;rD! +e-뵎.gYnV O?pLV<>7~>c*cyqV <@ZuwxjUT}j1ݐ&ib@ +Pd%lSe׻.1Iuo4~Ϳ$P F*/%Oլ8-pg4>|F2 ;#aPi'~p#LXÿI4cnz=k:#MVoߨ#gQW˱K1MK9K͊WZb=+q1SO[otl{ilHo:uQF,%^U1+%0ׇ6KC-ċ݃i<1CIuٰ})g?N}_W<ݤ6~[n/_Tn0 }WpXizv] +vZ`i(2 U$C}ĩӋ,^)AR 3)^9p +^t܉w)<8A 6߹K{-QeYA< (ų.TxVO,׆s;Nc;i6ƴb 80)HkԴ`KpZ ZI4A\ N 2r0R RxHja +K9pPۂ$)GF;!_/ +P ᒑ[n 2w+փ\V02tkm^ظmkW1qVS>8xƤ 򀫀tl $Fzg!JZ.k{9KemF"(~Jzl/iZǭjnjmoQf ̡&JpTxFͶZiLJ9%\D>❬pD%>R.,'m~4U IjZ3i|TK]MkA +C!vn>qi +E #MP8vM.ѱTxVY w~p1(AR#;IYaޥV0f~"H)T< q傥y̜rC0[%XCA'RѝT`#1mt EAT&92)%fpz|ZS4l$Æ]Џ_al4>uK M<pGRV'K3_c&/ldq +[-Or`OgGh.weOO#1 XWZXeG$f5fy];gV(a +̰~rY։OAcؓweS hG 4:۬9&%v}TIE~;FY-Rk*<։HxRKk[1gQMvU(cݹ#15%ȱS0tS4|sq4nj}$I1 ~&s{1PЇԐz|Ioӏ$bg[&卆a4LW*HjsR~ yI >qeży̜rCuX[%XCA'1 jIcRk庶F>,$9ALxݻY|_m6aM](/oqu+gs'a3|Rpbb1/c +~@<*)c`{dLߡɰQ7b`w'G㏦³W7_5MvVMo6WLYH +8ڱ&EHb{(`"J*9Jbt J-Ɏ]'k4oFsK `dNsZha8p + ,ȘgJh,F+#Ac(kh%3 +Y +ljjib[kK eL-8jQH<׫1 + .FBً<_Rls3Fُ;qc̘.U";Zm\!U-]CTK7pQ'өD & \6e7cC [c|~x[];'-vw#ϴfMꌫ׍iΤZ !|z-ʣu?3UÙ&`Whhyd7_Lz ,m#eI:yt=ZSuB9>ZȅB-&koO0SI?ut\XsȾP>9u4&wys{!OsKf0d{hag8v]*,$B" 93:)3-S"AK+oX!(d'pΌ‹U8W̒` +>cmup9\b;pf1ZBVdĪ mI!)^uRpTADЪDfnʼb9wJ2  6R GpctbڜqlAa>~KPѸ/t7Pܹ(Z +tJ +`X7%Lv4)3f>x68팃2{3 [#F<0B8nSvy_JS(ReEަ[Zo`(ф܁5HcMZñoH£}4e.Rܢ (ܴi9L[ZN,(M`@+(N W3`DG{'Ѹzn¨Mk}jJkـ3-zp\BƳ#Pzc2ؘÏvckWf -d4*_Yo_?YxV"BYbNns:z  +\4i P=ӁЂ,ߟJTkJa2]^}q%ð{Ge) j1uHz4qi +EήD#mJ޽z퀛ҹfO3\BE6LhV.>LgY3<:/} xA2Yk;o}Mv.)J;Mf"EYsׇs<ڈzJep\ MkO؎( W5u1%f#]Gў sskG>G +di3}]Vbc}Uxubl 6V4^`Ӓ$c~ޥUXA[%ԭ;.'#ӏ腸Ѿ2izW 鍎O&tTNÓly)LqFVۃI?s*'nHOV6oP&^LKّ=:]Oo1ݪJR"#rwl<M2~>#mF?tIp\A"!-cmO,D/ vSʑN`OAG;Zªj*%'5FБ !p`5$@dhz?_<>-S;7[ + ooQMPT#ޝ+v ѩc\"Fj($H~+q'8bp+Q3֤˻iڏXysn5܇Ͷv,|vK_ͫ U]KBAW*tu"b3]Zw9~8<3.fpOxz,7 xw^LE,DT)tDcES*g*Raθ?G F ([vV=6w.Uc A= i@ra7aC,~.60b(ZXI g]ه|YFJ#*ܸ_mFYp YYb[{nⳠ-n,2?LpQa`-1lHc>F DϳЯyCx}hH\xβFKpUt ~go`cw.H{iX;dg6OSNK4p".mVT@+i6px;ۙ-IQB@G6nح~qz̋S8*& ++ŗho+B6/>0/r!?EE\([R9D0d0NV^&g5*ߥҶλˏނ f K dWyQcq7lat7*{mfo4kZt{1Kʬ> 3p5UP`_<KQkQFЂw'84XR3ֽIm·Jػ(f ØA`U=L۱uRMoA )W )j#CHhΌl/U,M?<yUm2Zr:dM /aȌE0 A97;'P!f>lsr_rH0Vp~,qXrK$s<#KA{'d6xGO 9BVZ ht`\PbAagp_=-fylU \ Cj8 SAx%iZ q@J#<~Umd$%7Gcqy`bjtD3%ff(Kgef GХ ǢNM.LwL S{z5n{A/7hJ`ޱP]YbFV[UVϸ@'³sF(ڢ4V Za\wVNfT VNKw䯉b5[z'Ӏ~ }[3iwP$wk$^{Nwwz[TKo7ﯘ*+H1z]N\AsglVffVP>zH }ߪ uc2ZeW!If81|* CnJP)p9<fo"/ڑYN,沈J&tK|Q,FYx5\q' +]Uf2/86#!tݿ.FJJT1 vU&*p 3)@2pEJ> ۯ+/nvJЪ^#mΔܱ:}_Hl,E4"]*fXui1r<"Xްxp^W(A^7URGCZK<44Mڐ~FvG*fgXw + #;%4i=8S45uڜzLdG_A+?ӷd2#hmDܵT_:49?<հpIAnԉO+XVMn0k CBiTRU$0l;=)$YA+&BmHLS[;R+OD|њy4y+<C8 @{nuk??{'c{c`BןG_}Qn0+搃l1zuH M"I+A/(K$卻3\ᐑS+[~qFd6N0cr * ϰ9 Uuū炑>{CMZuF h'+apKy,B7X‘u"dְW7!wÈ +5S:ڪ´ʑ^ +NG)M:]T'9'9$}HmOk1)!Ibz6Rz +Yl$*KbF&|"I]4iqlBƓD89^lZa)zNH2b{Fs41={SGOKV,H%͎dA!kEQ-yw2\l(.<2F׻m˖2YaE.5b&vʳ*І5a[yUO= >7q51 +MX$h{ $-)3Όwi^핡Π)c&2<9 :٬rJ_Bm%ɢ.{RG?΃)KKz:!RVn6W̡EZ'EC(41Sb4S3ܵ^ EjkK<>μy(nشX;ָ^WpkB{wUQO_ +`S  TzJ{h6`%FxVccW:x>Cy*@m@ [OC^N21XBcBM{ԬDLHWEѡ2OA:ˠ@Q(`| `Ewؒ_th!$Ye]j/`5 E_Շ?m>ۻ +y;ų8e)&(^'cpp^2+ 26)CgԖ%m U60.$ܱ}g Vh'·@_3]w)Г][AZ9t9[C73R`.c*,\٣  + `U4OU >8ZY5c﬿PBMc,SGFA90)ō{t#+ڀ Y-CQ7e)^yL]zQѩ RtN6DG^uō`u[Q1ϜkxӋho1@ȷ +oϲ +tp_<|!`c΀q0#$[;TCTRF϶Xo~Gc4$}&1g#̋/L~V@-c">:Ġ ߢCA>')\i!ޔ(?ufvDT ^zFeQSC3 Fao-B" ]3Y|s@rN%;duʻ&J7M@M5o7avgwCE^NTrQvvNOs;sxZ1v!_RJ>!f`j!TXҡ g0U 洃Ɣ`sAE[Q?yI͞cSckmJx_0\,jJzt;&ӶZYwߘ֝,P{hhZsJ%0*@dGqFl`SSX NU? xZ-tt~TY^^]s",Gŋ*W_MoYjc|U3MѲmRn0+ /^u]GM-"I#A/(Qy̼mGW:$Jr/}6J].UFi +p3Rh[նd2'o09`,t4"T8 +3W:NS05պfQK)01$,K(I}|Kd8n}i!#TjHFUNSk:&0v1 )oMbyLKo"Ϛ]F٦|zc^K_kُtw\(M Ϙcp CC%]ZH+($2e Bf4@(_ +MC({xry512hx +B60WJ2@DuI*!tE乸"2tRe(0:4^_ߧ21 rm}sL<\%LꏔZio?Pr]#l3z;qLx%1{[ +~{# I8N3D*NRpΕ&Ό_=`4xD{ѰS"gr6cR;'Jc6#2B] w!lHN\X. Lϳi<Րt_Z[tU/n-x9|隢;<)A3*Lu6nc KU 6j0?o^I񠠥6dșj @t/n GN%w$B{O*=ZxS;Z4tV2Wi%*M!5e3y$)*1)Lk@J$-puLFjlfh4׻Pg>J׻^Vnjn P, gb8Uk"j4ƉD{5. ,0P]sE#^ sfI&0%]R'iEDn%K!"꽵(5oD2SƴM"?|x$PWS*>8>ԣ(Kҳ]75zb,3}nc*7&,?~4jsXc]Y4Qt]%RV +FDFY͖=pR!t)/%DrYO*Է̉k% +Y%-uy2ۉxOoL8Rϳ +^%}v *y5B@0JYlfѢkl?mڎɨzv^;\ Nz~\iIydվG@Ёlʔkr9zG7Q_mu%vE2gn^ݎw㐧nv۶7۶ubsZ$6͝+ڢ3!\s}XB6 CGcU7n;^0mebmy]Xه"`oNY[Iu8֦K]9kٕۙȱbYW\~WջՓfz䓈5Iuq=7*7Cχ/O';1vN(Ok|n? g?vVMo0 Wp@Enצe-V(v(P2 %MC KvK#H9Ov,>$8cxșqf@mAfp-XIl[iZ |DDm -}rk|&2" ܴ*W(GgD@=Vjl;k( +D&uA,b#1[fuurP1 ;b e):F"PO#A +4PXUdE鎮X,?3Jb~:yQNa,|%bXV="c8e댔f[bhu<&]רȉyסWU:b_HE،*ȫr|Ms3 +Y)Sڸ2pdQ*B6powmhTI{\Ý,CX~0*p&X#rm}"eZ'g,A̓CmIIy"SG$C. іZ[$i>K6JOptP)Gl溾?4C53ToRe uB:qq+OL6[CR'Lj-o°VXKg0sFl%a=݃vNΠ>Vi7'V:v.V5>۳utt_nqG+KAO0OCRؽRV HX ΄X8eOZ*rHۤJln͌sÕ9I-<+G{6J. x.U@4A8StQL[yZ2RO-y|s>i<Ĕ"& ܸґuְW/%ܺGVL (SX_ V 4@X(Z6y],4ƥ`,E@§X*.`k/ 曖/#* +NHS)5%I"ֆGXGI"*VUDWTဉVM#7 1m`BLyL8 Q4mЭ`?/Շi k%w]HkZr2{T=a׸uQ4.l)\ `O-ğmzkNN<\Yx +V/(X?ڬ-oث%+D{r,,mGQ6.m~6NUPMO[A ﯘ[@6mQT/겻Q5Oxx񡆊}"ᩚDomWY/!*QQI }|02$nag̸IbAj}:<I-RgXpUCR7"(w%`Et/,0!%zc9+#Y,51)9vzpV,aK.-`ͼA<×`ezb䮬Ə̝-ZęҲd;[aqoJ1~Ⱦ†K.R 'Z ?@٠\\bc|L_ݫT]o0 |^&muX}a֢Pd:HH'Av6L/㑢|q +J-<&^I~_i:FFpW(\iEg9|-" ~y.µ7o0?3VY1,q%0KpA+Z1A 5ժdIz!/yZI4LnFf N ]x RUa\ չ08OAl_yd WFQI!3p>]B5d5މL|Rk/v ӸA5G# +#-: V0‰%5~)ٕ< *'Թ5K[%q@@ #Rv}S:l[T;{Ӱ} PIVe*dN-YpII-qpH!J&5wB JK/!1W.i3H[Bt Wp(̥0 +>8 lsZaOg@MO̍Bf_SE(|+)-{E}ǿv\#wn+I<&Ww%-+NxқV>if?׼\kc^nMnsħN +EX!ȬxfWK)7imOk1)Ibz@cjg-QYYP݋vhuͼ==-?$бVݻ颚*:A< eEh݁o+&vVQ)>)⬂%rxu $( Kcl<!(t01hv1sPa^݋;A.1I] HIqCs}s_Pc08srGZKa]"ϫ@{Dh)?ħGUeJ$<?`jJ2 eL-L"̲ϔrlZxj*W +6R:(8Ddg:s/uVo9X=K]U4 I6,IU`D֪3JnbͻRNl79PCQW~Qn1+怴I$*Ph".M޷Y6Mvz<3͌={ۈL$lm4;|omBc&D͂ඵOt+l`dxϞiXf=]ViMQX21fi\6R:!aUi0S;k'MёN'K~uy[V :iE5VZHW>̆`B|^Qn[aѿTEyb62U8nZpF r<+r֠t;[C<ě;r|*7LvI2=|"dYi/zQ;B0Z|ao4C3;c +0@XLu\On&d=QgXC>_Ok1Sd$1֎84$CYZY7w1%褝{N?CȀqЊ|H6Jx4 Ao+L%XF`**pv+Hb--|È47]xpPFc@ih-]I^7( an],V$J2ԒԲZs“A!(W +ᡒ=5XWb5d"2#X8[-{`aӽO)RpD1ZAJeX!*Ά*Cs]κ\S{|t)Psf c'ʗJRk$;w}MF;/-qK>tat +җ +;SwCC"<8LGȧߎ\K}Vi3ޙ/X[O9~ϯ8QehVIKSHز=k{h_s\QyqC#=% \?ŨwnvaQ H b]^,DO.# ^Ї#>2 +> q<))pqB <@p-}TB c 5r@BT BXS||'e@T0#y% "]pB""DZD:DJ4|/.g'gHx`̂@$\֌98s5rj+ȍ)K"]t +\#o'NW3cuRph 'b2`UQvwwÚ4Wl". 8f;tM4N\>TDljn0 +\gTC%*((4%MS͊)2 ^mJZ9N `6Edh&&`ኇOzjaȐg6Sñ :5\{껠6$WyScHm C/MB_"C(| G4t s=y2+CDG*M? Z YL| GŃ:ɣ7?:Dӎl. #ʅ* +DJTB|ؽµoz_PfzK.zŶSybilc0flK-#QM_JL+X sŻ靑2~Ll#kz•&ah>ք2ֻssv!$ +vkԬ?0MeX޽Ai*ZnUh6K6ͫ}ބep.]*l{\-oTjQ=͢jo yYi#H3'S[}k lS^ܤa=&;1UE65+8k<o LaK [s)5)JR,KefҔ9Zu-68?Qi̓iU"*\˛Ẉ[-\U]KBAW)tu"b3]Zw9~8<3y(&-Ӎ(z,$w AЄHbY,|DT9lȉm"Vr2k`Ik+l+Ę%_(Hjúr>gzBo%!dZ 9 P"Y!O{ojuN[ + .]y-;$%)xfߛړP0g!Em1n {m*c CP$+Xf7[2Z | * I&/MXL@P흐YFĝ'O 5B}TOhk4:F0(1ލ XT6iy_泇ǧ &(^ KARx4!mTFXԊqENNje*fXDZ7Mj`A' 3k?pkEoJƶI82k%و8;_(g$ߎ;BKk E-y Hu'::"zrҤ4?vrj:7(`Mٓ<5g+.2)$(A?xIȸU^m8n`}]UPNA W[@6Dբ­*g֛1ބ߫&~~K +j]a4tAцBb-31e'a~kIH3WC})yIjgLU +A$,{ˢ{Y`c?Wv 2BjBN(Iam]5 [R4AkqmVk3|n]5k!xHbԹ^?By ;N,99I?y7GoƩQ|$"׻>j +ǔG'A"O^~UK#\\b{C?T]kH}ׯ8 -MMkR(iHv]J* a'2#և]Ϝ{=]ikf(܋&{!;ONJX& ͌*pW -DJ#!>IT3`FzrZ3B .JW)d3b2# \E(|]%Y(0'C,a#hެoC(Ua,ra[.ʱ wq]̍z'a.36BOa{*I] 4߼MT<2 oׇNV?Nը`4ґAeMtvZҞ][lA0=o;3G͇maY )>]qy2*}w!ȻcT0 +陸wfcD#[ߗeI.,5f]# +p 5ilMB}V^Ϝt/> {ʡȳxn9Z'dz=zXn(^tOG7Pb! 1PFv0xee{Hc'b܈Xq߃3!\ /yU{#g}ΊHh[I}'DQ],LxYtڛgp +Bx-qv1囬?P\vx2yg^'}Ro0_q<@D:(˨VG$dKbͱCHiG'|2p <ϣ$x)u\Kbp9=}@ +&T"O!&QOrD4)@PaS׭"'c*Ph`( T6>!r`2fYwXL)v$Ϋv%2lϕG&҆%e,yZ?26hUzȅB^@"xca=r,͏TN@+ !;J@6T +!hCzXv֡VNb;{7c-(pw$WS2.F{N )by0<j*nFUB i^S= _D۸Ec 4;Z8 +>>GJ@ZD3 +1Xք5>\x )q)|lJ'Iw/"- +d+$s.ܣy;2/-2^D5"3 ^ + Ԟam7z-\27:Uh-<©Q*D(sxHkrFgGP}u 7,=gXJ"|]¢ ^,|q@ +F۴jq(d6`LC>>nei;9޸*4yzU ֵDq2%w%5%g#]w7o`Ɠ5R¯|2L(,&PMo1xvWmW҆UTcmyf"ٴycn>Fђv*Qɒ"jZL5~#XFTI: J]a[]֊*/S h"(| $%$ ?1w=`}V ёb~Y~{Xf%+FkE-V $'MС=U^m҄{]x62- +3~4aUm +5;8=|2)!\hgGhZ~F{Xt,R F|.r &QpV1qY}:w&HտmHioKz/e޽rQ\L +{2nmTZ4F͐%JI7%)SW[o6~8RX2d{(4K$E F:R$Gd !y.^7]h1`bVTk_dxWp k. Zmx>T6|S$Y +/Dx),Lzf}O:bz]a£oM5Ћ|QV@Mq _.g5d]L2pڳh-`y몼iy6w/t4e5$h+bmeh\r O8'&I-WdX&pEQ; au %Hthhm*uSce8>ly}?=lXдDRd~OU+L~r:I + +-6/ n`SɴZZ6q+ Rޞ~Hcq*qT)ӧ?d>"< ='(w|lDkTۣuĞn>UPn1 +v`$vA6sr#!$8F/v0OPⲆ}"ቚDomWY?M!*QQI ]/|0*$>OE2kbI~>mboH-RwY=pUCRBPnK6ފ":EY1wEb3Ĥqzxj f [RQ߽6Z g.3k%ϸ $e=^d\z3 eƯ6w'R8c)9R~Gl::~cc(a% tE<78@5cz Fg>oSo0xJDAuVCjX)2 y±'CvXClߧ'_zQŘaҒc}h~'< @bWCIk%8eZng_+d 0Tlfq\rwa X#07n|yqA;+LZF1HPƀ ilRBvTaM&緑jLJ%Qr +-aCRլˏU4Cx їQXAhB +Uق+w:pV CO>"CԈgiJjsTl860_/ ,ZKz&=J\p]]Of0:^huo׳(c~aLa9(5KRw*k|Gi2 d7,*5B>E?=h{>^~~VujokDdUo8~_1ZսB˶GYiTGIqbձ#{EOvP7}3~͒ b -iC ݟ40DX ,d<'\g'# A#x0 +/;pˌm{lk2K)x p%LsbZۜeo%\J[ +"&e$B&Y/~X"p9V#pWoR 3KAn]]}G8Ngomf]Jfc0cq_D4͉mei.+&$O0oA%(:M"$Dpf03V;wR9̈#ܛA3Η:zCaotHw> +O]|+ȽqݱdrNa`hXAX&vv\ʨT}f>EG _Ͱ5eu,N}<"(41Q^shha.QH] 3$ϔώ7<5Ӟh\iqZ8e"_@OY]OrW̍)[}BAEXO9cCޣd@lvB-Mʘ' /J GTEWBEt&_ˮ:cj㦎Ϊ [=L;JVo6~_qX +dyCɌ dA%Z:K\iRQv5@$w[ww?x.K31LOFȼ}4=&ى'r5 cڀZCʷxA1W^$5G\k`3jCpɴīU~,$|5\Rm U&:1DJWQ7JIֹ5٭JBrd@F["v~B1S cq͓un̓l!eV- F&KF630uò!\o2bY)_ѥ-b9|9W'H0"XfrReQq/L[&ˍmneoA8Vyul)X&v+ >LZv^_#%<2~`ס0&Mǡ +**:BzkgS"BgM[{yao/n'L[I qU7Sg8&B0=o@5ݪ~໇ҕݍҋ=tPhr-ۢ߼eVbdkR+ն_l{HUh*i@B1:#t +|R]ʥ<~ G\\Y.Fp +GTN3] (=/&Xkr`?ncJ#RF g,ί]\EL}40ju%UO^3.ĦysVSU&w~?mxə\* ADTzqhEu-^ʨ>j?wNe3 gS4/s~ +ci]G} ߧ^wT] S0L-EO$3.ԙҐRs6A}s~l]\;һÒZoUMo6WL+ʦc7EE],[D)R xb{AK}y~ +b5j=ddr*-@ڂ璿|Zwep%@7ik +V9<(7jHÕ9&U r|XM4ؒ`g$ [+\jAh^9}Owo̖ha +nZ,*`m 5oTSűDbEFFD}"$i%C/֋I;Mz4 jtT{Ok֪PԮȟz_IӍc%DUuOo1IJP +T T8&٬k[3M#^D +yoJ[P$ e4]sRFHM%2)1n|r_5 ;RAfq]֗܉g\+_D<%GfuznW1?pbfH^j2>'T+aqP$]M|_ [O)ҏ:K:Iq2zUMSMaX<9p2MCƼX- 9ٽ}TMO@ẂP@DH!UHhǫwШW>%3o{7HKF[GWw0h~I,՞_*jR1$!:ʵIA "GSuVNw=+0NiG%zdS}/{I&xtD_4FôŏMhNa`~^)!qбB9x[~&+I~šmF]@E'O$+Z9&-ix9ة}UҽeVmo6_qVrfi2/ +m4À$$Fyg iٖ%CIu**HQ*a1vlI/*tӏ$:F09H!JXCbf`eV#-;8VC#Gᘄ_F 'n**4BoB fK5Kc V +[+R0=JpOwyvC s %ޙJiUG%JHo]$jui陁V,&scyI +,J6v1"spSqfRtftF9 p$,}~Q,+^[޾7QbVGƤ헿H8&wcƝJQ3eyiP92\ +43>-|ӄzEݔ?I 1_5q r1ZI }snC!7w֑ѳp +'?q`?qұl%y*;if¿@#̯"lr^8v\Z{OZ6Z.ly:af,)4Ϳ?iN:y=j1 q7,66@)ѬE=݆w3T7ibJs}xv.0r&uOta!>+cB&u>Mx97jڸRfA"7/j'rF>ә#XƢSp.r)t^};xG-b [ 8',oi$2ʻNDVC$ܧwiuԸȡLӼO5DC槵c8 C9mq*Rsv/ CB"%KED(ksP+e._mO1 ߧ&>[ +[mBBi׋KM|)PZ!N,9] E.W!}ꍓQ?>A4"p3 nsS랼Z ]ك/ |,3'8p.0p%z80qC4 L +j^/RkzحDCd5p!+" +\0Afa +k9p(l%i[(1@rB"_ۇDIRR@r^I" - +a+[p 4L0o0a.d<؝W+GYm'k%_2x|RrJz "PV~NdjE&{>.-YxfGlʑ֊e-_X2\rsG.yU| 3QM.񎶩{v+.BOʚHZRXh U1 [r*HFҊ8s(\ɸ ®b)w(CK.t#!(3,*;ZmbĶGX]@-%i#j\RlT( mj&pL:Z\&}5[qZOCW =YXi减!<ՌeZ7T ^`7''fi/{czNTk0_qCڐn#nl1 +9U$MwNF!5CՋuY_P4"`N{^{(A/RAx\wJ/4:zrY`a00'U~ZefX b-,`.+LakrU.`дƀܺG-!h۸7(aqx?nW?gWQ*5J0A kXiVyrm]˃̊Np~) s{iQyX'湺gkb\2 +3AvUp+[F7գD6A}[-w)t#ѣdL>pAui6u(eJTxŞZ\2~H|VA2ZSړ9rW)St +(W?=>~[F8=56548X\:ʏ?Pc{$; v\V#zVMo6WL;nNf7M mjlRidIbH+PCy%rޛP?@q.CHٕ|4;)fYhTO%kt!m׷_((΂ohu-H53 AlVLS^ ?]х=Iyfhpt>E%J`ATqQقc==Wc84 =I RRÁ1AUPU;E"M‰É &0FΕN0Z0GD_Rc5|ÝJuZ}R6U l< .BWV8iQ'ec満3dnD8R)rHQ7od#/'<n։P h)2ΪDuviZ\,'F'Ƙh;-Vd +a<]J cA;id +ZQe}%5@pF U{TmOo1)ĂHho)JUBv׊ TGJx,O(H[ŔGae#@q~0 CMDi,D_b]=fhw>T |dGd{&bGy[|'ڪ(F9|x䃥drwfۈx +RZKzr[EqC;uuw>bR+AE&vYT`7 ̩Š4a]+^_LL[#\G:v삥9d0+Nc뇄Oj`WBU 6:ZQ6NnlwQђwӝ}pBK-n歎م^ަ~tl?{߫XNv3Hy;4?3x?o,x7zUW6N:c1΍_WU[oJ~$ۈ@HiҞUOEXXU۫ ;P)3|7W_]\AQdKf.qc3̀"ڂ!g;P!>JUj-D4@Em \-&iOS\Nb,#CLkQB8z j8+i~l9΁v=3 0I]ˤH Ž}xλ9'RfXžOH)i,@Exȉ?̃[b;+(% yenX,~2J] }yPNpF`?5 +f1숆=|Ui#aNm>2}$T0> N0V;e`W2'<=uШXkTa&'_,+,,T^tY9hSϻhcsdFJqz9K?шrb8Xo̤FBsУ5#% ,_j;Vp}T|2P*Nғ͵܃wW1 +Iqk b t" 㳁FJ3QMȸd ׫*5\_C[ +4.:Pr>Lvc0|s}Iʑ*{4Z0V4l%K'CC//Lh>Ʌ›"'Rs%e;pf|WCQX V;cdQ0lܵwHWS.{i?y.y&y~~?!U/ܗ3߽M kyi6X\ gCRh1mE=Fç9=AO1xT1!鶳tBiv1w+xo{,3 d6%mrw3Ha5{$ +=ͼs1“aOY +f:Wzl[tPƬ\A2W\΂/1CnWhφB!pc>hPI6NU*A(`Ms<o<M0kg?pveکQEPNB1+xT`$Cģ)}6}㿛=LoϨ+4(*]Lr81ssz.9 [Qk42[W w =LDmO󨢛 lhkxrZ̲ϔr+Uxhk uܟ{(:*xӱ=-͢;SoG[Pq +GV—Ԉ#T"M*:[Y1CN$4m`{ +jg{jW*MЉ2}Qo1WLH k%*jUEϷY5^P^Py5̎?6&mS/ +-K 5 |l@cj=]#;'/b+v4XP106 *7| i Mhr`\yx7@"ach}-3+&lUDmi!|5APyX8&':۫kzT#S.p޿wǥmF3&`ɁŁI63F* |&;0A$( &`5H2V(9^HM7/4#M!GN1P2p' zO IVHBs6*2l49py}v'^LunWq7$5/ J]`v3J1G퐾ǿn +hM#A%:VkZh0=EB)}d&YsX{|8 $LpU;Y~3IXFQ{mp!煨#1]{$u)ѵ7+]RGˡ7 ]cT:eW=m^Fk)v"gE) Uf>' +eaT]k0}Iil`cڗєױ- :%!ĎsΕ|]'")3[2Ӓk0. 9b" c"68@(5b<F"L,\1#q^+fI0 0GW.tQ+IFrRV Jk ΣvuOo1."A?EZUHɍkű,5HZ5⦅G7Idf3(DyAOel'Y7X4nGW +bӁݶTg/^'bi4]W&}Zgq:+bD1y*B`abvV".2!lZaE흡 .l"?kq1 2^nE(`aӌ0EbBxf\ ]{gah?k좳'DrrZ0>)Fjѡ3XfVEu9ْmU!R7i߹%N/WSY0~²!u]tBX)>N^ C⏄l,?UULw3O;.6/I |$` rہNG.R\vN;2;v}Diֵ*ZIIr_)eUj'تN3H+zRMːޑAx ޔ6X +Jx  +ܸ֡uc!SI-{K-B9LZI4AR'XY3Qx}}\>ob$[*86D:Iʌ;![Ak-mde{q{.#deR v _;I5>10:^?2J$n5)HWԪ Plv^=$wL r$w%[b[G.Q1#kYhxP\9.x>żAi>S@ȁ 0k$g=5ͯFVӓć$uPUrAb8Z2AQƗ3PIُ%bp_`gUuF1F'Kxe , E=ipſ4q.HV>h~~Un1~:pB"%DO=׶{vY;$OU]^“K+7\[1&BTˊ2!=]̊Rs=s&|H6"Vray_x>ǖ+mGPDM=\q8wJoe!<Y%&BG:s6w_wj.*V&Ei7_;u +6YMM9>2RuRtfT]nI'mZ<[qQr-}MEwxUﯰ/ckO~~2RMAϯx$,kĀMLkOWS].$!WRа <ɦw'/M|Va/h}`DjmW 7k7OVXFۆ_k)=X]K +<AhD;QXhN#:x13|lE{2/)0e}X߯>=F:2( v:>ˠ9FWzΉ#]˷2EUhX7Z W٬a-QDh$ZgA#Z +BϠL;t@Q1/g!$OQEuA!aך[Q.XAvR03&4C#$ +~_:O*?_ƈy(~^>8R8kf=-Ep\^mrvӑpڸڊoTMo0 WPv5niXݰZI ^HNE2G=xj 2Zd߾)i1 Kc CP$pט53HSdF%'*$ ^. +'Džb1Glb * E+xWHlWht`JnbM}M?_T4J`*]-`cٷ3VAiFϓ",k7[\kL[ w,2RDgjY:](G޾bF(-9X*45&#w[ƴ:yƱ: as訶U{qKu\N,Jop8ܔ#X{S6ZGCA-ab'!:2NI+s6!TUD_}(U4t^czmSMo0 W!v5in +l]t,ӱPEH:Y0Rb'٪L=җW P0g!Uyf X q)Ƈ-e#Cj{CKE{\| ,QaC1@{'dV<'nwht`\ix7`Q1&?=.b$L%Q ]/`cٷ:S+4S$GYrt b02N]t7[\V1/F?̒6߂b8.C^+k^\h%2/Z ™i 5U{BPaAG,.Gn=x΢Yߴ{drܯ/jr?i8kϋ ۣҒO*:Dzn5ᖝ_*?mobkoc[VȬn!aO9eWj 7XַQN@6I=* S(䶀#vEV /Is%vp!C26Ct`| 6xeZvXvo6U={B;|5JCthaE~u:(@I֤ hj^BrhyyӢDc>6߇zGfY>lDYYgD]h1C5JE .Wa(Tȴ2p6v3˞ gSCB얎,T= ^;Upjb(}NAŖhQSojM^|Sߓ֍qAQOpco`{Q;Qۉ%_3 ӣ'TTA~țIT}dU z&qnc /j+amhҀA^`BPc_D9ɏT32MF +^k^u^/(4 iL`bT|IE;=0,\-5Sѧ]$8 lj>C. nG&_/a!WʧX|_̲?EA;n0z9` N^kuk$$AS+-M˕ Ȼ%ϡ(o~]Mޅ:$m +-Koq6*2RX}mt%>٬k@xώ*X"&VUI!hpG 1&f䃥B;ajso=CjBX }LO5\$Wy(1 ,Hڵ>.7nU;JS,*3RC7 ڗȣ̩ Š4VFY̚[3f[bV :szn…F~MRAlVhT)˥. +7Z}^0<<]t.ؗӎ/N)J|Iv?]}MrD  Ŭ';/{Ot9]̽zÿx-=lgk=1}SKo0 Wp@P#ݵntm XN](2 S$kĮktE{(!Ca옔 JtItkJ#( Mx&Җ+R! \AxN GpSX Bs\ ˢD[j L&5ؒ ǖ #Jq悕5GPjaprv6Ƹ K Sn˅,޼Iir/2b~߼HE j+HnHpER Zh#i$¯p!dK+/':>T+ْM&TIڤ&^?OUc3XXP3}ԟ +Y@z)qԧ.aMv. "bס޳]!sHKm'7YcZX3t _~8T !exN*υv*0 z]x[0VCW{\3]YЇXG ܳEgv-DjҼÿX9˯ߥVn7}W95},ՉkAZװ%AqG)rJ{AbM H\ϜٯiB\2Y#}iK8AAXkM?K\toDXxB$zBc ΘQx*p1%Y\cΨ<8Ou* SpjC+m&LJw'-GEB0+@*VfyLl,A$(MIg#p) #&|<"(#3Pj?-,nh%#׿青EhηRĊW4b-и DjĖY1u%fe%3]u}O7m\vM/@e{jIuU(J +^ȵ"k2n/> 꾏UZ KZ@+e/5s#ts Bu:8^y:Jxy.b.;zùMdbBbtcjLQҕ9yT Yjc1ü&Z4vvr)ٕfiPjN1#0,Bh|T&eV0j1`fj4W+F832<ώTK:3HvcRM5Qҝ.-3ΑO ץq|!>5ܧ$Q. VW:S !FGthiPqQ2:r SHG'L7ѺN'}V&3\V0.?o)r=h6KP3$+T>a|@"8r9m/w$1J󟺦;^|KO0~r9fS J:eu9qX򏌔L)KS!Jo2)?84^]oXgeCdciA eQYOV>uZTŎQ]lug²kp¼3VQ`Mһӫyh/]V؟YozQJ׬e1#܋&kWÏNؑ?y52ZPz:2b^9Ój?V[k#7~8g3c}X +q:l mRKȚc, Gg옒^4mw>$N Bat?d+VVJ#( /2;Ic@+ߴ0/HJe}p)+a3Hpʅ$N&!I-S䊄cU5*Jqʬ,m+khap=>xPYc p)vc`߼)Ii勑t_bA9Cd6e K[2بQcRȸ`}g!/"&HUZx4>"liUć|%&b=92D~C 1λ2.% G=*YiSe.Od(aawg#/}QwPUݛR1tQP#k[=ȭ7\QpiWr<={btS;@9w\_gGg!)"´{-t9 WUapdok0WCvHmY֮euc/sϱ"9Yߋkل^Ow PȔEaCx&r`Z1DPw\p`i2{vW/b0Gv\iQ :H1 ,CKՠ6NSG[[fV), +wZaO%ũ|Sa}Cø , c<&؁hgrņK',Ȭ>Vp^j2W(6X:!}w7+1}iHO+=G}PMo1x( J5RTEJ^*!eG55^*TɇFd* +[].RWѰ_[Nh8!Q/-oNx*J[x'gOM4[滚7xI3u$0me"aWeAR9طvl'&($†i{=?=̿γաF5 5cز\>N,TyTx%FÏ:Is$ C6F.9j]Τ(1J4G|\nآͰXJgjwW}x~\vopQ+ҋr7,+w_6;K9IRh}iJ7WɾQMk1ﯘCƎv\iB%T0+*KIkcJ{RN{3o捦|HTZe{ +wI1[jm: p5^q(el ?cT* Q /c77VA:Y8tByPOh%@жvQ;;7$aiy_iT^,6"b'HaceIN+ +6F4"\\o$*`<`[NJ4)5=뭈qB_ԡ}2ZnL`Άȭ噁RdhMd4iNm_3fbnJEH+$|g'uhֈ`eOņjw⠝-ttb2LǠ % gn>N+~Un8+SK`u5lP7KFQ$f(&,re/ˋ(ͼy3C+( LٓpelPA18Ali5bnRC*3@g8dr8 + ι]tC4 ƓZV7[_" #{@k%02ʚ186 +ݟW7Ka+r;.a| >g[D6o%%FO4I*q)iϵ#έaOB?M3|Z5Jo IF#aXv6# lCՂ/zdMXii%mx] lFui3PH0Pq>`nu ^[=Wl/Kk5 !V+yE%yP,~ZxX:EmҔ4 +YB([H\a7}哙krV蛬Y1ppzʋNx|b6ׇ)ʦ&Vs>%}n0 z +Z.Y֮h m@tDDN }A;TIh#hNX$2*SYkb!@ Q'ot!@aJO~I&:y[75NGf!;`]CtALhJHBM=g;2|F ?P3–p=,TgLiv$$&`B}| GmVYMRܗ7 yi#>D'rس;fJ)432=v? + h32\Dkd!ߍ @u/ExBJl(]"oBqJ˫7\\=__%:j^H"*р{RDJ܆^5kZl4,P?9Mfаp 3$:UK#̅%8p~ w[4Ԛ_SȯIVg(hr[Ѕfw.*tOȗaƭ3tuBVp`ru=/MN`IƟ*դvA*OKٕwX1Zz/J$|GOOl$^f|ͱcj&-$TbVQSp*:~H5K{xޢ?Vmo6_q(SI3*(7mָ!0gQnnd!Q"(P; +Le nKFks*b)F1|q_Wַ֔K ѐ1 ^)䵨E"kS8PKB>D݋/"QTk3h7;w^@_>oWkޏ'?*$cAokMGqr-_ɿBdDkڐ\1-8;QMY+s\XTlG Bze4 X+=~R^0jɆ$1lJfsx: zS2z辧'[S;aktEЗ 4&S*>r7] ;6nc Ć.b:ݏ)|bjр_u-Plj6)<dσjø<7BeF ͓`Yn8M-IP<7gm{4YrDIr;BP9NK?6cmL-qx芓6dl71Lp 9۶vqO./흍3*I.O Nky:nmBܢ܏kܙ錓9/! -5#~?I2Y?$97ao! (rzLOͱX'&o]ZiR'p} ?6}t[ڿ7|ߤ3~mQw59F'M5эIWwFVQo6~ׯ.,1dxh#Zua):L&5c )ʖd)mEǻ;^ϒ ")Sjeǿ.Nᯄ4Ĕ")2YBw2)zN |J 27*)7bp9*ҥ&K2YLD0VJ{H&A4n)qD,՚bYL#ln߇a>\`&aLCDl$`lZ#p!Qg#r2rH gˏ>>-?MLJ˦rЭ||']80 XE5ԴRaC3EąG Q,&'''k,?z 2Ki1Yޥ{MS.bRcfhei wr1C+J6 OOfmխ8Sa'=:p%Q9-]U΍T#UJPr =GOji~> zD]0hFO^V\62_:a<.&WQ~t7h ^TTx~V4Add KvS5$a-7Ŋvq͖-ܹHԃ.vKAe_3 =|iΏNAԇq߬iMw{3]sRBV(Z(Ź e ;vg2\x7*7V@rV +Lύ"FW*ډL"Gx[˖]u1\-ߓCb퉺J5',M1q@rvj-ytzv'Gs¥IE&Slo>OHggeri'ןBN(OiZV\Q~&0K(8R^ +<8AS)Hu/ek-ݬ8 Z ,QMס+TeE2P6vV6U= kp~2aڶs? &lU@aE9,%$]ELP&<߻eItdžAh^ȤoGs˟TLD~5,7 q([۰wvCVث<7JtM㱝ƻ IzmXj k-i_3To_N{qYvyeWh=;\ =ame5ls 8ώSXڢM_3>J!>GgCorfD˹m= =*o0PyR r2$>3bpǃNώIr$'cvi}—?TMo@W!P(i#*FR4;EU{ҠD!>7=}LQFouI'YAV`n +aץ<E19|p)l+hpSE"ST 0+Y% Z+lSX?DBtBP[[/L/`Z +V.*j /bC`B9r?$hK}I"02W[*bv-ɫ °F HT_$"G5*dM9k`^y<[(WF;npVLz G6g;d)}~Mkkyɏ!!rAF)ARw4؀v駪 fV#gQ=z28`A-Kޚ]X-Yτ 2P~\>V?w_w1}ZcܳmNh^+ߛX^?vFjL۶ mW?s<Vetmn0EB +@ b4@Pq192$;3cBrUŗxvk5 ~ٱ~̫ +E"#( +!xžo*\vv V_#,X$N6%@ ߸gB% %IE? +Y`c;'efy% 9]D&eF5YG)|S{u^s/ᲟoD#y?W(i1!fbUrQ>Ʈ7_Oj擙eòqM3>5zIOb ^,˱niX@z6VH8l6\{W?mMo1+DP@iHHQAYblwfjSo;o)" Fjc~2V]&(mBb7ք=*ꉧ\ȍi! 9CLiƒP֨Y-ph!44Dt qr2 ukYBր6m.CRPH,; V>セ<=X?w5lt`%O` YW^s+*Wwְ_EU" +lXQ[As܇]a{$|WZj?ߊu 6mr]\k|}Z!@Wzz'U&As!dyt]v-YhOq\zAggY=PNAW!!؄EsDBZOafӻߣ1~ԣW5Vt +gTڮ]^QzI4T13=ԝ&famj Kc)V\f3~pbَ+KMC(Tփmߋ")!|O$ޤSD߈Q~>ZOR`a:i/v؊EAJw|g>Cydd≭Qfeꍛ::I@?0a'،ݤx SWQWERn0+"5]A(@AS(M˕Ϳ%yݓH汣O H[ŔGab %5&6`"bИ g><6\bGlI,WRQrq&U U+c/3!ԭFjk%ƻ3K*6w_o7 &lUDe^*l4>5Aj<ΜZS Jw{I1r:)#6NIUVZWqAZBUbYPvC $} eF1\k布6%GO2w}d䞞$oC,Qc8{Cת=j"9:Xv,(h0.˽sio:›'M!)=x!=޽GMӮ3G7GO}k+8/bp0۫>ӳWFGhRظǼ(DE&iW7 U>Q]O@|J#R(T!Jrħw:!Ȏ +{9wfǟˢDNT{]$'QrO 4snI aڔ?2u7Ga0Fاٮ0išsGM_ɍtb_'x m^h=&|SjL۲Qe Nn^*BgBw\[u[R#>*H3 q04bv#[Lb [fiy#_)x1=%ϭQo0)Uj[i]`MO&׹66wwGNҴ{?5ݹ'oB@maBF˽<l4 3c CP$gpS%PGHf^ +:3ror?DTVHpPb  + i%R"*kAoc5#7xwbe}ELUR ,CaFJh}EAbm9Oz@J#ܔ"$8r9WG]dvwQhg(Evtٔ[U! a;ȇC8](Kc_:Uq PЛuGIsZMw,TiIw$Zzzn5cԸJ;VG۟1-و- +3ET\6Ͷ"ܚ]W6WZnhoGkjN 7OQ!l9`20S!J2ʶ԰lD[wb1Խ;$eljUԵ@-a~ ^E. ٴ} |H\/^.}_]9'V'6 +ۓR8bϽIN#~R]+1#{NJkz~ϽtfYORKo1*尋B($JHQA"X*C}g{FI\g$W_Adr' +[Q/Cq em µ7 +{%f47C?몦X{J0A,;ax{`OY +BpaŨ'#u;wws0MP1bm-X;ɼĆ-roBRKx şm",wZSW$0;#Gk<(N#o@$Iވ>LAJLlNwCT&?;W-MBOŚ(wA}oY,`Stx~1rc5?9 ;t.R\ѾS1 ogvɵY1#[ t>UFTd:LpU<Ыήh]QT.b<莖Sj?\oܿ _vjo[VQo6~ׯ"qܾڱ,M A$ P`:[Di;]@J%h1`_,Q}苟\ GaꙔ:$ ByX(<8A vZyqmݖԲ`HeWd~|Eb ^ΛggsY bٸtC4 IK뀷 E5]J,,+kiapo>=WQa#"Z F8Iҵk*VH:nmG1Ѣ6v\+ P6xyxRrzK?3IpV5=ߎ+"iLpBl\'q +{j黮pV""LÝi _9kiŝ1uOwKVҬskbԧDnNnZ~NџI57e&yb NKߨ&I}:<, t(Χ%bOaoޚEs_[h8Ͳ^w1!-/BG6Ռװt*!8S~_oIQmAk[1s6&mJ0v"(Ϯ)EviݝF+: + MԄ}c%}{=]aϑ='+C鱎è/(-&a;ɄSd]yGﮰWcH VzYXDM;Mx7X=~,~H OzS'/KBM䕰g:OOۇYWt,p`V^ JwpٿV|db6>qCZҞB҆\?m`ec:og:Ct{ޜ6T9.iJL?l9EOo/-/TN@+搃Q(4@A*RYi7q+8"͒PK9j LZ.?edO/%( PXTh#LR ܰEb,.[ug9^3TRB׾栝Y%[{ %BQzӽfhB 9{ޠ +KUsn8|WK5T+ V$%H>5vyyXFx*?DI +& gJ5I>\/C JzEf4u(NAҒv +ݑEWo|[_s6׍!s30 dpT sHs([ ImqJzz5džzV44C鿳8{0cFL=!f}Vf{09q?C_ai ']۞Ɉ}%)μ"Q^rGx0*_XrG͇QUD-fj.ӬZ_.8& 5 R]o0}ϯ8 +| 1TUIqnkoh +Cd'c߇2 'mS7 +-e(NFٰ(%X < bÞS)>#| bGu{izU>SEql?BK rhͺ`3$GF +%ƻK*jCۆ~xX-TLJ%تĿ^ckW maԆbPX*ߚFFYKYQ%Eokqm4W~Z1\edM) Geo[uNQ@H,JBȃitWkk4T(T>zw2g&ɤ;uzQ%WM4 +ݢrPΖ,tS`6Y?譒h}baj|GMgK-#$ւ:ζ"2RdOms)ɻN:,&_JsٜS\!M*hO%ب?_E z-}a5ioբ7XmQ +01܅E mj +⃦t1J/eEʭb&)nag<R5]0wGVx4=&0 G$ tH e{I͙,pF<*K&$M#zK0.V~eVy)S<(*_5 Gb +SL)MU ›pRyضz#aP=e -C[XrjdَKSs+zNU_l;͘!C3Ú ?r {09z r/m]RKT.UP&nl7[arEZ=+!7)#܇W.vPf۲e- Li0zε"T@fqe !޳yGݹzʜCKpSh-mgLJaE=ԟy?FA[\3A1TߵV$`/q?&a@zsWh<-YXAM*Q5Qhc% mgJT{IvJLk@Y !R5e ʩqFԠ'@nVߺHfE${oӤky8Lkxoߵaߏ)犛pF<G~k 6'> s%[Xn.KW"nr 3oF&tz+;pw/ hG +sp8_i΅#Rq@+ [ /pTo0_q Z26DYnBe6Uƹ$Vm(';ᳯ>LCX1B:wY GOVUE.8$^#WҒ)8%C{DG:a:r];ؕ6#mĔu]Qf9DqsXUj;"S,Zbꀭ3m$ZFH"v2֟jʜZ5#¥vpeG"^"]|(v0 =Xje|gp)RKmNYpkǭ  2HE1R]^%[xa3^C-~&A ;ZRsƆBRT*N} {)ovtY?ΰa71NχykL?w:!aų={\ wT?-/=PjA |߯<$v4!RB]T(=ݭZH:S.>j4c .͕s#{ڄ+|lТ:jGL+|R!ܗ(nF/uG>s|[[Vh""JTŕWOUЏ w؅X\eV(tEM2NV;K=_'g+ܷң =p s+4Ad\sedUwN+cs4]nӚ. +FxB#p'ҶUFe4;8?&8pKhzP=gl%+Ҟ uc\'X]*$iu\]]P1nc+ &F(O,E2$cG;(+\ZD 1|V4FCgccőP(9cᜄɾDT]f_ >9Zket#]K?##pRS#z( +ݑdcr.>";tV6dmtP8AvI6/nBZQQ̚Dl!M23yZjbH/N2^d9NR|yߐ3',yLE9G>gQؑ&rl'Sbe&ض~^9B/.-M(ߊkqXHewyjdVnF}WL #V7R| +m,CvVP\x]^&QI +To9hwt!@0BXەF>17q hFT1cÁ q4u&p#|F.Iύ;><˙0AKStQi) !P$V)~P6F! XwOт( \I@fY?C7- 1`Bn^‚l*ި@# f}̨F#[[-z#Q"!D w\[5tL p0!&,x1=9g4Mf(mwNsZf *i~bubϴh|ut2<(~nqw0 g/׃ݟІzwzr2XjY*9s2ܮ EtqQ-[a+K{OjJh'2H,3Mlyw4=5w eRoՃ1ia)HewYN46+TL._SaO0_HR4iZ`mH*׹4NB$aB)w~;SHKf(f)'& 7H$*+S?B(ggqȌy::QAIYN:`*1/6nER=Z +NJY2'",a%.Χgg22P2DM/JP +yVNbK9ㄫs}WMn*$ +)c'$Klnr$dbՒn\ȋO:L\~9:9=='A: pbVF#f2F1nkxU=UbL]ahZvxwn$?u]FcH^{mN{P05CKvPgt 1!cB%ٝEEɵ,H\UoZVpCM*%;vhw^ +;q% +찷~m +}#~w5\aTSP(W!5~n +5ida~Fm\aIo&Ak1+ޡ8^cIk\jHZC] %13M(e7>⦄|tBgj[!y6+1H`Eqb 6tYat,synM{t 'nxSc:,(ܖP(H.9,zX# ,.Fi))Ses@䔰gGn\zX Qc1 ;Ejl6܉'\+_UHMpEUHwg3tM/FjSOFvO5:5y"'ONvd~%eiNf7[! /{'/uӔ;Ak1+ЃLB&165ԥ@jZxm)e7v 6b͌J(h''4R񬺺po!*ژQQ6'08\0cH&&b$u}֗ }`GS.3u$⺄B\Fs6ug,z +\RM)zJeyt9OP9%Vo{-P*OM_N1)F(́˒@QH Td-5e +^9@Tߍ矙-AR 3)c;n=ڍp+S'N:W؊`Z7'5bYOd>kaé upЄJ%ƹZ1A 5LjR%p0-ZI4AB N +EݷjasP YA8-I"H-## +NH\Х-Nd4*6#tp1BX +'BާHQ$V[lhJ|d4YH\9JnV/,-Ctދ{`U*k/ 5Q\u䮓&>xXFE_i^o9z lC^-i 5a{g2j5~ 3`Zjkl](>nǣƨ4V>궏Y.Q,ߟU[9 +{m@ Pf਺ L[d²@/y?6&˻"H97(cy\9HïI=`~%@6Iȯ|bY% e9*>`uQMk[1_1lcbz֩IheZ"KjP{ĥ'jgv43jvT}ᬚ*KA#!ԐyeLYe 7ĕFƇ@ +椑cY1yEń">rˊy9^,Ϝr5\j-i9$yFӆwRA$ݐIcT[]t|ݭꍙ'Î +j){-|I:K򤊴1"lhaoI& x,9XmQk42Eʟw^s}ܖH 6SQ̖HUt=ĆVm5T00>SʁZ6Vp)jmȟ}*PO-Q:UNq +=˧Ͳ]So+X~C.Uc$[GX{[V"ԘFˣ25+EJG=7@GX .,x)"~6k#;{ &Sm~mN1~9B OZhDE r خwU}~;w5TxdD[t]*ezq<| Qjpg{bVSAqq2>M2)fܲa"U & lp%kˮɋMi@zJp#c"UikSD+&r?G}0 Vًۨ:қ#\&5ZGaY1]α1.Y|NSf/Q2P2E7]S̊)./,5śAl/S\]MKC1EwiKiqݪR R˂y`^2Uۂ̝]` ]vLrsQߠ7ςEј|(W O**i9eD-LgJ9P' +.E-i5 |L u9;BXXȁLb6YͻUbbgoUرzhW^R[TydmHueYi1.X1R9s^)V +nr +.|]N@ 2ORc%DQőNȩ54#lW*$\cBJMaXs\I夯,C; ld/JP:שL'g-Jp ~4m#PKNu=O7]AKA +CHW[Z*D +(t6 NgLZ.m=x +I͛LϨ+4(*]pbƕAW d+`F=1O ^"!AS+fs>kآl#% 3ac +oZMRNIд!wt`G86IvV9K@>rxY/U1V5سzhgVlylQV2?6<14]sv;&qUf%?n(wO?a>vءizؒ-.1^N̏Vo6~_q-J2gɌd+Vd:% :YDhR;Rv5@eYvzɻ>~ c͸o #uδI(vg-IT͍%-pɌ_Q!19%.QYԿW3nn>'bDA9]̥[ٌke,܆Q}'Nӫ&p]C۽dIrC‘wS-"J U{mFz(qǨ{i%T ɊCe +{mZRp+"+}uLCݒ"ш)'Ps~bd$iC:vZ^w}'y +Hr0F1jsz5qc1 )LCA]*<E,e.3@Ήh%pm=n6%*fcQdʂ "RŶJ{RҒs-挢LR0)&Pp$bwyVk[5f2b`O4$LX m^RQ*@uA(]FJ>8EAPjMPD)a|Rޞ.&r5v[bMD8]L^R9Eχ vR@)GC~d_úqv9 pgxn^xv#֢>>|gh2<1favLy;I+oVGZ}<ڡi0Cgm >ESZ; } wC ;DE8kGjU@w\wO[_Kz/~nx!CMZ|z礥nEpuѦWn8}W.,'ve)6@6)/mX""*I 6/uˢ~93C, 0%} ՃzP'w||NM|#LϞ~8w!O {D$|ZEx6;(a'(TY!Rԁ@X!gJin\JfyBXS"͸E9B" K3RCa*! +DBD] #XR%E|12GcBn~҉Rsc^B35LPfQ%Rd|*9Sx^)Q*8>8@ Ϛ]#"^Lz$eq ' iJõ懇3D*7NΌ~ +In|3V0\[u͈_pIi `\^^&:|XB >)Pdk56,FeÜQŏH l xYlKD7I gJܴ@ mCg p2MZ:.TKqvBw 22k#Eטq>G55W]0: әFYѿ@Dn%zU9jF +T`ݯx]S-;Fѵ*N4 +usGDmps +ٯiqFt/߮<)Ft>]oaڃ1,8Z֖IRel6ڝ[k[yNKihs odX5+ ),Z|mkֺȩ@9nTW5,}x#`qtc}QkN[Uz~Gqx<"Jȱ3;0K|PTx0\aB6DWH Yt.}"M^IX~5_ aV?l4{ &*nn&{P% 4N{__>e?AB }:z~hE~*9pn.ΨLPE}?ޠV˕9&%yTʬҖT +((ԡf{hZ_(%c`孼O8O\Q I Sf:P +mIP.ʎ:NRvc$4{y +1ı/$' SE7lu` D$q l +su@c\0K}.=HH( s O<,MBDcgqa.F8]WEX;HfaɌoFLj$8^N>T{lRJ(-};(AB$sf LOË/Ї ف.O/pr58>6sGWW? /x<8?>؂9L.&ЇZ`\bk<: G`t6TUōjײq89Ї6-8EH2I~P]d2d clxJ J6fXIx~^[R6~aHA8-L+V{Zm([ 5ujp KрggH O + +7W*EH8F +j +ӇѤ³tF֝]3{@l&UQAdI~߄#IF%YLjẖ̸zޞ.+gųAbΖ%-ʠfX"syizN kh fz`NIbjK&*eto6 ]yeM^YWVV^;wwr|J~ո4НL׽i꩘j{#tE@5=] 8؃{L(#erVkKRѸ{Hlt ROƛ7<%wA NْznqRDs[YB};f "E-@O=ܙ=XG@WQ %XUĄ5DzQjn Ga +}3Ofh4 ;~]˫EdW[.~ y-3g{xߌWÍ_į="i7Q6e#Uݐn+$N]|}eys*JyqXe\mY׈2h7cc=:L@A*yǏ~-rDMSy^XwWr Z5f!~[ B^(L'XSnv`ɉ:իniUmQdn߸^[Iqϩi.tL#iNYZI4mf}{1izԳ0k+Vz{wMgE켨%}s<(n~|B?kz)xt3&v+;K +@H[Z rfz-e{,%(1@25Bx3% [c$EwKo6l[\Yo:\[?jT yCVkj>7صuioބ4ѬXPQaڒWS6[[Z]6S6\L.Ԗ+>|+{?[X`l̑YbmE2"qCW[\(kstrySQ*\c~z^YZyFLjjBd6}fǣ i>E=jdu?ΡHj-BgRo +7DXEjp_ަ6z§BΟ^neºX_u ˹vR""@&*˶}nhZNrtn'p9^ꦪWS ^4'qxa}mRͲsQJCA WC[Vm- "z$o_^7]vZVIf236̃Y1qNxd0³F1VMo8WL4 '^$btCm"JZrd/[Rd &o{3, B.hμȃ<&B,$1CcxH/=("nu3bBڤVc$2-νA(a[f!̅l%@Nչ\G䉧X6c!a^.y^n/]fYgLmILQduNɧTj54+ˍj?`2r |J;'d4)3#6ξ3/~adrN\VP?=C:g$Hvv``⶟^Y  ^ThIHLp ++e3Ͽ9v:'UbZghKo/x4; $GbQ@er=)7>x:W-u - xcĝ4z4dE{a!xUyii ,{ +pfSFaK+KgNCz[,iSJCfV^X6LhRqeɐz&zA#0bVpT$E7ָO`~߿Ŵb&6[AVS̑9ctk*G'Q :??$j^9K}NOgSn0+KN$nZ)$\1{AYɩ4&IokngLDj=\,ܚRUqJ% p[@TeGj%'49_"khw{ǴVerKp!JBsGig +}}a fkR2Uڿ\øtC>tAϐ#X*ۗK$^[H;SwCԊt5RMO1Ẃ$C6@C#"U nm&ت׶ff^y @Uem7޼2Td2D[}Lr~<5w^ ddTí+:11)?_:%GOOb8C4[瓊.(1jLYvRT_X: N:qS<`i}_]V`PaU@Z6Uȧ&bC:KdjL+WE$ZOE`+])~ Ɂ*0mN'ȷE +wLM_}a~aŖm.lZ!3>.:(I_ P8О̛:Kbz1%TA`(׉ P~5CyaYZaAӽ'mqZCu7/Kyo~ *5S +{R"Vn۲k B}O]Pj#1+Oȃ\^a!sBX"$=q̒_4CN]=.cDxO,Λ9;28£d*c E7N1 Jd\\T0=̧ϏD=E fr.˜rJ6E-4Mgzeo9 +>-O90 n/7j5SG :/YaAkyIC C3i˒2V2/25fO^Ey;~dj $Z +6,R;7\C6Ɏ%'(J!pW8,aV_aJF=5mN W0(HݤouQMo@ẂHP@hZJTB%JsZcSTWuhsx;yǪ 'cQ( *lt͇l [P(%7t@ -R'q-g 0Aqt99M etjS*+t9TxSp$Z`mِ  +/;Tn%  ӾwͿqTkLKTc+g-Ak1'B`Y,v#$ùtǖVke?μ *N(d$bp vμ+x K.ʿa +]ݎFѱW 7='zc@Q;i%FӧgZkU2PhԥCl|ыvrW2g[Ҹ < zMn۱ մ;4kdQ%je^mQ/4N_?SMo0 Wp@A>f]+Pl:;(ʒ@ɂ}'ntG>'/=䨍bL0iyp1$~}YR \V8JA̜0-KTg-µQY\(xhϣx +B7"+_zt`9hgiQ#u R"1mH d Ǖrvޠ ++uw7pK5¤Tk $%H\A9d1%͸P#o'3P˓ҽQo~oR= ߥhA;w&TQOA~_1Mh8J*jBѢ>XC9n㲻ٝIo88I>@f,$$:=;)x+KKuaIT҃E`R䂎#(cWN2XiBB=t1>N4E5\QNNfU:a4;98_6fpFJvh%iO ujY=BҲȻ GP2,C"%Nl$w#s}.&ҋ^Z΀jB JϾI$Tj*L/Ba='͗9gS99:@gMw7SV$gXyΡz|؟\>).a&Ya L<܋n  tUaU 4OM& R~8y% +g>v[> 4KQlRD ԭ*d +q 'ThUɆs!E婷*Ž3?I*uz*I<$wu6\prG/iS܏Yj}Ʀ@}Wд-n ~!5>fgˬuЂa0# r~)YdUS#?WA*{D +նڕϺ}BGCJ1WΚÛ76K]3g- Y[o6~8Z +e{7]a.h -hQ#)>("e%E~~ѫ6FFR :^LN'p +[*aMvK̎+? *ވ F?P( rU?rw"RQXW^̷9$K!tU(.dE;.@mcXz7 ffk.vDQ gH$ž9ۻk eS[HH,ia +TmAi%/Dlʜ$["nF#ɤ,єʇEwqzJ(#d.h?xpoys}|2{ +wK&;gR B3uKԢLJ$ +F,ٚn )\vLsAD!$f~tOAͶbh!1DE5O-&fA!:q%h/8.:TPD}D=i[4r-aW?RHO}d v p^ *P !@M a9l@UlYOπLa^ϗ@h7Oaި*]ES-t3}J$Mk2uٱEǶ(? M2Wys`6 |I4D pS`ARA| `" 4!xbcj1rX)t@Q aDS80vxGؖR*M✵!VK@"/_s30wPSdl 4`e{[(l=8_r{7+Ym6~->Pߣ2btmqSs?ֆ'4s"0S%W[Roz:_v\}wœռz+I&MFro a?hzw+X"Kf/ 97f2|[e ֗ $M6[M1D}lI 8U? 6w`jѐft@KHq齥`UKTcLF9!S[Z{.콧"k.$[Ӷşhzvf)0x+;Tv +/{9T[&] ]Ӥ׫U^\\@V0vmwk`F:ydk!u?ؠjZ.10O,][{tmY8T>^W, a1Euz93QZ*^еRj] , +lwb7sHV|W lGvdΩi<_R+C!1 9EwxУf_KuћhΗ$1K8s3=5en4L㬷sErϣ_g.DI¶'CcͰWVSMk+o C%a9d\\o!+ +Й-׭a0({]N4>lw;i3bsłոa? ~Roc%d-?i{B0toHz۟jt/%o̭-4M{}G wH*0ɽPHBd1|Z[}+~\=x-.>mHV~j]eK1mAKd!O8Z*hACkuue3L/ o̺˃_6tTbԏx_}^zrVJMٵ`+iJa{֜~:!&3}`zOoZy88!!Rˍ.QFSAq_JxXmo8ί*PrvMmK8Cc[N';BRO?3h&5#b +N0 [n p +S!,8oAi"I "ށ/V!|L=%G*<,c<90G)-©I j#S1pȊINں\[aK |PSm3FBC0Cx8 箯Fgo2 gQX2Tp7lm+Gkލ gL>yϵrdsNQ0OUyUb)j)+cPS?>z'8Ef&oڈRRΚ:kΧ:{qialdK*ע'Xh B~NR ۝Xإ3@ R hwųp&)*XV k >ofEڗ8 =H]jBQovE) &Z֦JH4m ӟgr)t;&䔾GYTh@fPI֜wT =޻b7qqLLȌOwƽyS"s7-fF[fh T~*)33u1)CO4K~y:u39j5?RK* :^/~n(HCFq<]FTIf=(?KVr)(=띸|-gQ{ǷzjUo:~_q*Q ޫ'hvjto*5c[ mծnoޙ@\2#+8֠KN ݮ +9_1ǧZʣliܝ~YIuϽǃFگA#ϺQl.;EQMk1WC!$-y6mp[CR(ZWT̬]SnԹI>e4e*D;L2WV;/h} xAH-:+qS'*YtSW }`E6Ę&wR4ac{M,φ_C;Bۇ>wcxcէ8Adywj1HŴ4^kK\jMlᡳL?e QL\-Z+c\"Xĭ7.9@+oMz,&eb>W{QڔSga|5S8Ove|Ey1 cvr56> Az9SR8T]O@|JM׆RH0MAPPt9:*ʎ H?] Fa3)^9?ݠ +(<8A 6Tͱ@["5K232=-{8dtynxrc +J`~S8uC4@&i lWS$V@k%xeK`e8#.~hT1NBx_ka )paۜ$r'0"CDN CH7r_ed H&ORyݷXQD|ڸ/]Y* qù"li DAj=+ۅ\5\2C7ZZpJ9,Xɪ).j% l;_N;xI]\EiL]tvKKPl[ZK>fcpu zH!~>LXωS 5C}S 4Tf֐USFfcd!Ƞ hTk-u7AEm,}|>wՖ0??ڟDY^p}{ʜ+h{c>M|32V27T׾OL6˄ύIٮpc"aذVn6}W$NSGi }M,biR|A/-N]>Y˙9sH, FÐ)SdHOagpҁxLA4"Ȅc ܧjg(Ua%bE%ưRN"H-:F,2!SnׂaH 9~׊m3Q["CNm<Z3P*JdCCm밗1uv%f(#4H-VcTΚ\3w`prWK`F̩`.Va`r A*U2BV+c-qv13tk;*rȹ3`p: Cت{a҂MbKB-w l[53y3AZ!;@RA7vQ{fU7_AnYث^ס*.]l?wU7Y೅.-!2;%)UjWBʧRڐ,idu9t)5NHVK(ߔ~rҞ&VlZOhkX^Wd@{tKeb  \5O ѽEKL9&9?V{b돩[ޫ~Q9Zfo,s4z‹Yܐj<-l- e^DasVlQ1r"ٮk8| &H\2utro_3dkr݃8I4˜)4T6kHn)7 4c\uuAOmO>KʛX6sjSrUgc* *yCmMN*B͈IvBnۃ.6'h7myi|nXc=l{zn.$}iLIwr4Jv177Xj1\onXV:<7QyތƵLT\'.EƝ\u遣Z'Xyg(o|IɄja^</,*}0\?O(S5>ߓm!!?DJ' PX]aU?VFO&-=4:v&S|=q^lCn>p*tR~|ӏu‡(d|EL@ $jdENWi~J#d*2;lGh]2ť!&}6$;{jS+lD^KlгSʁ:#lRVS§TP!w8֩4V91r +ӱϽ[ՃT,]T8KybmHuevGƸ`EhկNrw+*q5Up̷EJAuLB0xNh c@&ٙa7?.s좪=dQ @=gLF#{,ȶ(RE{!K$<?`fK2Tt?ƆvVm T00>Sʁ:#lR»VSTP!w8֩4V91r +ӱϽ˷Ͳ[ՃT,]T8KybmHueGƸ`EH(w'NJl79PCQW|EJAuLB0xN1 ǀLf{33to~]6s좪=dQ @=eLF#{4,ȶ(R=Uʧ;!K$<?`fK2t?ƚVm uT00>Sʁz#lRNSTt!86V91r +={]V_uSo+Y~ƁC{xI]q Dےd+o2mAՕPnqt3@GX 6WeEJAuLBH1 ǀLf{ٙa7?.s좪gQ @w=gۛLG#y,ȶ(RM z2s!J$<?`nK2O*cK{+6Z*EXd)@6Vp)j}_S*PO;w`GQT9^vխ[ +*.pd^R[<56$:۲N#3c\"X%Nrw+*q5Up̷EAO1+xE$Ɛ m,mәe!nE޼fz]epOxz,7I5TyA 2Ev +gdzB޻cpRrDB:sOeUg0Vq:w8^rǎr}Ad[ba-1.ZT̙_:*ZDj)O0 EAK1ؖUbAfgM0Ml"w ߛ7oerpj0M (· b718M*XZN.zV{h c)BDATV3oCf'4mp=$G!)J$+Sn=mכ_5SoA~FC[vKIHR#m~>0sl{7=9;*ARç2mOk1"T("أP&4̬(eo͛C 3X3&D< 5( jvIgA@# ݻL919мգQYx2|&SB +&ɮl%f1\ ׽3Bs0Dppt̽n٭Gͨ_щt9Ln*8iCZqHPBII +7S@jKL(؆ +3L +oKOP_uAk1mR^m'uhK =X;]Kbf֎)e׻N(.B3O7O($8&$ :%W n7OQMoIlu(;fs6GkU}7&$ 'z=oHak$E^EKkA+"JΚ(rlo0냐v㱋z~\BE֛LVHnob:*0«cA͞d":>Ф@Xt;c'o'eMP^IEwc4l\3„ +6\b:Bz޹=[ +BPHL>^,7eSgG#X~‘A;xm !IuF+YMiy[@'P W䩡U|?EAO1@<TcHHbJwNMg0nv9˛6\g&td0«gÁl"x>Ġo,S,7]`m؟IE7clioEF ޵;BXXȁL~y\lWݩ^LUbeQ#v.Ug婉!6ޖu#(w|I)V%&j(~oEOO1@<"*J$?cK<, f:*Ye)@:Zm#_c*G`[w"|Rȁ4]u9_L4z"i95Lg䱊&VNet(efSlNCҖh(瀎B269PMQK}EAO1@<$FIHbJwNMg0nv9˛6\g&td0«gÁl"x>Ġo,S,7]`m؟IE7clioEF ޵;BXXȁL~i\=oWݩ^LUbeQ#v.Ug婉!6ޖueur;<+q5Up ̷EJAuLB0xNhH0 ǀtfzٙa7?.s?feX6 +D7rw;VQޜ>0 SQ|SO,R>3c@ +fT"}c~-IS3\01.sʁ (Z}p +16zpu* Oqq|^֋vٽꋩ#ő/-^+/-aTTLqTi/2*H(J!vy2; >)G+]- G\ͯEJAuLB0xNh$ ǀtfzٙa7?.s좪=da*<-軞3pZMFFxs^PLEjl?MޱH\S %2O.*Q<_clyO"VrL.<)haR"ϩ@nCoN!)0}eXnݪL)$^~A;xIm1 yREjX2QY]iU@"XoW$ny2;'hᨂU}W?EAO1+x%I!36YU8/͛oeX6 +D7j8 +#8/}`xAH56xRw,S,)f̩D^[ޓGn`.ga]wFP0)jVS‡TQ!w GaXҐȁI>^nUGX/]lqI<"5, c㨬ӮȬL あMX I9Z"&n8"~UEjAuTHΚDs8ۛi?B=&cQ5y>"lhaz$ x,9XmQkG3/?x.lSQĖHUU8ĆvVm+`"Wc}AXwMETPB8֩4V9!r +#ө[-gͼ}So'+X~YTmyI.U#mC#-˴~V/,gGRۛMPT2E_KBASGQz#BG!ֽsݡǽȅsgT--Cϙ?15 -Tc@#1O\x=C`'Lm4^QEwCikEF<Ӟ +rfgJ9Pk\ZxT +}pQ:*8Ddp`:vw/mh_u[ +*_U8zh^Ҿ8K%Dېd+o2mF&Ƹ`Eʻ'G@'X 6WemAkA +!ۤ1N$84Ж@z yV:$Sq HOKi +*GjPX?n:qgu($\ ;r$lȏq+q)b1ł$rs_U|sgސZܲ`²4s A$lZˢGCXè?:I!Y^BN(I~YC=)+5.V<y򌧆1;*cts>*Շf*=d$BUmn<6a9Im4y.lnϖ'A!dnm7ώ{yJ{r{wEKkB1g"JڗV("إPb2 MBfJ{.pΜf]%tKF咉5( <YAuH#1ORL%fAO*˜pGAxB L*VZ|C@ GO./6E'ͰAZxNM1y6eG&JIj.ej] Y(Z&:) +&~oEOk1"JV +E{JLfl2vq͛˰d.4`)Ȼ\3p#ޜgT><#"HvΟh9)_pDs0DZyba=5/P{a]FhaR⏍T P5! E&XRk)i&;c@ +fT"w16#QO/rL.`]wBP`Sw"ϩ@nCO[N!)0uXmݪ:RHPy9gqWKjeT]*OL%eUca,sw +|7UpENAuB AE Fcx$1l/3qvf2fWc}v@{[sfOx`0d*TcGbSl%2O.*R<^QwCyK"ySYvS +AMQ5 |J>V( TR90 ,ꊩ#ő,pmyIb6Ucad+Ge6G&@"|]kw^,I9V&n8 +̷EKkA+"JΚ" A0G!f3tB{M.m5` D ;}S&td0›gAÁl"5X{Ġw,R>DC +D󤦛16TYQOR\]Rac ZME +SzBӆ޹;Bؤ)Y!}eXnݪLUfV%\S$[GX{[Vidf V +<:RۋĻhGQ|EAO1@<$HbJwNMg0nv9{͛m` D ;}Lr}5`WςE|AXY+n **i?ϓnފx +rgJ9Pg\Zxj*|H u?svN)Y!}i\=oWݪz8ZAˢ +GVKj#T+OM IӮ^GNrw+"q5Up̷EAO1@<$HbmMg0nv9{mv@[}r}5`W /TO z2ߝb`+q(~pQJ֩4>1r`=[Ճ#ő.pXMyj"5,,c㨬ӮH/,)xu'X v79pQ|EJAuI4"Bc@fg{ٙa7?.=vQ_OϨ[h$Z雞3jbn Yp ۢH tc08) {]DS +DW1vTYQ/QT.vj!X¤BDjpɄ׍eѿ‡,@41Ȟ8mlqN}HN ;)4Ξv ̂3읢f{k57 >gaܖ8OX':}dTU>:U&mJbT(4IM'<0(ՊEmdU}W?EjAuT%gMb"JCNBg{3MfgwXEuTMϨ['Z點3Mb^= jd[F]bW\%TT0%lwѣΊx=Lb̲ϔr6+np +}p6Q:*8Ddp`:vwϫeh_u[ +*_U8zh[^Ҿ8KեDېdko*mE&Ƹ`EFkvi9P89ʭb%^-nr +6?S]k@|ׯHƎ髝iBu |Z[Gwv($KdCܓO.scL_n>&h<`?2ERA8 +3ū;^Zg L7_03ogCXR+aᚪZ10)Hkثea*d==Tk%2+75Cp!lLJb8 ;A**ga +;pXl%i82bDx΄? EQSed HR8 ~=$(;!r^^Ҹx!MOHjA= \J'q=i7a*0YCO2LjHrx hraWwAX,5k9`]7"qOЧa+dh&hQ,'IZ66d!l.!lȹ7`pבͽNpA/^ˑ 3owG7]0`\ë<1֪aCb4ò=IUZښw-V~.qѧ H-P0M&'''67iq?_oR՝{.Q1W} .*&F7R:M{ K t+:os{qCER O^I^QꏊaYZ&'<îPhu65'x 'y oho+`A+X cՎӔ@SAZ^"[{5a|rOVL (~+XY3$ah߼x?Z̓Tk؋J֋*>%Aڪ<,RpBjQQH-Bd  +%;L&ɤj'p#v+L5*i85cm[k1WB&kKͅJ 0FIhf^$RfӏF4l:J\&gN"pZGFju "%Ehqk݂+ q<# +N)y>o 8Č${N8m,!vA ^l +~ + jmu09s0oCz u"vLX8^˫/lUSK% 'Z`Bsd&4ۖǕHqk)݄e"Ӫ%sy+Òߡ{!19Z穃H3Kk ~TK;g Jެ 2xeY=<< y^O:C4eiG~3),7~Akգ9695j +&v^߶kF`\Ե_Wƿ9zGGoVcvBWa,-̾}PJfG{{ʪm[k1WB&m@)dl$HB3k'"Eh47#}&!T%Y-w?폪_e,#$-na1){#u.) L%OLXqM%&pMD(@/: BB9`}҃>#ńE}OL'٪4&F X^բ +%MСٴJfÓq~s_m沪}Ao0 <`i]4Ztha(a-30EH:A1r&dQ#=]l@JՕ>guc&##/ҕAq3'P>q$  9Ң=:aI-zpO=1XXd)@E;)*r~N }`_E(>oP}cȁPvCߗۻ˻b5,v(yٳWZԳ%;<17$-C~H?Dcz)sE[P܊~1[QF` +ݣ8hCQo0`E2R|*R취Ż1ZoOʦ(ʽ=uy.js3}®潬T-/F,`UOLs<>j"#.ڔ߶gaWgY4_m2'%[~2zwy1uAk1 n fm%%RchK-gh‡EnwuG-<@QՋG:kd:#_BOl">E6!Hq֩7n~h%œ\"ľZ//8yڄo::ϙ(TO}%Z&]Ok1:B!4Hi Cr,v+"KB3ׄ|Mی~hq̕hvVuX.Lksvjp ZN(+be^yn0(nce:ƍY ʁVc=o "QG?猅%sBf5f91{أ{Q{g9Å6 a䙄x;{xWP`ڑbKދlv2>[8 aIdˎ}=l6+ ̡i] I7$^qPg?ʡ|jH; QRgy9nڅ%lcn kҝa,~zaM#/?u`փ}N9*۲k֧]G} ˚w]OKA )VֿE "GAlgplkj=[2aHx&۫+x&XhcbDE%1!naPKKȏq#q(bFz}O<פ);Pr{!(7%ugEx_mW9+#;Y,51)cy7=.wO˻~`vh.Zt4靵g<Ey>2u|"U2 MgE?\0΍_@b{|e?46l}qy+C:8MƗr]OK1f+Rj*D +(HmqffEeWe~evUcEC!{`g;=v8*&E2!)Ci&ԝM47„3/Ly iՒgG7~97uAk1LBۦ1(%V(ٕ2YSnBs{ޛ +:4zij.bE#VRCqfbQ KgM"yb-u{zwqnZ$Yu sI< ೘v6+,01%g(}ge*c0oʹj.f b}h6yT;UpB\ y} ?lb#K_ئяdT1=.kbԴBsڸ.[Y%o=ZV7z`aꅅ>n~?VQo8~ϯxKI^3k];'j~I +'1W_$dCKFpZKg4` +!I"6)tb:!ynBL}GCQ]O1^7aKfI0-iN%: 0׊Xf %LJo-GeZiHhՄT"[~FOFebaw\VP䒷:3H(6ea03 ,Auz?Nj|>YD^Oa"]o:1Ovh|;VLO{YUcfV3#h <#Z_#c + %j"V.4lõ!rɬ(3~Ff@-Fl!2#1S] EMx_lҩgB >!J##tsO|K?5:Q`EgF9k\|)^hŕJ\ZEron>w%_=:=ǟx<+(  +Ԓb}rgcaRAkAB^LLH)3V/?ZENLJ +4__ʴpIYv䏦#wFhHg}!.uߵصkΔ҃b&G]z.8bSq&9GQRw]7Х9ztlU3v~Bbx} ['wApf)kAbZ{5uP(c;ɻ!V)(5ܿ^\= r_ȯD=g?5 O{~W*HE]K1E+c[JϭZ-D +Qiv$dfeW>˨*<-^2pf#^>0 SQ[<1eʗ?b`x((~rQJž'ߍ=zXssEvS܂XMEO@nB[:xQ>֩H}c$SY^vTWL)N$ɫ%52lS2&ucu[Hk|MgX?/m~mQn1WC(jĵ PR PD ڳc[q +ԓ3o{}]eH/*w}\^Zl_/}`LH#ΟÄKY)z;-ؒD7+<@E=E츲`[mvS܀haRTC$I1`No qLr$)Sa<w$L)T`}bWmKb&Yt0!}t2N5%KQ?;u`ѴQD' 78%o7݋G_z^,%ۮe(YI͖ FIGP,#KA_sHd6c 3*#ЄdS0sV!Nv:uXoEMnriڰ׶K +W7R +WENAuB AE cx$1lqvf2fWcW}` D ;}s&Ox`0gÁl"X{>ȠsS>Dc +D.󨢻!6l#\YRac5+|J >Q:*8Ddp`:v|Y:0Vq巋*Y=/RuAhlamYm1.X,U+INQhqrC@'X Wemj1z];Ӑ@)X0vU$13k㖼{%{43ξ6!,S%Js=5Ol| xAH<~G}]VQ _U0iΗ ݌6bA1f2\s)@66p)*u6]p'zQV}c@V;O}*VxyeQZK\j3IZ~H?LxEWȐ>fj]u +n;m)w!F,Hm~gp昚dLS޽V+(wN]TͻW\(e/,TlCAU}5xA;ݧ¹\.ƃ˖鐩֙9)Nn1ԼVmoF_1'%gS8KHc1Nidm`1X؉j1|R>+3ska7D k\0⊅x1~ Zn OB C>iAi2o!b&8#=GA38eĘ!@@.#g +)`0w]Cc O7$zq +שdә!RD.tӄ\.[y\ VؘFdj%\"#.@r9o s xbN 6m9wS9csBL̻2d|;V( v6Lui٦cڋ̰+սm8s{أP>Mˍ9S:V ,Cz4sLPFO8ݘѧN/nȕ%1p+I)?hqzBF>Y-~ + JYh,Q1O0'$C+T  v0"q!%^YjV;\1)KWKek $';#LKf[pNovz&tHlmAk1+!ۤ1&N6$PJ9X;k*]J{ƛnzz{3Z_в$P'kTQk@~ϯDOz,- f4K0;ѓ1ګpy||\|˳RRb/LdT0c!Y_0i/>>zz vUAް~H9"C? +69L3 x=~0C?ʴIp7:/b$ilaӎpŨQD} TuF +=yrL9۶6(ssMY1>9Imn@{]ٸLIyXpޤ3~d,AY>HǴok+t2*9sX:6^;#[q;?qۧfI6Co6V<`.'.lJ' @׆r/VhbTn07Wỗ~Vr6}W4$=8N۱L5Ԟ4v"&Yr$7K:EErݧAg<^RL# %ع}hԛyw97q hFT9_%\G I3oH)#AnqL%Hpj:ר@&SHUdjwY!$kv{=xb.JEryu > }B!.d] Wvk;],O69SؠL>uwVm@ (U`jvnceĥ <鐱 _l ˸&4Sų?{ n]~1>Y S-(ÓVAPo^1UR(?0!c8l= +%pdzpQzklՏZ)%d۲ ۬tf{4#]T&ok i7A5!K7TΖťAb?26_\'g/<ѤM ;hVmQГc}>tݷ;{hn0ArS<=KSAo=>h株 IFXK|M +"6|8wb]֣<:^W;Y9lݕdB1 [m)-B7ey`GZYLzk_:}mEp|~[{_T[o0~ϯ8} :cZih&!km>9$%H/'|r-Ϥ$x~mGІ\yȔFP $WKDI[ixQ[-9HmŊ@٬< *'2.s}&Ydď{߷z^J\d(ؓHBqgbF,z +`0 ڬz8v7A4ȭeʙYFy=4,ZZH!-sA &i3tar)&z;4oy$Gt&&>jGsۏ[KO~\U~`Ҵ!cyfڭt{ l1L8NlGi ~o_Oo1)!@4B(mHQ%rf`GږgU?IGOa1b$EX]h!>'^Vko?T02Ӥ/4J#'50`H!:B? ^$[!A+¢qvՎ-y!_TVLMFŴ2Y6^4ǚd 6w75I4pW4 ۍ w5MQS<Έ(r͊ z=LV&fl+ 0Qmس_x*Mbx/l o#zOlm|.&?[B;;YّLhOڶȷ];mdIHTh!z>_KA WWRAq$vә!m).ZO^<&yL/ϨRSqfdxV*쥠LjH ^<+f)U0pC\kdV0%|:ԓ/xXpˊi94ϜrN5\j-i1M + ׽Sq CbtC&)Salwl4P}0dQA-ۋk< _RR}|\EpKOz^UDcm,ςp4|TU kƸ?Gl9bgR0Ls4܇qMrWfix |oܭ/=?;G"rg<Κ|Eyr&ҙg(3Q.JU;z`4>IRRe9)۫:h +f[]2 q4쐽 鴕If/N%Xy1YAYxŘs]ۉPZŸm^U%͊Xl5Z΋Wo+T/*l qYU^d=oOx06AJTx_S,kCtD%اyӚWJʖȖ/OV|q?nz5_od +% 3w}2HL I}ҡQ럛$1QE ޓ7Vo8_1KRA}Rh9^+hC[!Lu,OIH'_ZL|}## (tQ7s߽sy9DTCH 2 Bx/H4FBn]F\߃3Ae4\vpЁ.6p\\p+#B2@xFElYB˜1VQF<jE !ka4UR P6D`lZGE|pB- Dzkɵq<=>M^i+iMf`8da}[vBn|,zD3W .T0(8Y}Sldlz35L)_bGb ЕdBn49ܮ*ߜ_&WR51-? 2(>(PF9x+>mQH]ǀ{?cTۻɬI[VBH%pjoa2 TY8!yle6[o!^ Bݧ8sXo66x8vS'<1 fC-4cc֭x82'mА_.U-NE ް3HbTFZ W3gJ|NFeԄ~5 lWkh/jGٯk7E&(QYܭl/2P: |-AO$J7PL-wA6A:l9%eF6CJۉZ`8WGE9yBOzjT&K-S h/( M/2cn]?AeKntm ٩.Wzuz Q&ژ_θu Ÿ>xJYx ;OB] ]ܤH ƿQ,){iQh~R؆.ckn 5be +9;g_j <4&ÆV/WTaAҀ#T%^}Tg.gۉC=U" Aa.'50{|B9ez&屛8o{]kVsmK,5 <V3HXEtBC2FL Y~hͦa vJ]! huV3U ~;.g4D zPxQG΋W}x~"؟o7WT_ a-Yw[ fj;Wa2RΎFac\xIb(ڇأAnv!E wuaTi~ G`8(~ Bzaaݛn"7;oI |ʰ%4zcM=_9߱JZ5.* C0|Z|206F[oG# +аwe4>v_! QŶ͟ + @+OqB"(BؑZV?!M_ T(Q~bVt~ |RU<|vփgo;}1P>B`j8(Diy R<yl^.bh'a3k%u:>v0^ﯻvQ3\&X46)!jO^WG~*yaHRE^*cMaq3}y>p(@x}H'fh=Um?mAOA +P`4!Dٮ8L]UⱯ}:>&lhaz$Č Yp ۢH ֞w44߼bpS"6NES["ͫc=OTYQRT"L[lamyH#c\"X[[G;k$6RTI4*> ǴJ Rf/ =KKCA +β-EqkT,RNp:3$-Er]'92HM8؇*x.'EV^ :.Ţԓ.FaɄ')^2oECSlhgEBۥޫJ{㒧LaeXn̢7aGt4gK@XG/>|dg#i eIvmCnL8FkܷuAkA +6&׮ ) S!j=AƘ^ƉKrsfjXI?}wrȊՋH'ԣ.fa/ _HLiݟRg|+=5V*5QP goE`0N)!7:qc7.y+pq{}޴S` 8EX e@e8GtI>}snҗ=r9Hƶq8u;Zh?/%uMrG|Jy׹?}Ao1+!@4)*RUEMnUw`ςP^y%ivo=ɇXEl 5]O}\xl:M$}e|]q,BeTSL+R)G4O0* +O{N "Yf#㢨S~79&^l.BiGq{n'#=,QJMEp$5`oYh0@ҁV)+Q!UXE+gK=*ޙpп!wp77^_񄵖N/pm$c_m-ϨT0+@tQҢ]iZqbؖgB{e֗${o潱2 +9J-<Cz1p#0K0zc +i @&i {)z +>[\ lKA6Z+Z(y/)HEc\ kaGp0OAڼ<Ɍ#9! +=&2Ͳgqu"}| izR-!BfԂXhy` `2 <f Ϋ`s ЪBU~MޥDp+:6.@tF+ @ZAUe7u rZsY|a݅+12C=oD밒:U[iu`bb +uyaʌ1P)bH "$9@EHä@Ƒh-Ý{|-l+'̤h>1IXZ"PG R7$r/3O/}%4|\ +maLr5̥H:W}mC-S{9SlK ClTmO9翊>A51:O\R7œUVR(W_Ilv +`8:95]p8WCCǎҧ:UwbJSd5[".v]ڌo65-@,DDeZ[fQuWi3@[ . s <؟͂lI8ڨJT<*fm>>Ly`Y*lF1 + +7@5^5jv92 pƧq N8Bc8+GA ֨̐%CrrڒbNMM+y1=|6 +dpÌźG8̒` +bnlyc%LSpȈuNآ6@ BK RpTAXjDfw~ޕ(a;f! # JxsJɓ@m8ḲAn/]g_}’ $.0oL-U'>is1shЪZ `-3Q'Ό2Br^L&(wVmp 64*O+6QnA E,_KYVL),yÊxtx 3O_+ܕ1+k}$πvps ;gbؿ.cqTmrUpU+U+Uh[-ge21kH#29"߳cL ܑ[k-;ɝavY;<_,Aw2XC)wxFp$5T!H1z7 ;K;le 3דIƍ0E6Fsfİԛk*{ZN<)&o 帾N]2g;>Fpmu, Ѹ7CW[UO0~_qh+JוBTM{\B,\;9Wƹ|=9KB5{ M>hǂ A$XbxąNx-x΍B*zDc NQ8]O`KFV0ט?L8AHt@`*5bZm(#lR/Z +Y1+:D"#\\~_P^ #m01X'tj8a.y(BJG?}EAocOdq_]4$/"kPv\2"_)y`4t6JʊHq9:sbĚYQcn!fJפֲ͕^Gt󘁓t)(U܅{sLy{j46*z%]脉/HB e0IBogS720~ ߐ-ij]nlB%5t.><7m^Ke Ԩfv66zC }yK(O*"^iBe߲SyWrwnʴ{*;a__^ #ݬ&&fT4_)jmeIVR, kͰoV jY'ϕ?\ ;КܧgJcF Y"֧vGA{H4B>ͭt ?5B=80R}?/Sn1|Jyڄ@Q%RESΪ5i+w^xfgfj9j{,d{~`a8jt;vFԙ/Tc!=ʊb)aBYВlOl,!|0bdpttj}}yHRM0-dv89-)Ě-Ƽ<͂9TJÏg:Lu7Rz"|nJ6ugE',.%8PVȫ7.5W;fwEo5 hX-Z: P]#+ipܔ#8I/VlI([->n8r3ْu][Gj{O#hOVnF}WHKd9v\IP19^;KD/]qwrΙ١ySMcNWt09JP9mV|:@mqeʩe#\:<3r/4<' >rgm\y A&Cjwj^zMu9cQjt=Xk2 ^+kI+OW7!TM&Aҥf KA)6'~\& 9Gt[ʹxle͗OfEZekNT~Q^ +[WA_x}aLNNp"bO)p S+ts-L}Q;],im?ׅUx΂ +CCve& Ƕh5UfYKZģ9ɽw,yaS#> _ޒH0d]+WxFpԼEv@܄x<-qE1R+!w@6`x]A-uձaR0v+㾨Z=- 2 7WޑiKv +KQ8bm1"L"쭆ENwؔZE- q0,LEn+@"%z|2=wmNnBCTpzl\vhwK푱Z nLM`6fӠ@kOnU@z.;w`7{If!:%IѩeG>,l>o9iya,PcLQ + TdR195>,{ԌEaeq=ͨHXdvmq.+k7_VZVM9 +Ø pKG*~?)VW(ϷM&e S % +9I@(͂w0_S\.}̖N:Tk0!?צ͚,$> "cQYҤs0)4Iҽ{/&7"b +N+tQ4FЅ\8ȄD :y.8 [ b*{Zrpì»u}8̑` +D 73Am$z 0׊XRJGKQ92m FB!lBtzP`0ti9i]0R@gG>럡#(*G*G0>='lu: +T(6ͣ;"l(dL.h'H=H,Pe؊.m ޾+: +}|V[rxĤ[!m{c wr*^/>;y1*AKG-(]b㤪گL[d<]ɫ\?fnIVM$t2 >q?к`?0RM4=_mU,r8;5-Y-m Ӫ?ԸTcne|w=ozu1A=NUI߷j?宒if,RiթC^=7uԖU\֧E܀۳׻$CUmo6_q(RH2xIcMb6t6$ˣC@%ۉ/wϽ=w:p9fYLY4H:t 0@Os<ԸfiŔ;H>Yp!#8gV>8<1>2r)Z8Z02ܠ6"0CpRpE[paRH ʻז"CEBM1':#\ࢴf|g Tqp>y҅2)"fHe8"(*ǥ2Xܤ_J?*Ww$($# +׷h4 T2@ӁќYs&:Rn3pJ\ˤuqS'im"#}qULy6Zd(?Y/C*13`"b2K: n|XW\ĎpmI,]M_ާ%TE1 ĸ/PRB;a3sl?{TYm-N="]U~|cR)JE&nHI棯Y/_,eN-( ;yibĄ#x^,97\گ z=\-M,Nj_bOTڻ(\k|"ZzjƬv:OO;h~06}l3[6YRF5rPuFOM8sGj.׏\S*N,>mҖ3'y.4!8`+ިќd6/#zPd>j)qgujޙbNRjAyı8Gb/So6Vo6 ~_Nw/MkC^zEL"y"d;?0K&)~G2>P% zJYV"UI<~0Nzpa f( 9L ׮^ fE4Cn)s\EI4 mYzyn/y`=Z2s( JC+MǛ& +!E9 <rgqI\"ܧq8eU}Asumdkֆ +:{tNV\[G|RP+џT fhLG_KYrM 'PztAA+qfh舄], `̡}¢Olcq`UI8y7]|6oSIoB6CLt}0L 9Xi +lm}JT,34Zv䔐 Y<ᲮΛ5@8Ƶen:EVI)E@7+Lkp_tMkhknj( ԩD 0}8In+,hyYY8-fhUu/;lC{œowp6_zMQKBAWGIzM+K wC;Wǽe83ߙ3gfg8 +z85/^* Tˑ zbȻW %2.Z1yW +E}>e>:p45_/H@sx[!rUӢ$c^Ȳ(:[q{ŶeQXoD`^X6$ hjd6yl1e`}0 v^+`g' Ml> v6QNGq|up}7@p{ o|49qʨx<'LG?ĩ& /(V) +iӵ'ɅB(p9![mSn0 }Wp@ARcMk,@K @t-LNZAd9<7j1̠o^]pSn![R~s +č~ѐKf$n<x`8s4piFd d!'elp PB@twh#DWrZ G~wss*QNBm c8qJxr!D*%dZ"z.&"2G.rb"\!\fŧ{iy`ohh%W,F<3das'#Sӆ!RXy}pdTG$E{MoiT`4Q]ޮ_k:?AmDJZ2yD~pͧȢ7hs-#RZ*"lṱrp,W=ؚ24 hGشAA)ڬG\tw3 +eq7n|| \sKhS&;5*+YAAʍ.[KɄ[~b¬0KC2sUhP+If% As zyÆRNj "Ν +N:75 8*wܘd8N&ʟmWh5B3b5"Mn?cO,ז|zR]o0 |/^KWdXa(f/(2 S$E n~`z'W;(QjAy&%y;c>NFp_+ʃ`+UZču;R!9\A& NWī0L|_ڡu#)AZäV-N% +Z|Th$Lei#XYS(E1Vx(?ha [5pl@AڲoyAD_m"$ >22*2KA$vӲw WiV5 Jt5N0??$ F:ZeGp7NN /ptsa|2\Jkf|p{}vHCl ukV#x2Eȁ̱#S-(i^8# UAߒgjACeҵ:/-VSk[0}s|<"fݺ:[A;,-FN&'To6PdqՎ]gYhـY0"F%[-k1~EݏW8PjA'%:t B1J#('ȃK<*ʺ-U!)\AA gdp +J K$b +i LOn@KAh$FP&^Y3Q0“Mu(U ]/`|>g[D6k(##NH/Jip:;Rǟ?_k\(Z0mU?)W0 `$~[e6Wԓg;0M&A=[:ץG>4iۤ5p΂ y-zB/n:Jt?[,-]j%8 WLtRWmD̚Y$x¬&tJ޼7=m<[o=)JtzrrHѼ’ Q9$Ff3xז>JD09cxcPr[ rKP|X+Qb\jRX9x?fN/N޵FK2-IhBJk‡ aϕPW}uJq3x˖Y h=YڹfRoX.kubi{KY@æ^4QS'7/5:A_`ыob0~y~ݿj鐓tHl_ +Y@bt6/N!,v[ }* 5j4=GN1)R~zK +FmJ(AZ9] c[lB%jљ9ߙwCFR O^INy(uIRJTas\jI̺WEhN!|ܑ瀡N0'c 'td(05բbCcxa=$֐J er+k4@X*Z}_&g|GTx +%8 mqO I*z#$¦t4ׁ\ylz$RQJxne d?pB+ߦߧx6\N1B2wƮLk~'#Z +&weg o'+#v5$Yiۏ;<<.cX +&wsen6TjmSO&L \ ʾ5?EUupzfwQV} S=m/˛`L[XAYll^bώb|n} IZ˷ǐHeL/y2TC]UZSΊ,D!!ry4B/7+X#LxA1w݉J'}s;Y|ca1g'ÚMWfwjza(˳l Ͱ4̲/_Yq+NyUբ=a-q0ITQ"J&{ +uEWHX/q+ +h< |4a-ZR;h<EAO1@<"DcHhbm,mәe1nv<{mv@[}r}5` /T#O z2ߝb`+q(~pQJCjcHTq'Xɿ_Tn0+ I}ܚ8um>:M%"4)+F/(ˎ8@y;%/ߗENI^$?y^D~> R#HvxL&kebGl*qFFTO0HĤ}Y)mM|ۦ\׶NK5ONI˗${}2 +bY +%?MzI'A.t`= +*gcW,gX$pɚB@!k6waBSt^TÙk.laXE3F{vu+ T +Ħz@+)H;ss.XEվoσdRU| n]2XJ_)Y5O"sr]<4?t(4I:G׽6`0ߎhk@f\JQ:ZazdZ.9ӌ UD)1{41 T2Y9'lы"9qFA;8tb az&-zCe/GV<-lЁ &}WG6_YW;qur!M+Ÿ>VxGޞ3._ل݆U.x. Ou_JR !n'p]Zx*ڠi*vޠrtIeA㻸GGrAr*+HM0> Oى+5ZhŖBv2N>4l0JtW ֞~- m=w\>R9,ﯶ&UCp4{'̥o7AJ%`jzt&&u_):8|U*ڷ_u̵3^ioC!?_0*ͺV8ZOuR[o0~ϯ8* ((FVZ+@ڥqNUc[C+CHsiv/T BʉFXͨ]ڽB3^ۃ6,f fPD[1<$ld > `WN3jkhCqɋLAp!›P^|Y~b7$RQ,Q+YYQa)$)ABVT*fwMDlņ(Dxh˫+(2HH{a +Te"AHxZ<1AYCFįhTJ-K@ ?K=S@Qh/&"K+-9MZsJjG헉#u\?G ̛Ն[<] T*?g]v +͵&VnScoWN-X43_ fW}HgFcM?1.$ UEa4N3bl0Y`[]Znl}Ӝ'0kN!V>hn2" V}{w ?Xf9@^f4OOx.]A-VF ]3J([0n0&;" +cOWU+w2vXYe邤iIô5ȵ@U + MF}) 6hƑN+i^5?+a{6;93'p^A6 ÑFjLu{W΂@Fddħ6U(Ŗ0;LV\ I2:gbU8f.N5sٱ|vuvM2w  &S@=찕,}$kt2Aa| +)RTLPOκp"Ls+C6_jsg\4~REߚ=ύvϊHBEA2e2K: 6eӅ  +xj1JCfmA-^ nd?TQB(/vL$벱)V5tTY`BWS0o U+^tj'-4&܏5 E."N%UJ B{Ζr]ռm0p>*Wua_ٝRJ5HG GU6|{nvO ٕ"1[@ߘzv )^@hWV|[ +{WMaЋ~.,w)E:6,0OF<[?̏FM<]*L+_uJs[e;u.?}0"GnW-Gp9cEy>l_MTZC{"ϭOm Ñ'оkw05_Րnd4TM@'xg;@O0O.` o?;lqS"C8Ϡ|%Iܵ>ѮʅmwS($~! ?KJo ϳvfBS*Ѣ}qEoLFD[ v.햬fn6+#j^; []:(e+t: 30^x7'9*W4W2~ E&GL{ &y}5;k͒*zV5r9u&;htě-a$IrL,io5) +exUARk O3, +]*1mt2?_qŌIiо5`KŸC`UB'TcMI&<QxFpjٵG6p`B"(?aOU O.!9@Wb7j6z5>`@ x VutoF +}@bA{k Jhx &)mb@ͤvr!a)ݫS%Q;}) ++<}_oOUD0H #8)N}xg2DMQaz4r# Ȝ;u>.^.qs%*A S,uZ¯`<Xg6lI:euֶ#'wR%Ϡۭ41eگ2_lftkB:~CY0mu$ Pu`NiDxދa}5z]!.^C%_jbz+LuvA.#f]*5 `ɤ +sc6#}:s"> Ug5_Iˌ_Hd(&Y٘Vmo"7ίPwA4:jC8r)j)I>TZglá*H3_Lj A.ؑf5?w~]xHa@/`5 C\ibļB]2-9`V>KSq `-\rҤT\+bI[W8(EXx)Cj튑ЪF"sklׁ*KRFar_FP +w[uRo)BgG2Ewwz)7&dk\+G0Of=^OMo>/qP`ֲ+ngM8r59 k\{E^dnkfìÙ7ב}uVg+TT˿FH:U3O%s[n.fe<,+֌ފA]T;*n\bo՞->{a1#KкRppHt=Z9̵.R :|aA c"^šPQ.=fY[i[k CP^!a_:\V(ڡC.N:=87rb&A\%r<FO0A57z+aQt5{Y^^ Sʫwzcԫt`tHA~ ZnrO+yD)|1WBOrao2i\;wi tq*enzu|>6~NDzM]bX"Ww .7nvָ^nԝ {&])V-J!r`{Z-ϛewTL)$ޫv%2lϕ&Җ%eUz;}dbL+rcl (Zc2MoA +|@JR-\Z*q!Mf#&$w4JQ)͞~絧ocD P_(7Iq~R |k@<BY-qǮnvWLA*05LxcÕu#&dJWM=f `)Ur|A`{L{gQxmChap}Y\ΣZc(쵰4"Pv 2kh,mc&,ۍL"qin E {FdC^Jc +VYl $(ߍOQ B̏ X951u0`Є5&-XSXQFR>?8t^~md8jup +5;Jh%5' +[yJ 9dqm6CeiA=䥿P8jFAc\mo6_ +I@/w״1v4`Ҭ5WTI/8"%%J3|/Ù!5Fwx=Fr= ~kp /bLztE{\n@5L\D{зE̻tVA~3k؍#M5b]*0z@p+Rt֛NJPk#Dvj mS F .@6z=1vĥ젱JkcƦe5`1?a'@M>iE%W7~ĄI,Y'G$ܚťi@ԄEF_gN=% +-a3_0zķ)zȽ:{tOX?$Kߒ'DHE'4\p~_wF(gBK7Bo +(7+W&Ͳ3*Cyx-?R(ufcKL٫iX©5 }DhbDtk%oERys*ec/t#JVP6\FA+NNۉpy OinMnSl_rDXsL <=.gg'iּUS]Ӛ-o[S.gTL*JGue`pwbU o`{cz6$5!> B_zb0X2zhsfˠ'lz1+?:NW(/\ ]W=ERtEp~1Ysekhxk/#BKv sJbTV׋:N4ظ[_mS.wX㻴xh$.cܥSWc +F?P-okF8R:?7_ zt`㣃FCbāي*vY~dƌ@1cHM;L& +>jY,0 UcV)8cP:U^XۭXAoN4+HMoJ@G,$H4-M +n3j9DGɇH:,`w `.:rHp qN7_y=8;ٯ !'7|hh M#7|L=L,F?$TCk!h]$SL:K!m@<('yZ$w$ ##/_8Տ8a~!zrՋg hhQ@MqQɢ{cCwҬ{HXTte\=q&˶y[bD+b4tU>[%tՂCyV6ϑ,/!8Q%\;k1pX89"meK:-cl-z*gvYfe=̭HG)̽9ۻ8sȹÅDeZ "rCrx~!vB>̩οmѺw2ʀnuܑ,pǐbf;\1Bxv8_EΤw /M?$IN?Ϧߒԩ * :9[_]ee#)_hg?>.ko뇢:vN57<<;34vm{!K/hE%(H/uADHy-?LO d)&HA$\q[VkI&ɔ7)nKA^Phgd7THF3JhuyB$4[$$g7̹|s"@e]]a,La2*Y"*1t?JoL$TZ LTy Ih| &@.ה̿iBafB_',DBV E)/{Ȝo Do /˳s , '{{IJ \#ɫ;IVKْ6x@%^R^ڣ.yʾ]?J#<ֻ<@A7I*E(oRt깾ֺe"'& %fT=En2#V\9;Y˳=7zJ|6$,Bghgxv5ꉗRS3̩o1 V.db+P>jftzPDG1x Z,+(r̄4$3P|W%"{.ԉYQ߁ex`wȷZңWz~ۤjBOLgk**ohJ śoLIE@Gh4r~t ITqj"CT6$,,=^ "=bJo#~|󋖾=_ W۵,*yzMRn? gH3.d̲\r*aq W#9z3}}kzTQ{4l*O5?]M]=Ya r &SD̈{a3*t=΃ aCƈTpj^|eh;+QBxnM :Ns_jZל߲lN{+6->Hl>fl@>h.,Z$ho nӉQ!]nuy=̿ﳴo1/\7SFEc;g$1rcJ@ C(,[; @h c/^O[?'U5<$6$E +f4}PELӆEP!ܸYkK^,|Ɲh-^}[4u + YlhDcb)hda!GUzʕUƋ|xԤ8uӑ_yjuDZ[e pBO̕5tuX B^[9҈ɯl%HևMn sL~Tͤ"]A!C< ̈ 3Pw[>HY7Q3e +i74b.3# pPh_{|es [T=.庳/f y<6e!6؃"m*%jQu +UdISb-e!fŚ#RxnOɿ壳~ݶ Pn%Iw^i lkFnμؒLIOO.bQ y?]3(R/T}̪&Mž7֛z7@ 0]O:o8^ d2mx|Xf'H^=Dncn1MS`2`t}&0&K8#0,&B ]6 ekRi^FpΌX-YAݶXh2Uj5H +6uۧ^P=u=y߭"j蛂JvR&9kiDŽ:",C^״fc((39050iU}0zpT 1l\4ި[ׅbwG 8V̌H_hìX +L{|mVl#YYlQYrْ5$ :#A' !͈DTxg?t3E)UC:7@NP'6r&;RL~?[)7, (^R:I4LZxSwU(p*͟iVS3}uh`c*>6Mil?n^6E*٩iL!Wg!tQVT WA`eO#xb&}qsljho]8t:ߚ^gPzMae{"AOoh07Y7˧Wyo}15c0#,yXƴ< F +@Žvz,$ѭb;s\ƜC:T&]7OO@&=Ѱ5E 4P06#]nvj1f2/y%l:gWKQпtqL ^cfty:`L|0$A+EH:(PlHEO%0\&y:0$ N' + +T{nz=S +;Xh#KmSJ;_5nk`\bPX*Iyqv/u +KSЋ`i9@' +#&be)-9ӆ8rX?n~O ?=hf֯f[M:dq='oxƀВ=G+46~(xT=o0+5t)dj!)L%"")ߑ2Xrt#5mZ^-WR7÷v`SѻsѳumTu2PW+ϧ*6LJ@.AcK)"Pr \(@KXQ1 !GAG6?84w 9r;L~/s^(:WCybjz2_1ݤ#!,G KZ2{d}5?sHZaǻKxZ`e"_9cbп'1? |eSl3,]~^a0/6dv'Zn6E 1cK[!ɜc,ž0;sHMWOK*(PPRVp wqsuUVVRPSN@+|KVzBP +B@RilO-kwK $@AO{oތ N9$#8 ƟF.y~6;]Ly7?l9lv lY++ ;Euk2dQwf6@NYF`jCh㎢ZuӴ&MvS=w>{Dߏ*ZaeT-9@bp Vud#2khH*eON~=wPF&`WVR)l!P,4d*@JjP m%ZC Ec#H'J2fY~'p5ͧU/?e ++j=Ssr~ul*:մF:Žu C;M!NBhXUtBCp^ m B[~^AԚnz`{mgD35s6+-vYҽG#dĢ+p?#Ԏhvq -+#>a€i~?s<]Qk7Zj)Jn +eЇ:'$]¬MGPGv;//(*J-)-SRPPPPN,/H+H//JWA&敔%pZs +g40&6cӮk`.^vnch??߈0 2rݛDCʋGBMB \ No newline at end of file diff --git a/tools/php-cs-fixer b/tools/php-cs-fixer new file mode 100755 index 000000000..c1ebe8cd5 Binary files /dev/null and b/tools/php-cs-fixer differ diff --git a/tools/phpstan b/tools/phpstan new file mode 120000 index 000000000..5e15af476 --- /dev/null +++ b/tools/phpstan @@ -0,0 +1 @@ +.phpstan/vendor/bin/phpstan \ No newline at end of file