pax_global_header00006660000000000000000000000064147515352530014524gustar00rootroot0000000000000052 comment=ea1d8849b018030f82ec8e7bb601539cf0d31c9e flask-openapi3-4.1.0/000077500000000000000000000000001475153525300143425ustar00rootroot00000000000000flask-openapi3-4.1.0/.github/000077500000000000000000000000001475153525300157025ustar00rootroot00000000000000flask-openapi3-4.1.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001475153525300200655ustar00rootroot00000000000000flask-openapi3-4.1.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000003101475153525300225510ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- Environment: - Python version: - Operating system: - Flask version: - flask-openapi3 version: flask-openapi3-4.1.0/.github/dependabot.yml000066400000000000000000000001661475153525300205350ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" flask-openapi3-4.1.0/.github/pull_request_template.md000066400000000000000000000003171475153525300226440ustar00rootroot00000000000000Checklist: - [ ] Run `pytest tests` and no failed. - [ ] Run `ruff check flask_openapi3 tests examples` and no failed. - [ ] Run `mypy flask_openapi3` and no failed. - [ ] Run `mkdocs serve` and no failed. flask-openapi3-4.1.0/.github/workflows/000077500000000000000000000000001475153525300177375ustar00rootroot00000000000000flask-openapi3-4.1.0/.github/workflows/docs.yml000066400000000000000000000024211475153525300214110ustar00rootroot00000000000000# This is a basic workflow to help you get started with Actions name: mkdocs # Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the master branch push: branches: [ master ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: 3.x cache: "pip" cache-dependency-path: "requirements/doc.txt" - name: Install dependencies run: pip install -U -r ./requirements/doc.txt - name: Build docs run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" git fetch origin gh-pages:gh-pages mike deploy v4.x git push origin gh-pages flask-openapi3-4.1.0/.github/workflows/publish.yml000066400000000000000000000015271475153525300221350ustar00rootroot00000000000000# This workflow will upload a Python Package using Twine when a release is created # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries name: publish on: release: types: [ created ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install dependencies run: | python -m pip install --upgrade pip pip install build twine - name: Build and publish env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | python -m build twine upload dist/* flask-openapi3-4.1.0/.github/workflows/stale.yml000066400000000000000000000006511475153525300215740ustar00rootroot00000000000000name: stale on: schedule: # Executed on the 1st day of each month - cron: "0 0 1 * *" jobs: stale: runs-on: ubuntu-latest steps: - uses: actions/stale@v9 with: days-before-issue-close: 365 close-issue-message: > This issue has been automatically closed because we haven't heard back for more than 365 days, please reopen this issue if necessary.flask-openapi3-4.1.0/.github/workflows/tests.yml000066400000000000000000000034671475153525300216360ustar00rootroot00000000000000# This workflow will install Python dependencies, run tests and lint with a single version of Python # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions name: tests on: push: branches: [ master ] paths-ignore: - "docs/**" - "*.md" - "*.rst" pull_request: branches: [ master ] paths-ignore: - "docs/**" - "*.md" - "*.rst" jobs: tests: runs-on: ubuntu-latest strategy: max-parallel: 4 matrix: python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ] flask-version: [ "Flask>=2.0,<3.0", "Flask>=3.0" ] env: PYTHONPATH: . steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} allow-prereleases: true cache: "pip" cache-dependency-path: "requirements/*.txt" - name: Install dependencies run: | python -m pip install --upgrade pip pip install -U "${{ matrix.flask-version }}" pip install -U pydantic pip install -U -r ./requirements/test.txt pip install -U -r ./requirements/ruff.txt pip install -U -r ./requirements/mypy.txt pip install -e .[swagger] # pip install -e .[swagger,redoc,rapidoc,rapipdf,scalar,elements] - name: Test with pytest run: pytest tests - name: ruff run: ruff check flask_openapi3 tests examples - name: cache mypy uses: actions/cache@v4 with: path: ./.mypy_cache key: mypy|${{ matrix.python-version }}|${{ hashFiles('pyproject.toml') }} - name: mypy run: mypy flask_openapi3 flask-openapi3-4.1.0/.gitignore000066400000000000000000000040151475153525300163320ustar00rootroot00000000000000### Python template # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ .ideaflask-openapi3-4.1.0/CHANGELOG.md000066400000000000000000000330271475153525300161600ustar00rootroot00000000000000## v4.1.0 2025-02-08 - Support for Python 3.13 by @luolingchun in #200 - Fix query, form, header model extra not honored by @luolingchun in #201 - Better APISpec init to allow to modify it before generating spec_json. by @ddorian in #195 - Schema.maximum type float -> int | float by @ddorian in #217 - Allow url_prefix to be set during API/APIView registration by @luolingchun in #215 - Drop support for Python 3.8 by @luolingchun in #199 ## v4.0.3 2024-11-23 - Add PrefixItems to Schema Model for use with Tuple types by @JesseDeLoore in #197 ## v4.0.2 2024-11-10 - Reuse schema["title"] if it's defined by @ddorian in #186 - Simple webhook schema by @ddorian in #191 - Fix missing Field.default when it's value is None in openapi spec by @ddorian in #189 - ServerVariable.enum should be optional by @luolingchun in #194 ## v4.0.1 2024-10-05 - Fix alias in query and form by @luolingchun in #184 ## v4.0.0 2024-09-29 * Support plugins for ui templates by @luolingchun in #151 * Add py.typed marker file for PEP-561 support by @luolingchun in #160 * Use ruff instead of flake8 by @luolingchun in #164 * Remove experimental export markdown by @luolingchun in #161 * Fix `populate_by_name` when execute `model_validate` by @luolingchun in #167 * Update docs by @luolingchun in #155 * Fix __get_pydantic_core_schema__ by @luolingchun in #179 * Fix list with default value by @luolingchun in #180 ## v4.0.0rc3 2024-08-31 - Add py.typed marker file for PEP-561 support by @luolingchun in #160 - Use ruff instead of flake8 by @luolingchun in #164 - Remove experimental export markdown by @luolingchun in #161 - Fix `populate_by_name` when execute `model_validate` by @luolingchun in #167 - Optimize performance and unit testing by @luolingchun in #155 ## v4.0.0rc2 2024-06-16 - Fix empty list in body by @luolingchun in #154 ## v4.0.0rc1 2024-06-02 - Support plugins for ui templates - Support for OPENAPI_HTML_STRING in app.config ## v3.1.3 2024-06-16 - Fix empty list in body by @luolingchun in #154 ## v3.1.2 2024-06-02 - Support SWAGGER_CONFIG and OAUTH_CONFIG in app.config by @luolingchun in #153 **DeprecationWarning** - The `api_doc_url` is deprecated in v4.x, use `doc_url` instead. - The `swagger_url` is deprecated in v4.x. - The `redoc_url` is deprecated in v4.x. - The `rapidoc_url` is deprecated in v4.x. - The `oauth_config` is deprecated in v4.x, use `app.config['OAUTH_CONFIG']` instead. - The `doc_expansion` is deprecated in v4.x, use `app.config['SWAGGER_CONFIG']` instead. - The `swagger_config` is deprecated in v4.x, use `app.config['SWAGGER_CONFIG']` instead. - The `ui_templates` is deprecated in v4.x. ## v3.1.1 2024-04-21 - Wrong types of exclusiveMinimum & exclusiveMaximum fields in Schema class by @Lavertis in #149 ## v3.1.0 2024-03-24 - Add the swagger_config parameter to configure the swagger ui by @luolingchun in #146 - Use full links in Swagger and RapiDoc - Upgrade Redoc to 2.1.3 - Upgrade Swagger to 5.12.0 **DeprecationWarning** - The doc_expansion parameter is deprecated; use swagger_config instead. ## v3.0.2 2024-01-28 - Fix missing Pydantic Calculated Fields (#141). Thanks, @thebmw. ## v3.0.1 2023-11-26 - Fix the same operationId in APIBlueprint (#133). Thanks, @fluffybrain3. - Make body required false (#130). Thanks, @styper. - The default value defined in the form is invalid (#129). Thanks, @seekplum. ## v3.0.0 2023-10-22 - Upgrade pydantic to v2. - Remove deprecated code. - Drop support for Python 3.7. - support for raw requests (#109). - Upgrade Swagger UI v5.9.0. - Upgrade Redoc v2.1.2 - Update RapiDoc 9.3.4. - [#105](https://github.com/luolingchun/flask-openapi3/pull/105) Supports valid properties only. Thanks, @ota42y. - [#106](https://github.com/luolingchun/flask-openapi3/pull/106) Bugfix for parameter object. Thanks, @ota42y. - [#107](https://github.com/luolingchun/flask-openapi3/pull/107) Bugfix for generics class. Thanks, @ota42y. - [#114](https://github.com/luolingchun/flask-openapi3/pull/114) Support Flask 3.0. - [#118](https://github.com/luolingchun/flask-openapi3/discussions/118) Fix missed components schemas in ValidationErrorModel. Thanks, @SeFeX. - [#122](https://github.com/luolingchun/flask-openapi3/issues/122) Skip 422 response non parameters. Thanks, @Danielsn1. ## v3.0.0rc2 2023-10-03 - [#105](https://github.com/luolingchun/flask-openapi3/pull/105) Supports valid properties only. Thanks, @ota42y. - [#106](https://github.com/luolingchun/flask-openapi3/pull/106) Bugfix for parameter object. Thanks, @ota42y. - [#107](https://github.com/luolingchun/flask-openapi3/pull/107) Bugfix for generics class. Thanks, @ota42y. - [#114](https://github.com/luolingchun/flask-openapi3/pull/114) Support Flask 3.0. ## v3.0.0rc1 2023-09-03 - Upgrade pydantic to v2 - Remove deprecated code - Drop support for Python 3.7 ## v2.5.5 2023-11-26 - Fix the same operationId in APIBlueprint (#133). Thanks, @fluffybrain3. - Make body required false (#130). Thanks, @styper. - The default value defined in the form is invalid (#129). Thanks, @seekplum. ## v2.5.4 2023-10-22 - [#118](https://github.com/luolingchun/flask-openapi3/discussions/118) Fix missed components schemas in ValidationErrorModel. Thanks, @SeFeX. - [#122](https://github.com/luolingchun/flask-openapi3/issues/122) Skip 422 response non parameters. Thanks, @Danielsn1. ## v2.5.3 2023-10-03 - [#105](https://github.com/luolingchun/flask-openapi3/pull/105) Supports valid properties only. Thanks, @ota42y. - [#106](https://github.com/luolingchun/flask-openapi3/pull/106) Bugfix for parameter object. Thanks, @ota42y. - [#107](https://github.com/luolingchun/flask-openapi3/pull/107) Bugfix for generics class. Thanks, @ota42y. ## v2.5.2 2023-08-13 - [#97](https://github.com/luolingchun/flask-openapi3/issues/97) Fix response miss description. Thanks, @tekrei. ## v2.5.1 2023-08-07 - [#95](https://github.com/luolingchun/flask-openapi3/pull/95) Added ability to deserialize complex form parameter objects. Thanks, @BlackGad. ## v2.5.0 2023-08-02 - [#79](https://github.com/luolingchun/flask-openapi3/discussions/79) Support `by_alias` in Model Config. Thanks, @candleindark. - [#82](https://github.com/luolingchun/flask-openapi3/issues/82) Fix parameter in url_prefix. Thanks, @riedgar-ms. - [#83](https://github.com/luolingchun/flask-openapi3/pull/83) Be able to change 422 validation errors to other http response status. Thanks, @CostcoFanboy. - [#86](https://github.com/luolingchun/flask-openapi3/issues/86) Responses key supports both string, int, and HTTPStatus. Thanks, @CostcoFanboy. ## v2.4.0 2023-06-04 - [#72](https://github.com/luolingchun/flask-openapi3/pull/72) security_schemes(SecurityScheme) supports a json format. - [#68](https://github.com/luolingchun/flask-openapi3/pull/68) feat: Add operation_id_callback. Thanks, @BoyanYK. - [#64](https://github.com/luolingchun/flask-openapi3/pull/64) Explains the usage of flask openapi command more clearly. Thanks, @candleindark. - [#75](https://github.com/luolingchun/flask-openapi3/pull/75) Init view_class and pass view_kwargs. Thanks, @stufisher. - [#70](https://github.com/luolingchun/flask-openapi3/issues/70) Support for Specification Extensions in OpenAPI Object and Operation Object. Thanks, @simonblund. - [#73](https://github.com/luolingchun/flask-openapi3/issues/73) BaseModel Config support openapi_extra. - Merge `extra_responses` to `responses` and deprecate `extra_responses`. **DeprecationWarning:** - Add DeprecationWarning to `APIKey`, `HTTPBase`, `OAuth2`, `OpenIdConnect`, `HTTPBearer` that will be deprecated in v3.0. - Add DeprecationWarning to `extra_form`, `extra_body` and `extra_responses` that will be deprecated in v3.0. ## v2.3.2 2023-04-03 - [#61](https://github.com/luolingchun/flask-openapi3/issues/61) Fix headers with pydantic alias ## v2.3.1 2023-02-13 - remove * in install_requires for setuptools 67+ ## v2.3.0 2023-02-12 - Support for custom UI templates (#55) - endpoint index rename to openapi - fix missing enum in component schemas ## v2.2.2 2023-01-01 - Fix async - Fix duplicate tags ## v2.2.1 2022-11-23 - Add dependent files ## v2.2.0 2022-11-14 - support APIView - Add mypy - Support for python 3.11 - Upgrade Swagger UI 4.15.5 - Upgrade Redoc UI 2.0.0 ## v2.1.1 2022-10-12 - [#41](https://github.com/luolingchun/flask-openapi3/issues/41) Set the `requestBody required` default value to True. Thanks, @Colin-b - Fix multi decorator for api - [#42](https://github.com/luolingchun/flask-openapi3/issues/42) Fix required header is not found when `_` in header field. Thanks, @elirud ## v2.1.0 2022-09-04 - [#36](https://github.com/luolingchun/flask-openapi3/issues/36) Add extra_form for operation. Thanks, @Colin-b - [#36](https://github.com/luolingchun/flask-openapi3/issues/36) Add extra_body for operation. Thanks, @Colin-b - Add external_docs for operation - Add servers for operation - Support to parse extra field in parameters - [#35](https://github.com/luolingchun/flask-openapi3/issues/35) Fixed extra_responses can now be used to set every field in Response. Thanks, @Colin-b - Upgrade Swagger UI 4.14.0 - Upgrade Redoc UI 2.0.0-rc.76 - Upgrade RapiDoc UI 9.3.3 ### Breaking Changes - [#39](https://github.com/luolingchun/flask-openapi3/issues/39) Remove configuration FLASK_OPENAPI_VALIDATE_RESPONSE ## v2.0.1 2022-08-07 - [#32](https://github.com/luolingchun/flask-openapi3/issues/32) Fix: parse_rule is deprecated in werkzeug>=2.2.0. ## v2.0.0 2022-06-26 - [#26](https://github.com/luolingchun/flask-openapi3/issues/26) Fixed: Body throws exception when receiving str instead of dict. Thanks, @nor3th - [#23](https://github.com/luolingchun/flask-openapi3/pull/23) Fixed externalDocs support. Thanks, @dvaerum - [#28](https://github.com/luolingchun/flask-openapi3/pull/28) Fixed to enable `__root__` property when validation responses. Thanks, @dvaerum - [#17](https://github.com/luolingchun/flask-openapi3/issues/17) Support for Nested APIBlueprint enhancement. Thanks, @dvaerum - [#29](https://github.com/luolingchun/flask-openapi3/pull/29) Support disable warnings. Thanks, @dvaerum - Support for empty response body. Thanks, @dvaerum - Support reload authorizations in Swagger UI - Add `flask openapi` command - Add options in view functions - Upgrade flask to v2.x ### Breaking Changes - Remove export markdown to `flask openapi` command - Configuration `VALIDATE_RESPONSE` rename to `FLASK_OPENAPI_VALIDATE_RESPONSE` ## v1.1.4 2022-05-05 - fix: Trailing slash in APIBlueprint ## v1.1.3 2022-05-01 - fix: Find globalns for the unwrapped func - [#19](https://github.com/luolingchun/flask-openapi3/issues/19) fix: Trailing slash in APIBlueprint. Thanks, @ev-agelos - add description for UnprocessableEntity - remove printouts in `__init__.py` ## v1.1.2 2022-04-01 - [#16](https://github.com/luolingchun/flask-openapi3/issues/16) Fix fileStorage list is not supported. Thanks, @tekrei ## v1.1.0 2022-03-13 - [#13](https://github.com/luolingchun/flask-openapi3/issues/13) drop support for flask 1.0.x. Thanks, @danmur - [#15](https://github.com/luolingchun/flask-openapi3/pull/15) Fix to enable BaseModel with `__root__` property. Thanks, @tarcisiojr - [#14](https://github.com/luolingchun/flask-openapi3/pull/14) Custom parameters: doc_prefix, api_doc_url, swagger_url, redoc_url, rapidoc_url. Thanks, @barryrobison - Upgrade swagger UI v4.6.2 - Upgrade Redoc v2.0.0-rc.63 - Upgrade RapiDoc v9.2.0 ## v1.0.1 2022-02-12 - add operation_id for OpenAPI Specification ## v1.0.0 2022-01-11 - [#10](https://github.com/luolingchun/flask-openapi3/issues/10) Fix: header's title case. Thanks, @rrr34 - [#9](https://github.com/luolingchun/flask-openapi3/issues/9) Support for extra responses. Thanks, @blynn99 - [#12](https://github.com/luolingchun/flask-openapi3/pull/12) Support for path operation field deprecated. Thanks, @blynn99 - Add keyword parameters `summary` and `description` - Add servers for OpenAPI - Upgrade swagger UI v4.1.3 - Upgrade Redoc v2.0.0-rc.59 - Add rapidoc ### Breaking Changes - Renamed `securitySchemes` to `security_schemes` - Renamed `docExpansion` to `doc_expansion` ## v0.9.9 2021-12-09 - fix: default value in a query and form model - fix: empty form and body - support `from __future__ import annotations` - drop python36 ## v0.9.8 2021-11-12 - add Configuration `docExpansion` - query and form add array support ## v0.9.7 2021-08-19 - fix: path $ref - fix: markdown enum ## v0.9.6 2021-08-18 - Export to markdown(Experimental) ## v0.9.5 2021-07-11 - remove `validate_resp` and add `VALIDATE_RESPONSE` ## v0.9.4 2021-07-03 - OpenAPI add responses and APIBlueprint add abp_responses - fix: validate response error when responses is empty dict - [#3](https://github.com/luolingchun/flask-openapi3/issues/3) endpoint and APIBlueprint add `doc_ui`. Thanks, @DerManoMann - [#4](https://github.com/luolingchun/flask-openapi3/issues/4) fix: response description. Thanks, @DerManoMann - [#5](https://github.com/luolingchun/flask-openapi3/issues/5) add custom parameter `oauth_config`. Thanks, @DerManoMann - [#6](https://github.com/luolingchun/flask-openapi3/issues/6) support validation Flask Response. Thanks, @DerManoMann - [#7](https://github.com/luolingchun/flask-openapi3/issues/7) fix: response validation does not work when uses http.HTTPStatus enums as status_code. Thanks, @DerManoMann ## v0.9.3 2021-06-08 - APIBlueprint adds abp_tags and abp_security - fix: tags de-duplication - fix: operation summary and description ## v0.9.2 2021-05-17 - fix: _do_decorator - add doc_ui args. support close swagger UI and redoc ## v0.9.1 2021-05-16 - fix:request data is None - json-->body - set 422 Content-Type application/json - raise response validate exception - fix: TypeError: issubclass() arg 1 must be a class ## v0.9.0 2021-05-13 - first version flask-openapi3-4.1.0/CODE_OF_CONDUCT.md000066400000000000000000000121511475153525300171410ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders 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, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at luolingchun@outlook.com. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. flask-openapi3-4.1.0/CONTRIBUTING.md000066400000000000000000000016611475153525300165770ustar00rootroot00000000000000## Contributing Guide Thank you for contributing to Flask OpenAPI3. 1. [Create a new issue](https://github.com/luolingchun/flask-openapi3/issues/new) 2. [Fork and Create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) Before submitting pr, you need to complete the following steps: 1. Install requirements ```bash pip install -U flask pydantic ``` 2. Running the tests ```bash set pythonpath=. # or export pythonpath=. pytest tests ``` 3. Running the ruff ```bash ruff check flask_openapi3 tests examples ``` 4. Running the mypy ```bash mypy flask_openapi3 ``` 5. Building the docs Serve the live docs with [Material for MkDocs](https://github.com/squidfunk/mkdocs-material), and make sure it's correct. ```bash mkdocs serve ``` flask-openapi3-4.1.0/LICENSE.rst000066400000000000000000000020431475153525300161550ustar00rootroot00000000000000MIT License Copyright (c) 2021 llc 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.flask-openapi3-4.1.0/README.md000066400000000000000000000146561475153525300156350ustar00rootroot00000000000000
logo

Generate REST API and OpenAPI documentation for your Flask project.

test pypi pypistats pypi versions

**Flask OpenAPI3** is a web API framework based on **Flask**. It uses **Pydantic** to verify data and automatic generation of interaction documentation. The key features are: - **Easy to code:** Easy to use and easy to learn - **Standard document specification:** Based on [OpenAPI Specification](https://spec.openapis.org/oas/v3.1.0) - **Interactive OpenAPI documentation:** [Swagger](https://github.com/swagger-api/swagger-ui), [Redoc](https://github.com/Redocly/redoc), [RapiDoc](https://github.com/rapi-doc/RapiDoc), [RapiPdf](https://mrin9.github.io/RapiPdf/), [Scalar](https://github.com/scalar/scalar), [Elements](https://github.com/stoplightio/elements) - **Data validation:** Fast data verification based on [Pydantic](https://github.com/pydantic/pydantic) ## Requirements Python 3.9+ flask-openapi3 is dependent on the following libraries: - [Flask](https://github.com/pallets/flask) for the web app. - [Pydantic](https://github.com/pydantic/pydantic) for the data validation. ## Installation ```bash pip install -U flask-openapi3[swagger] ``` or ```bash conda install -c conda-forge flask-openapi3[swagger] ```
Optional dependencies - [python-email-validator](https://github.com/JoshData/python-email-validator) supports email verification. - [python-dotenv](https://github.com/theskumar/python-dotenv#readme) enables support for [Environment Variables From dotenv](https://flask.palletsprojects.com/en/latest/cli/#dotenv) when running `flask` commands. - [pyyaml](https://github.com/yaml/pyyaml) is used to output the OpenAPI document in yaml format. - [asgiref](https://github.com/django/asgiref) allows views to be defined with `async def` and use `await`. - [flask-openapi3-plugins](https://github.com/luolingchun/flask-openapi3-plugins) Provide OpenAPI UI for flask-openapi3. To install these dependencies with flask-openapi3: ```bash pip install flask-openapi3[yaml] # or pip install flask-openapi3[async] # or pip install flask-openapi3[dotenv] # or pip install flask-openapi3[email] # or all pip install flask-openapi3[yaml,async,dotenv,email] # or manually pip install pyyaml asgiref python-dotenv email-validator # OpenAPI UI plugins pip install -U flask-openapi3[swagger,redoc,rapidoc,rapipdf,scalar,elements] ```
## A Simple Example Here's a simple example, further go to the [Example](https://luolingchun.github.io/flask-openapi3/latest/Example/). ```python from pydantic import BaseModel from flask_openapi3 import Info, Tag from flask_openapi3 import OpenAPI info = Info(title="book API", version="1.0.0") app = OpenAPI(__name__, info=info) book_tag = Tag(name="book", description="Some Book") class BookQuery(BaseModel): age: int author: str @app.get("/book", summary="get books", tags=[book_tag]) def get_book(query: BookQuery): """ to get all books """ return { "code": 0, "message": "ok", "data": [ {"bid": 1, "age": query.age, "author": query.author}, {"bid": 2, "age": query.age, "author": query.author} ] } if __name__ == "__main__": app.run(debug=True) ```
Class-based API View Example ```python from typing import Optional from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI, Tag, Info, APIView info = Info(title='book API', version='1.0.0') app = OpenAPI(__name__, info=info) api_view = APIView(url_prefix="/api/v1", view_tags=[Tag(name="book")]) class BookPath(BaseModel): id: int = Field(..., description="book ID") class BookQuery(BaseModel): age: Optional[int] = Field(None, description='Age') class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') @api_view.route("/book") class BookListAPIView: a = 1 @api_view.doc(summary="get book list") def get(self, query: BookQuery): print(self.a) return query.model_dump_json() @api_view.doc(summary="create book") def post(self, body: BookBody): """description for a created book""" return body.model_dump_json() @api_view.route("/book/") class BookAPIView: @api_view.doc(summary="get book") def get(self, path: BookPath): print(path) return "get" @api_view.doc(summary="update book") def put(self, path: BookPath): print(path) return "put" @api_view.doc(summary="delete book", deprecated=True) def delete(self, path: BookPath): print(path) return "delete" app.register_api_view(api_view) if __name__ == "__main__": app.run(debug=True) ```
## API Document Run the [simple example](https://github.com/luolingchun/flask-openapi3/blob/master/examples/simple_demo.py), and go to http://127.0.0.1:5000/openapi. > OpenAPI UI plugins are optional dependencies that require manual installation. > > `pip install -U flask-openapi3[swagger,redoc,rapidoc,rapipdf,scalar,elements]` > > More optional ui templates goto the document > about [UI_Templates](https://luolingchun.github.io/flask-openapi3/latest/Usage/UI_Templates/). ![openapi](https://raw.githubusercontent.com/luolingchun/flask-openapi3/master/docs/images/openapi-all.png) flask-openapi3-4.1.0/docs/000077500000000000000000000000001475153525300152725ustar00rootroot00000000000000flask-openapi3-4.1.0/docs/Changelog.md000066400000000000000000000000261475153525300175010ustar00rootroot00000000000000 --8<-- "CHANGELOG.md"flask-openapi3-4.1.0/docs/Contributing.md000066400000000000000000000000311475153525300202550ustar00rootroot00000000000000 --8<-- "CONTRIBUTING.md"flask-openapi3-4.1.0/docs/Example.md000066400000000000000000000146441475153525300172200ustar00rootroot00000000000000## Simple Demo ```python from pydantic import BaseModel from flask_openapi3 import Info, Tag from flask_openapi3 import OpenAPI info = Info(title='book API', version='1.0.0') app = OpenAPI(__name__, info=info) book_tag = Tag(name='book', description='Some Book') class BookQuery(BaseModel): age: int author: str @app.get('/book', tags=[book_tag]) def get_book(query: BookQuery): """get books to get all books """ return { "code": 0, "message": "ok", "data": [ {"bid": 1, "age": query.age, "author": query.author}, {"bid": 2, "age": query.age, "author": query.author} ] } if __name__ == '__main__': app.run(debug=True) ``` ## REST Demo ```python from http import HTTPStatus from typing import Optional, List from pydantic import BaseModel, Field from flask_openapi3 import Info, Tag from flask_openapi3 import OpenAPI info = Info(title='book API', version='1.0.0') # Basic Authentication Sample basic = { "type": "http", "scheme": "basic" } # JWT Bearer Sample jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } # API Key Sample api_key = { "type": "apiKey", "name": "api_key", "in": "header" } # Implicit OAuth2 Sample oauth2 = { "type": "oauth2", "flows": { "implicit": { "authorizationUrl": "https://example.com/api/oauth/dialog", "scopes": { "write:pets": "modify pets in your account", "read:pets": "read your pets" } } } } security_schemes = {"jwt": jwt, "api_key": api_key, "oauth2": oauth2, "basic": basic} class NotFoundResponse(BaseModel): code: int = Field(-1, description="Status Code") message: str = Field("Resource not found!", description="Exception Information") app = OpenAPI(__name__, info=info, security_schemes=security_schemes, responses={404: NotFoundResponse}) book_tag = Tag(name='book', description='Some Book') security = [ {"jwt": []}, {"oauth2": ["write:pets", "read:pets"]} ] class BookPath(BaseModel): bid: int = Field(..., description='book id') class BookQuery(BaseModel): age: Optional[int] = Field(None, description='Age') s_list: List[str] = Field(None, alias='s_list[]', description='some array') class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') class BookBodyWithID(BaseModel): bid: int = Field(..., description='book id') age: Optional[int] = Field(None, ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') class BookResponse(BaseModel): code: int = Field(0, description="Status Code") message: str = Field("ok", description="Exception Information") data: Optional[BookBodyWithID] @app.get( '/book/', tags=[book_tag], summary='new summary', description='new description', responses={200: BookResponse, 201: {"content": {"text/csv": {"schema": {"type": "string"}}}}}, security=security ) def get_book(path: BookPath): """Get a book to get some book by id, like: http://localhost:5000/book/3 """ if path.bid == 4: return NotFoundResponse().dict(), 404 return {"code": 0, "message": "ok", "data": {"bid": path.bid, "age": 3, "author": 'no'}} # set doc_ui False disable openapi UI @app.get('/book', doc_ui=True, deprecated=True) def get_books(query: BookQuery): """get books to get all books """ print(query) return { "code": 0, "message": "ok", "data": [ {"bid": 1, "age": query.age, "author": 'a1'}, {"bid": 2, "age": query.age, "author": 'a2'} ] } @app.post('/book', tags=[book_tag], responses={200: BookResponse}) def create_book(body: BookBody): print(body) return {"code": 0, "message": "ok"}, HTTPStatus.OK @app.put('/book/', tags=[book_tag]) def update_book(path: BookPath, body: BookBody): print(path) print(body) return {"code": 0, "message": "ok"} @app.delete('/book/', tags=[book_tag], doc_ui=False) def delete_book(path: BookPath): print(path) return {"code": 0, "message": "ok"} if __name__ == '__main__': app.run(debug=True) ``` ## APIBlueprint ```python from typing import Optional from pydantic import BaseModel, Field from flask_openapi3 import APIBlueprint, OpenAPI from flask_openapi3 import Tag, Info info = Info(title='book API', version='1.0.0') jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } security_schemes = {"jwt": jwt} app = OpenAPI(__name__, info=info, security_schemes=security_schemes) tag = Tag(name='book', description="Some Book") security = [{"jwt": []}] class Unauthorized(BaseModel): code: int = Field(-1, description="Status Code") message: str = Field("Unauthorized!", description="Exception Information") api = APIBlueprint( '/book', __name__, url_prefix='/api', abp_tags=[tag], abp_security=security, abp_responses={"401": Unauthorized}, # disable openapi UI doc_ui=True ) class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') class Path(BaseModel): bid: int = Field(..., description='book id') @api.get('/book', doc_ui=False) def get_book(): return {"code": 0, "message": "ok"} @api.post('/book', responses={201: {"content": {"text/csv": {"schema": {"type": "string"}}}}}) def create_book(body: BookBody): assert body.age == 3 return {"code": 0, "message": "ok"} @api.put('/book/') def update_book(path: Path, body: BookBody): assert path.bid == 1 assert body.age == 3 return {"code": 0, "message": "ok"} # register api app.register_api(api) if __name__ == '__main__': app.run(debug=True) ``` ## Upload File Demo ```python from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI, FileStorage app = OpenAPI(__name__) class UploadFileForm(BaseModel): file: FileStorage file_type: str = Field(None, description="File Type") @app.post('/upload') def upload_file(form: UploadFileForm): print(form.file.filename) print(form.file_type) form.file.save('test.jpg') return {"code": 0, "message": "ok"} if __name__ == '__main__': app.run(debug=True) ``` ## A complete project see [flask-api-demo](https://github.com/luolingchun/flask-api-demo)flask-openapi3-4.1.0/docs/LICENSE.md000066400000000000000000000000251475153525300166730ustar00rootroot00000000000000 --8<-- "LICENSE.rst"flask-openapi3-4.1.0/docs/Quickstart.md000066400000000000000000000104171475153525300177510ustar00rootroot00000000000000**`flask_openapi3`** based on [Flask](https://github.com/pallets/flask/) and [Pydantic](https://github.com/pydantic/pydantic), So you can use it like Flask. ## A Minimal Application Just like [Flask](https://flask.palletsprojects.com/en/latest/quickstart/#a-minimal-application), Create `hello.py`: ``` python from flask_openapi3 import OpenAPI app = OpenAPI(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run() ``` And then run it: ```shell python hello.py ``` You will see the output information: ``` * Serving Flask app 'just_flask' (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) ``` ## REST API You can use **`get`**, **`post`**, **`put`**, **`patch`**, **`delete`** REST API in `flask-openapi3`. ```python from flask_openapi3 import OpenAPI app = OpenAPI(__name__) @app.get('/book') def get_books(): return ["book1", "book2"] @app.post('/book') def create_book(): return {"message": "success"} if __name__ == '__main__': app.run() ``` ## APIBlueprint [APIBlueprint](./Reference/APIBlueprint.md) based on [Flask Blueprint](https://flask.palletsprojects.com/en/latest/tutorial/views/#create-a-blueprint), you should use **`register_api`** instead of **`register_blueprint`**. ```python hl_lines="14" from flask_openapi3 import OpenAPI app = OpenAPI(__name__) api = APIBlueprint('book', __name__, url_prefix='/api') @api.post('/book') def create_book(): return {"message": "success"} # register api app.register_api(api) if __name__ == '__main__': app.run() ``` ## Nested APIBlueprint Allow an **API Blueprint** to be registered on another **API Blueprint**. For more information, please see [Flask Nesting Blueprints](https://flask.palletsprojects.com/en/latest/blueprints/#nesting-blueprints). ```python hl_lines="21 22" from flask_openapi3 import OpenAPI, APIBlueprint app = OpenAPI(__name__) api = APIBlueprint('book', __name__, url_prefix='/api/book') api_english = APIBlueprint('english', __name__) api_chinese = APIBlueprint('chinese', __name__) @api_english.post('/english') def create_english_book(): return {"message": "english"} @api_chinese.post('/chinese') def create_chinese_book(): return {"message": "chinese"} # register nested api api.register_api(api_english) api.register_api(api_chinese) # register api app.register_api(api) if __name__ == '__main__': app.run(debug=True) ``` ## APIView [Class-based API View](./Reference/APIView.md), click [here](https://github.com/luolingchun/flask-openapi3/blob/master/examples/api_view_demo.py) for the complete example: ```python @api_view.route("/book") class BookListAPIView: a = 1 @api_view.doc(summary="get book list") def get(self, query: BookQuery): print(self.a) return query.json() @api_view.doc(summary="create book") def post(self, body: BookBody): """description for a created book""" return body.json() ``` You can define a parameter named `view_kwargs` (the only parameter of the `__init__` function), and using `view_kwargs.get` get the required keys for each. ```python @api_view.route("/book") class BookListAPIView: def __init__(self, view_kwargs=None): self.a = view_kwargs.get("a") def get(self): ... def post(self): ... @api_view.route("/book/") class BookAPIView: def __init__(self, view_kwargs=None): self.b = view_kwargs.get("b") def get(self): ... def put(self): ... def delete(self): ... app.register_api_view( api_view, view_kwargs={ "a": 1, "b": 2 } ) ``` ## Async API Just use `async` when defining functions. More information goes to [Using async and await — Flask Documentation](https://flask.palletsprojects.com/en/latest/async-await/). !!! info You need to manually install `asgiref` using pip: ```bash pip install flask-openapi3[async] # or pip install asgiref ``` ```python hl_lines="2" @app.post('/open/api') async def post_openapi(body: Query): print(body) return 'POST, OpenAPI!' ```flask-openapi3-4.1.0/docs/Quickstart.zh.md000066400000000000000000000071311475153525300203700ustar00rootroot00000000000000**`flask_openapi3`** 基于 [Flask](https://github.com/pallets/flask/) 和 [Pydantic](https://github.com/pydantic/pydantic),因此你可以像使用Flask一样使用 **`flask_openapi3`**。 ## 最小应用 像 [Flask](https://flask.palletsprojects.com/en/latest/quickstart/#a-minimal-application) 一样,创建 `hello.py`: ``` python from flask_openapi3 import OpenAPI app = OpenAPI(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run() ``` 然后运行: ```shell python hello.py ``` 你将会看到输出信息: ``` * Serving Flask app 'just_flask' (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) ``` ## REST API 你可以在 `flask-openapi3` 中使用 **`get`**,**`post`**,**`put`**,**`patch`**,**`delete`** 等 REST API 。 ```python from flask_openapi3 import OpenAPI app = OpenAPI(__name__) @app.get('/book') def get_books(): return ["book1", "book2"] @app.post('/book') def create_book(): return {"message": "success"} if __name__ == '__main__': app.run() ``` ## APIBlueprint [APIBlueprint](./Reference/APIBlueprint.md) 基于 [Flask Blueprint](https://flask.palletsprojects.com/en/latest/tutorial/views/#create-a-blueprint), 你应该使用 **`register_api`** 来代替 **`register_blueprint`**。 ```python hl_lines="14" from flask_openapi3 import OpenAPI app = OpenAPI(__name__) api = APIBlueprint('/book', __name__, url_prefix='/api') @api.post('/book') def create_book(): return {"message": "success"} # register api app.register_api(api) if __name__ == '__main__': app.run() ``` ## 嵌套 APIBlueprint 允许一个 **API Blueprint** 被另一个 **API Blueprint** 注册。 更多信息请查看 [Flask Nesting Blueprints](https://flask.palletsprojects.com/en/latest/blueprints/#nesting-blueprints)。 ```python hl_lines="21 22" from flask_openapi3 import OpenAPI, APIBlueprint app = OpenAPI(__name__) api = APIBlueprint('book', __name__, url_prefix='/api/book') api_english = APIBlueprint('english', __name__) api_chinese = APIBlueprint('chinese', __name__) @api_english.post('/english') def create_english_book(): return {"message": "english"} @api_chinese.post('/chinese') def create_chinese_book(): return {"message": "chinese"} # register nested api api.register_api(api_english) api.register_api(api_chinese) # register api app.register_api(api) if __name__ == '__main__': app.run(debug=True) ``` ## APIView [基于类的 API 视图](./Reference/APIView.md), 点击[这里](https://github.com/luolingchun/flask-openapi3/blob/APIView/examples/api_view_demo.py) 查看完整示例: ```python @api_view.route("/book") class BookListAPIView: a = 1 @api_view.doc(summary="get book list") def get(self, query: BookQuery): print(self.a) return query.json() @api_view.doc(summary="create book") def post(self, body: BookBody): """description for create book""" return body.json() ``` ## 异步 API 在定义函数时使用 `async`。 更多信息参考 [Using async and await — Flask Documentation](https://flask.palletsprojects.com/en/latest/async-await/)。 !!! info 你需要使用 pip 手动安装 `asgiref`: ```bash pip install flask-openapi3[async] # or pip install asgiref ``` ```python hl_lines="2" @app.post('/open/api') async def post_openapi(body: Query): print(body) return 'POST, OpenAPI!' ```flask-openapi3-4.1.0/docs/Reference/000077500000000000000000000000001475153525300171705ustar00rootroot00000000000000flask-openapi3-4.1.0/docs/Reference/APIBlueprint.md000066400000000000000000000000371475153525300220100ustar00rootroot00000000000000::: flask_openapi3.APIBlueprintflask-openapi3-4.1.0/docs/Reference/APIView.md000066400000000000000000000000371475153525300207560ustar00rootroot00000000000000::: flask_openapi3.view.APIViewflask-openapi3-4.1.0/docs/Reference/Models.md000066400000000000000000000024361475153525300207420ustar00rootroot00000000000000::: flask_openapi3.models.APISpec ::: flask_openapi3.models.OAuthConfig ::: flask_openapi3.models.Callback ::: flask_openapi3.models.Components ::: flask_openapi3.models.Contact ::: flask_openapi3.models.Discriminator ::: flask_openapi3.models.Encoding ::: flask_openapi3.models.Example ::: flask_openapi3.models.ExternalDocumentation ::: flask_openapi3.models.FileStorage ::: flask_openapi3.models.Header ::: flask_openapi3.models.Info ::: flask_openapi3.models.License ::: flask_openapi3.models.Link ::: flask_openapi3.models.MediaType ::: flask_openapi3.models.OAuthFlow ::: flask_openapi3.models.OAuthFlows ::: flask_openapi3.models.Operation ::: flask_openapi3.models.Parameter ::: flask_openapi3.models.ParameterInType ::: flask_openapi3.models.PathItem ::: flask_openapi3.models.Paths ::: flask_openapi3.models.Reference ::: flask_openapi3.models.RequestBody ::: flask_openapi3.models.Response ::: flask_openapi3.models.Responses ::: flask_openapi3.models.Schema ::: flask_openapi3.models.SecurityRequirement ::: flask_openapi3.models.SecurityScheme ::: flask_openapi3.models.Server ::: flask_openapi3.models.ServerVariable ::: flask_openapi3.models.StyleValues ::: flask_openapi3.models.Tag ::: flask_openapi3.models.ValidationErrorModel ::: flask_openapi3.models.XML flask-openapi3-4.1.0/docs/Reference/OpenAPI.md000066400000000000000000000000321475153525300207400ustar00rootroot00000000000000::: flask_openapi3.OpenAPIflask-openapi3-4.1.0/docs/Reference/Scaffold.md000066400000000000000000000000471475153525300212340ustar00rootroot00000000000000::: flask_openapi3.scaffold.APIScaffoldflask-openapi3-4.1.0/docs/Usage/000077500000000000000000000000001475153525300163365ustar00rootroot00000000000000flask-openapi3-4.1.0/docs/Usage/Configuration.md000066400000000000000000000064421475153525300214750ustar00rootroot00000000000000Flask supports many [configurations](https://flask.palletsprojects.com/en/latest/config/), and there are also some configurations in this library that can be used. ## SWAGGER_HTML_STRING You can customize the custom behavior of this template. [The default `SWAGGER_HTML_STRING` is here](https://github.com/luolingchun/flask-openapi3-plugins/blob/master/flask-openapi3-swagger/flask_openapi3_swagger/templates/__init__.py). ## SWAGGER_CONFIG You can change the default behavior of the Swagger UI. ```python from flask_openapi3 import OpenAPI app = OpenAPI(__name__) app.config["SWAGGER_CONFIG"] = { "docExpansion": "none", "validatorUrl": "https://www.b.com" } ``` [More configuration options for Swagger UI](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md). ## OAUTH_CONFIG You can configure OAuth 2.0 authorization for Swagger UI. ```python from flask_openapi3 import OpenAPI app = OpenAPI(__name__) app.config["OAUTH_CONFIG"] = {"clientId": "xxx", "clientSecret": "xxx"} ``` [More configuration options for Swagger UI](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md). ## SCALAR_HTML_STRING You can customize the custom behavior of this template. [The default `SCALAR_HTML_STRING` is here](https://github.com/luolingchun/flask-openapi3-plugins/blob/master/flask-openapi3-scalar/flask_openapi3_scalar/templates/__init__.py). ## SCALAR_CONFIG You can change the default behavior of the Scalar UI. [More configuration options for Swagger UI](https://github.com/scalar/scalar/blob/main/documentation/configuration.md). ## REDOC_HTML_STRING You can customize the custom behavior of this template. [The default `REDOC_HTML_STRING` is here](https://github.com/luolingchun/flask-openapi3-plugins/blob/master/flask-openapi3-redoc/flask_openapi3_redoc/templates/__init__.py). ## REDOC_CONFIG You can change the default behavior of the Redoc UI. [More configuration options for Redoc UI](https://github.com/Redocly/redoc/blob/main/docs/config.md). ## RAPIDOC_HTML_STRING You can customize the custom behavior of this template. [The default `RAPIDOC_HTML_STRING` is here](https://github.com/luolingchun/flask-openapi3-plugins/blob/master/flask-openapi3-rapidoc/flask_openapi3_rapidoc/templates/__init__.py). ## RAPIDOC_CONFIG You can change the default behavior of the Rapidoc UI. [More configuration options for Rapidoc UI](https://rapidocweb.com/examples.html). ## RAPIPDF_HTML_STRING You can customize the custom behavior of this template. [The default `RAPIPDF_HTML_STRING` is here](https://github.com/luolingchun/flask-openapi3-plugins/blob/master/flask-openapi3-rapipdf/flask_openapi3_rapipdf/templates/__init__.py). ## RAPIPDF_CONFIG You can change the default behavior of the Rapipdf UI. [More configuration options for Rapipdf UI](https://mrin9.github.io/RapiPdf/). ## ELEMENTS_HTML_STRING You can customize the custom behavior of this template. [The default `ELEMENTS_HTML_STRING` is here](https://github.com/luolingchun/flask-openapi3-plugins/blob/master/flask-openapi3-elements/flask_openapi3_elements/templates/__init__.py). ## ELEMENTS_CONFIG You can change the default behavior of the Elements UI. [More configuration options for Rapipdf UI](https://github.com/stoplightio/elements/blob/main/docs/getting-started/elements/elements-options.md).flask-openapi3-4.1.0/docs/Usage/JSON.md000066400000000000000000000022401475153525300174270ustar00rootroot00000000000000## Use `orjson` ```python hl_lines="17" import orjson from flask.json.provider import JSONProvider class OrJSONProvider(JSONProvider): # https://github.com/ijl/orjson#option option = orjson.OPT_INDENT_2 def dumps(self, obj, **kwargs): return orjson.dumps(obj, option=self.option).decode() def loads(self, s, **kwargs): return orjson.loads(s) app = OpenAPI(__name__, info=info) # use orjson orjson_provider = OrJSONProvider(app) app.json = orjson_provider ``` ## Use `ujson` ```python hl_lines="24" import ujson from flask.json.provider import JSONProvider class UJSONProvider(JSONProvider): # https://github.com/ultrajson/ultrajson encode_html_chars = False ensure_ascii = False indent = 4 def dumps(self, obj, **kwargs): option = { "encode_html_chars": self.encode_html_chars, "ensure_ascii": self.ensure_ascii, "indent": self.indent } return ujson.dumps(obj, **option) def loads(self, s, **kwargs): return ujson.loads(s) app = OpenAPI(__name__, info=info) # use ujson ujson_provider = UJSONProvider(app) app.json = ujson_provider ```flask-openapi3-4.1.0/docs/Usage/Model_Config.md000066400000000000000000000067001475153525300212100ustar00rootroot00000000000000The [BaseModel](https://docs.pydantic.dev/latest/usage/models/) in [Pydantic](https://github.com/pydantic/pydantic) supports some custom configurations([Model Config](https://docs.pydantic.dev/latest/usage/model_config/)), so we can use the `openapi_extra` to extend OpenAPI Specification. ## openapi_extra The `openapi_extra` will be merged with the automatically generated OpenAPI schema. ### form ```python class UploadFilesForm(BaseModel): file: FileStorage str_list: List[str] model_config = dict( openapi_extra={ # "example": {"a": 123}, "examples": { "Example 01": { "summary": "An example", "value": { "file": "Example-01.jpg", "str_list": ["a", "b", "c"] } }, "Example 02": { "summary": "Another example", "value": { "str_list": ["1", "2", "3"] } } } } ) ``` Effect in Redoc: ![](../assets/Snipaste_2023-06-02_11-05-11.png) ### body ```python class BookBody(BaseModel): age: int author: str model_config = dict( openapi_extra={ "description": "This is post RequestBody", "example": {"age": 12, "author": "author1"}, "examples": { "example1": { "summary": "example summary1", "description": "example description1", "value": { "age": 24, "author": "author2" } }, "example2": { "summary": "example summary2", "description": "example description2", "value": { "age": 48, "author": "author3" } } }} ) ``` Effect in swagger: ![](../assets/Snipaste_2023-06-02_11-06-59.png) You can use `reqiured` in `openapi_extra` to mark the RequestBody as Optional. ```python class PingBody(BaseModel): ping: Optional[str] = Field("ok", description="String to return, 'ok' when null.") model_config = dict( openapi_extra = { "required": False } ) ``` ### responses ```python class MessageResponse(BaseModel): message: str = Field(..., description="The message") model_config = dict( openapi_extra={ # "example": {"message": "aaa"}, "examples": { "example1": { "summary": "example1 summary", "value": { "message": "bbb" } }, "example2": { "summary": "example2 summary", "value": { "message": "ccc" } } } } ) ``` Effect in swagger: ![](../assets/Snipaste_2023-06-02_11-08-40.png) ## by_alias Sometimes you may not want to use aliases (such as in the responses model). In that case, `by_alias` will be convenient: ```python class MessageResponse(BaseModel): message: str = Field(..., description="The message") metadata: dict[str, str] = Field(alias="metadata_") model_config = dict( by_alias=False ) ```flask-openapi3-4.1.0/docs/Usage/Request.md000066400000000000000000000060111475153525300203060ustar00rootroot00000000000000## Request declaration First, you need to import `BaseModel` from `pydantic`: ```python from pydantic import BaseModel ``` ### path Request parameter in rules,**`@app.get('/book/')`**. You have to declare **path** model as a class that inherits from **`BaseModel`**: ```python hl_lines="6" class BookPath(BaseModel): bid: int = Field(..., description='book id') @app.get('/book/', tags=[book_tag], security=security) def get_book(path: BookPath): ... ``` ### query Receive flask **`request.args`**. !!! info ```python from flask import request ``` like [path](#path), you need pass **`query`** to view function. ```python hl_lines="7" class BookQuery(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') @app.get('/book/', tags=[book_tag], security=security) def get_book(path: BookPath, query: BookQuery): ... ``` ### form Receive flask **`request.form`** and **`request.files`**. ```python hl_lines="7" class UploadFileForm(BaseModel): file: FileStorage # request.files["file"] file_type: str = Field(None, description="File type") @app.post('/upload') def upload_file(form: UploadFileForm): ... ``` ### body Receive flask **`request.json`**. ```python hl_lines="7" class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') @app.post('/book', tags=[book_tag]) def create_book(body: BookBody): ... ``` ### header Receive flask **`request.headers`**. ### cookie Receive flask **`request.cookies`**. ### raw Receive flask **`request`** and no data validation. ```python from flask_openapi3 import RawModel class BookRaw(RawModel): mimetypes = ["text/csv", "application/json"] @app.post("/book") def get_book(raw: BookRaw): # raw equals to flask.request print(raw.data) print(raw.mimetype) return "ok" ``` ## Request model First, you need to define a [pydantic](https://github.com/pydantic/pydantic) model: ```python class BookQuery(BaseModel): age: int = Field(..., ge=2, le=4, description='Age') author: str = Field(None, description='Author') ``` More information to see [BaseModel](https://docs.pydantic.dev/latest/usage/models/), and you can [Customize the Field](https://docs.pydantic.dev/latest/usage/fields/). However, you can also use **Field** to extend [Parameter Object](https://spec.openapis.org/oas/v3.1.0#parameter-object). Here is an example: `age` with **`example`** and `author` with **`deprecated`**. ```python class BookQuery(BaseModel): age: int = Field(..., ge=2, le=4, description='Age', json_schema_extra={"example": 3}) author: str = Field(None, description='Author', json_schema_extra={"deprecated": True}) ``` Magic: ![](../assets/Snipaste_2022-09-04_10-10-03.png) More available fields to see [Parameter Object Fixed Fields](https://spec.openapis.org/oas/v3.1.0#fixed-fields-9). flask-openapi3-4.1.0/docs/Usage/Response.md000066400000000000000000000035621475153525300204640ustar00rootroot00000000000000## responses If you want to generate **Schemas**, pass the **`responses`**. ```python hl_lines="13" class BookBodyWithID(BaseModel): bid: int = Field(..., description='book id') age: Optional[int] = Field(None, ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') class BookResponse(BaseModel): code: int = Field(0, description="status code") message: str = Field("ok", description="exception information") data: BookBodyWithID @app.get('/book/', tags=[book_tag], responses={ 200: BookResponse, # Version 2.4.0 starts supporting response for dictionary types 201: {"content": {"text/csv": {"schema": {"type": "string"}}}} }) def get_book(path: BookPath, query: BookBody): """get a book get book by id, age or author """ return {"code": 0, "message": "ok", "data": {}} ``` Now you can use `string`, `int`, and `HTTPStatus` as response's key. ```python hl_lines="5 7" from http import HTTPStatus class BookResponse(BaseModel): message: str = Field(..., description="The message") @api.get("/hello/", responses={ HTTPStatus.OK: BookResponse, "201": {"content": {"text/csv": {"schema": {"type": "string"}}}}, 204: None }) def hello(path: HelloPath): message = {"message": f"""Hello {path.name}!"""} response = make_response(json.dumps(message), HTTPStatus.OK) response.mimetype = "application/json" return response ``` ![image-20210526104627124](../assets/image-20210526104627124.png) ## More information about OpenAPI responses - [OpenAPI Responses Object](https://spec.openapis.org/oas/v3.1.0#responses-object), it includes the Response Object. - [OpenAPI Response Object](https://spec.openapis.org/oas/v3.1.0#response-object). flask-openapi3-4.1.0/docs/Usage/Route_Operation.md000066400000000000000000000162361475153525300220060ustar00rootroot00000000000000## tags You can also specify tag for apis like this: ```python hl_lines="3 6" from flask_openapi3 import Tag book_tag = Tag(name='book', description='Some Book') @api.get('/book', tags=[book_tag]) def get_book(): ... ``` and then you will get the magic. ![image-20210525160744617](../assets/image-20210525160744617.png) ## abp_tags & view_tags You don't need to specify **tags** for every api. ```python hl_lines="3 4" tag = Tag(name='book', description="Some Book") api = APIBlueprint('/book', __name__, url_prefix='/api', abp_tags=[tag]) api_view = APIView('/book', __name__, url_prefix='/api', view_tags=[tag]) @api.post('/book') def create_book(body: BookBody): ... ``` ## summary and description You need to add docs to the view-func. The first line is the **summary**, and the rest is the **description**. Like this: ```python hl_lines="3 4 5 6" @app.get('/book/', tags=[book_tag], responses={200: BookResponse}, security=security) def get_book(path: BookPath, query: BookBody): """Get book Get some book by id, like: http://localhost:5000/book/3 """ return {"code": 0, "message": "ok", "data": {"bid": path.bid, "age": query.age, "author": query.author}} ``` ![image-20210605115557426](../assets/image-20210605115557426.png) Now keyword parameters `summary` and `description` is supported, it will be take first. ```python hl_lines="1" @app.get('/book/', summary="new summary", description='new description') def get_book(path: BookPath, query: BookBody): """Get book Get some book by id, like: http://localhost:5000/book/3 """ return {"code": 0, "message": "ok", "data": {}} ``` ![Snipaste_2022-03-19_15-10-06.png](../assets/Snipaste_2022-03-19_15-10-06.png) ## external_docs Allows referencing an external resource for extended documentation. More information to see [External Documentation Object](https://spec.openapis.org/oas/v3.1.0#external-documentation-object). ```python hl_lines="10" from flask_openapi3 import OpenAPI, ExternalDocumentation app = OpenAPI(__name__, info=info) @app.get( '/book/', tags=[book_tag], summary='new summary', description='new description', external_docs=ExternalDocumentation( url="https://www.openapis.org/", description="Something great got better, get excited!") ) def get_book(path: BookPath): ... ``` ## operation_id You can set `operation_id` for an api (operation). The default is automatically. ```python hl_lines="6" @app.get( '/book/', tags=[book_tag], summary='new summary', description='new description', operation_id="get_book_id" ) def get_book(path: BookPath): ... ``` ## operation_id_callback You can set a custom callback to automatically set `operation_id` for an api (operation). Just add a `operation_id_callback` param to the constructor of `OpenAPI` or `APIBlueprint` or `APIView`. The example shows setting the default `operation_id` to be the function name, in this case `create_book`. ```python hl_lines="6" def get_operation_id_for_path(*, name: str, path: str, method: str) -> str: return name api = APIBlueprint('book', __name__, url_prefix='/api', operation_id_callback=get_operation_id_for_path) @api.post('/book/') def create_book(body: BookBody): ... ``` ## deprecated `deprecated`: mark as deprecated support. default to not True. ```python @app.get('/book', deprecated=True) def get_books(query: BookQuery): ... ``` ## security pass the **security** to your api, like this: ```python hl_lines="1" @app.get('/book/', tags=[book_tag], security=security) def get_book(path: Path, query: BookBody): ... ``` There are many kinds of security supported here: ```python # Basic Authentication Sample basic = { "type": "http", "scheme": "basic" } # JWT Bearer Sample jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } # API Key Sample api_key = { "type": "apiKey", "name": "api_key", "in": "header" } # Implicit OAuth2 Sample oauth2 = { "type": "oauth2", "flows": { "implicit": { "authorizationUrl": "https://example.com/api/oauth/dialog", "scopes": { "write:pets": "modify pets in your account", "read:pets": "read your pets" } } } } security_schemes = {"jwt": jwt, "api_key": api_key, "oauth2": oauth2, "basic": basic} app = OpenAPI(__name__, info=info, security_schemes=security_schemes) security = [ {"jwt": []}, {"oauth2": ["write:pets", "read:pets"]}, {"basic": []} ] @app.get( '/book/', tags=[book_tag], summary='new summary', description='new description', security=security ) def get_book(path: BookPath): ... ``` ## abp_security & view_security You don't need to specify **security** for every api. ```python hl_lines="3 4" tag = Tag(name='book', description="Some Book") security = [{"jwt": []}] api = APIBlueprint('/book', __name__, abp_tags=[tag], abp_security=security) api_view = APIView('/book', __name__, abp_tags=[tag], view_security=security) @api.post('/book') def create_book(body: BookBody): ... ``` ## servers An array of Server Objects, which provide connectivity information to a target server. If the server's property is not provided, or is an empty array, the default value would be a Server Object with an url value of /. ```python from flask_openapi3 import OpenAPI, Server app = OpenAPI(__name__, info=info) @app.get( '/book/', tags=[book_tag], summary='new summary', description='new description', servers=[Server(url="https://www.openapis.org/", description="openapi")] ) def get_book(path: BookPath): ... ``` ## openapi_extensions While the OpenAPI Specification tries to accommodate most use cases, additional data can be added to extend the specification at certain points. See [Specification Extensions](https://spec.openapis.org/oas/v3.1.0#specification-extensions). ```python hl_lines="3 12 19 28 42" from flask_openapi3 import OpenAPI, APIBlueprint, APIView app = OpenAPI(__name__, openapi_extensions={ "x-google-endpoints": [ { "name": "my-cool-api.endpoints.my-project-id.cloud.goog", "allowCors": True } ] }) openapi_extensions = { "x-google-backend": { "address": "https://-.a.run.app", "protocol": "h2" } } @app.get("/", openapi_extensions=openapi_extensions) def hello(): return "ok" # APIBlueprint api = APIBlueprint("book", __name__, url_prefix="/api") @api.get('/book', openapi_extensions=openapi_extensions) def get_book(): return {"code": 0, "message": "ok"} app.register_api(api) # APIView api_view = APIView() @api_view.route("/view/book") class BookListAPIView: @api_view.doc(openapi_extensions=openapi_extensions) def post(self): return "ok" app.register_api_view(api_view) ``` ## doc_ui You can pass `doc_ui=False` to disable the `OpenAPI spec` when init `OpenAPI `. ```python app = OpenAPI(__name__, info=info, doc_ui=False) ``` You can also use `doc_ui` in endpoint or when initializing `APIBlueprint`. ```python hl_lines="4 9" api = APIBlueprint( '/book', __name__, doc_ui=False ) # or @api.get('/book', doc_ui=False) def get_book(): ... ``` flask-openapi3-4.1.0/docs/Usage/Specification.md000066400000000000000000000202561475153525300214450ustar00rootroot00000000000000## Specification If you need the complete Specification, go to http://127.0.0.1:5000/openapi/openapi.json ## command: flask openapi The `flask openapi` command will export the OpenAPI Specification to console when you execute the command. ``` flask --app IMPORT openapi ``` where `IMPORT` is the Flask application, in our case an OpenAPI application, to loan. For example, if your OpenAPI application is `app` defined in `hello.py`, as in the example in [Quickstart](../Quickstart.md#rest-api), the command is `flask --app hello:app openapi `. (For more information about the command line interface of Flask, please check out the [Flask CLI documentation](https://flask.palletsprojects.com/en/latest/cli/#application-discovery).) Execute `flask --app IMPORT openapi --help` for more information about the command: Again, assuming your OpenAPI application is `app` defined in `hello.py`, ``` flask --app hello:app openapi --help Usage: flask openapi [OPTIONS] Export the OpenAPI Specification to console or a file Options: -o, --output PATH The output file path. -f, --format [json|yaml] The output file format. -i, --indent INTEGER The indentation for JSON dumps. --help Show this message and exit. ``` Please note, by default, the command will export the OpenAPI specification in JSON. If you want the OpenAPI specification in YAML, by running the command with the `-f yaml` option, you need to install the `pyyaml` package. ```bash pip install flask-openapi3[yaml] # or pip install pyyaml ``` ## info **`flask-openapi3`** provide [Swagger UI](https://github.com/swagger-api/swagger-ui), [Redoc](https://github.com/Redocly/redoc) and [RapiDoc](https://github.com/rapi-doc/RapiDoc) interactive documentation. Before that, you should know something about the [OpenAPI Specification](https://spec.openapis.org/oas/v3.1.0). You must import **`Info`** from **`flask-openapi3`**, it needs some parameters: **`title`**, **`version`**... , more information sees the [OpenAPI Specification Info Object](https://spec.openapis.org/oas/v3.1.0#info-object). ```python hl_lines="4 5" from flask_openapi3 import Info from flask_openapi3 import OpenAPI, APIBlueprint info = Info(title='book API', version='1.0.0') app = OpenAPI(__name__, info=info) api = APIBlueprint('/book', __name__, url_prefix='/api') if __name__ == '__main__': app.run() ``` run it, and go to http://127.0.0.1:5000/openapi, you will see the documentation. ![openapi](../images/openapi-all.png) ![image-20210525160157057](../assets/image-20210525160157057.png) ## security_schemes There are some examples for Security Scheme Object, more features see the [OpenAPI Specification Security Scheme Object](https://spec.openapis.org/oas/v3.1.0#security-scheme-object). ```python # Basic Authentication Sample basic = { "type": "http", "scheme": "basic" } # JWT Bearer Sample jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } # API Key Sample api_key = { "type": "apiKey", "name": "api_key", "in": "header" } # Implicit OAuth2 Sample oauth2 = { "type": "oauth2", "flows": { "implicit": { "authorizationUrl": "https://example.com/api/oauth/dialog", "scopes": { "write:pets": "modify pets in your account", "read:pets": "read your pets" } } } } security_schemes = {"jwt": jwt, "api_key": api_key, "oauth2": oauth2, "basic": basic} ``` First, you need to define the **security_schemes** and **security** variable: ```python jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } security_schemes = {"jwt": jwt} security = [{"jwt": []}] app = OpenAPI(__name__, info=info, security_schemes=security_schemes) ``` Second, add pass the [**security**](./Route_Operation.md#security) to your api, like this: ```python hl_lines="1" @app.get('/book/', tags=[book_tag], security=security) def get_book(path: Path, query: BookBody): ... ``` result: ![image-20210525165350520](../assets/image-20210525165350520.png) ## responses You can add `responses` for each API under the `app` wrapper. ```python hl_lines="4" app = OpenAPI( __name__, info=info, responses={404: NotFoundResponse} ) @app.get(...) def endpoint(): ... ``` ## abp_responses & view_responses You can add `responses` for each API under the `api` or `api_view` wrapper. ```python hl_lines="10" class Unauthorized(BaseModel): code: int = Field(-1, description="Status Code") message: str = Field("Unauthorized!", description="Exception Information") api = APIBlueprint( "/book", __name__, url_prefix="/api", abp_responses={401: Unauthorized} ) api_view = APIView( "/book", view_responses={401: Unauthorized} ) @api.get(...) def endpoint(): ... ``` ## doc_ui You can pass `doc_ui=False` to disable the `OpenAPI spec` when init [`OpenAPI`](../Reference/OpenAPI.md). ```python app = OpenAPI(__name__, info=info, doc_ui=False) ``` You can also use `doc_ui` in endpoint or when initializing [`APIBlueprint`](../Reference/APIBlueprint.md) or [`APIView`](../Reference/APIView.md). ```python hl_lines="4 9" api = APIBlueprint( '/book', __name__, doc_ui=False ) # or @api.get('/book', doc_ui=False) def get_book(): ... ``` ## servers An array of Server Objects, which provide connectivity information to a target server. If the server's property is not provided, or is an empty array, the default value would be a Server Object with an url value of /. ```python from flask_openapi3 import OpenAPI, Server servers = [ Server(url='http://127.0.0.1:5000'), Server(url='https://127.0.0.1:5000'), ] app = OpenAPI(__name__, info=info, servers=servers) ``` ## external_docs Allows referencing an external resource for extended documentation. More information to see [External Documentation Object](https://spec.openapis.org/oas/v3.1.0#external-documentation-object). ```python from flask_openapi3 import OpenAPI, ExternalDocumentation external_docs=ExternalDocumentation( url="https://www.openapis.org/", description="Something great got better, get excited!" ) app = OpenAPI(__name__, info=info, external_docs=external_docs) ``` ## openapi_extensions While the OpenAPI Specification tries to accommodate most use cases, additional data can be added to extend the specification at certain points. See [Specification Extensions](https://spec.openapis.org/oas/v3.1.0#specification-extensions). It can also be available in **APIBlueprint** and **APIView**, goto [Operation](Route_Operation.md#openapi_extensions). ```python hl_lines="3" from flask_openapi3 import OpenAPI app = OpenAPI(__name__, openapi_extensions={ "x-google-endpoints": [ { "name": "my-cool-api.endpoints.my-project-id.cloud.goog", "allowCors": True } ] }) @app.get("/") def hello(): return "ok" if __name__ == "__main__": app.run(debug=True) ``` ## validation error You can override validation error response use `validation_error_status`, `validation_error_model` and `validation_error_callback`. - validation_error_status: HTTP Status of the response given when a validation error is detected by pydantic. Defaults to 422. - validation_error_model: Validation error response model for OpenAPI Specification. - validation_error_callback: Validation error response callback, the return format corresponds to the validation_error_model. Receive `ValidationError` and return `Flask Response`. ```python from flask.wrappers import Response as FlaskResponse from pydantic import BaseModel, ValidationError class ValidationErrorModel(BaseModel): code: str message: str def validation_error_callback(e: ValidationError) -> FlaskResponse: validation_error_object = ValidationErrorModel(code="400", message=e.json()) response = make_response(validation_error_object.json()) response.headers["Content-Type"] = "application/json" response.status_code = getattr(current_app, "validation_error_status", 422) return response app = OpenAPI( __name__, validation_error_status=400, validation_error_model=ValidationErrorModel, validation_error_callback=validation_error_callback ) ``` flask-openapi3-4.1.0/docs/Usage/UI_Templates.md000066400000000000000000000015161475153525300212160ustar00rootroot00000000000000Since version v4, a plugin repository has been added, supporting the following UI templates: `pip install -U flask-openapi3[swagger,redoc,rapidoc,rapipdf,scalar,elements]` - [Swagger](https://github.com/swagger-api/swagger-ui) - [Redoc](https://github.com/Redocly/redoc) - [RapiDoc](https://github.com/rapi-doc/RapiDoc) - [RapiPdf](https://mrin9.github.io/RapiPdf/) - [Scalar](https://github.com/scalar/scalar) - [Elements](https://github.com/stoplightio/elements) Manual Installation: ```bash pip install -U flask-openapi3-swagger pip install -U flask-openapi3-redoc pip install -U flask-openapi3-rapidoc pip install -U flask-openapi3-rapipdf pip install -U flask-openapi3-scalar pip install -U flask-openapi3-elements ``` For more UI templates, please check the [plugin repository](https://github.com/luolingchun/flask-openapi3-plugins). flask-openapi3-4.1.0/docs/assets/000077500000000000000000000000001475153525300165745ustar00rootroot00000000000000flask-openapi3-4.1.0/docs/assets/Snipaste_2022-03-19_15-10-06.png000066400000000000000000000435351475153525300231640ustar00rootroot00000000000000PNG  IHDR\uZi pHYsetEXtSoftwareSnipaste] IDATx\uLڰfC.Aɸ:pR<쒤Qv4Of顥u3KMRJcqt3cLsV Cd 3(߼ucu͘דz*Uv7h ݚ{+BCQkC{ r_!˺EǛ}v… .\p@.?t_ܻꢧ59\N7ꏞ :Y?໿8D8ߡ]73}{B>]e9ne]LjqXsxPs5WpE޳Ǟ}x !2as }=rFs@?8gĨf~}(r/ݚN7sAD$#kTco7SrYj?yCG^ݍZFOyeT{W^5ySiv~?Ju>{hۊ_.+v{:@(B-z)%lX[r5ZGGvz檥`Q +(P66)Q?[WP9Z+5WW'j;9e=Ԫڼmy2zvZl/<*"#qaêLlubFʕn_4?(D# ]'7Į~M,gڿ7ͺ.2d6{ OWԢ7Wfۻl^vkclLCVAwo2?1+" l-YeG&\_m̷31gǻKH ԉ4*b~p^""bE'_oᓟ0]t=N7oIuփ_hy:uSȿzBpRykz옵yԸ/:b_ّ y:6 չE3FuyO.ʮwu E7<=R~t]W/ 7^/vGMrv͞?zDwS/ 4 鹣9'uMCp E?[k;O*a&O"r uY EC! |QefBs^ߟ&ҎK&!%k{Fc覀EމQ~ߩ皍C_s㍺:/Bq5J7:=g 0l}\_h7,[/;n m2(=pi2%=z8hxmNnR+ogz= Y7@'MO`xkrmd)US=*amScWkW EC?;p F_{M. CmЙ7:B^};w@u xU}hsZ*]ɚ,i*޽Z?驤a &RgBUj+kIͱ}'U? 5\|+5_~†G7٬'Ϟ7 EUV:OyXtvwkd]U3b"=["7aW[ [YQTed Eom^@kEt qA\s._xnDɾZR5'-FQNش-^1GO$ TM lusCt? SơT|ebMQEȨӓ]ߺ/K-"bZ>S-OȘ1n"{""b0t:0&"y.[Etᦑ?6ѡ^'mxpAUDש]D= ԋv|[NҲ.?i?5u }Hm\0#lʕݖE=i#|\[ֿ[ M|vNWl}v H=g'3{#<,K;j |љ4ve馻WI[cި{mOĔUu/Sຘ;g.(/7dgsϧ([/QPDzEJ Q*^̦^ɋWܮfCe9Nu{RoLTTo,zuzM^I˙x"+|T%i8 "I6-q[QVO~HDvi>WHK7wuB-95?!+pۊG2>^+T؜9o[$iaև񻥩1}+} s |+;X"2o/S{ kłRuIpc+;6eĊiKv|D&-'Ge1>{>{esߪ|lAeyﮙ׊jڿVpG;>hǎ_3>ho?v=Fk}^QfH́Vԗe/nF Չ4UnZὰR{7>8"g)ic#Bڳ]+z8#'~4_DD*Z`rR."aq}M;}f՟*EƏ15%mltLN_VW}Xxc@W|aUNZ9IC"b/k˷.xz߲Gǥ&ńDD}.LZYlb-X̖ r3NEǥNM9PDg/[-ֺe-In)-{.(D"ibXnut2dMwIힴ˞͇.Zg7,[FqdS'd6469sЍ2UXpc;l^ȭːUo " 5,x#{]e|Ŕ0mTf\޴s&R z5A35U-[Q>zx$rx}q!TvT=fMdH 'L}jݨ=bQ;qิ__~5+!*ď%:cZεpóUs'S>ٰ|M䅿^3O[,uͯ$B)?ll~-g\vDCJbkl*-!9urmv،X*eGk/}_ ?G:[/ֽ|ݵI)*ؑߺ ;0\#kw/{<\=Njg i^ڤTl^xW8 ԺiK3v:EevK)s/\g1IX;oJ!PzDiKYQ/)wzi.Qnx$?[;8`@zHK7Ѭ]u] oc=EDԣ~RtHؔ=‰dvZnœ/"qК}*rZZ_6驵,;Ⱥ'3ff^R;:,mKD"Q""W>T#*9}Ik"1Dο$"eC"G'ED -n"2>5ID ;*~:ktiB`g]'TÌLWGň[Ddm!6mRqIٍSs65}vLi6bfJx%5eL1r50"ֱҳ`j!PD1&ĉ+(je"bLKL3s!Tؠh$M[ЛLf);6xbWN*"^!06qQDl['y9GU <ѻ݈i""mۍr{ͽSfv>dM4nЩ?ܱ~AX-.?FDDY3۝7ՖCVng4vWwټ7lw}XOݗ*diP[j) "q3Ӽ)XD'w߆XcmA<&,~ve;W$4HQzdQҺSu|qm]";qRI5-!AݷܢOa?i?)XcbYJlQF-Y8:$KٜHC2U3YȶMyWm5e̙))"H^ouqtmGn3GwaJW.1(?9lڪuZD/ v׈Oߣ3hfBљ ״yd=jww=m??kAAMhZL̶WUHi` j]}Gg?wCWاX8iut-L5u2tAEQD.69}}ϫ-7tw^M7Ǧ&Ewتa7 KXʕFCC_D7q?Vtz)B*yH?:&=ZZ)w^4=fŶ/s,;'Gl04_HNE1MNړ]U(*1n8%F3!ky0 XZ?C̀7 jd+[=MdrB?4뇮wM-.Jкl(T˳64wTMDZ gt \kӻu"v1It)!aZh_rP$fxQrTcU9ߏ88:idg9d/ڼ,4|g6$ܛsߵ"CR־Q"gRELLPZ:}z}aF}f^8vcݭ1S]]5x!uA-zs$n"`}_WZ}VZk]sBcLڒ}a9@R3H 3gDj٘Yǫ5Z-椎.Ap2D*9DDjJ+)EDtw2g)EN""QuM !U\&"bq_B=^D s}U;bDe1/T'/[yAģ[#>bq{u kwU*Q_VfNŔG=E 6iۯStͣE82Q<+-1D$ݬn_ikA֫c9R}C]F720Z˖?|fK4Sb43H$@TZtfu7>n Aon9qv:QvOVSQTD#Jv5/U_sc~j5Lkm\%"!Lwg_{z–22YYebݻaDi'\nMbRٞDDg;hוI6;P6]{4{/^ȭm,#%_+"2e|c`aBtif{ 1QKMĐ<ֿ%Dg=]l5GRz^{j-,eVT%g̝HC1*'+Re0cr  Whc?~1͚ۅF8-TOJIVݡ":Co7kGAg7i=D%{YvMDa=bvR}9Al\+nXRq+ﳇ2W ?!vkWņE+Nk_lDqលqfg׏_izQl%w=X|ߴo=/a\ƛ S#u"6*jl65:o2rzETѼÕ6}{)Q&<ə)1q\… pvUU+ID~˫Yoj{}t/|UUkI]VUt?2  ? |Q-~{|Qv-nyنشV r;" |Snb~k<-uJ\hG_RRɖۊ-6E45%݌c9lM^I )83ssFFݵͶԥKuWyw)#ӳVy[ .]v"P3RQ>aʊ{ݝǪ[it"g=7qqazCZ޺1/:_qV{f9$}/'A)UCF Rg-EDF/`/qؕ֯!ãzZj 9L ͅ"i^Qs xGt罚Rus~@`>8݅_7uDwAA5g76Է:pk IDAT.1E\s0zӻ֯4g?Z^ߜv6\{PzPWXEμŻ-G·6I?\6S*`]PwǙ4}v ]7rR*ɳS5wn I{K1zCڶ.;ra}al݊cOz "w(@bO \( PrC.yzBCemE'lJkxX=+;MyhȫM}F"X'~3S) 0@G4&8:)}iRzo7O?WS *T[D<>DI"խr^W!2_L7]%;}pu1U6j\9\4B@<=j4P@E4P@E4P@z,/>\c=vSՆXqoP4192[~(2yb,,2; n[\FzX7 c/|Zߵ5k$_U~nk\Uj*!Qƶ#.K٩_+ACbu"^^m/6(v&EvdUQ>}%Dn C>6'")k?J>?wm="mU]X14k`̥3Z4z"ߞuӋD{oNLVddKGvyZ7N|Am[unsʘ>؎|Svgu3^̝r~` oH"""%^_DVs4w;ܔ镔Pz =c1gd-jڰ|Qcgg.MoJs\m{ey-?V<D"20"ss%[}6'&ƘpcKrӊ5>VQJ7|ѺjEDlLLb[ϵCh$"R[T~ [JDDOTV~3%uGqDWU[groG22ש"CFt1T/=]jSv]1:g[]u?l Hj4ƄcWFU jNkC\qтG|EDm(ܢM:@>hng"91ΎKvw T)r)޴^̿xa1NeòUԋZ<)ݭa_f1myIƖ*^Z('(aibQlRsmzG,-?>TFEmUh]¨vKFg$_P·Vmؿ^e˲^m<劽vO̞Gc_cQE ^_~v1D&/EJLkϋb_AWRn. I]thl?(]kQukRuV~'4PD}cHeEb:[ܚtKMzav4WwVح֬~T#v[o.VD ӂEDj?)p{R]Dhm["ED='"&Ljܽ~rTDGʼnHYQiyI?ۍLŮB%/yya["Ф_h)?eW/"ٿɜ\3LDD~2+ٳ`JIzlۺ}"t3nHDCbN7iߐO ( h3=[n!"" ?b1ݿzmh +ѳ_\6DDێ7yݍ>$;̓g+"" e3ڊ"5+ 㸉#DDrNT\bHmU!K5,"eGN+ڒ$!.ye~\+"6}wqp_{|li $VTct\bٽVDm@}3'uyi}HLڬD)҆й>/vCDDԃ,>]@BQl0džƴtjP~LD$f0 /#IC3pN"q#"r޺!$s\W&-S$fܣ[GuW¸}c9ZDD<<΃Ycp :פ)ocb|8`ڋ8l+Gc%Gd؍>7(x/9K&T.'ׯ=b/b8vP1%ƅJDRe'"RUnQEdt5QLѾY{6ПBxEW俵 ?4v4x.yW庎E~7("" jwK7xpKЎg GG=]m>S_uuݞ2<.YDf)qvT(RD"3j^Du%G*D4fxpW־?݋?.è޵ɌٿPoKdu<.j??w Z) }Hȇf{z}[G1$+\j4J-5"G: Ky 'D)',҉ԕ  - 07}t'/ԹTG'gf%UZ}Iu,ry ""DRW |zV#KTߣ-}o7`j`CouͣED,GN4I4ҏ(?aN. h9 '8!am "ߠ6^dlY~wNAKxt+9})pTv֝\T: &D;W8zaԸD'OXE c[1 7NJR*=$"Gs[G%>SQ]qg|X:R]4X,VUiu֤!_ |UIj: DDlg 3;၎EqC(?+4"M!"q,B&Sbяir8vYD,GkE’ՒsrV5tM9e[ 9/򹗈85 8D|d?kp^%8-VDD=m{RH;<5BO "&T}.KED'7":IXCi"bd/Z[Z~T}cuߚg7lkICeE+u^륊~ S^V""wW=Z1Sv;+׍u"SgD9rumԚҼ:^,˲+&5Gs[ivz6Ph} ꜻxVOIX0B'Kv%Rg=fwDd,vIЯB "b=YgYT{Eu@RwLAI;ƳnuKa8p[20nƒiW)S`5I]E]DgXvz}^{3x浼3*oBaE\I&6rje' T H}qikvkQVHy~W,I,z5+{Y~.jhpG$2qf'Joͻ7'ҨƯ(>@cOiW|3HmM}˲zeS=E"m3T*jl viᎊKl=ln_Y[5U;lɈ9k__v[:[E]F^~T6d\ƛk,кWBGL^nir# im))};+=3>G9_-L6:/jSTR2~{GT_ђtWGQ4q ^z Pjnn .8aUU}&M BRg<}.bjU 5f>{^7SRS׊n-ѡ!+Y_Cto6iv_HEĜ|QDf=No 0en12t7 #-Pg0F_j8wS,ws9BM#4BM#4TcLYUq=E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4P@E4m/mnn|^g(jfP,BBtE@JoNQsss]"א B(uStPsO~S,x+M#4BM#4BM#4BM#4BM#4BM#4BM#4BM#4BM#4BM#4BM#4BM#4BM#4BM#4BM#4BM#4BM#дJy[A}m τy/N*p󠐤Lp/l}DzKݘz/\5LOx>7~`do*sZK>?gޗtKo7 :P?DDD-þo>N~Ƀ{mWs%y[\h(Ks{cMWJ[sQ!S&Z Ew=:?q=yUU9{+[Kj9u{c~_Q{[kUe?b<ȿceb#=szEDt7G uvj}ߝwaD`}(E?wNiG1@}:2wb raoܪ<zm:=/JHDBQ"*"|[ ۿ^o+'H~CG}?ڭ.}y_("gxAu&>lєΎ9G79nMkOk5+X=_M%*7zXDtO~mmYӴ=/נ{ӟy<;EU6~糧nHU=iA|g+Tsﺇ*"mіHD"oxݫoiIDnlE9lD~w;}>G"%oduc?njg.yEyAWm@͞gHD[?ZZO-\D$"*Epu'=EgCG <(d(azRcw5e&]Tl|ˆ0Z["W>voN}>7`/TU_MÏ͉OjXq;tפ i<ɡC6,W zw}ĕjt-H*x%Tyq㨜'&U}jm"_wzAE{ͧ[6BL?aؖ*ػ_~*-?6z}ށ9?`j)7&O;yTXWjB]_=vhi3s ^g0di5OL6قtʵAUrc²<6Z, Fd?V)_爤1C=6#ivZےrX/c<& ER?߈&9L*a|Ͽ9I"NwzLzQO"t!{X4Zmygc^IU ~EE.-ttE[Xևv^g%ޏ5lm_jtr×Vs,OIDAT;h%c^!a攨í'2(2s@飡(2xWu7c=?CU'}kgFWo=|AFVQYb[{=9^M7.z_ E.x_r@DscO;&F>Pοd/o[QP˞w1hlL~~E]owRLPG[Wk:o-݋1?}wc>|ޱڶ,}{%ѓ\V}4ug]n~cۢî.^îPc!Sꂇ;(s Iz/ sK+_JG -JO,@//zh9*<}Å^/OuujH7opODG.ݣ7ZKŏq"r}{}/]숶T1WANז۞iT_\!6CɯrM9[/^<$4s8xŏj.tSbr[P/xy;^#ϳ: Yzm-Zɳ{K>xoP[K`u}(@7z_>~I/t{ u'zuWـL~S T}z^Wd9Ґ|ϝԲ7OCovAD'(" +"]g/"2z"D$}G[ZUK|Vƨ ᄉUEw9F7a7~wYY[[>$ -*o{<VNUNVNDzRujP [7(2ܷj(ggJ ̹jBQ0S Iٵ޽FE>(HADQ#0+p.nw:s A5BT(M:.&ښ uo4]?rS?l'߼q_8>}zj/]}#JB@۶1 iﯮzpdЬ߽v? =Vzcs[e􏚵=fjϠ~=Y1u‰ߞ8;ýO~q̑㻕K+>;q*: ׺}`HM" 5Q&D(RE@jHM" 5Q&D(RE@jHM" 5Q&D(RE@jHM+=OGZ`a>C-VD(RE@jHM" !)*QNra>we(*Q|YoAӶmmD;sRJ)QUUUǾ(ҶQT6vUQ몔Ae!ϕU]mU/A$Nrg#K"?}Vͯ5 E}t[D(RE@jHM" ϰѴm#IENDB`flask-openapi3-4.1.0/docs/assets/Snipaste_2022-09-04_10-10-03.png000066400000000000000000001156401475153525300231510ustar00rootroot00000000000000PNG  IHDRisN pHYsetEXtSoftwareSnipaste] IDATx\Tu3" *l2_kqiZYdҲٚVj03Z(VsH, R : KL&,x( ۭ9muEvjhhzp8E9S{;{j]_`gd'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvu:{J}DS\#~>7%Q&#ki^muAOܟPsL)-':{@WWN,DuHZK;n;Yܽ=$IEDD ~n½2b;s0(簟;x\3'JA{5mdYyfpct SD)7MD$d0'"com[]>yZE5k`;W+R{Y۹{rbjRP׳VDzlN Q=Oqbsm !w6p]3ID E<٩VqTM7o3ܖG&G7 -g>+v[Ԋ9sps;e梹lt`/fx )8W#V/KʗV[5O2-k4MfaN|'{?r_?W=sŻj #v;_yz{eDDm׋z c34:õTgar>Oȷ{7#8b4}o_;+owWpbݛn$"_u_:rb3kDk.c7B_~bmqo_r7jchEg~]$"~V_~8>6gID!˸m͸~MpSND(*sǃ[WoE挟Oh+ZiƏQ*c7fyΦ|wX>sy"'?dߌDϊs_Wu^sww5q۞ٝ{7zJM駻]|wdikMh>EΝ tk#;9_iIײ47wKc#~ieչ3~;T`84$܉}a$#:ɇ%<˙Nw<8pCxr# "r&wtP\7rы8^^oq^|I}^g?:cl;\<_/"0oz~{$ڑ"3v _NeR7e8+}Wp^KXxZ gşvo? )řE:qN~lCk{0g/cS5DҹUq}3EȨa۞YZ};ߵɹq\ G}pfصsPGM4cXEUADv_wzIBn&mH]w3NS'R{JKӫ{{Yƀ_?rgӱt dK'~ MΑ sM4c}4:g'r//{*s*+Z~t Sb @C~7?6!G9kkRAEb|rβoúO6/#qƆ$BەΜ*mg+VG[O>"4"E~`E]7V˷{s'zMz;= ;Dfl߅ ṣIdЭXHF?w}Eğ3w-=#¼_Т)/o| }ӑiƑ_]C]B9}6vPcXر닸wʢ=l֛W5z6}_tߊt&-=4w}Q]=IEvN׏25ۦO& j-_ܦu>gDoEw⭉cg]&g+o5Pk?**}kg ]5f/bQ'Vתo9\vN+ʅM4wߧn>N~cStZد^CMڹ_{85~uzqA9Mϊi~߷vP}k}zrN{_пwZ("b|7]-N?:C/J'X%R+]5=G*)g3>ym 9-5c?76E#~}%=ܹl8ߝ 9m|VH_=X.Z}D؎*(NPk:;p oVgm.K6wQwE}ˣԃ|GNOa #5#d~NSEDx_]cQwz{-u(J Dp8EQΟ?޾ߞ2 򫵞4[1xErϜ8vjn8 TwūyܿKK,6PC{j=6G ٸVq\Q-"(cD_}K޹,ՊOtL<Е@ݵf. ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@]oZ>ggWn_ü#;:#;:#;:;H@@GuMrM\N ߞu|p;AD:i:&;H```FI@}@CC Z(htW:p^@j4@gn"9f8 :{{͎:Qf'N?x6.#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:#;:vq_+Hfw:M+Ӈh`.E6/Kt5N76t7D˯tUQ"""-7ѥ54 1$ g_eG~u9kDD^ɹ%k*@k5u('ނ2oG6^7ȰKL*;mê5UW N"ܵСlpĥ:CLD\/af`Iʴ dl-8e<Ӛj܌vJZIkZղ?[ pzeQoe[t7$JnڱOt%Eyksfqv)J+|׼킯+&8}[o,٧ݘwWc?ڇG]W~UkJ];%y'E:gc1HkqXsΐ8o,w$kKcj cykcq뫞vO/J9l+^ʽ?f oVtqΛ?_wةsL*.F0g{I6>2uP\c[YAVf_="A2>Zn|kns>WBYm)n0Ňp豗Ǫ$/`kqz&К휮ߖ0Qc~(WNY2]{))yoޥ"l`݆G6l/gϞ]iٻq+sܿ Z/<0*a.O睪 ̟>wz돨ۼeEFA$"`^U1wzOpMϦ,7g]kKY٤.{`EWp${y^GcH<Ƴ-l.xxϽEs3=3=6/ipg|3+vvM-YϜk9,E+X󁱟ڶ0lݫ]Wю^|Y6-5͜UG.VN*Ò$"v,Pr@~ٔS~7|˶59ԞnB78{¥.ze{fW{maLPy-{sl&>&@m[<%l<3(:e¥6EDD8`\Y:1Qjvp>Jig 9K-wws vp}vcQ긨)-\h9' 򚍱zjMi%ޞ4!>wq۪>3J_$MM ͷzGb" tCL!o[\3/ĝQc>.MuޫZUj+١׻\A~fODDbfO)X}ZDWnHӌy+uLWYZ7+NjN "2=o6\RF$՚guӑғ2*^Dtsf.vkM""2-" -8sG#\CT7fbOph]O.`44_ࣖ )Ol.d̄~KJ_l݈6UÓu'}ӆ7j,]ҴHkMd)}v"yID8w-V-"sG:Uwn/:f) {QƉ,m%"aQ"Mo8]vqϛzPsSd);y)+:e66kFJHԐQ[?-{3]6S :1rDUzM2lG'|]YU%r1JNy>#6=sA:8ڨɯO$Zs[go1Rٹ6L0fRc-mgxtIEJݯuCbSRW]vs+(tWO_a6`^A+6}EK;Pgv:@I$(nܤOyCĶlRBMiSFM:_.%j Vg٧4]8QCd ܥD>*h17kYn(_}UUxMh.MvU=?:}qN"/\ZD-Wؙ};\9O]~Yw4jBty5G6/qTӓG9oXxsa%mJjW! ) Rfޑ2URϼ>Z놪?bWV1KRokl6 ։as :Xm)" йVD#9}[;4."RW`Un߲2 '(.S`V>\As56[Vc*=Rbof'tN7rn5*wq}$d\ Ψ8ҤO7ƙ(ݼSm+>%\vļ>.qͯuL`Իu[>/l SUK2~/qbp!Hu;:f'Tf<ݳ]t҅^%fmErN4zDTحC g[n lxOi?2%N{'cvnpN=Yvw#BvզۛIJ}Eᡦ!z:s$:C?wyK:W;k8PA#-QDD)I}olJwyM,+Z]3ID0!Sᒼ3:]nED~(C9CGLO]VpF*P;MOlӐ`egg{nrRS$yǡ8_FLlݮi;:-HdqN OԴMٍIy;*=#V"Y6~S o:?M S5Wkd͞~ptSWi{6f/oUzzJbTBĻG6+DU-膤Z@BG7?r"ÚІ 7oի 6zN)-zG =IUo^X>N0tC/n{ᣀ^1ZS5)C.:@uυjhhzp8v(O^/"/;K9} +yKFVcSFRlcUaz^RcהJۯAf%rWo6}|0F&$(lU2{X^O IDATF(rmY,bolnY]5fOF7D\&h㕿.:NcG$D |!:jp>7Ʃh>TsMDvud'PGvuԊK:{W5@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@ ԑ@]@꯻:{NOn͝=y'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'PGvud'P׭pY(ֲeo-8h3-ÍkhD,kɊK*>]t_IDkaY}pԄ6*aiN74kZmխ䕵u)ۑ?{kOPhKW,_Q`Z~'_ܛ2y11('|lC{NɲCcW~ɄS߸YHbYm}sUNYFvWrSǖUOlt]Z)I.9wDkJk)luKg'y'8m|qr2nKBzCtBʌE۸hծ{e+$"̺(egV+;ܢ>%-G7}pӚSzͪ={)5MD4kVeߚ=ꤹcZJ*mU5+=TVUDDJG&oYO<Ԙ-}d/=}TTߴI=FݾM{UϲͲ-.{3$*i aaZQfMv}Z^^fRY'QA6ɛ^{3*eJSD k:{eFm,b"cb_UWZߍʊnZ~D~FѲ&*vdmr1~t:{|9kQrm@55dӟrwK?ʪZ@YxŝV__p8{c:tx]a~O49 ,Zvƃ4a„[b43鶎{_Nݼwr,͋Lx.ҷߒT7Eqkp^tyWf_ o7;#[{ lj9jAS2/kLO؊ 6x F}Qi&3{r.!{@9sݖȥ$o۽ Ln'~]`ӚM 1DMj>Xrjkv7X9|=d&kakaGFY׮ K=\N"";ϻ_9TQmگ 1xcĤ[FG)2~mRl:+>(DDxWl]-ή [2D~荴%;ED"ꄨN: u| 8y{;s0Td^~jk]Olwzmn/mQ`gٻ/푑x\7=uv_v4\}>*غwl3bt)w> <~VD=#f),"" q⇊}BsNW=7z1Ҽ)1qhqDDDٙ_Tә@ga*fO|!r?!;u$GIQMDD74J/AFe (<\ht8=j1ZQl6CDDZ}喝?0zhTإ1\$U-{?RFDD7zčA"q݊ޜ]mnǎYTؼ>3f ~fvYDbg[n28۲U6Y#+C3:uqHyﺳn`H$R۽pv'3t4Z=~.)FDDJ{q~ꙶet5yk3@DDNlN5$G4;MN{Cy>* }I_|pӟD}M΍r۽dt営zW]y""[;5 c96kN^ɄXVlM_"""!Scv҈-h)1?0CاF}tO)dYqfM!_zbFLG3@+"rς7ٖ`Srho&)!rzm{춴ڤ1-""66~n3»k /c4LGL)s?/L5jP˾|Lw븄VVl ّݲy݁};""q}rq)8V6ol܅'\:w:>+ܳN#+""E.oYaw쵵"m7{w"4so90{Pn"Rn&OYէ.b$ׯ~ݵ!@q[׮2IG-5"ҭk6e{Lݔ+3Q;RDDI#Z 竢 앧WSа_O8I{eQ3'4Oj yҝ\X} <[Y6vۃ'}tTwDt:twH}t&$ $א&"̃ҼghZjRA>oTWݹ.C-9-"G>Y8%HU3u>*j4n).dn6sk=(""4zS2ShXb}WGAy=]c׏1UK9y=r7|nQ+CxViZw_jw";~g^G栣둻ycR:7y=rz<ӵ|JF k{ɜn/:Svm|Rt牕۷l>w!WG>gt"N9_6WTn#w&]ǏƟ9ܽu:ˍ7Ww{}&{K`z;<&j*_[& >ڟ^ˡ_ii췺ۛo֨yZ lKWm\w0932A/MlX G; =+Srke՜V,k#YgZif-.1p+;WEgźG=Tddۇ_u=C""bOu 6?pSLOk>^?ԻY߆K]AB=86&;l=?=m4(}e5#w _o6޵%!vJHK_|k+8e N"R=1{;:Gpy2'~Dχ|f(r+ N";\rҍ <)rTRU>o==1!?1159 -*DX5g`_s_:o< OrMK>6ۗ|{+f,~ob-+;Q}^{Ȩ(UFgcL,5=Ԕ[YωH N3DXORD9 V \Afl FEwPd' N)t),1Yǀg޴Xth1'8'W{Сgeg!qTݝw*\7o ] yy~#&.xr+!;ɘ~S~fkqB9$pU PEUS "R__p8nWEE>=h?fSDtՇy'hIM~NNNNNNNNNNN1;9Jr~2yҴi(v`NuTWG&MHGu*խph'ʞ!].tH?p6[k嘹t +Gw8fc;XXc:`{= (, 5{XˊR:?\&8foك"6dٺ}UY#RFzܙwVZPXu睊W?+NXs& ;Um[4+K'owL^!MYCh" LiڦhcV]D$(tp/o>jSF,^XB8l3XhvO98;{Yp?+{NYTm5Y6}Xܴ%4C"&D9fڒ<{5V=kws=YdM؟  }ǷN[]du={i'6ɠ({cEzS_.)ÙarZƢe9!q;ֽ/.Z:jcYsF}WY/%ӉHHdvȍeg&-{{Wy%Hĥ:'ڹza glhZoNnݵcVYlOgJЋ>ީe+Q yMenS"mfsSX ﰈz`)JD s-ʮ ²|uoĜaДS-3ܻ[ <\D{Qfv4qK3broL؍FYpÅkROu(@y:㝓<+B"DD,zPgI'M""{ \""6-ʿXlrX\QDlwjQxF^DBwMIbp]٩"*CX`_DzݤBD}E5eND74)FDD9^^%-4EtȻpAs)ʘ=9fܧv}弛 եYFWl""dZ^\ʪDt"$/Va Q;j3~7-]DfH)5.sO, ;n)Z^x"9|*Fv*}vE%z[+[]e7)*Cڊa: ;Idi\gSkr3'/[yMvͮAjmV/TDup\j|_ÆEuGv_ ~vS?KLZIRcRgѧ>kNIj2gV:'mH̥9j^Hx0cPFݐ.No~y棶f׋E,X858ps^=fbN*s^NVbsN_ԕ?|%#OD;yl\(ާ>/kHoCSվ--K]Y-֮'bk_;uAػws"(&NA2492-l6gٛr6m93Wjsee.4fiOLgH tB '|8?Уp^8]4$MC"[4ehMUUHBG'&DtVTܰWҵ||ȈQd~ϙ:)*,Zv0coIUƬʿApNW^ȸ?7UY ͝= ,sAIIF_c榿vafzm7*f:"k:a/-<87f ɽknd>89y$L8W]͚utyਡdB=]0fpe(ViW1rLv+/Zˊ U81Ə 73i~Ws;U"M_K:mGs:wqC-۫ˎ||hgg/pSmy%Ξ"CJeA֮܊:q.,^\SNK&; VI2g/"I2U[7.ZY&#9MiRFׂ{{z^4})E-/Ü}VgoL}S$Yo]zg^M-37<29˖TrKT)$e'uvSMiֆvJ˝4ؚxOoq_HZRG%Qw@bNv XǑ%4\2LRΉh˜_p'C!M-X̛e*/o$E 8G3+ $"UW87VR)?ߞWc34}]<ԼEi%VB"Df5g]]Ov/^NFSVmv`K +^y{/[[%8 p';M*rKhb)H<ٹQC~Zg4Ǭ'F$?XlTy䬰SԽ#[]W8musV.]g{wmݽ0rҟ5`[tŞjI:[T9-4U5MC,9c3q[Jv&-LMjdm(H 'BckJwriTeީhR'ƿ&>춊n[9F2\`klfS{pU-<>'±eݜbUFdi¬5_qgn[FR S"gTۗk=$aެݘ'I6 [fy͚6vCڼzǑ <ĵNVަ\T("ɺta|d'k ,1bڊsZ؀sWT/18~ɖ7Jͦ]8>p-efg/_{.3XK\N@G-Tvp[as=4Ŵ+"?+:ld)J[M˗$ nD؄ɝ4X>bp8w4lZ'!a3\Eyuprj ƶ#v[Z6Fmpj1114cgvӳ:Tw}QZ1,>>i.is5iƃS^JsbAd}5گSJx{Sft<+?Cڌ(+,ѽcia7 a2- sq9cx{ZNx'cSPd'[֍9VIsgNWqc"!AFI=WZ-SeN@-f݌JL3T?lWZUI Wv6eeǪ+S姫ξQZd{p5tH֟$(uvfV-OOT3[d"I} XLV_[?ܲwZjKzTǚ$E1ז8QǍhrUڰ/ђ4:TZ3'ǻ[`ӡL_,)Y";7N@[SKAgTJSNs~=޹{;dnstycp(;Q@W\V4)wʊ:Ogiȩ-xkٳ[N׾RA;hk/q'ߎ~k'rDN}<]IOrIKӎؤKKQɎekMF/t *W_tMwD quN*)-u -EUJRhҼS:٩Eg74{y^#;.zbzWvop]Lr5eҗTx&;9,\XUi9!qƶk_--w۪*_;giג;J1)~l';Iڛ6{u޺Oɋkgz2$y_y)BUMiΫ3.סj[>2f[LqIRp' Ջ;}yOѺH9}bxf9Y֍[ra#Wr&;}Nae,<$UW|׷qٹu=Ibm{U*fM)'kdnmWm^KTJ铯-1vm+gDNucDž:\v(e pV˲4- '1IR1}ն';ItYHqMd)J=Ւ¦,Yv\W>lsqIS:?9twf$9cufG?"]d×;8a:KyQY=n9cӳv5)bTvxoP!鎘 JAUM^m.|~8#85𔧗% \#p2ͿSf6d7 +<]IOp[GH4H6OvCOv楊jGdTj|DۻMaI[HxEӜTx oG9{_w N&;bR^\=?a?sFS3z1m~… ug1}}nc00p~C$= Z]DBccnm6jy I%]\w`aII_Y?겢绯6$?p谰on8ZPmטOo* [ p]cNeuF2NxIvoԈON}yˑ Gb[Z|)FnQYTO5H&=4T?+ǘΝ۟a-a4(OHcg?4\ fՁ-l||}k ٷ7v\ 6"@uu2pwSJrg%v$N$OO-;T|L|sSЖ7sOJ?G}#n$sAo3~p}cɷBFq˘)ߞ8 I!>=%=iDKRЈ[:{FEwr$]ڷeZ2ՒUJ$QC*j{F |v^ҠQM*˱:="~u]{IQi:ߘ'$EǷ+u2T~I;x&)xhF3 ~:I}† k{*%-^YitRu<#W'ArZXy$ ɦg'r.J d7:񛷸Z2P$Qt.JOw9wN ft𖳧 >;-)pmϴW|ԮVNx\TY+0 Vgv52i dIqt:Dny+E\?z򘽀~~t\Mӧ*$Fzcg <_HR!nyl+ C\^.y#/pM٩o_IUo)G_<|ZR[Hge%) %?#.i-m>>do2Dn9uMZR8fj:}yhKvqooF ֣QAΞZtcC5墡>M[^tct 񑤺7_:RQ}?6vrJPxz_ՍVНztv)?M,>%?/+27Va?1ms\#`FMv~ƻڹ#) tckU6Eoߗ8zUֳdM~ێF.{a/wUo̫2V>Sq/nz3?~-WW/E|,p}f6X?lI~tWSgkէ谆'~e:a#h S&qaS||%VjN #;{d'p=׫5x힮<]hkA=\c=]p1s4.f|mFt@b5'*+Ӆ-ooO$E7ިot@N #;{d'p=GvN #;{d'p=GvN tZ[w\}y{}r%ct1 N@ ˙:Kݢ^o,5S`O׃ p:I[yI7LFEKhZ-˙3g,5 ~{(\N$YTzp|}}}}}M&l՝ "[kCNN@qN #;{d'p=GvN #;{d'p=t?"I25zT_@vAgKJ >zO LaL9@Ԭ4'jA.;-+'ďlrѳYN3 RÙ?7 kp78.x[4'IqvlǗ>Tytd'ם'v4_MNЏ!1p&D\Naq`>򏜂IRpmC=\ .c+roహ^L zDv[)u0 e ;e6g|Lo-ft7 M;6Uo9yx8`KBv[ǣ[}Pw߳srU#Քf̷Nu+ ap:}=]. *qea~gE#;Wi@љfϖFvy\*p՜<]8N\sNrg%I}nik";w}2 ,$ߑpa'ϔfo^[oATʼnOu $(OV KBvY}+kSMut%a{;{\S"tC$^6˙j3xSM!&sd'Lo @b=p5US( IDAT=GvN #;{d'p=GvN %e+Gjt!Jqw. $BA\ _Μ9SWWZ@;s挚~k d6O0uuufY)rpc' I 6[?&h4zy1UccjX,'88rRsd@k 8򑝀}Ο;oX뗗_[6|~VGvN #;{d'p=GvN #;{d'p=GvN #;{d'p=GvN #;{d'p=GvN #;{>.+ߟn$6kZTpINWZUۙ:\I5bNV{͞uy5|TUwp:f䣂Åe_ ?.ntŽVf$dtu6*IF)8aX,~MLNܿ '$FDv[8<1q =smGAiMAd9+wqdV*𔧗%6`yz"IJ^FJF^ՍQ3~VGfeJ2M,HP5`ibt3M18$"4}٪ ӏ$C[gVmv:omjʖPAM1RwS-]؜%8=I1 rfܤ;ÛjJwrii_2U"$vI-N+fכr:_* IJ}2eJAِaIM[%-KQOW쩖$&=`}1Mz`ƯMXj+նg#6iO6Y+Z.2=2uYVLh$O](;$9AYg/:wo òSBF9,{ÔIV0DDG:}*;~8!Wq3wrT}^z²*>&I/s YCDtr%+;qgtW 梌ߞ[Ts,+j !]&l?rPNY٩27l4txLTHW{cj.ZE*>*'e'sjgp2h6؊';z#i!' A٩(s[$iجտaY:#ڌF }^w]<cT!ssU\+1\)=gO"&w NRy'OJ vl/ߟa<J+*. ;'9>H. s(c b=_ӭhd'9P2{k;; XggN|e) K7((q`+9n⾋g;ǖT֜uT">l\"bzNv2ƏwߺEe]V,g5qQ<9殙?Z%bTܵz- vږm6ۖ_nX ,>qXi$[ޖ)^r_cKm_U6eI.vv]zZAf[%K?Z`%_۟WA䅓?yf$kuKh=wlITc[-"IK] *޷dś ,heb0oqju\$GH1$~)]%㘹k̝nal=;9pAjNB@g+˘f,K{q.vy2ML]vn*pټN!Lccnm6jy I%=}m}n GZ:VKy'sD~ϚKKUzKx"^zUIRÆ=ݭG+=׃wziHRmExބtj:+ũ*|;3C"C*ۡߦ.c$ I}i0Oaйڏ?t'IYʯk'͖&\0)],ٙ]re76m\β+\<IRbr$_78XTcs#:Љ$Iq1Snj$vɵz&x p$k#:%$ 8UdݓWɪ)b/˲Hqbxz$î=6`G;yf8:_мys?tbzs5" &ѻJ-% !n?h"SRPDظNh{uY^n^~9&Gt @Av\~I2Nd3޸gUڟ׊i."{ͣ$i?ύruK{RҊ%ť>G)mڻ{H^FJj 2^~mnD]=!d7喴z)FOrmTs_Yd\M:zk$ÔIb㞙Pq<3JkVSGm!lpHDK7h36xR穋Қ!($$bp#i.{`[@AA_r$I|}LIȸI-ɜUSG媲'S#{gC&6m=`_'Kӎ$>sc'4=(Y2j&ko(/wU"&İ+[Fwګڗ_:)>DzZԭ=L) yd p9RViJ^z}-G!S~=wغ7ll N_\=;Ճ)j$K*3_VIŻ=ܚӢLSha3d'`Jضs)*1ّ|]Ύ7tWR󰴬tBpI`W-bR~U:7lVJKEv*pLg7Kt1`^7<sMw̽jvr#Wn+NyCP@ߩ)iʤ` <9œA(rOҷWKl`蘖 w-}M!**Rl'CVNKni45X4n(aZr¸.E'nk>Ӳm0)O,j%|{ ,anamb)=Fr3cŇr2 I$um>:?(TU%U6)riQ)ص5\y╡ץ[$SP<2/>N;3^~gj\ѶUVCU|ױqB|t3E$MZ")eSۋJ'l2IEy{JܷҶfC&wvIuF,lV4{Ov8a)wWKջV?^7m`$Քe*rfMòO-Z?cI#M*ͣbRF$sG7)ZƐ_XbkJӋNڐIs$0v-N\͇:c txeE!,-޵q~*O]H睵[~j[!nڧ طaovUDۢNWXqGb2){NⓆmM;.УY$M4$5ys E,uǸwLL.)_,zaYఱf̼oKyo1^~!QwDH4`֋{,.D q*nںR(y %$~xrT}p,{3[962~3.igJ]>ubegO&EY8wgw_$|԰FO{Ŵ#/(:>=o,Z)ET\ߣ)O af͞tL5y)(DJRu4w0%Ua+͔쭤~*辕o纽2r-W: sYƒZTSib];-ig$7xHXXtzTv ﱙږw#'&yWD!,ii4bԸ+>d.0tTfxYUe$M ׻^yLac^ ?ShԸ+Gٓ)fdVRXy֫//]4ЩsgK׼md)ھm46֣$0#5aFjUiޮodL_*5!1ٶdi_`DT⢔9 ݰBnГSA7y17{I\?؜gLrb6.`q+"e]<%kQON!C#db708(nvfZ;HsIvwO_9ϤnƏU"sU!($l̤Y)u!޴v$C?i,{1EA7gd?ύqV[NYּ .r.y)u9OsZiy*`ښD\ГuÜ/dkqEWVwbP5eTI춊-O$8Gӵn>cw-VI2 4kFHv<'w=c2t1{ #;{|'OEƇք ѵ^6*p-mx}x @sM٣ ڳE7&йơC=]zkq^k?x{ksw5~ׁc=GvN #;{d'pe'j3ny=h^?rzZj=_X0p7xFWve^+(z^z]'|OЃw^?fQF=8;Uv8*!{SM%5ΚҺLaԁG8omj[ڠ x`Tw6l 5p~Mwam <|[ v՟<պ, `k~JTg F |xAvͻ;x>8[7eZ{p?C軽wRop큇|;fPArV}$P]oʢzfvbOнcyW۷(=ImPm~3l;mg}g7{ScOpAzcUl釫]yw²~8ck[s׎=^% {?զ?(>MR8oT{镰Y[?vxϻݴ>߻Zk#2dٶ<{ڛvl~ɒ.3dޗʳ3Xޘ!gmh)UuszlծƅWƜt껻w:`U;P?v8m\zbv|0n533KcS?=E:y*S*,ݹ+-ߝgTqKw RA3^<сvQREI G7J5^ ?ސb?Ⱦ9?X$Xy['ՇNI>gS'Om?/]7W%/N|qL?[z%vIay7#߫OO^/ՍN=!TCM7]=͸zzղ/}T?0zc6YzNKgzMnwyg]zG͍-w#|{;k% TĤ}cxxlKU:Wue~OQCMw+ori@/{1jҹ:?-iwǪڎTlr™ۛ&?[BYW=䟻Km(г}()iJ*CR-IRxUHO..#I7:Ng&NE$)sMNO(M$kS>KRdUoXVR|߹k;!վw?t^x%M99sp;dy).2IDATުB#~+7Hj|zQʍ;wHj9{%zXvaInJmP^;݆OIwCc;K~$Mr{$)盻j#7NRߝ/yIn[ 6E%CvRl\7d)꧆v+bJü <ֻ+bbwUTUo.GU$ئ oؔE9a٩SǷZd$I??Wq&PB_yE mM續(dKj|/|KҴI"/̼lJ$ ck?`tuCA$ɞq*T>@Z+}*$ax5:I ʗ:NnZo/Hzh]mBM:[q+[nNy$ޮ A tS:Sg ޡ{3L1Ũ. MUҺc[dQ7Y٩#ˇ$i 3mVGi=3{|^˔7ߖ:_ٖߒ%S$I~΃Fuw'|aNU>痽?jxMҡ q- YuN뗓 {I 3X/l jE0cSWgjަge!KwNwgz$X |FI_?#X}51 O^|Q7M] ٯiYA]ؓ[榤ZA3nǒs^AuϓW6d"~ؐ"x)oXQ򂁉}$%N;}jucs$Rݧ?gU^7m%7K7HJܗ/5'A_So|~+?ޯ޷}Wc賛l{=(S{4!EsoM\."\)^S} ov B4Mkfi7?=smnx wKҎM A]zneJ| ƘV̝gYrMS;Loh=(v{UM w #3#7?Ǎ7 %Vjll`vfZ?$鹤К~뀣H ۼ8gvf=d;Ыrt$~+I6IdJ3RwHRv tI=lOCGFzKv}P׽񏸲<*""A# &+^mzkkrumrc&tng:;t:4d0ibolԦ`G(+ ..yZyZo}q>;?!;m-^w{a}=^w9>1>ifGv._vIJJJ2eJ7vy_uƜHyqݯ]Bn߾ƇMHm-=.ɔj6 NLjSL1ͩ&nktQ&S˗sDW[e_fƕΑokSMI%Wo;N|ݞRYfq=fYvεO=;i~7'Kɒ. 1W.t]ND%ٜ ;M5{qq8y$LJ}d0A_G{cc#]&MaI_+EnD\׻z=5u?ǵJȾ;gm >j҇[8-Ō8jqWCy#nDNdf POJL|ǝwg̉㒳v%.煳#] &䚽j'ڥo(8t4Uy7.h֔pUy._ u\&cN8|d2B/U\hIҴe6fFKy\oښ!T[)dN*sG\qޫ+MW^eoɪeWO=5~=Ol^}_S||uIwKUO?^0+3ƝNQ.sfc{//,\|fMx3}A^$JRt綝$)fc:?zѾ&]?޹aQT=\ u)~ajk?'I~w%WlË$)1OmPIvxHptb$Ύň٩uhpqOo>Qro^(I Y$Y?t߳siYkرbI1 $Z.bI- $*ɦ_wdžW'b$)q`ϦO*dֶ @u]R,M'Fz^IEYÎ8)(fGMϏo9^ڢ@s>Ii/ػtpGJȽ[yB\ =5/ؽ$Y<=KaIsDpG??#i\V+/\Jɘ;@uIӗ)jKݗ-2I5E`s= ]g9蔤]H08!9e8W^ey=C)X>p썏ߩl9eLO1,W)K ,ϛrقG^23ܖ8u%Ŷx)ihJ>:T*dI-^mL@EJD+$jꛤZYNɶjkvNSOoo@ ~OOOww+$r?5 pKCef>d]8kg#s~}^NkwYRjtmE0Ξ=+)5}kcy'I6_*suvW_YӮR9mM728ucq+Qߌp5HRJs.` sgCi??.~սQҍcG\ԞP6LGzw gVUKm7N7+Fɝ"]4OvntNm@49}"Wz |g>)>Pvޙ~׮5Ev&5gZ{? XN`5{@<u]} y 3pNW_Y-!ߑ#ijuih/WWrYzL3[-V:_}hmspo+g}|UyٳG8u|3.Yp?_pr)~I%߰eL۞eGX␤mo 5WO]bW?gY/`[kPGj;WUف=`%DI>o15|הѭ$X\;fQX8}-$Ú= Grjp2MZ֫M>GC"V:rO2YAS**upV~W 2rjBIϘ'vz} 8LR;k+wg\ '3,*(%/۱z簭ϖ%9~WTvYRO˙fyg(xlf5F|+j$?>P!+>SWؚ[Ħ8P;y<۵cm_pdQ V q Ny~㾁$4;gY?'^^hK5-=P՚2p XCr>?p+Kd[rGL$\y=_`rJY}Ȏblkf7&g-?g[Ö+N7 oY~E}Kc'낏WuЁ#3k0y9?PQҍ1շwl[W`.A$nрWޭ4dZSR3SS;|jNb >&v8mъЛ}d+yoɔ&\.4̑i Se/JY #aBv3 Pgq#9fv_6D7q>x57odp!.43lK!Wmգm>.Is}D7fظT~ړ!SȵL;9/_k$ q}7z `&.[&K_drlN@TܼeEt6|/W{G,|8ү>cw\.|Ձ7^|=eQN&O].bu)ר=ݼ &^xPp|uooW{;%ɲtqrI-^Guɞϱ.y?Ƽ6;\c?;vm+\o$ i-klcMVn!mb"v>CC hJYXiP!@T}}z{{%{zz^&g3n y.5:\%ek1N{)Kt[NVus^j[>}=K N$Meg̲^O}Dٳg%gGLV-d?Fgߙ~Cg5Yfd]c-)rR&21#;1#;1#;1#;1#;1#;1#;Ht`OpNL#]//>pcNL8In;҅~7 F~^M_!;2Z񉒚OD,hDLb$)>ifIII񱱱Sp9)Uoooww8M3'͌tQN@9-! 9>Gv'4%x:|.'k9ה&gK`‚ a +                              YqsIENDB`flask-openapi3-4.1.0/docs/assets/Snipaste_2023-06-02_11-05-11.png000066400000000000000000001401221475153525300231420ustar00rootroot00000000000000PNG  IHDR " pHYs+tEXtSoftwareSnipaste] IDATxg\TgiCCb#**M4!.bZq!5 15VT0YQ#j|$PQ A^,8a3)Ϲϙp#WUUE@(ZĢKwZF^ (H(H(H(g~.WWPχ鿱_}\oFVv? @Ut賫4, պIףv[c/f-oN*][H%w^@@{-Æ [fMK EEEY"d]24~iӢ?4 [߿ϟDD:::۷o5j@ h'w$*Z5&kQ{,--Y 4BaCgDѢ~QMMׯllD %" "uW&mI1hQ۸q9sڵk{QUUׯ_Ȉ;kCDny]ݔ߸nTyzzt W77ȭ9߭qvQZ4Fw\z9sNٳN/k"“kn͍"`ܸ5g)rk<Ҹ;6?Sdddtt4999̘1#44N¹Vs6SZZ.qCwdd%݄U5nCWEu%&QnYs H%B(l4ќ9sO~= "8q;wf͚iӦJ:%L6z67X\"8a+ 9lԪcoGDc u(f@*Z!%qϞ=_}ٳgիW>\9)4a z::D, dE7$d`Kq`GDɱ  :Y 68q(Qo7XؑNΉ\^h[ܸq/koc} #u:]&ر|2ߝb7νZvdV0av6:.'TKh?µvKݯ_vut\knN<EJ5 .E,;ɺxe$im/ (H(H(H(H(H(HBKN U@.֢,0F Y Y Y YEQUEEeyieEyW%{ʝTUԔ:uu-, -RUQQT [կ^UVVkt~+(@T"կ^UʺFA(u $(:Eڕ?IdQ6dQ6dQ6c?_>xJϟ?{lSɓݻw'&&*((,^6--MKKȈpKIImjos v]KKQF5,/Ddnn.''GKYA(@ǔo߾wJwmu322߿?//%%%ԩSlvLnn}.\ŋ)\du)))?rҥ/'==_d˗999͛8q\uuѣG; ix~)W^WSSSUUuV]]&Z:dQ4''{F9d _J~Oyyw-WYYY.cRKW9.Bڵ+QNNNSSSRH]]]VЫWJKKe]@o/^痕5ĪbɇInիW 6զ_ZgVdaaauuu[S]]]XX،kZ* ǰ/%MsjCkzZ/s'~ h/[nۣɓ'N8p`HHѣG?ND{۷ zɓ۷oqۢ5vI&p߿?***''m0`Ç_񍌌u~~e=z(5k… ׭[~&ŋڵkSUU>:u*R߽{BBBN:)SM`yRؑ666~~~GݱcG~~>iiiM>KAAҟTVV۷o޽\ s֭[HRWZչse˖iiiIhʕ+[lIOO򥼼 zPZZ8pʕ+544$W^^~Eu-000))ʢELLLDƟ{,hɞ/z^x=W^}JJJ >he/mUJk)xBEeeݻw&ɒ텅w޽u===… }뭭%\%88~STTtvv622*((8uԶmnܸ|Ν;#m۶3FIIƍ|ƍ1B0 %%EKKݽSN}ibhh([ŋAAA3fKvu}^222.\t޽ҥKO>~ΝsaI__?44_~/<_ɽ|rϞ=)))C]lzůȻ ˛3gOڍ:pիWǎ򪮮srrfϞ=uTv/ljxVVV{eD[lYXX?ZK!Ǐ[YY^+㯿Zzu#_͛7׭[f[[[e˖ܹsʕW^=s挣ڵkb um߾}޼yǏgqaaa~˗/o~ԩ\ՋUzQPPf]ЊZ -ʞXHyT#""ɛFjT ?,?,8QH.KkBW <$i߳YfM;>M:9&^{ NNqpL'Vp4q֬Y,q㌌]Vߋ=O???.QnݦN/|ܔ?~\^^ND<8y򤞞ܹs_>MΝ;O:+@YYK]]eee ؘ99.]EI_}KBC>|xaa%Ek})ܹse1ã-nY999cǎeA0x}^z޽{DK===Pjuuu#E5?-544&N~6zzz~~~!ʦ_|_%%%U4t eytM 5%z6-BT#"QC'/<-2 IL&Z;ɿU/2N[[̬bO{۷mmmDvѣW^"ʄBٳg/^ߞcggesuo vzݳgOv}}}mm첲2$ydiՋ?%XYYY/_{J~)‚]^^ۦ \5p@vuuuccO'Rt3 8P$+ e҄sۢNxI'vJ#b*2G\x{fs"N#'N B\ӂ"";mSFZ0?}x έwZzfkeٖTܖd/ 1aKH"%z](Wq-$:͏iP1DDys[|%zvlͽ'⡗K$?+7W*;8)ֻZr_EE)++\ē'OJ޽z255}W_}:D^>)Mv \7vm*k֬ٽ{ҥK3vrKܹbkd/^x򥲲ɓٳg{{{pX,igۘwhzռHś}o,AvtK1&ڥIf"Zpw344ظ9y_MI;w,njh[l[mJD!?ll:]0i蟈HON&㥭;'?q@]"6|w,j;0N@D{ϑ "gQM2 LHDD;y7MX8Hw-OHd^8yoKpa&8e 5VoALH0m1kGk J𮽜S Y{g~^L/@8wbp[Y)k$!i~؍ ۜ}&.J=^[ǵR6&$,yԵeﱁ |S^36Ͽ{0ۈ(kvZ贀6JzoJvf[\\C";"Аqiii7g===[wqs8"+>x Yon@{Ν; 6mj;9Z͛7HWWWlm䔗\N:_ oTSS?~|C{vڢ;w۷/44>h՟={Ş{ltիW^uԉ@++ʞ}!"tNpYj~Wmb6N־h#X4"ݑE *9;Y!{ Xݷ>s|{oc044koOD9:yt-;-oeۏ}5gla$'߼yKBD4--]45?l+WVVPڌO\#llVgNNή_^֦liii%jhڙDDگ{l%"JJYtZ#;#}&48d;DŽ[v'k{({am2rnO7=oKW`m@gqFrB~ky٦`w9~ Yor_oV=ٖTc_ߣ`c)"!{wEi[6pl}J DkD=e,Ǜw!;wЂcS#T05y(|rX`;w=/^:Wff[ ؠкLLL oݺ֏{nzz1˱Ddnn>{"ך͛7 M_~1Bwܩo~999ss+V Ƽ֥_ʽ{DaII˗wbOi/[nZZZuIeeիWߖٳg~~kѣ+++{bڵk=244'"KKKuuTn'Njjjffnk}H#ҡidgݑL4+yMáqm:u6_v," 9OF߻?~([KPK`IhgtsLo:n>3j뙳X#6l6.;CCѣўHiao,7tɂ"tѝ;D[gnZS;ή눏Xzz@DD]4DGFڼ[MȈ.miOe?!ҭǘ "2ھbt'wc"zGI ŝL".F"D j}%nܯS\`Qz}WP*ݹ/a]#td=Vp޸]w[QC;}DH xD{d;ncvɥ^AԘᲶƼ+ l}(LNrrK :QzzzhΜ9#'O:u]hǎbO;vG_~Ã8e {fmmmii/pŋ KMm!''_~aW9llUU͛7]oL?gSWVV^t钥e}kѣO>999?L:|xx7JKKhw~4i@lTTTZ/deeܹ˗ld A=x`BΝ;裏>b ֣ZZZFo&bR3fq76/wYFrz('+**!kZK|YmЏ?>ϫ))~ Y|A?~@kK˵ϓNE6&4X=Ye'euGhc5FNNGYkLI9NIJO2ھ¡v:Em\G ]O͜,ڹS nML7 xFmc-PmeDzл}Ђ Nr;===gge˖9s/^777O? ss+W>|ɓ'Gʲ8qb})KNNnܸqgϞ?~;wbcc>s6v 2d_2GKKkԩK,ٷo_zzǫW9{?~߾}m8 ǏxѣGSSSG ¸nڴI[[ŋ[n}䉋gϢo߾)XMRw~ܹ/yȑdee3f鉽D~)_|Ezzz||iӸ2BiӶm܇C qqq1c?֔pΝ;]]]GD W5-###777۷o_Æ c %"eeS޺uСCfff'N(,,7nkfG]]gϞk׮=ztuu?Ϻc@:o3+Aۈ(-A"l.22x_P4Q IDAT=f pw1 :?~nABcFbqCёcQ ,pz!ՎȯGִŎѯo;v6i#j QRXtqY"{3'S}7fz2N8>x $cYwh^E}jv HXO)!x-[X btWRv vmS:o`~,ћ?FD$h_""" έwZp')6'//?cƌ?3:::::ZKKkO>{ݵ֭[w9vȑ#Ν[w!wq"Қ;w㹷:88̟??88ȑ#GQTT=hР .[:te6lؐŠtttoΜ9ZE^z֭KJJₖƘ1cظVccs}t_|Ŵi/dƌk׮ a[,,,ϟN}ZKaÆ7n~H +V5xBUUuҥFFF` 0dv CݬED$nzvɓ'O6ُ?rQJDFFFFM`=ӧOw^JJJpp٨QZ9%Hhғ[44rzMhuCDDgtrʐ8%9%Gr69%zqDx'zg̘nmiG$*ݷx>vv"1gO>9S軀GQ"j_ڡˏ;f:wRgQ<>.}NA!3ݶppL v[v#6,K}[=ٌ-[ lm'q5pUEo\ӂRY*;-+@тKȭZf`7^lkJ c]&s[V+..|444իWGFFΟ?ŋEEE^RUU~{K./|UUUu_yճg^z!t%vT%kK9{UTTT?Ҋ_Juu5ABSmW- 5޲ӧO <;/1p4?7)/CD_$~EPZ7Yq@u9IkU#ao;k֮|Nĝ43IK׷&q+q pnFp[s.\w>_Y_H['a$-T_=Ŷ__)HfkCDԤ chҖE+# -͢o[Ijh7^:ѽ~_ ^[^5gK{.im 5z3woK :9756_{SV{K{Ȣ gQY5Y)MdQYSY[G:5mb6뜩 m,z6.gt{;پ~<4Nhoؔ&"x#7Ȣ<ȢR, Рw+ɍ{cj.MҊYINB"EЎIZDYhy6;*Yaȟ|ȢV~y2(--]ADwdZ[Zuo݂u[@@Dciiٝ{FNn^^~A Ɲ>_[-''u!vEYޯ "]S/C tMK""踐D5Ոhi'"zr?P{DKv 1j'}bb<4w5`$~GT+))))թPS}KUUEYYlce!|8RQQqC$B~}'%4lǞ u3hlhq](S%%%M7i<>UMMWϞUUU 7۶m[xŋ vI&Y[[QllIJJJ(''O?eۙX{{h}}ŋXxƍYkrrr)l˰arrrX &M4lذ 6xxxٳG]]}۶mTKKK;;ŋϚ5k֬Yܹr~7m۶+%%ښ5;k,+wjEMSXvlf=6/UckjEAv6Gɫ{d=&Ƹnjm=>γx +;_>b3j]"EMWM>w?13]tGVWWWRR/rX񓧜 {:uhȎ]U$R}{بg]45S2Y[)*~܀a"RSS5 gm"juĬ]vm۶_~֭uwwONNƩ| c̝;WWWWOO/55/^V?:yيG}-))ٵkה)ShڵfffDb {"zYNN۾m67(27''788ݝm[9KJJbcc;wm۶[ G:5iކLCޮ= Gsw[Ne+'^JQ8cƏ #cşMM3xćQFHUUU8 $vXY% USSlԉǏYi}OkwInnѿYjZ5ǥe;ܢE؂ٳ'l,999=yDOOoƌgrݺuwaG~'%wڊlgw'L>8ĄlԥKn{7(ҙjbbwٙV_Ν;ϟ?ʕ75;;u&@YTͣFjP PN&V^ʼsZzܙ (3&3f!m$˷ tdM{xYB֭ȔXY [͋UUϋҭw{Enڤz cWi6V/^6a*wvFOSSY?S'zSv$;;m6` VWW6\nբ6.##:##CBOf111mT-a͍K IqdžkNgQDA4C""]ҶQ"DHkުy&'=)G4%^MSwM6jt!(pXSqf6QOm2]_sX3߅DUA&5mf{@G7__ɁJMMu !†ju^|:/_a{s.]MݤsΞYbڥ9uY֍t5_et!"%h?w,HuZ[Z~l)6-.}ݻw(**֭[?7<<=|ff;wXhNNNxxx-HXHOOҒkDKWWҥKϟgWowN8qlPq  28)؍K |beGCօ;_`kg1kZ1ڑbr 6Cȩl2μIr:xNl ^oSZC"tQu&"ʼ;yeTܒXI53 xhka_mx TĮ:T '%Y,rE;kD^s?EFn%+Iъ+z%r@Ϟ=544-Z4|.ww_Nڿބ ƍnݺC uhkko%KfϞ-''GDC=p@9͸`&WE=<<\]]jW\iI<~u-b 2xʥ.բ,,ߜW$%'m_0rh!CEm\G ]OAښ~R #c'@D~SrM[M|cL|/x{*iqX?pK[.Κ.,mi^{E;\\aE^q})֘j=f4.dwo_߂*W[݇ĘV0jUvڵk׊mܹ믿bb%?-4eBE.͝ί17(ݝQ`",HGL\ܯ&7p4E@ZE=%ճMt@ҋX}Ք4Fzډ F'^M#/x id>^өИ Ϟ)qŚJa0ȹ17P: fk nbb(18:|1c2;$IVQߪEb%^2tc;liLZ7w DTYUΒ-ׁ]~lqK;Z "kDNaz[Y]edX[W֣yү]SsUVUmߧ##]̿ȓgΊ}"UqEB~O2բ}d>@= RShGbu@f Y2'ric&:r,ne5!ek(3V[GsՌEe\Xβ) ]?Vs%m 3a=t'PeGMm[<" jVҼ)좢n"`>e%ʪKWH.7Q kd4}& 8?6H5_o9f:w]kme?~ K9>dp5 hZ:_ŧɠ>DDzVژ2q,nMvy5 "ͱ NOU"Y̬jš`n<%:]qYxKjGqݛLX `Iv'MVwX/@[pvRYi_ptռ27<̇YGNGɍћDt٤KWXߦQ"^GG}e**+WmnF612>d0דt1bucǞc4249½n&\ͬ[lblT_ .-,3KDDc7yc-A1Ocy?\qoWo4;Q&1&ϕ&1Ï|5O\"mei~/"22481z:z:q,zm U$7f :}'#鳹5;)/ѥɹy,*9˶MG44Իp,Zqv-}z(@{sYuY uMYΓF.\TYU$9yX#C};B} ehcmwM&yoRaJJz:[~ namY@W:6ץv=u қHrS/Z_7 5~i_63ؿ/[[^mj4EK.Yڭ]ԑƸLjV=-_~×_~YVV&bC{YêjKt1A^^ŋE\Rd˦M'#+xMUwnnOjʺڒ% w?p֭_tvn۩۳ Р/^SA***X o=oo#FDEEM6Mdwtttc~z^^Yo,X`ii&;Y._{ݻws[>}*zcCƌ•˺ x' ʕJ-1۷tv}?)S؉|ҥKxb2GMD***=zHOOGCuȢ.}vDD… n߾_QQq5kܾ֨}+WAhhhRRҞ={z2W^ł@ {uB*+""E- DaEEEddETTT8S^^~%VjTTTddz W/\ſ=zUTTu՗H+**vmw?E ҥKhh(9**i``p#F`.敔>}ԩSȢ(*** Ʀc&=z1+ ˯]6fKiccӣGmp^WWWͭ.]T7rWqss;~xyyݛ2NNN`++k׮ʯ]oVKKk͚5,uYܘiiiϞ=9s&WP(dvکS' :::>}=gΜ`Bl¬Y,X6~Var.dQxO81,,,,,>N:t"D UTTt"\6o $Wˎ744iʕ+=YhAA䑺MUPPܣG[}ʒ{K.駟yeKK˼W 6,6fׯϛ7/??;e3)8ݻw(XeȢrttttto(l3pDT^^ٳ9!!!ʺج\رcDyf6{~zzP(!e%PqvJWPPfeB33#FhkkXfzzz^^?ϸѣG߸qΝ;ΫVRUUݻwƍ#""i&v q}KTwro $w톆{iiiDt1>>>>l(dtڵ޽{7X ˏ?vvv6ڵ-44TB gY ۍQpQQQfgg0Xffflׯ1aСܟ*&&&{NOOf- -;ŵ$Yrfuw]~"6ur~6ͻaZ,*@퟊_-*enn.2' cEV-Y^gu?4;Uii[w>?kyZZQ?~okUiYӈ=55/7Zޝ|?"7Hd%i")MHjZjk5**f(iTt2-j)I"!{'zod)?N;7D{y{|sǘ{|Z# VVH:u T6:P_w457ojҜi7ѣ4<[եVUzKQ^Jf/.@Ez4##~ȠOxn7c_r?ٟoBtp>_4mBmkdg'0|s~췓'W<2>x[_+u/Ʀ-[#v@b]q*+Ucƹn=>hG@y45W)`ʌ yip goX 4:mV둜摜FԈǎ7tuݱ[[[ !Z73Yvaa } (2෧zxg-, ]Pf&7?9rKKK ]Nbdd~3VxdQ9駲3t!, 74q~6t n=k̋yQƥƧ;2oɶc (h.@C6.:(ƈL0,YodQEFYodQEFYodQEfdG 7ǻzTsog.}cYuZGoyֶXnmbbRkq5/(dee͞=;!!aԨQ-9rʾ} lmmBM,OɩN0333?\QbRet|n??9}OPr9[XX111ILL ٲEI/O{+Sƍ}Bly󦓓Sbb φGbme]Q&{6 kz WO}yR<E:=F~gϞ:t={=vج,!Dllǎ7vXoogVg;\%^r̟+W7o^PP^;' KJwΩnn&&&4i֨j ge{x/B{{g&Sի?X-[ph^^{ࡗ&MNLL211)).Ĥ/ܮ\v~Ĥ^x۷3\<=˖v .3_nRTku ktzŋG^fbʕjZѧO#Glܸq޼y*Jo߾9s;::k+O>esO?l2777F$XhQ߾}SRRƏ_ h(Y3#Z=&=#K}fͲ򛛷r->+'׹cm+yM5321inȴrxႿ4m3#ݻSXܼEo5mȴys͌7oSq͌ :8sp02m޲U Yܹw~3##+k떭Z5732)_B889^_/곻 ]B"ٳ͛']b#/ Bt֭yΝ#5jPMQѷ32;8Mzi\^~ArJbQ9l]ٖV|u?c !:89 |T{ZB~~Ch4￴u>JʾZh6lMz, ^EC>)Ϟ=vڲ 鍛ѣlvcǎ;vGմ3M^h4QѱWv}£֍e{Rrc'z?PEw/Om|Fs'eC߇Gscz ݇M;8mdݮo=v"%-nghL:S=ݽ<̖\ d}EyQAOE.~:[4W]c=ªp-LpœUV͜9s̙WX~jժG~qyoҥh" Zh4yGޒ٘R6%j d˗;?藺0Fڎ;qFvKիB33+qk9BRr̜OJN`@ü(`` P'NktU*СCw$سgojѦB|VVV)3g(ݔ322uwttLJJBԝ\tҥKO> HKK[x#߸q㫯⢟ڤǏ/Xlڵ;w^p"<<<<<ǧ o\2o<333f{xx#SR/ ,~؉N4j'ҝܽaSE{7*{;!DFFfIIIIIfKio(P-Zayr:Kd˶aVIoWT;e@yyy|! 1{l9yҥ(%9ljjtڴi:t/~W!ҥKPJիWnz 6mB;z͛7/^7n\hhQ|||Ï?ާO/R5k,9~{!!snn<.sɏ VVVJZY̙37lЭ['N!,Xݹ,O2֯_E!o1|#Fxxx8p̬ ב%%%?#h ]kff{̙}*!aR>ʨ|B߿__tiݮ=z􈈈HIIQw ڪUիW!!!ӦM{CCC]6m4|UXXgkĈ999/_mݺR؋/N>}֬Y999III6l_n\I~ĉ7|EwY{n믿kO<޽{WIlڴ)==].L\'Nxd QYfi'U!U^^ލ7P},,  Ѐw}ݻwP8pUQ0O899YXX!,--Cyyy}վիWz|K.)3#=e:x≲[3fťsB_~e2vݻwssseGi޼GѶmM6:th[۫WJG!=Z0r)PY@"m222}]E{{LGea$wyyyaaavvveCfهBo߾V=W9s\Ul/,,޽ҦMu]\\&NiӦM6ɥJ񑳬:ڶm[ eU PߑE4Fyyy&LP§|3//me}߲iGD{{rn$#*,,\v֭[u9A* *x)mV{ݻeIOO)C hRRRr$*DXXBȇN{\?naa~Q{NNή]ڋiLsܹ!DxxHj!DvڴiS͛˫n* \R_ciiٮ])0 E4F.]rpp~P>yڵz 壞re|^TrrrjҤSufffӦM Y`Bg]VVV3gB}t...>#Fy|O]i`ΝSѽ|ë^kR\\l:%-k?_ܼ:$OoT>w=_N8 )׷iӦ"e|g3f̨vD5('''88xĉl õ{;]Yڭg7Fjmnޢ+ߪ|m;*_[(bXά`FvvmV[ *mP>}ѱoV9r劇GE.8pիJ]/"`EzZ:mj|"VSkZ}I?jf|sim ۳A0f'79wݴ勚uuv}tӫ}G|ԳRPgmE&>={vӧܤuv MU4ѣ4#cg[եVUzKQ^TAȧ޷= q qK)W')ړ]!~=}{ ~ѡo #YA9~ȧ)i~wlIOj+υ_تU(GkwW.5r!ʅS&+nJ%:K_la@"7(}*Y ؗr&*[ tp>_4mB>&jdg'0|s~췓'W<2>xܦRnlڲ:FT֓]8m;>kNӳgN/OiѪռ^uiׇ봿^v|!DNCGU !,|STF :o?pؗƌ.**:U_+ =hgk kɵm'ן}H3ϙfn>#"CG5Md%l4_.ie#ѹCsSSy),@a.Pٹ+.*siFFίq*/3W2 joxk=ش᧰i{ګO[k۳ܵ=a7{!ys6m"m\i/iI)r݌ ~Y5)..6t @`lllwg!k5{UꦹyjB>m6^Uq>G\)O/PwU+m;*_[(bX̋5)΅7t!@>@ ش  QcI-`YodQ(h.@C6.:(ƈL0,, 7(@Ȣ}#, 7(@x( 7C0YodQEFYodQEfds7naaծ5t9@CC@0owwcff>JLJ6\EjktƼ(_aC/](jqW1u[[ !{v쓘!Ċet|'vMLL ٲE^"Xr-nXxx]۰!ǍyDq_7(EPxw϶ٶ5`áG/EE_/چGJ'|4p}v ߒǶ/&&&}Ȩ.iQ!g 21EPۿ?33cGgB ~i&M;+#~yi$[n4irx/:c8xIj'GSLLL 211jZ/_hX 16mn2oҴ *sc QIXXfbb>N]}q䈡 T.413513361)Pk,mwMLw,m c*VƦ7ir{416mnشEvMLzջw/9sG kZaШݿwP.)kBjY 0Nݽ:RTT4# ] P__(ꓩ&ٝ8u:&6͵l_4tLt9a+xׯ "ϝӪw8zPh셰&vSЃ[?D_4u҄ aڣ ñE#ϝ.w|yVWS'v~4T=u YI32N:뛽ݼSu}ܥS~mPϧ6,^=pr!;z_m޶}|sa^ӪU3+(,[ 0u'M3&=2`a6y8h }gϭۿL7H3qT7{~Vx>zLqԙ#vl~A*ؼmurtоv@?ߞOyܽĩ3B_jkc}ԙG=ҪejBwIM _P뛽BG>z,r"&6mǔn] 6xP.*_aA?9+xgk̩LDEPoxw󲳵L-WPp'3+-**!pkzy/. Enʹ\[TT|+#C6F_/yȠI#ٕ_6/8JF@1g'/?jEooE+Ϸ*s \g{XWYtp8zĬyU? x^À~s\@;l ert:i®/>{m*G:ON4qNd.gLoժeXDr6rLLl\ϧQQ޷ yQ=ڹNq.,|q;ovyδ7f 8}9's^77*UgQQqą_oljj_P0ĩ3isּkB>Y'C@Ҥ5uKZ\*C܅z}3oVZ k.w岥C6+;*UYkmU}7EsNJwf.@Ȣ}yQƜ8uӃ ]P0/ 7(@Ȣ}#, 7(@Ȣ}={v޼y-VVVnnn* ŰuE@@@ğ?qijg(VEh޼ywh4ydQ.RTvvvdQ@v=CEdQ8{Ǝ}vǎYeql<{ٕ+W|ʕ<66v+W.@gFڴ׹0++kر򔿿?B137P)+Ԩ ]Ec".ʊ-;v Zvm>}4M``ٳ?caʕGCBB\]]k͛7jԨOҢE{͞=m۶OVTrC{)%ٳ'##CD)oN6M!KMOOΝ;WP~2ecǎ%EwK (Ph4+V9Rnٲ% @CJ5s܍7ʳ'OӦMS>} سg;B *|Z588X{zSNJ///__Hѣ }=mlldB۷˖-M2E޽{ݺuY+Rվ}=zTy޽{[n=x`M֭322E:FL!DnݬRRR/_αBJ㓐4jjU969Aڿ899iwή/, .OݲeKlllh4 CU:thBBq̙}رi0;;;''GCme[!mPPYYY{֢&U9:o޼a#uJ lݺ 9;utȑfJ%/ԩsr≴F-]T3qDD*U! 2iH(P̝;̙3Y[;w9rDIVeiυZ[[+uXYYY[[W>}DDD]VtҬ ?~|DDHWXVY@Jݾ}\pd"Nӧu>ZYY^e"Iф{xx8::V>}5J=/_NMM-+AYT*ѣsrrN2jvj4~[&`ڔN:*-{QVرcϞ=SL )[jOH]3g;v.,==]Hn,Ieط~[+88XY+.MP~QӧϨQ4~5kرC*88XI77 .x{{S7oNt+Wmjsoθi(aC~fD##I5, z~DZ읎Y3d.BĤ 2xܘ~LpŲwKߩQwWG-[yl"K_4/~fЄqc}}pї&M^{{ys^wYvrE_#YXXT۷o?l=,,,MLJB=z)*S>;G##cvprBBj4saa2uY*>???U*RXTLlU޹srJY+??O(Kjk۷3;w6dc_)(WbRr 2YYP!FS>>ԏ?Ժu|oݺ{JYtlj7ƍyQ^A<h :l'v ;}kyΩWKKK\]ٵH.+p;ƍ}y℗'NB&_1ẏޘ.BkyΰQ#_Wi41)99%UEY`W;ĤUygVsNZ`.Ժ^=zTNyQEÆ 7E!W|!ȢP#.FF>puУl[ktFYodQE>/.WX(7t!ФIS3&&&:E#@rBCY04(40@Y0onpп;:w0t!, u%&%zBYodQ@hTȢ0ᄋF, 7(@Ȣ0Ĥ-Z,~kk'F.K1t yQ{7.b\94cgX.J=9ڀ~EWjz, G7ޘ6Ѽ={~ɑ4O?1˱&H_=&oNchiS_GeXپM:-v3˞R9$GR9-Nԯ߳F|` Y}Vܿ_8Z{Ǧ*UwmfcT2/*IM0-2ܷ/.S;~*U9so{`^%, ?]B6W$1 ںu3+g\lo@y^<(P/U0QeFуhֆEf];:)3sʼwk\ BȇN8پM.Y{VV+Ue^z{roX.ە%raIG%)I^trfXZ ^<տj9۷)sEWnj*O3;i;j_{%i < ;(P_=2hrwj3;={{ܯw !Z~QYG>s:ml!DϘulI[i"[ |&OFn 5ANtp H_Wd2 Wq"ll=++u֫#c'~>sFs_drq c;{N !b45o!bܮ}ֆewjWv&%4Eod̫E/h8K\Тӏf5aԭs]{L]\}{ͺtULPuŸ@B&MCkʦ)v7;dܒGnbf66fݽ,={Vyw?\(YY?Zoi4?Ӟe3YY{nXF.V9]p{ݛ7=,?lߦwX۱Y E's{ rPuˉSe(0c$av]\mKYQtkR]^b=f66ll?Y,uX|Lv5Uɥͬ?YodccR_jS}[ėSgZ=^+\he]`2,7W{{svlfcc1isf+?SVv M ]P'便U;wJoTvFPro+INQLEȅvy)P/44}]*C܅z}3op0)B;Þ38=.} ǮBҢ~w*䔼dBs_AӲؒVV\Som]DYGS_S7uXW!'/߯3v#Kc?omum;*_[(bX̋O-Փ "f"Թ׋uVCsZtڪtZCo=[͛VViQiy6Nəii[glֳ;(oTy/P?vp0B LKNXS t~jF:Mnkde%W04u:Fޝ;?^έ"5*UN]O6*h_ !<=gϚ!xg##; ףO9ԨL0s{nFFBQ_\dfY;gg&d=tnq'BTdT[nMfˇi&#'J)ޥY")vllswj_#S#b$;-;3!ӥ*EDPt(*a@LK-ɿ/7@w)&m,Cф'Td~63Ǯ'q7̒yU+жmEף+ ?Ů|ηvFBfNid^y=3lir-{SώA>HN[7Y`/FoZ\UqT[&BH>}ן͜>y1cMOK%~G 9,ʕ~^YdQ'JtN{vT'9*Y(##zN'e&dn4d 8<_Ks s麴4`lz&.xLq=~ܗd0ۿ|m3euQJJm;**ݪiE57:xIz*T.;>QUN]yQgE?T|:-mlJrmTw9U(*6[TX\''K[bU˵W/W_E. Bϝz8tq%^i'^5R?|*2?ۿw#P]K!\oZ?gx{cCTièQ]Zjot"pЩKG^2u+kXGIw];tqha2|bDtwGѹCpпk5yQ{ʎ;r}GF=e#F9CynИ1.\WiRe,C:Tnd#PA=j oƥ?N=;undjdbn*_Mem>!xRh̄L{; ğ?) 9ձg'5tۋ?":r?Y쏣V(Ŝ\B 痩bdjTZTX KJs%KAx(z~Q@EQGȷ(/tvm-gs7sFCqc?ݯ"7$_ahOVmfR?tޣ#̪[yQDLM̋`^PS`Xb^FYodQК41t_PdQO-p4iE@=;]kbbUzż(@Ȣ}#, 7.@agSw=3gpz\A}S',RRP@OllC?8333?\QbRrK, zF}nIލ{t2rB X>woٳ~Kl6yS:W]RCVy@oȢ7ҢԹyw2hSd/vBܾvK^u~K 8<_/>w[~=%r*Reavr*Ll*\̣ !RRnm~sC]k(ە+u!CۿY ȢOVz$f<u  !/.詐S'6@~x>Ud[Ѡ#BNɄ, ^3Kν]vqoY+m['[Ƽ(=/,)jMN˻S7uXW!lZ)kXZT*2UvKB5@߼YgʽGU50@WZZ?&''߹se˖}12omR\\l%P-{!Bn=k7Rkv@Tkf$%%Nضs"֭[999gjgg'?Fh 6ywt`ZШ%''Q!Dvvvbb5@6%B=q%PQ TVAA۵'Kɢ p5EEE1ϋ, 7(@Ȣ}#, 7(@Ȣ}32t@m[րOtֵw;rllC?8DW?/PmVX:cKZtޫ(Ңy7ll33c!wi:WMjY V|M&39ӢE'ɸ?Qu8J#ͻ'93dPy (̒y=Wލ(S!NUυFI@]j#.}% }#, 7(@Ȣ}#, 7(@Ȣ}#o_YN?ѩK[׶Tȩr q 4[glIN[@¼(Ңy7d&g>a&+%@UDü(wz3>o;z9)Sx 1/ +xLy4r:xIiXڿ|ݡ8u+ [F˩Sm,ϗSRY YGCktFYodQEFwqUUǗEA@QnܚTK4-5d^KidLDMK׌a)ffZ^ Y9Z^xDQ+@A94}g<x., PY6(@mdQȢEj0w;wwffs] {G@C2s0w!j9Ej#F, PY6(@m_ I9>>ޙY.@EА̜=%, PY6(@mdQȢEj#F, PY30pْS"~d *(@mdQ(pjjaQ+Q@A la̬ls@%dQ_iYAhRȢE`N=_8!77܅P @v855r$sW@mF, PY6(@mdQȢEj#F, PY6(@mdQȢEj#F, PY6(@mdQȢEj#faа9u0w Ej#F, PY6(@mdQx(@Ӓ>sTǸ(@S.n`\6(@mdQȢEj#F, PY6(@mdQ=9vQ#] 'X4l{{~cii7&ks-.#uC@ؑ7Hّ[ame'PO1GuÏX[Y7nZY.؉gSEXXHNdgͽc!DsS'ӃUNH}:'7ra4sxӦLRܑ{wր /{zȱct.~j`y yJ!ϮZrb٪5zH^_~ +128e{jb\u!t !f؄ݺoZ׿LN<9xhN[/X[Y8;8;Ɂv%n0;??`2g1L{{O\s=qc"_Rޘڣ]~Yq(HAE~nY]Oq}Cu8U =rȱcj{ onA9gf={oFޱ>ѿJWUt JJڏ={N9vLuqSCi#OzVǽ)̀jȢK,/--3^UɁy )NE8;:߯y<䀡ϼ=Lx9-j ,^6UfN-kF6,uׄғP{ޮ7{LȢK{?½cGO?~!D.ӣ'kB[]Џ egW !JK:|ayd2hܕ'/w2ya0y:'n5`PY]Sk+ Pq !QǎԿQ X/ :tAno5rh'B%Wc*79vܳ>^no9+{Z/je$,PeUi̜lax`k_p@;~]IG #&M_#Ҳ)_׏XU:٦de(ʜ`ގ1쎤r#={ꊟ`]sϞPMzU5jS?\ZZqW=1iJu/)f:!.Z Ҙ$-t.69DէBokb:q`~LGȢ}{Zv P pQs[[sT YĕǪ22=9D\Zsl~\ EHTexSW_ 1. ?"#XG>, PY6(@mdQȢEP+殢}-dQJvve*껝vmUYr3`h|z +s#dQԊC}volppQe[6.GjؾҤk׮z¢mv[XvloZ, : l]9Ej#FP+gϘqaqQȢEj#F, PY6(@mdQȢEj#F, P lj޽qyyGKNMS=<1x96{֭߰=_8!?sl@G@Jd''Zضսde-.(굧urr 1NC2a|(3+;vP`lzP9ڻWв%eWJ^!+Q<طwo++̬+V(Ixsc+CcFxoC>>+陙%/gt~= H/Р^Nf%-_pj~B8995K~k~]ӷqJ׌VVVOvRsrϖ8 Btt'Ockk;Ogھ09x) rphcccSRRq)??++?}P^^۹<+tH2-*#'NB8;o.((k]޻?[G=?.33ꔩ23t:ݪ/ ]U_tY%_30YlY!Cd^]666m 'ٴm]֭Zwvu;w)g-Z^*ii:_/{{y:T /4oʪmk q'OZYZZxm쭢S m˗haaݲm-,,,[4(زe-,-eةuK;{SepQҲ?_oaaخ}V"ƍ7VqNZ/+]E@z4-BF^*)>}ZԣiE^.NB.NW^-T|_XdS9 46՞巒RN\]+**~ܳW9%²BOz46_ CEEEnnn1Yo8͝"hSw3pKd{V;6:T3?Yys^J[߱O5hii9f}S^ǯOjNtڵk/L:Ol۾ |5expKG7___Vk^ZѴ_pbRQQyt'~uU٘}8%'NV|f)+_orϞo9yUV-pM;~B}Zf΅dewNNNzR>6tVS igw\/vxq Be휙_ndАfSG:8G9v޽{켽gaqkf.st:#Wfef(8 j/P4zj.Ȣ@]C?(k^ 4YdQl~P3, PY6(@mdQȢEj#F@׽[}ɻf͘.X}ɻwzϽվȈ?M Ym7mۼzޭkW٧U@baUi{+z;]ހqQw6o:[ƹ2fLߗ뽷 B =3kS_ f*/xr~r~on={gY'FFU:ٶyҕҹ|~ΤdYk`X,[yb;1x:TC@6kuxwl/$=:o|Ϝyg23|xT.t+Ѐm۴y\80,42blyyE|*4&:8vHڱ<.ZX|,c3_|]nBYƣ!(BݻoA1~#eFLhbNj|}85LJB|ekM>&{eg}BoٙlŪ)^B=vxzz͵ }rR;;y.c WWp%lja773ޘ##,;׬__FnXu/ޞ5 7dQkfLW&&ӚBA<>}JzxKRUUt"EF:qT~kۦM!)_$S~?|<=cg}C*HNN&hXȢ>|$#iL^!0ޓ{m۴:}h\1?ʯ۶}+4Aq./##z{zh0>%1zp!ĿyKZMh4Ȣh"#u~P]Rn$him1N+FMv͖rEs꥜Sdk.ɠY3{yx][4}d>l*ak>_&LJNٿʶLcw&Zw$'3r?#QNmղչ2GҎ=3JUՁ}~uIIiTI&=v|)K-l۾cs>i~())=yꔛxeOU׃u=|3G1?^j'ş.9 hКt:s .Z Ҙ{ Ό{-m[ۍfL'Lb0H80,t޻|R`֌r_%FFY0J{שGVrNs3GPI>sFTӷϥ?2_Qy0GP2XEO z*Mst h]c.u, PY6(@mdQȢEj#F, PY6(@m.Mו.y0. PY6(@mdQȢEj#F, PY6(@mdQȢEj#F, PY6(@mdQȢY@mcFxoCqAA2Z->^DMvrrRZ23fΞr=_8!)yu_Sxyz|HJg/XX'LһW^^٧(3N_zw!6W;Ll̬gz ZdW{u:q*٧6jdQ4EŖr<8uϱm['3ΝcgZ@+_(R'>5zJ?  {tk,??GΝ'P2WWPq S;vY0Lb.mo=۷Oo9V|ϗࠗG'IMϿ0O+햖М2g`2 !ç1rq`SwcB̬)Sbor :rv|̦, &D.,|%jGnj;w{řBaB J48udSkCmllv'%ݽRQdZx7'}|e5"#k1vQ_?Uɿ׌䯪,3XX7nC6L Ca!q( 8.rr኷[_Z*鿥&oSI@#&_"g65~k sty>jb?(,,}])4+:%.V{Y޺ 9&([?:7G'8?<̾\?svp/ӓuSib~J\2Vin! jB8 VJ&F)]5h$ttk_B>ZLEQyжwVs_61 @'hoojZ|gRwu) j,=nXbL7IB9kIodKK|QavUG174`jftTW ]xSW\[!nd/fmy}mN_ڹuy +7?4 qEwoinIz]p2e։ ynr5+a~'y 5)2%N3MXƓG3/E@)1|AJw('/5%Ns&~McX/*̻=Qh،c:6YXh>o?Uwieܤ#"4B{FmnխM05#jҘwMOM.u<;!a7צj)qn7!m Z^'ůL9M57-YMӌ/J7Nc̈́uKGf=;_T#K<ѳɡKoOXt[޺ |K7q=)1`!DxUwHEz!BI]-۷:Y-x@O[$pBЀ[Yć۶52!Ƶ7nJ‚4!57[cd<UCCӵy 5ʆ&e&D!scN"jn۶2RHεuQ&ۻs7ƴS'efE)~K7Nh0|gԾWB,{yp~׻O_1gStxJJ'. M+׮^i*G.;\ʼhoѬY3''K۾۹m6mZ2whBF=?%_* ͹~EW*~w͛VCݜIENDB`flask-openapi3-4.1.0/docs/assets/Snipaste_2023-06-02_11-06-59.png000066400000000000000000000715371475153525300231740ustar00rootroot00000000000000PNG  IHDR " pHYs+tEXtSoftwareSnipaste] IDATx{tUkB=`ȉ(tS&FeKA6(on F-6,E2 lyr(llcPIPQ9Ԛ\d%$!k%~1לb7iWjapWdQȢEf#:֎?;Py*IUT~qyQ@z~a(Gw/q@t3?z=E꣦ITe"@G RϦ\RSScfQnjmC-=Ef#F, 0Y`6(ldQȢEf ٝ>m$)OWpҿ]$QLT]2jV^'9eѪU"I߿JhuνPisy"26%:Nto|eڤKgO7 y?M1JNw_7%@ѕhE#z>?R'>~8+jښ̛tR$''^P]#IA}KpVŜ??_~.#Ek*tMNm( 7f΂[8{V@ɥzǞ?{hޓxW9ֳ5eǸ,ɹm߽c}F?ܟ r{_nlz`o8TmO2䭴Q͔Z-*r+ޕ$}o -0,fq{t/{=_uQI~™Cf-^[Tsݬ3]i_ *Rɡe唴 ZRR())ϟo&FuA٢//ܫ<7 >.eşSP7?^zq~zKsS} _Kr^D%edtkڄ//u7TQ =%ɭh=g˻;$w͢~m;HïZ彠gÛf)~6$qȷ=?Ւ r)G>Խee>P.fx?WtHcc:sG7h^M;N=_psҞQI}F=3ojtF$ oUSe+1nAs;ٲ\58mu;u^/>ulFh Ǣ"fǗg?ܬi#JݱeϾ?wvk!hfZU6.=S_:nֲ*;OMՅf߹l5t8jEX)?@v8hpEQh>^~b -QGիW/^؜:\f M_|9/TKk{[wO>=` {|ќ1Qݥ_tօo$EK5R, Xxqwvj=S.ŕt>y^c۷&is߶9^+Zҥ?n]6`co>v?͢E~'Z{ y-U2w_aS %Uyqα]ujN}z؟|#Ĉݵwstͥ/)i|Է'\ӡan/S,xΘ[wO?-Z_DH咤KKώ#4y?jUEx g ޵1^:K~Deϗ.*vzCgTnSByRj_$_ >n|G+WV^G*1>ؿƷtY:GWRg%($+=zg_yA?r{O'y z䥧ܮȲ_ vLj/]an7 Gs_kN^װ> sXjj֍N5.?]Qۺmoϟ.:<{nYptI"nзwp@ck/D]w]zV;UхJ]چN([kS]#F, 0Y`6(ldQȢEf#F, 0Y`6(lWr£UbFs#KMM9wtcVkozt%5448 ,hmplF0bX,V`MۙEY___WWW_____o)tFZ2eYob8Uk&quLȢYG˭/~MwvUAQS/BMFn[Ӧut9/jv\,z;]sWnŢ'{]ئT>sɢqjԵ:&F];2mmv]#hn઀Z[EM]'^Y2ӂ^/ڜ _~eqU@Шk=2 ׅ@YtֱC5&qڡG:|ˎ?Q_:PgݚHЁ:14B, 0Y`6(ldQ}]@#Co0:cחgVͫp|čQ{D W̪^w՞|uHrt̽M=gy7o jhkMi~[ *&}OƬ86KM.j?f[ v6/jw̶T7uL/ =F:Sv?* ! ]:PrOV-^8d썭}sz_I7N6UvtYw}NHjS=vG[8tYAtw31ޟUϋJںws^^?Ʃ#f_^Ve';u 2&;x|V\-jCzATomo~:/ZHn:@Zkȟ~?:IUZڻo쟝N{݄zcw:|w&ffLOc~F%= j0_͎뼨}nt>z⠱Q {K:wYåzط׍Nd3-V\vt7-Q͎鼨7?_=(x~ {F{tU nJsi'/ӯiE<1SQ_͎뼨7|TN~˟}l_Yw߹ gJ/䗿(m<ٱyxKsϑngihhh98а>s95RSSsl6[mmmuuurŖJ>ƕcMw^s- 9?zkoZr:2 G7zhD !ZeƚWk5^|{dyMۜ5ھ- ׍W5svpЍ-Nrս9t#fΒY9msuQ4y[3g{.}3O,ݺ/o뾼H7MN$)jFa=_dѢ揋>Όbyn㗩}yߞ%zcIECkNm}1^Ckm)}y[F`Ӗ"WGq\+>RiؙOU a?itߑfYԕU{Yt{rejqdBF{Hw=<#\RHdTb?|rˌ-Ӿ(hϿPEYd$vɑ3aR=wi,D p3v𖢓eeex1:eY/E3ê,e@lv}ܹ 9wI%k{&xc%ߔ48v&䖷 -cb(Q?0m^#*6lN(/ 0X:al#mc肗/+"1̘6JմU2#~K8pyھd+8ZvY#oŸHE*Y5Ү/EctCŰTȌiSd_ٱ}Qb\Uu/㟗y}+СOuJw4j4R2>hεBfﭔ4eJQCһPr==%XƯhnM֏l^LZ/ dtھ60yQ她}Iۚh)>"&G{44u9D``wm.UNVO|-F[)D``[nI>m]%_,j _յY, %tɢfd'hȢWȈ.DE$,zQEJWLP(]V@wB͌.UpWLOS1/ 0Y`6(l^bb-L]]m˯sMi}Kŗ}]bb5:Wv%Q|jZ{͋Z,'E`. D&;., 0Y`6(ldQȢEf#F, 0Y`6(ldQȢEfq=Jr9{KJt6=ng$jh *9:& u?rovQY8~b^vl9Û9.a׆$i"Վʝ Ƥ.0ew-%2iY27t3&(zՓK*w,b$e*1Ȣ8'@zu#] S))#71TFޛ{UAS/#.GdyL(6+v6ge{;5_zRcdKE۵fOeZLKXV6-HŜYQ2uI9)zt%)tS Ɲrcb✤肷ud==*nRRtYJI >*Vŕ3OvjfX!I7V|'NPa'`+wn*4~ӈI;,LQM"fb^T"&=;GLv(ѐ1#GDiijhxanݳbD p?nhr*;oBh3:Q>nii@ e =mMuv꾼+vfG^ֽ{xJ5Ypա4oܜs+@W'=NR79TYQxu鰣G5aNpaG.;nM{+*+wwV]ʢFnbg'NٜuűӼ+[ކ}Un~tna^˞6^{9#zt<{%Br䙱\Ȗ^YŪ^q\+E޶rQީ[1酵Zx2ھ73+NLoZGZ8=6}Q^ͷ֬Oe$gL,pml.qs$U]lf¢Ypbrc f,555m=fVWW'Wlh'TvG<_hN IDATod?ػw ]SV\ܘDڍ, 0=/Nqsrw@0/ 0Y`6(ldQȢEf#F, 0Y`6(ldQȢEf#F, 0Y`6(lWtv] "]B+f5^QnC=3MڟE`R`Y`6(ldQȢEf u_3^qaٜۀX,VsEf544l>|܏?sMM+1#[`ZD%A .%Ÿ~? /,:tHdQY7o;%?k}]Ņ_6o_zuEEw6>. kK~Hҟ|xϑEfݹQ9W׵ Yni]'%7 enupMx^UmcٺĂdQXM].@CR*O%aܡ)^.|+tCSB6?ݾd!?(v{YZ$Iloknk85:a 9$YRN[EYTtE7rޞMwj/  4*cOj6-_=X_i^$U,xR70Òf&s!6'{zdگ [v3smt⛙SJWLG&I۟s~^[4_[UζG-x ս^pooO>۷}R yȨF{ WOgRkU)UvJnsTyK39e*xiJ$S77K+rR':䶻,7^*_Uk.b/l?yDe܀)s~{"nRz4 I_275y^? A,)sRhp:U^pz*ATҎg>vdYMyUxuɌwC^p=e7JQQ1Amذ{ٰaC111vUU&,ZX'IIF[y0o=.9hyr $yHßîۗ8U6]X1!upk;&&^CEcc[Qiwޖ`{hِ6ckXC#~M}`㚷?@6-sS)RqAW,=5uHqmb])oϦţF'6Q}c$ݿqHI I|qMo>:P*R%)65i<Ò4ިmSxIڱ- k}ҾHIcv3Jr{IO{uw-4iٳqtÆ g~SSS}[[ݢ{! 6m)r_=mR$9weoIżXj33^+"Q_ȵ>3㵒-)zzⰜ oI/M_rP|cRA<HQsg6_ #5Icl~ ];>ص/x^I:w?dFJ;pmS ܯ;ZMEqIщaӼڶRb3zTQ[`:o<۶֯_/iG#;1?9#UHFL>>uYQVf?#"'>rW\^Q$IOo -*=[{^3+Ic')v]tD-V>:a`I IeOJc^"0ӑLnQ/ר ׼h+4Y֨?_,E¼!/j0}c?ٳ{Hdaܗr`;PHr/ZD%UI{zcն}* AwObzȳ݀GD4Gc:_h55555'QI$)>0e}G?X~ʴn¦enr7X#,t}tBۏxTK8:*c=+i*sB?2cڔVl)צ{=I^ʛpWhg8;4z*py5/ڳٟ xe^+ C/"wϝ{hn"Q.ͨv-Y.2t,4}n3z %]xׅ-1~E_., xgXV뭷"Q /QҭbZ-+.N}C{.hIPPट5RwV5((趡>rm?BݏioO6֠ =wԧOAQ}]}E,K@W^ӧOPP+j(h^V:/Vk```PP1)@挣\OF4ZsDYh'_t+]V@7C, 0Y`6(ldQN"3uug .$մד^E-@^]jDu%0(IQ,EenNdbc"ȢEf#F, 0Y`6(ldQȢEf l9(85H{$ Cgq%yQϋjkjZ8fVWW'Wlh0MWu `^`6(ldQȢEf#F, 0Y`6(ldQȢEf#F-Ї>JrF~S׬Q0/`L M]?1$9/=;*5Sk}[Qe͊k5eTVS+}Wԕ:d;׬YkaJB8zYu ˖ܴvr=#fj]~ :&E4mNu͋]Vp yr:'&msi\mMS3YEYơmsդɫ$E*zݗ-v~dR1r nQ~-֌0MYaeNjْlνSw䌒kv;/l/Rka6%e=G/g5Έ:wh&"9j|}J-nRR*o,ZYQ!y>PQyBbĤޗՉMw'I;7*?:xێ#m,)?3abr䄗nǬ]ŮO:$cμs/{ʝ $͙}Q 3ÌJu|dRMJg|x"zNbP䙱2#%[=fQgpjFh -s 2&&/ipׇ73ōfRTc^q=JJ ȨRPeEΖW*XAQ3:ZFTjo;ݝeXJ?"Tŕ,8P6dWٹw#az6~^e&V1SnFڝ5Ⱥe {U)bғI]n9Í=^bK ^`۫1kQl¦gۿqʷ#c l΋N47'wwVxWV>>S#BfAj{n6s*ecrcѩız8W~epSt3_՞u|&3 \ ȇ{݆;|xbd fʝ8+rԫ#Ky<6 þbx1tΕ5_glkƈ"OdYn{JˎZhTb;:u{c_Snkڣ;q 2*iowtkD..c"nnQ&&K2^2qnL4>5ki 3mW kܜEJXlzD O_WI:.F7ݻwPP.Z)+WlnW"*GveN#u{tw-Lvz$d߉hOe֊I.G`6(ldQȢEf#F, 0Y`6(ldQȢEf#F, 0Y`6(l>{y&,ʚr$Zbw*{c^`6΋ Ix!1ULY#ٯk5>zFO]zRI:~b^E&IkN\nl1lΒe^IȷoYھg|jS#:G݉_NO019acIwӮҐYqNqxW_egR-~y#J*hv.K^Ka^IvQIe)Kv;(r^T8yBvm;^[,EO};bYQGOܛᱧF8*Әա2%e&:>TՈ'RsWpYqΗsBոH(̦*+N@7Y#f-ݻ`o~$GlyN226.TR;dPPI#BF#;&2ɥT1DiiDxT˒\h3ѽ39K25$)#wwV y׳^T?>ޱ'?32eDG(.ۻl݈sKnG>ݶeD h41]\c]%>aeE=mvIWR'WWoL㵏"hmݼ&%EdKz+iÝôcшxLtĬENd?O9rcWO7LZ4us{O)tj^\I h2 mdq,sg#6:bKG;N|eI7]Oҙu!7`޽6\:OYrescϟoܾh7(l\Afsn%ά{]b:ϲhCCf;y[wZ_U9ACedZ(ɢF/.y ڏ~\ >[GQ|6/ZW[=gGmM?},^J4ŋgmC{svǂ'{uE&|vfKg{%?$/EOwWgI"?ža|EE}rkcL\, 0Y`6ӥY??鑾dN}fm7͋i92EATҎg>rtqdQҨ=l۳<%Zv%誺F3V{qZt$3r6obNq nRW{BYbfz$ }ta=C]P6ߔ<]'5ːV߷r}{`ʺUd?XщofN ?Kկȁ)^&tGe28~2;HVl|fmk\ż8-3q(m… ~{|I߾}蓪C-\1D%r).GJڱq{NGϋSU)wo1: qߩBbQ ✇qBZD-qG^~vظ0[ۃ_HDT9rԏ$)f(Vt8I9mdsӞS_ȩiw]_r6׍ ~zIg6>A ϋ6j484?XmmJs6it91+,?kdLY6ѣd| A݂yѮc?ٳ +>/ :K=xq3'5k8upJP"s\Rtx\k{t.ԬlX+hǡ=>T:^\x|ʴ{+l<6{{ܯ; W밤G+6}ܮf|r_{UܿD^[ޣk_Jpfmoޘo1R4gؤ|lHXcXܤa#[*];gvV{2rwqxG*`$7//H7ޣ+Mt,c[1ёBlŔܡ._87|Erym5;m񼨽`IN|ilGr>tYM Fб#Qs%^BAj=w6bjkk+d }w_<=j:=>x+z&=gYnlSM]t R3V4f{`.-k.uSźaZ2a{V'nK#MbTnv&_Ɍs微sȌoߜ2e/,\"~Bܨ~Yw]zSNgʼL`ʦW#"vTԶGm{V4tǙ<spSu][_ >,b)ǹ vwG:# o. YT;xty`zŌse lzOa^|[nqaDgC 1gEޕ=ac췶ѮU٘7JSшڽbE%s"R#"ڟ((ӝ=a( ^jЅ֬'F8Hㆊ7pEƁ =mi~|;١l;W.7xȢC/\SU^|wgTKR-M/Om)JgќZ֗DDgW^MK}ŅC,kʶgReVGG[DԴ7U͉874\q 2xNqI~]i}ɐEU۳;2rYU^18(fm^MOm~[U6v\wg氕Q u?(N_BkmXK}{cMKzhhH$okt#"fT.*k>1h޳fDĬˆ9y""?A\#GD^W1xyŅ ݭ?Xֈ [j;z sQm=[hY^Sѐ^Lw-]~ӌyŅ }3Zu7bVDԬ-W\}WK3;zEfrߑ($Yt#{2Kj۶~8Ҹ#!KjG垾R1'oV?6dζh()Ӽ94muyݙ]}ChiKV4v,Z{|Mk#xb̵xȢsUul]n}y ӛ3M'MgCME›f ΢ ,]׶{EtH+-xnD&^vYݻnAqM=&ץp,?zh\W28Oc`bEHx̢_J[֦Ru)Sr]Z-J k<΋0ɢ$M il}`W*oe=r"}TjT5g0c7՗g_>nH={.,z蕣p}?=\nC?y}U}E#"~ڷwз7Ke\+5ȾeCܷl: Ju峯_!̧_6>2 {-H_-Qq_loǗm[`]R7k5/j=w{|O戈3K.Yӏވʴ^c3#g{EGԒk7]|0"KK?ܯSӟW]~1x!UKJ|o{'=oɵGleՖFDTRK"<Ӈ^k""3W=KW|h3?G3X~>8%~ѓooNO-X^x ~һFw/>̊K~}ك~%DT{V&Nm:pI**Lۺ<聈<.4sM?knt{i?s~F Y X£O_JDl.~?2G<W~4!&EO/-Yr[m*U\03JWqabg|re`b8EPȢ$M il}`W*oe#[k[C<~/w}TjTѽ4k-+]㉯_ܐ^?zces+K[:g4.H.,z蕣.!#3_u+9 /ܿeˍ[ܸ.>"">N& 9?yݾqߟN|f=#|Tڕ5RK #"" /u^[8qCC[xjɵGlЪo׽?7~!V:K|olq[?V~.苍ߜwuL˫/~x;)sOT#G/`.,w݂t[:%6]>o5RK~{ I"bza<^\|h?/hj}K _f}]jw=zϡ+^Y_|O<{D|O1{}ˠtr[_8 oy!_ܭ%1=?eˏ/uV߽{ooXxWn㈈hqK[]_otWxgLEG9ψx}}ٲ9"?켯PDDg˺ֈR6_mzcsqtGFdv~G""WƏ#"z5"f o|!"_^s 7~ѓ=oɵGx}xK:fW[?֋_޲sq~x8Y4";H{FuriiDa񚈈>QG_x'T*o~I"Vλlv+|ߎF_#=u~1`l&Nm:pI**L'ێN/MDgզRy{KRTDƫ{"V/syU>8V~4rya_|㷮~n{{qWߛy˭/[ _;6i"aN8q7|k?s_θ/\#"r]pZgܷgu饗N:u1\ęB!4Yɢ$meѲRTj#x-E[-(.~smu)'-FDġWoݞ28?e`BYtoOcu2{׿RJFϔ\05RK΃q9/z{oW ΏqEes]x/Zq_m8u{s] xˢίqF M i(IEH, @dQ,:i9<;s\fѹ(& (d2}PjϦ]߮(ٞUѕjkZOZݷ )} #ktG/Zt}Ikm{k[{DAպ5t%"=-%wVEČaӏUհy%74t49}uٶΞW#F刈+fnYݝ 5 %}#">.4pM}+-upm܈#{Z#a]ر(H%cjY4H5"ܴ5",^yZlS _ZPqE4uy 7t ^[{GȶVƥ+3 O=sKcFf^88z9-\ICm{m(f`.97o-Z$":jvkrKj]tiFyUf?oq3oѣd[}13`}YF=N45m6FDDʁepD[6-<([[hұc:mZD8̉'?oV>Y絾c5'5 +YDr]/=V?K/tԩ'yrL](, @ųU~i8 p0/ @dQ&4Yɢ$M i(IEH, @dQ&4Yɢ$M i(IEH, @dQ&FDμM:]=QQaO:iPQYɢg>V+ZZ֗֊96%pZ5ӌʇ*>הc鼢`g#V4tGDDIM˲Т۳!o{tY5ݭtet:Y5֯ ?wGEm1\wgOM][DDIyV=\7R}{V4tesnNwpIמ.re3[l}QQ~5BّӍѸa3٫tg?i[ p~xM+)RR5~7IDATnU˪t74vDDhn*ɄTQzhʲ6eiayuyZZ PqSzؽM=o\P3xRR&vdܩiPw8{|Gz1 +j2WT9 {b]}SKyOWma{,68莪W;ڻjλtyǧKnCw"e'`K-?{O*|K:zejN7;,T p6rF3;W7 j+ȏ_*byMYm  (uuWW|Wޞ;zGtmhgNߑ(̗͋&SЩEy˚ZT]]/yHo1=[p*9];+0;F|_HO憨ھ,@5Juڴ8>q'N?~7߬}k}Nu ~p/Y rgu饗N:u1S_ Y.[h\q. i(IEH, @dQ&4Yɢ$M iSFl:m]{#"֜̋Ǐ{>'N8~oYd̋4Yɢ$M i(IEH, @dQ&4Yɢ$M i(IEH, @dQ&4Yɢ$M i(IEH, @dQ&4Yɢ$M i(IEH, @dQ&4Yɢ$M i(IEH, @dQ&4Yɢ$M i(IEH, @dQ&4Yɢ$M i(IEH, @dQ&4Yɢ$M i(IrV8G&Mݙgt ~a5JsgF4# 0A$<;ΉsX Ή c^΀, @dQ&4Yɢ$M i(IEH, @dQ&4Yɢ$M i(IEHڔq}{'sTw-"8E_yzDD|r]Qn3`vĐ%m߿8Oɍ鏯}Kwp jϴ7߶وkgt^pfEƒW|ʂagh<l\̋{]ለ.b/aAtOLш8˛^m\>(*ض [#fAg?}ʃѸ}k_k_qݳ"'__zL'7O@P ơ뮨iv(*V/zo3;J_#">Wdnt*`Na\͋F\s׷2g{GcܖuWh:̬d;W75ns7~'"??NA p "^d\4W|W{bݏ|3 ǟ>n\h~ku z_`Ne\dшU]Eġ'#W^1OzU̻yzNǿC?x[O}'"bVAAO5~EIK|qo?gYTә'$ ? j\(Xv;ܸu彟ڶa[I<7")<:o|EID|b5n;tx|f[w%V:o1><^G*c83/1;?"?]'W?u▘ſ?F*g\_ɾf_2rNz=lұcĉǏûo(k/:Ɛ @R?|SN2;˫+79?DߞU6 ii~|an/fy۳=eLPvWt =f-'n_WzޗdUiQXUpe^DDԑzԱ|k9=pF4?|w `,>ܲ0E;oYpkkgް`P}Ƈ?,݆D#"o!l}yq֯6 +#"jCKXs\eM-޾Έ̗E/:w;e܁2b$۳+"+꺶 'D[nɦљ7rA{7}9ֈ;Gљ?/gTȨ[ݰ|C87?Zw74v,Z[Ct쿯&Q_\PYumQVQUp~sEm{aUÕgUMCWAݝ#DY:j*JjZi??ag,]Y[BJVm2 K._:BPf޲`A~vCho1__椰#=x?27o*Άq+" *(~`&shŅі:8$FD{ݪ#7~G7նaHW]7~е{gJm^XS 7 Die3sN#"77OIь>v㖏 ;_СCS>x߻ɗ5#?/\̘W\}=1+bKfT.*k&댈h+i=F?#:#"ڟ7T]כ!9҈L˙glZ[q`I2QQLG+կO/֯~P!E%s""Zk*SޞMG2'ZȬ MOŜ_Ѷ'B#{{GgdSfn''ϧwwFޜ`| -ʏ5UeXTvw$*0aeҤI?s:n#aDLi!&\S%pbqEj#Fm杶ߺЙwTfwfɸ(`ԝy~w΄Mw>ϼdQj;i, EgDED.;kiؘ ѳEivB?'\ stj#F, P.M6" 1hPպ 0. P۰~z64E&_ܬtZ7Ej#F, PY6(@mdQȢEj#F, PY6(@mEX{˭|GI5-%56qZR+!]USi)Z>{& ͳ&ךQf\ 2巋_ckTT,~n^DD5MGIJQ]cFHZ]!?0?Wu#m]BS?Ȑfh%H,So+k1hsVRG kk\&{OjO?W6I\&kS/ I5yN,bkAク1Pimvԉ${m:}?wMnoFR|^OQ4;_7z?Zv$$Nk j6 瘬ւdf8*$9Þ-q-ܼRgLƄ-m͵\yN}v4xJJ\sY*sHġzQkZw#S^#"me{ZEyOSDbv "{jojIl;2DDE #Z~֕d [o9+^zi8+l{J5[KwO9KbqoTq:tUX."1'We^]kh >țG qemr$J I˔,eaRMD 7uDiEDDMbkAHE].S}{%34p-mZXm;Ջ47Np9\0W; Va7ӟb5deohmIN}t 8~IuOgg`o <Cbr;Eh}ִer͉1")ִkI>"cFZBߚ,ndZ'?C6 oOO7Z) `b""m@HϳoNh(JjEMfH%\)5U_ ߼PeN fDEň4Q(X;iP–o?MGIvEH\o0 k|aΊ`|aa*yrʇt)x e\-h.>^]kZwv]}꺌6G+fq m [JEߔ]ߤlkc|[EeKstysbr>-3͇5eԕ?W.?E#eyieCʤ׿MTmcbݖƶݖ{d뇦㢢<'huk 0 !2㷑 |&}X6FJCm|f²''̴u`%Qn{'<LIC6MDezzz^oWW׆c@.;D pfݠxn'~ʳfODDNF9Q4tػQ2n?z}/xkZՏS'_!{Oz!ִm?_sSJ[/xO|К<ߌeaE[wWquѠWOFu]><.w΋Z@x~<2H[lO{/`킗j :Oe3b܋MeB{;+7 g?z %*/息FD1\pɢdG^,hG.;Ef̱rPTYqY 3b."vI]3T,I8dQwMVnh-9q;LGCVr13)Vԉc2o\e0G7v}g\|{S~G#=k8<:JRJkfcH5ƴ,Lݵ!=];xmּ>3@C8.9S7{zGp{YdJPEwny rK83]r%kC 些ەeQQMM[/2/oXMѩں?ͳ, =:bѠr?:v^? %6[F!nf~3×Yhrd`h=g6)u&X,Ίj噢 1Y+Lj6X,eN4ʻzcۡ?y q?Yۚ&"#xR['.ι_:d\j~"09㞟ja埛oDЎwͳ-}4Xtaz`o, -/g2sne3n6.IC6MDezzz^oWW׆c@puND27u.~ʳfODDNtgg``!j]kMK簫j} Ӹ(=_nʧ`.Zѵ\8ҺB1WQU|=ʥD\U]+z!F-AF ,1.%cb u6$kqQMLfcEkOgciRS@-""0 "Zl-hQ-ׇHZ[g~4Jꬎ7}8JRܼܜo\Y2\U+D.\%GIJSQF#v͵k}y)b(٪N) *-0flM. EOHLJKHGuwj}|P&2#6po[eV:OxY¤$X5; stTZ,OTKZVm4n ԐWb%ٮ4`GIJ~?CbSJZ[gXu(i2U׮%V.S1W5y͵v/M? K{U\]Wd+k~ &ȢZK0YU7Ҡxۜ jldFΕ~Y4aU>?;aYVE^us-n&֛2c^tQ'u)S;nHqf-*Ik}̖ɵ:ED$%R "7'ƔF1̨WտJ`hK+0=TTm=˨>@x jΔck#( d+++Z{krUXoN59lwdxvT$kڠ̼Jz{i3#y\kږ {i/r2KCEduNHkSt;Dh=SoeNd(3xƚpiG I4E]Ub zI0Yվbρz̰8YDIR ".kAU{i/91&k"JEQSѧn-27*c\4e;hHn+-Z*""yYyݸ KR_Ui16e^RFQLfcEAJTTА6V>)A^9ED,uJ?kyawvq=^$>#1mK; SWde5HbVf+\^ߞ:ODLfOKia.&uwwn4z]]]" /m1Ƃ7B/nЉOZ7_?Y'""BMOn0/Lݷ.[[oO|nf!M;\>G MMot,^B E8s폏2 f]'N}'^z*+3gҍ7i;?eioVUVI{8]%o!"o3"z{ɫ,"DξtZ)"?<>F YT.]S!")]O/]4.]zE 0tQ;FYTmj:[LOO0åc<~, PY6(@mdщzdvї-'[6TvBŎN<FTWQ3 oL@dQ\̱}8~e?.2m \/[kz_v#(՘uzNt =`Z~ݵ7ID,ytFvo%J.8^׺Y Y49^xW1y5G~+Nͻ5oXF,Ϋˑ\pD$9XgGXWn4m A=ʏ~h}s{Z ?q@~~U/"YG]b𶝱WՈ])W3` Z׷sرc_җ5iՀ~Gyף2Pǽ8~oPs:}ul]7З  -|t{Wn18}4KYfʜ G{~&D{JG= 99g^Q)x妠ګnٝU'xf3!?k 7i{ro8%Ѷ-L~CdJCL!wpΝ;ン;w9h2~_kժB;9"RS^}Fܕ?GW?kr67<Z# rG-ySDdNAс]U}MN?J;ۋEDj~49bSQ?= ,˷絛S{{?q<&[I]9""׺UDdYw:ZP[9Z犈D?#rNݮ](A}_|Ǜ52piI^ӵw6mzl6[f}Ν6mժU999ڶ-zOmMO} o1Z7Z2X pWKN3/;-KD䀣ol]ޮu搣/+1ZW~gua~f.obqtwvʕT(1ݵ(Cp7~䟇fݰ6JfQxO+bӕb6ʑ~'pBoj;L3mbk8"NWHtH4[ G-qǓ̶kMl6r JǓ#OXz?G7/)8RsH#?c>Co/̤ m6}X}8x-fHLA&~]aglZx~+J?G$\al6>1yyM9&CN)1j8-8`H%dAcϼG?egQh:pG̶>[o߮KNG\ݵ&^{mƌ"kݖfGE:d=r=cqgGMo:Og'FC\ l4kXN?];Rߥ-ʕZCo{f[p`hdѰw8D7O7Чϖ?!3WSo6zEե_r+GV\qYȣ\)2}5ךp?54uP\#"Fݖ9Rn4DɷA~1ۚFqMHE嘔U";Pw.HGK+*mbH q"|{8stlkf]#G6+[ yF>ڙ6~]!֨IsW?1 UD9>E=;mG\Y`jB83veXe]Cɑ:/0mU١j4D:}HA5]|7>ʮ|;_s&uwwn4z]]]" /m;.]ǖwUu}4hsN)5<Ͼ("'x㗦NL ?Wc޾S ʷ6+:,z0{8F׬t"r Oy쉈t'xrD]hnux$;'$`z!"-rWym#zԩSɟNm@zKx(c\Tm'OM-0~qE6ُ:Mh=kI7u;Vzxllo}*~?Gbcch6QSu0vo|g/_?׺Q@IL2}􈈈 TϛwmQAUn'Oxҥ `SNMMF^wu=7_?gaI5W&.6q{8dQm(=SLѺ!`hlPY6(@mdщn5(uJѩ (!jE^+=uk@dQ|S}{nZEߍݠ)7Δ>D&*V~8{aB>W4v[:Dȇ{wX;|ogJ*?axUH{k?K}eJ_Txzz{w3 oϼCN5A* _pUJbn*l"r~?\; E?VU9jSe""tU1ғDD8oέ-3]CU2zMoJC#"rܙ9ä~ 92𙬕EΔ>Oz*vjsN =SБѺ{)=`ZӢ5q+]y_~u#S{ìCNLOKO!x)-,q»ϔ>mJJp-pԷGZᅪZ0.fBK^TAȼ*%LN{O^Vξb=""= l?|Jn)"1we鶷$>b W|hg|EJLJmus"OosS Enpt79V}sHr c\T#wܟ7䨉Y{m2UnM𐩜ix\ ~_~zXAtP!+lEaBt鸦Jj%[Ql;cojFT郥EsK#ޒS}&#"~cХ=(}9yɁ4$ =GC3'DJn?>3;<>Z `l1.[=wSM~s.=F$VXfݠxn'~ʳfODDNOe]DĘQ5=RՋ}b0$iojvMNcY ژPRkWgGyDV5񾯕QG,~n7:Ϡ R6oK(IkKڕz|2ւl\+[kR 0"#%OssH\I*ӔQJ6B㮪m{7ì:;-% ,qQm5XRi)ZE.#NlOŲv1fD\g/TyѮqwn 慔 Xo9eogOlipEb @F4(HcAn72 {*z?H9ػ U>%j}ʶmW"XzE 'gƈ4dWIL:edS7'8ȘQRkޘ("mNZ{*2}2 "kյYFwhӛcD[ fS11dP|Q6kGquS\XT؂/6>kCQ^]k/ WGIkZM&[b}QS>%EkwnqRfK$`1GW[}F._f)+e; tv>RD 86!JD zH[PA/q"")C"t#6qԉX2L>et4Z?rISSQV@v75[6围V)2!!)쨓ƺoP,.$n3$,9S]1/j^v{B?׈,^<{|SjJv[x*_ l]2vx.^u"b4DzgZlhkpHHb|rPejHAݷ #X&ڏVkO̷vd6oKhhiޖV1stX/j]_2q",ve81-eATDRJbK4D2C$g-^iu "Ix%^Wۗ'" ELiwX5Wl͉H=US$, ֻa-R\bHKwM@#kC J+ڔyX[kQ7fd%RKi>ފH|^q9",feR;fde&px 7"ԭf1$-ڗ_c&ێ dŰM|_āGfRwwoMohյ)06WLr\7u+*uNDպ!)Ϛ=:n\Ej#>׭<{u֍!&Y0.]'p v]w5ujo%Fә3gMyG}GK.=ڂ.\p:dQh:qĀ_E?x`)Y0.]4>,5Y6(@mdQȢEj#F, PYZ7`:umW/[[ݟ}u[@,7Av^L>=~ωӺ-26Nh[GP`tY8J Ʈ_DQg~u+pedQm8np}f{\ jAQ`p=.Ej#F,noiii)_uC`|3O`V[cqsݦu`LEj#gٿӺ0ȢaRdVu,6,X[rn -hxi;{AnRc, PY6hxxYtKKcf @H9Ej#F,:)oi_hYZea,ܲK-[ZFt-u=%'1Gׯ)99~|-ug\{}F YtMB٫n_yF%ר`8u/uǔra̙3/ش$HnY˪""r[ED֔d*([r3ED"6-y|p%u]BDe|%/O-\fu#ՐC9}/Jy}~ܒ\gCi|i)&"핛Fԉ/KEdٽtljW|`ӂ}[޲jmK+ό dK<悕qk5-y}_+_5Wɟ-lI~,=k#/QDC9}/sKU#JDֿ$RK#n ąm""m""bYxL閖UPis_IOy^$vUKKKYnou-----ATQc xA/Y}}A\)8am3.(X @#^׎oD/Yds y\d*F-W.X섯mGO(eU; v 2j_Ί̽l,t_ l»5S3_pMdi?;>|3ЗOh z?t;9A~ eIebE[[37}eӋjiyJķSQnyd 'O';m"ugO'="'\H{|ZV=̭]bUZcDqqvKIC6MDezzz^oWW׆c{n䖵ٷ?tvGTKk,׺!C6k݄q)~ʳfODDN/٧u@ , PY6(@mdQȢEj#F, PY6(@mdQȢEj#F, PY6(@mdQmL>]&'n6np}f{\ j`M>k \YTsb㊶j)0*Mn랢mΉӺ-Z7`g nڹA'"6k, PY6(@mdQȢEj#F, PY6(@mdQڴ΢)4* o4;y[ZCfeGɃִMC̺SukOmu׼laWgI-G]U+յꍉu[k\AgZӶ4jf\AG?8̏IEA\vUɷmէn-L5^lnrhKZkKeYf(IyI7'ibĉs#;G;N5o7t#()'?{]~zܮ2_͉1mVmw#寍N8g{u|dzVXRV~X@,=Y_{ +=h^w{=WzQD("gִ9/=RemAD\gb4n|y\x՛y eη6ٞ+?L[QS'ɼsE|۹skqMWOZlG ~CL,(I+u&n  F2)e<%Zbhe[^<6~k^8Xqy_"???_>"S*?u5S]^zwQ[Tg(]r\oCMA[E}/KnV~vylStO,KbBX4h܏Nϟ;_~͖yeE[ u紻Liu91YX#"#a{J+MEc?c!vŏ.v-{rCcO&SE6ֈyAW?⥚w^vq4t h/Ͷ,~n^ mtJw9q{Cƥت'3ɓZꣶ)ƹI+^~O8~tgg[dQkri)ZvUO!UΑEgt+sUٟ#X]U`PYTK)  Du&6lhm^ "|,M^j hޖQDDbvDDZ 1Y;;X-:$j+HiYo4%GS%){nQhnηu.,EJ qm""bh%``\T[Ԍĺ*L Al]U+ %Z{m"  6ok5۫kյk- {|TȈ͵?1 veYywSc%??iJˌyfG4֥4NZLi1;`@I⮹BmܼR(G&7P[fp7IƏv.Q""b(ٚyo9ڋ\PQ*ii02!!w!7DNx*u)HiSΥ%+$ԛ3uUn1'%dqVT7+ok<ĸh80'3wѐFWZTD$qs+S&efXOUi1-:I2JqQl(Hi􏸚Ⓤ 7SMJiNF{ADL볭i".J x[7mxCxޮ MɗǶc2ֹ,tb0cb :9W|󞞞S'Ofitwy~m IDATɓEdQ)Adۉgzp}O>y{\,q,K^oUeew&_F[ \iӧk"\?xzzz?<."Q/xV{/y jҥK^WDsJz.]Һ-Eզպ3FY6(@mdQȢ2dˆN`!ɖsόz MoY,z.{}ê7:De.>R6P Y]~@h]a\˖я(5?L׈nY$pyȴᥣ?vؗhMZ0.:+sATDM&ӯkZE×iݮ7WGofyob9&/_HD˿:gy@5YmMo6\!rL"p8D!gH=6mzl6[ ܹfZ*''G۶ŷi_etY/gy"FF_{U|ܝ"ä" a6 @c| 1/ٻ0M1)KD˜Hc<]FKDjSj<脰crY*Lln eFlwkBȢ5 "}a})brv+?+{YDVݳ*֖,%/1zMU.xv}O[ɵe/ L*t{X;WB0 FwvKϷT 0W~ke/{OFz.-"!_T$LWd}w@X4444׺%KDd8Ck/\}&"_R[+!  ~Q}WƲLs:yoZ+iYh/..NL{2xIeex}vewm١u]ȢEj#FՆN+"|օQ*]DUۢEbbbvu"Z(f>õ"뮋Yh``C߼37|Z^Ժ`":]7͢Y̋-&&FKNN\2Տbu+W%''b/exB>z_J|"-ZxK}6VӺ +dQ)E,]O###Z!&&&66V/ E@0*sM0)YI \(fYTʿ/^u!#, PY6(@mdQȢEj#F, PY6(@mdQ"_%PY6(@mdjɱVwަb%3NR#V,WuN3yfҬ(&,+-ަbKl5""U9Ǽ(cLj1mfGc18342JRVf%L`F^i RvTYI㶚^eD䡿OgU̻^e6^!J71S7槴u{DD%a2K)jT*{vn 1lS lƼr%Srw䧈t4EˠO}A+A cx=""me9m~e&SJMeSĜv[-NiiB~Gse׼VoL7uM$#$ix"Tٶ)^2x"4b5;,%c\tNOm3r4E:,uӮX~QzOQV*ӉiQ骱[rJbL0FD2"=uQ{,Ct"T%^MO֊F8yTG xzF,:{VKYLTv!0sڃ ;H3Gx+"Bèk"śLteQkQ‚1.PW: _+/~" 7O6Sm?E?!Z={)4>oHX`|_lk^4444%KDd8Ck/Dyw8-sWp7I**s:yoZ+iYh/..NL{2xIGGG{{zhwBr-G/IExwݟovm/![vG]"f͚5k֌ׁ5Ej#F, PY6(@mdQȢEj#F, PY6(@mZpuNk㏵ EDd&0_,Y45xwwKNѺL,7} ̔?nKnWEُ58Z AqC|Ȣt.@e?(@mdQȢE}ܬu0lnIX2`j]fZSAMjk6U~""7ݛ*)qo[HM.q׸7Kwd8;Ⱥ}'d.PT٣ yuwZZZZZڦ*gIvZ#FdMZZڑnIsߺV%KMZçg}H\n\]Ԁ &Yt=S:hsU"SZZ'&}KE=Nd=|j@g=v\㊜N,zvZ!.',,N}j@/ܿ7-m Yt(u+d-8uD<ݒvݵE\ZTvd.eNd9SehhhhuKpd^S0ip}'dwD-u\`%g=ּ68@aY~t/K6}j{G۷;ω\?ںVK'?w4Ϣަ`[~.$= ZŜn .tV'%f+U|9߽R&m~޺o?ލ_h5R%z:gq2Q&?)]?Ҷ\{͉wb_* S{'˾YU'j\:?å8vVKyrfa60*u-[!y ߸r.HN7k*k{6ݒ˖hNޏ(nX ╅X iHO떄ǿ?Kv:ړkЉ %ۖ[\MhGUYkF|/l#ВS4W-8_wnDdٚou:^_F\$z͟ov&o\%L~of7_%e~㱧K ,Zr'˾W|^g3 rvn[-N^%SDD:+^.GT՗XT6DJ,ZI"۲q©6_h'^n>snVG͢n׹Vu'?C:7|Adq@7|O?dM5m:hvN }tUT.$"/)kM*oSW'JF1]RM$(UDUS ?jTl?LW= ;o՗jzED$`y^DD:,{۔]itf, ~)W kuWZZEDnʍoz0Ytk+{<rj}X驳*ފ.ÞT6*< ;-ԧD:$un+%Kl5ToSKQe)y5҂ޘn;U̻fSml]̈́ rJRiʋ<{?E< ޡ,cum$XT:Ķ9ixlZk㖜TxPsKhgDVIJ7pUh hBFٙczhXc-|Ѷ+ù?yWea]~ѩ>ּ]r*[F'yhN)S[R3fЃ-96fV8[ wpj0%{Ck/\07mo"鬴涒ŏt:]L̴WN9hȢꬬp-ZLExijFi34E.=< F, PY6(@mdQȢEj#F, @@IDATPY6(@mdQȢEj#F, PY6(@mdQȢEj#F, PY6(@mdQȢEj#F,:sJ<""֝^)!jTlTuF5{v*sdQ-<"cg:1$]Pvcy 4@]dQ-2LRs<,:[33+Tu ޘn(J5lȁ`*6I*XQbP*)[DDļa3]_r3u >Wczv髛͎ƃ-U"|㮱P;:͎F~ƃ)Qhs3\>wfnPVG0ث;DUų({K"ib^Tk&yoEcz3sC^|o|]=@CʨPh*yv0="nرf3ӞFKAti ^:Ѱ0挖$[{}ai0(jx&Tl?,͎x6N9!=#Xn{%QDD O00^|skaɷDx)%U]IЯ^wF9#"qpTn>V'P p5)ݔ &CTk4"Egcj$gl(m.T{ea]YRo,IVd$z^ToL7ԕf\eaT3M#"bՋv0ED9E Rbt|=|QT~Kl"XJZ?t11^E;KXWXp)! ETWިE16> m-Z$EʟB'|222oÒ%o_reLL q3,6%'"Tvþm(  Gs=~w(ljr۟\ D jeʱgDnݤڨbƒ k{o>sQFWVUAd,!ɣ-N}Dz\m4w&JwagOmqm)U$QkOek~=+D^jJ֖DDVіFo}""YJmG%"T>&2Ǫ/}"}vrIvo}fs=g6o޼uVmk5ؓ~+Iȸ}S=ݲ+KU%ݘ+/7M|猈ӕ yc, M*^OȊڢVe(+B̳}6 :tHDl6Q J#9,ǣ25"wfE xgw lR0uTR8X="zʑEkQ*ѝ"""ԮtPYʪ/))K$"8NkҍDq̿[x%2Uz^ڤb-D_ ~,{mT^X3lW\Cmݺu֭Q*̋z*מ "qԿ`/ȟd EM$(ZUr08ڲC"\6]PY66tX.WT -ZgG1k}]w]LL̢Eo -/byg6ob^Tm111X]rr?oʕ~l]r?/99Y{1/;C@NիW> .'h]iѢŋ/]4....:`^!M/dҘO}ϟѺ(` 111X]N~Q,qTkIQJJTD0ȢPexb  p @mdQȢEj#F, PY6(@mdQȢEj#b9.ȢEj#.8VKcfG6[-9%Mv|%Jf0Z`s*aͻ6fՌE0`y^i6W_b1W_RӰsK]W`8Ōל_/TdUf3EVcX-9U#?Exቚ^1Wۍ"=o?V]۱`̋\ V6j;e(HO-ޘniuuxzEM .]5G4,H[Yq؃L(11Y7Fg_}%녞Hi m4̉l4̩ 6Yu`,:?P$VK w(F{CuRw@JdfXpȢۀu'~5"ɛn][POպqqlW;Nyl""NȺ{eS;'#iJV.\[8'Ҫ_KW`|{N4¼BbκitdWiw@r,9Uݝ%iii'R\v?|Zv3iv,7O^>y,\|_αgq{ϧScdхy$^RTyUݧ$k{A_{_!nI{s@HŵnS}Z"XO<=|1lC̋F, 5(Gk.,@'") }љp]IXjڋy˙#iii&>@oݴSZZZME fYt~‹:ו ̖ք; fAvE_m.?y!LwcW8'zSJq͞,9;䄹#O~ĺ}'dʩ@Dl\W|*h=uBDNoʺ:EDn\.gq9K °F67fsM˥۽_ĩd՝) ~QG]HX,"ʡD'Jt/vɒ;WN<}G.~хd콝#Z &:h1`"̋B-6"0 0/ 5Lu(@mdQȢymmj]ي,ݹ^׶[gm{l]ܶٛdm`n 7ÕȢ>OֻHT/Ƿi~gmw^ujy>tasmwHEd s?3Nj9-"|?2-EqxZDM7={Gb04H@˝k=iZ?`<-[oNbM(8k[[v&ho"r0ߐE1ُ[]ߙo(au^o?)rgDGgY ,P]1C73wX/1BǏ>suk;[&λE1wZ;NgvLr_ Ycmw|^t(Auagqe؉Dٶ;/ƺ٪ %8hlhhhh׌ >_(k[wI4q M]w,|c*0U'^к9e龸8N3ӺyQh7|EpD, M3ɹw;w?FjŚ\5ʟɢݽ%N 57/}kn&,:׺'Z`HZ3:.y͚ǘV$hO h]&iMvhTD9;,_W*v͚Њkn^&<1sK)57']tsvvDycLZ~UMn[o\xUgZ٘}>o^uIym>~+b-Z/^?BJ~3Er)qo^%5i.[ѿ\х%&mcBIENDB`flask-openapi3-4.1.0/docs/assets/image-20210525160157057.png000066400000000000000000001331571475153525300222140ustar00rootroot00000000000000PNG  IHDR* pHYsetEXtSoftwareSnipaste] IDATxy\w//(K))X!(Xت\:ݪںekG{qVIhZ\^fBeB,AOxx$ꫯ~ϓ_$9-\{W u~C? ~@A=z!C?ػOkk/899ٻ `cq@imm&>(6i7={?HlE?`899 ;N9B0(?!rt?&c0j#!~vz{'pdDo0$R4$$Ķ vrr@={[+كܹnZqukW~VV,&u0 } a@CիbVgKOObKz3BBB@Oq۞:@I=?l>䶶[ C W%uUwN~%Y7T8y;r,ՏTetssYevZ~@к;O-OJ=xD0zPS\ALSTrܒrO ~8fQ,M 6^^{pA駓Il*_|7(x [>9*J$D" D.3%@zxߏoo:X9YbŎ;Fa@AA N}w (ЁƯZo>HP=~X$I$Ph=T*UkkB08yp iii+W4ϳm;TU+CGXM(JRooo`=R cٵkWeee!6DPN uss@Y!?0meUDz︹-jSU|𪙓nnnnn,ݝUl :%DՏX`p0!3d6Ys$-O0=i <"]5?m~";p0gޖ?F{vf5%$֖y<%%l<<5wd/90Y{W^'}U2翲@SOKiGnv1|CϜʂ o*nk cHRR@$ 9xXtGY2pԨQZ ~o<᪬ےy@%%Dw]ۓ:6(P!ڟKw#'$)(|dml "'Ww%E':ٖ=%ׯۧ;v.dL4iҤI=6@z?9u}%oH-v>ktȕ]zR'qb0nS!N*-(.+ rEu/%4EM$LIu#D'蔌':xj}%aĉÑÇ-)֕~/LٕUu;kiftdbZ<=9oW =o|{KCqɡ1IR+{#pU%D$ 0ڵ I:_ztnJqrrҹ0yŋcy'se1DDHy[~PuiZr1;5@+rnKj*&mkdvpSU=[?ga,/I|<)/a騫A&'5@5 z@:G*@D2~~ "WВV!ѴJlǦ*r6EmHy:5h.W>v%yD=)eTۥBb )J.Q3}:WDĝ\|UDD'GummA7I. GSmyqR&nkWܮ& W\v}&tD}zV$~IDorwFN':xPl`u׉Ls_cג B迪v\Tڻ]phglك$,>uzw@h"q&g;CUt#”DW]u@  ZQBW.o:IUݝX7~IDbQTSKg)/2я=aZL[p040&sJv>m<|:rHurJB@=sD)'WSWH[5U(N޵etGHu~^p q1©xg+.$':mt1,}Y:Y:[nRe(I1Q%UnذaCV ^yڏ2$doZ9/b#m;yoLZY9sWOz6Y%ٳg------ZVޕpss#&;ʜ9|%T*URRB0\.OJJ J|m*ox<oNNN prrbʿ;W]EuB!0z(~̝;Ϝ9tЛ)@*N%JөzhՄ Ν{̙ݻw޽Yke˖5v |>_RT}PtS?> ; &'GW\~Y|h:顔Εlyٲe=HcǎIbccSڰbqzKKFh46|+W(hkJ䔔ʮF&GE??wJwa˷خQΖ~~pqqqqqqvv0`;ܶSϧNjnVr@=BS:7ϗd!!Y !!!!2s_#{@oף s!!!ϟw-C? ~@A=C+5@=_oΞ={?p@{8p ͅ.Oߧ֎={i ̟h5[!aa%6~xyypB 8&5;soiCDÇsr/ =wrrbO5¥߮mfFh]A0=<<\P8;;3񃈘;991 ]$L==~FsEDDػj`ʈ#>|36 ~82'Lʌ8yv~477k4_||wqqquue_δ@vaA}||]/oSWWWQQԤV53!x80'!rs.t~@?{  @-.y`A??[$ c?@g0l,'} ؘa"b@B?{0 7@ߡ 4h*0`[1/4fL`\x561W@>Up(-Z9]p`?   l(,?= ^"Bb R_^PP]PPR($$$$$D(&$$$''DDR#y睃ѪU>c{W466?lCDϷpwww5&{J%H&ٻ.޽{DO6RPT~aªU?|777"rwx'0 \# % ;\dzg|||z>999fK'''?=P+`8t"HREGGڻ:޽{j944ٻ:kǎl?㣣vBP(Nŏ?+W\bSO6{x{{3;III?vb0~<{gGccW_}^x饗 d Rx[nݿ)ꫯ'g8nfTjovDKKKYYYO *j֬YD4o<&x>;v6^fR(B9sfAAAHHBgظg&TXҭ$ Zn@pl2J5o޼]v!{pTTT/H@?J={-Z$,???=zn:qĕN:5k,&{>|C@ ---J줈ޅP(͓Jg" ͝7oRJ,&{0ECRyYYYY]0MMM/B /BSSӞ={`CMgmܸe{Wsuƍ7@#֩rN:JDLwuװaÈHR=xLHcǖ-[:w]vضbvdɇ3:}Y:4p;qww޽~سg=FaU]ۿ|њs ^U&9㒋 "|EhG|e;7{ig+/0]ㅌY|EQ%;wώK-&ȤWo- ğ(s[.[oZ5vVgWu']_q䕵j /0"qRaUa+KsTdf/gF-ޑ7Qf+崱El_<ʕHuxp9Vdž f֡C=6lX@@=x𠪪ձ۷ggg'$$t:{P[W@tt +fG۷og>[l18|-[(55Y ؕ"91>Z31&~W3aEʬd#^ɷVV[c8{QsuO2sj iԕEC9(8\+8PGjjS|qu?>|tRDdmLJJb&=K.=}4YHKK IMM}w :u~7Dh"]l… ## ?q_}رcy5^uUgL*?7/.&TWO'<}uDlںiT#WOۿ:rj3N(_xVQTwmZ4'7ŕ7y4QٹE7[/U]۹hf, 5y8Dr狫]FhaRM'>Tjcdʲ\3'% g".V9F(p܄?~-j5S+l(/-'x%PTL>}@>Cy{{3 .XLD{(?|D1@Ϟ;mt~jEy# e",:~a)ߔ^Mcm5=)՛s ;AD?;$O6kiIv>_Mc]8D¹vJ̾^2giVf#mMDEj" @D-~هJė;[^+!1< 0*JܸBMC]_Б3YRtԩQU*ս{ZZZ]`>:>/_d˗NCщk2|T$fzwӾes y%a$slNeٵugp舙h_!"/mչkl"}2\"":6~&70ҫn}^ꝯU_-/i_.}_WvaubˊkHCDjA~<5/vRNk(x"Re:WܑUbX \N)-'n(/$ 6w^ Cg5;nYx<&겲^@%evP(d\.߱cmk^x@)www}?46'6ӏ4;u;ڤ2;#C-;"T#_vH(Q?bDˈH~nU.Ѳى3]HDLwŭ|\d&oe/*V% "uiIMg [Y Yn-F .igzq(l+9C./[L.[$0 $88յ|,Tevo~P(1bӧwe9|"z饗l{ڱcfddĘ>`Gjn&9U]عYnOBkr"JaqPhv+qÈD"9-+8#[O$#Sֵ\_@. IDATWV69꧟~""¬m۶555}fK2'dNTD4-,s;/ ?4ATVv^ 5xF~onY^LFDj<~H~(LE,Q|J QJ(j4Q[^KD~,>~>D T[WCgea+Ksi__鱼hffͯc|{f&QS' D㹨M27W6P?+o`ZUHQO&sṐZC:eُuTr' kmV>.FPkk0JJEuhEߓJ Kh7hf[s&.X:vyգ'9RbO^RiF0;3D>9)̗.=t$`^I?Y7TVvaXV䦩_r7kgdrJpGwN~Y,֛ `EM2|9+I*5ċHYmYG ƐkdJr _:ɤFG1sJP7W d=t!K:\i#fmwILCOjIy/L}+4g;+9[i_ZL, kecE'0WQ5"Q1&F_{c3W.x͏35O K[ə@UsO$D.) f=s8!.j(rK'?:mC|RmՊՋڭ um~ѣGDdrE,+vu\. ӫ.jUe<2c; UU}kwo% w|ѻ<ܡn,ܓ3'-UWSkua+KsT\Qihtv-Mho_QD߬{JeØƴ/ :-Y~Kr\=ç.3GTyZ$ N}\{sHpr񌝓ipvdQg$"*)*տ4$×|GupS/O?&+|ʤ:Dj^D cQIWLIJJqqq v.kdooo"*((&~EvqA; U*U[ʄѣG?m_1;~9]eV/Zɷ! ELva5pȲBG]4kWL_UUFD_EMH,R$"%2z""H [Y^[5DlGBθ3rq3rs7G^ɚZ"KԮcg土_vY{l.y"&46IBj:Lam;kGOD+ /`AD|>I " ܻ ׯggg6~dggSߊ̚R{bqC ߿ov70>,CHC Bhمa/Y@.Y؊""ᥱԪ29%kp/pn<"umyo)~C[_D6:gdDl,Y0f8aX͕]V6}KDD'6-9磅̟mޤW;yv"-,̧j׊1Ἳ9~]tӧE"܉=9JUUU{|~Wiii6<-?݋0k461&&&''kf޺u9 %j͹q e>!GePݕ}KDc~Ӧmg]l_#"a=8,J^9E鵝+H6,MDTq\HPHY4]ׇbs6-j#NFZ^_9K_f,m($ç}QHͼ{5VxGI!- {SZXKޞ>jdDO13ŕHԸ05k6uWK3Ӄy/ 7>قW¸Z">Lک3jؼKehɢc,z%jK DĎ"`"?VLJD6"rZYWmljDt+W>|"كzOl۷S.wwx"Ŷ:'s*A%ٿ9?o~yweǫȚAOџH3Qvn'Њ::B}moVOGh*J%G3Qs@D7}Eo;nJ6ѐv ^`|}I?oC"b ?t¸@"a( ]aA"v:ax,\]w~$sADv:tڻ:UUUD4l0??]QB[l>}43brӧtɖ999rU& KgKh]XUBgop 9׮bsOi̇; 8*5WoٸqeQŠ&榊/3{sbSruI~r&mu{Wӏެ# %s%V-و(2y&W/\SE۶3<^yN5wxSگE{uwgp*nn(zč6|ܨОO%uƗ0ކd|q9ܹ@U \g]]fe^R^Qۊf̌¶7d c%>t&/P%@"Rlci/@ll\^QGlt76kΎO'y_|T<զOvg7Hh&w ~tX~V}L$[WlF@߃ej 7vM+WܱcGjjL&ʓRTf"+W=Ǐ[ٰܠ?~;oeiUew. tc̴fwQ7"U6[MtbWJBYYf 9IO>J'D͟IM~xqΝ͏s>}KYvܦíP|J QJ(jvo.[–6%-j_6Jmxm6Ȕ%Kzv xn;6cmB T>)o(yrMy_3-j(HR.&fk 1#kcrjs%yDp܎d(a꯫#9qk꺼ȥm+7eL=wBTR¶.x[xgZ }c%P\:Ag^Z)>vQK[&""ϸř,f7aٛṷ;',Mw[,01|T}ԩT۵(JJڗiii B$uu5kBDvp@ZPPi{yaPPm'G]=6O! wŏ$eƄG/+Yۘ+.8 +tW"BL[Dnĉ.DI\I)/Gzo\<|Bт[׿npϘijqb7V▽,dNiPk#0唷]hliP6#q0MT ܢr4 j zf~V& ijLN],nԻ#'*}h_095mt9<3uƌd:57<i9[>oMoM4j ^fwvnQs7m\ &ɩw]8^Z:a&=Qk\x1)o`E~9/Gzi ri? GcMQ~;qjj]%˱->桡hcA T*f!ν*J$hTK6̦?~ՉCl?׳gZ[[}|,^zӟ"""?1v_򗒒77>:9h}-W.:rG ̗v]w7]N;sϵ:H[i`KnCOl<[DoMlJȬ$]GdWW>=m+ujHvvv\\\.كs5LlމCwwuֹٳ<.R}v&{`[-Fơf }r-f|jf0[tǍD$ ޽| ' &!!AP$%%]WP̚5+))IP$$$qqqn%%%Qҗ>}Ǐ]ÇǏ߾};3>=S[-zUSNآ4RQҧ^ >; ;v`6  ^^^B EyyyvvX,f򉷷-[VZezwNvv6Y78؜Wn|jllW_1?;V 1BT655)[n1{~^{ GWOqn nm J=*e^ q4&rJ\lP+/n6Y3T ?)fee܋J+88xԩ+W՝ÇbYB״dY zk3ֻ 7W̌ԉ5Ww>qC`!_.˯_ޙ}FcccIII~~O?u 2$&&&""=f|ow5ח˯^WֲzN8.Ph)qC&/,m[>օ,7i0z z9%н]p`{`=B@t#$B,UKKKW؞SG@n/ػ ?WlGkkZo ~p'~_0 ?t:@?ӧOW)?{W2mn7ӧO5+@݂lan0?b$/A\1v%8 _ٻj`7] 8C'>tн=!~@O !/GӰa]ػ_ ~@A=z!C? ~@A=z{ IDAT!C? ~@Ab pg0gϵh~Pg mՕ~ a&w-M̧s=!4?os@#~@* uYvs<͡S/"KӝCM=]ċCPngve~CX+r<Lc:Fmp vytN `o{ ~1Oߎ`98ŷW`j9ۅd:X3}߮D7ytvw=|œSs|0Ə!#X?"7(ak̳:=8\_9W󥷽л9V~޵0lsN?&#{moٻŏڻfva>QGTʠٻIL{_ crfStǃ moyLl^u#omm;v,caaǏ mX=!of|,X/>ݟTkyÇAyj=oyƘi?z%Fv-l$֬Y3sLcJT*H$2&`|0 z)<[gg/xhƾ}>rȓ'OeATTTRRH$H$~8sxq Anq~C߰Ͼxqb[̝OP[jib\^2KI QQQ)))7xrRT*%ɚ5kt @bWCc];w_ҕs˧Qp|'޳g%k[a+?!CFX1ҽ@ƌ'9jo{t%R_әCZ~~(R@//#G2sL&{7L^v-;D*.^D[&%%1ŋƺf`'9s֭[khn^^^K,1q>Kdz[csΙ3gYFʠ; { ά~3rDV~Io6qBAtnYdi,&gd23gJR̜9333?c¾}ݫe0K!n ??l5=wI_x uGk֬uZI$6xvD:-~Hdl^fXs{BnKt,ED >>XvT*oy۷md2f'OڵkaKȸRԹ^^^GekxQD3>>^ ܺuSD"޽{\^c)=KɄ]LYU*5| *ƪws-T׶+=mG?!Hd2"=xb 7loF}}=4OJJ2? _"n?Vg4޽{uX֮]&[9rUaaaaaD"ٷoӎ 26kb߾}l>%%[ÜDr1汆 ƍlUwݻWόOںu+7]p HĔ_f cNr׏Hn}nDkSV~SE7+^*ڔ_y[п42Ħ~ӾwGm3R:\hϞ=mvd%ߝ3cǎ5Vl̍Ç37Lbs}O<3fŋ nˋeɓ ĝn<ܥt7HR9¾"ll k<`c~-ꋿju?< '_ɝ: Sه߹ADow~3I*23Jf2vogĪM?fosas 3qvaƒ%K[n5qnC{i~_wY[P??Kj/aeM{2?_w_/?o~skK]}7~xξqm?__bPaaɓ~Έ߷o_NNŋ[f;.``y,8VFg^:7hŞG$,**2#7M/>R?|ܹ3^ xRv@s:ߝ6S⎆ZQ[}ț`d__lO&9(wm=og~qb㋯5ŞT;'Ec=yw_|qڵ܆81k:IRXJ];K;ڲ. w4nCn++ֲOMgAvq-KNMSFagαWkZCƒvsTƋ3{d!lFX񃕑dHNRtڵosrrV53/y999WV8wr<==(((DgNLb+]xJ1|};AAAfw1ց!-~4T?D6pWqدy/,ao_ ն\p͚5ޒ%K.]ʆ{*Jόbn;(t)HSRRŹkgL^^^IIIK,p3>a^dEu`e{g,~]Mz'M7S6=OS㛜[?؄W_ 2ڈRTjd>+h |1Wc7UoGի>%ÇiԪQ}y\SW,aZ ]v:Զδ:*V3V`vK\VKPMPD%H[qrr $~soνA<99Ϲ:%E. vd+oW*,|;~5[::ڱuIi4t8XPD >Q>Gqx?xs-WIG/5eBBu0k.XZ"}W=#B -6v׷)׊js wt "+;;;33^;uVz G*(9// ?Yt$,9NJJJHH`_:%HȠBH$͈?f-|}} j O%pdֿzJK+wk+TMjOWk+n~)99vݟh="oꝯ2_SO8$D!q{r_z3/>HYV{۩h!K3?ϻF^kBȶ=YbߦRߛfZ,?W\, MޮZ\ /璍{Gkݰ!{ev?UyKly\JM-کtiǃֶ^֮^ѣtAn*&zn.2G̜hz>V3.HH$tqr9Z -77H#иT*̵[T>]'iiiTW; z#elRƱ yK?"ߞ]̯BH' 7Fhxvզ!sNTᲂ'u{D{iUbT2:I%!^ z;m./$" H2t:Y>"eF{g/KBZ󋪩~>ƩF]Pތ<QNP4ɏm]cC DOTN]Pbukޱ ed;>:`c9axck۾+CzZQ?imnEO3* c3AA#77N Bˣ@@KIII#K ߲eKmmmrr2zkhΦONNכi.jmm322Xb50R&{6|i^XN>mZsx-j$ovMɟ*|9xq@k3!ўwRLe::N)wCk m^Xg='+My.dg|u=_}jBHѦ_fXlh[2J ޼h=a|{1?5TTηT sfLtt45]A,{ ʺti&'\ZTT&P(222n0CE/ ,HKKӌ=33,E@ߩ {N\J:OBMyX$$@v >4.'xFl8g` Qѫvy=!s־yF,!vo^/L$~C)\~(}_иc؁`*GK]ԵJKe >my݉>m ;\Ϩ?}ubځ5)m]='>=!aϽg@uk`& Xӏ w `CW% XLW  s,fKgee{gxlꍶnݺ|rիW|jָ8yZ.#(J!b?ڣcmڴҖB!qhomcyWBHl;5WK!=eZsߒ5JyܐL=Tר-ہBB#zyBWa5e8΀4c{^أcmݻ#"۽{ݾysk6DQ%jG-tgP.m4{,bmgt,|}4Kkb)Nt^LB&kJIIqXTh*:ר( M=5.'Ʈ"r8T{A teh/Y50Z~*B!qz7$(-ԚK !kTȋ͐m[}k70{* “?T5a&0dbIEaMW{G>iV.1 K~ε ?#f$oz N s];҉={5ǣҊN fiN P>Zgub5ri>!A.F t$@^M𙺿УUy[;6K W88 QXS崋u=\\^]ufۛ^.]:7l800d  J0v:78|CB\ᖿ̖zN-z|4S8zы̰A(t񷯯/3Bm+},#dW̗||| \g^Ƣ!rssR){XBB(^ =}u x*#_ZƲqgg!>et"cau`{YjBHՁWcaFܨTU ܊Cnm"-ہwsaF xm^3ÁJkA5ԦDAQQiQ~x[[QiP(輋̭[Fhr s·u'aBnݚ5ǧ/q0'r,hҭh8˙~=Ei׮wjC2$Շv N*n~2- [%oP}dCU_C5bYfFֹ\ϧF B,4ZZZ|OLL s,HwD"U^~;ahܠuB322b1 vdgg|]lBuZŲRK$i]V[[ˬdeeʹD[+bbb"## % u}}}ˉLQQ˻!D"888\ `^nsurBXV([\fu.ߔ*BBb/ѹ+͒m5 {eϨۓ 5qqbo|V!Շ~2r%Njy3P쫿1>0%:8ؓOw;4KXhUMO$MAgGdee:OMM,x$0ZvvvbbbJJ#++GjAvvH$b:mٲLJNna! B!X * B?bbbbbbpd̙Cp8|>_$%%%S\n!f֑j]*(O㣏*`r`N8х>c{6B11Is86>S*]cx.72BHEmUZ=>4U;s."FN0%کxFt֏IҼ_(ou\ʹիW|S|||$BivU*3',7FH$Ʀ=SߒrxG B))!WcuX-_+E{9L!s Ƹ?4j]()-#lמc_bpDȏأ8q/yr9p"7\2B;?nWFiB!;3 !訕.0st|*}ֶ}K;*NP?-A>9E$Fe|n -a}< IDAT՞ִZmmZ: cڌ HĬNt|r`L,oٲvԄQ_i^#;fs[~7o%&D/JznD7i.ߖL-y">9LGتWb_}0!gE{i9B6vΈ.n8B7-hݰ~ޔz㯑=Nqk|.ʟr_^yU20z$~ G{18`D.zsUC ̼rq㶡M=^?Vew>n`F2+N=9 9*v C طƿw'! [T-*?}!DGuez~2ꢽ ^5TlwZ]\l:҄wJB(:Uau:+l:=@L$D)}R~kj$רKizlȪ^}e]dئ|[KC(岊#;\CF!+{>BžܱO4dYD!63Rf "^)!ֶ}O[[qֆ ػ,}]w///ۆK ] ?DeN,d+<|f5!ٛeohW/_}wSsOf~ß`<:,HrժM2"|%?󕻻vvo*rYOxs}k{?kJV]:8UnbSUwrx[V,hBf!U6=C]~Y ͸[O_9xgjo`mᅯy񕃷~VcC]?Ǣ%XBiii1@5MKzq(zhך`u@+3D'bd'(.yO kv+ox'>J$56"f ே wUܮ{uȮQ G ̓brW$gWx]Kd (~Hz !$06ңb a8ޣ|Y/~ܽ0W(/Ǣ%ǣ!R5 9*!!ˑD[o z[ ]P@qo+岚 I]'!8;};.D!~FUW7Bx 4u\V]QA1qq=JYU'`G)QaB)7`Ge6B3_}J-pPu5%T㸡{k%_Q*ϹU :{S(2,> B:hmmvflˌ={>g3.`dr=&ٴ֒kk21Ks҂eގW+򷪲ϑ󳳳⴦axs.{nysP(vpp*=C$i'WbbbB!alEimmRKX6.2o>"&0ns+U_Ƶ(r {4 4U~B^CPI[{Q``4Il<ݞGGѻfyk_Vqzp&ow_أ9Y4B8[UykoX۸ግ8G5vo@Oy& tJWL%t<(W5)> cM D윜H$JJyH0?z(|RR-zH`uRRTf^H$lvo4{\mb~d}vTR?n=^+נQXurݒUwa{1233*B4'@ROtt4]7$$z^ɩ ++Kkl2|>?:::"ҨfԠ_IBEEE$22Iw@W###cbb"###""P]& ~PAq>{VSﵜlLN睾[{goqS]7BP$1___=;>񲳳lR** fA*ˋlSTTePV)++K$Brrr"""bbbDKd0 0_DZIaa3"""'W&dA襖KΚ0}Znޮ4DGzz:8g֭&kxP(e # ?(juD|>_,B !Z*8 ̬])qx<M"($$Z~TVVV\\ڊ5obL+TH(x<{ [FzzիMX{Q!++ZFEyyy㣣c7ޞ }aNhw^9wi¤ B:[秤PSRRRRR=ZTTD7HII ԓ0T =z4;;NMNII"(''RTTM>zhNN}5IOO5MII)**Ù'h=PP^Z"*]W5P]g ?x28gG\kF8;>3]ZBaf*05uC=.!!> @Swg{31deeP+x<="OHHزe ;s 5|>d0*%gffR߻S ]G?===11Q, ˗SyqqqbkGDDR:zhnn.HP݋iiiѕ"/_N%| B@u"777..QqH$ҺPㅄ2wҕGGE&|>_*`3ќ̈́T'n7ekS9Lܾn?ռ!0j@(R{dz'ScBCH=SSSsrr222Ba^^5R^Ɠ-|~??$$zR"PbD"+ B$Qo$J!!gu-Z_QmX5 uFAuo֭t8EEE<~ygwϟ*,0afԐ]IF>>>tϧ#:ÁS_390bCP0Zԩ"##kIJJ; JN_u,9yyyԞW,en{EoKВFU۠of90L3h5c0RZʪw9yKfcoo_[[N*g:88$$$ABkkkJJʗ_~I{sх---[nΐ JN,b ///#####2mڴ,*6ő'a0[ Lw'333Y@CDŒq{1>c;uC|Dqbs 3;^ sWGJ~f{l,B>x[,m"6d]]kj4ۮ^u &KFV]{Ir0JMˍ_"!5.޾3egΖc<}W_fi0؇~Dw[ka!Ng'Gt!|_7g'7zQvn[@sYD6K "g;wz Ys=!xo{J^J|"Y\GfkBք+[Z>8Vu~ڝ3%`r?tr;r-'O˟mmmVU7yaggcee5v==,mfpvrdiUrd]rtix]9w l47k&԰s=.bauq-ӿrq!GE>=-__#ـ#+Voa=גEAjOڭG>9dSsMzV {:8&>q7g@*!~ԢhMWx?_h';tlTNekk3kf :s8 Wpo;kiieis~tw\o,[\/'m|=`XC&1Bk߈O[xzZC,Nr |aZ˕=VVl{:a;˔ħmnHje&m\\9yɼV<{SZiiC!>h=雞K %v׮kO;yLo/w]{O=3xiJeNBH 6t؃8-?0(o}X\3?}h2w9 55ﯾg`Ft[MuxӕmCg?\,+WLQy~>Fܨ:W̳a巤~/ʟ=XAL3ӭ=b9X17KEĜii~X^TzDREya-o]gZ?=^.FKGw] ЮNp eҺ ٝfm\n^Ge}>=k'\p2S[Fҳc'\s`4J šC5ug IDAT{qQG:1~T6ɞj_/j%Z҃^FgiE;Ҥ@-GLfʓGFrS5-Z?m3g-`6?+Xh'ѹ1Ϩ;oE pwUWz:'k|%#Beeh]TLm+mQ(h裵͊Gu!6A.A.A.6tBUGAE/.fZ[~<5=sVUJf .irww,T',\Xqۘ*o͜_`5&6d+K3 QY;VYkB&u))kk-cO_14\/(o(]b06󞺄4Wll歴n\[m UL}BW90ЂkIÚeaiU}s`5N9`0gX6 fgVJPt̋ml,ZYfkVYXgnWq5v0j NsN.F{l&JJ &yW)~C?u+->{$x?'.3spHԑ_VR1sI#'O;ev>ՇeK5CY <`Ʀ"-,,`ۼzUJk'[+1EnE89LvjMYXLR[3g:5lcz`mCl3b]Erꚶ,G\X3%+3Nf#580dr,O[YuaEB=]5"xok\⭳`+Wߺs ǬlÚef)NۧW3/Doյmm# w<]73g) [Lj/參m17я񣬾q<_fA<= B!5E_*c>e- ׯ <!{OZ3g98jC)i6kAq&{;Km֢Pim6|پ!s 9k<44ѧ+b=Uqc3WyXwbxh6آ[륖@bI -`IrsW ;؇ ]Z]kh m3bIҋ$W WWys =F໣T]qu꺂1fxxE==u+ׯVUKoJA[ZN 5uޜy<;jݼ%;_zAVVV^K.jV+%T.z1Htrrhnf:;#8b}L/ ,dY,)18(mXGs4CDlcYƹfi~X^58Ea5 onk~ipkD 71JWYB\Ϭ[ͥs|sgغ,孺WJLQ쏨Z:vK S}> BgL'2kV.k٨* xɰ>:^(б͉Ȁa`MfO!™%0~__/]VzZZN5+g֘؉S9r󖌥X2gŌiO>)Ζ--?,kj..N1-t7WtߪS uOFOizE$ԬU@B͔c ?ΖFF;iҨz7xGWWJ`YYYr}n!a\8B!35>v:QBBV?;4V]xrwڸhB;oA6BdU<y'/ a YVv tyjxŴvNTook) 8M*AQ7_]C(peIiYm}y-.ȍck5]HYtQ !\%N+ToyTϝo4}}vƂ7mMϋ!Cjk6.OXZsoeB M;S?`3 'rut4fd<EFZU][[[z7AHoo9m\h ~bi4Ýz:Ζ4d >^Z[2qb֘sIn9A!H!O ?^-]C#Z;y !xQeУta[btPjlg,}//{=8Yօhk/QOǧw ;z޲k6V.hˢ[گXB63ߵFp#<՚aXMx}t:.v_ 0q|?{o\@={!xib&J/5Ͼt,8?g}nlA+)w3;:MZ <=\gr#|/7_Wo@{K7g3P:\1񯰧Hћo2pB-+ܨz-_H=31d`:,)ٴuWKH 7ny7jFx0wlHc$~Jݟlݞ>*Y}}}:/ao7}/U*{3גjҝΞ+cpqqßMHb?aR!٩iͣdqy/?>XzEvZw|T>{cWWy<^=ޓ博۽mu>}7G[){*m JUǑw6K Xޤ@>lV!Ɂʆ}7:=r׿LJ%]oұJU&!znDZ|u&_hjjޙ߶fbŶŒ02yN67Z&g_dZ?c7o]/FOO9հ7N3 w[~:ҀwUZG#TƫΎӜk# #sRͿ%!\I|&^~rlQyN˩?}.,dmk핒R߅s|B}fy9X溚kGcr{q_MQMj$BI5!SELsBjnE7m9wj*'oW&"~MwDM*QFۉ s qp\m6g9&7XsZ]0G}}Pu_}/kc{PΞ+{;Í~w{cJSSֶ頿Om##lC+(>ޞXVX=W67e 9%5ұd%{r6^ZtITR,=-8β%[F1[`1uTvT,o3Fjn4Ϩ;"E䦡9o]0:Ⱦ.p3%Egه"o|oc{P Y!kzzzw茱KǍ@M 56z~*2=F?ͲP^r}z_/*Q/:Sw4v)9vfyq+v j_XgX ܽ6&eqU5=DTn;S?:t?ufGGW#/UjfTR\eW3rؕjѥ[x@Zʮ>f [o\vgUj-ްSEG.!rAh=#\ 9ɣ۟bw\ c,b? ^{3R;:wNzRKU~80?{McAs\(QsA}~4{6[{e)Msvr\.\LoT/U[5R=q@Lq<=#`;;ėLnNR%j[?{ѺZo@?W~:KU 1M;w;&tqDwwOqj*,]q7gXfW_ۅGK=^_{M_|{?<}++qwsS8͐s__^_[;RwpOk;)_s8''U|Uznժ?zpnޅ'^,Z_hog{T*#ؔ,,,{VhazWW r,CsC_+)(ی.'yI'B~Jk@̲91yUs 󽓣o  ?gu\\+rQ|v/3qqq\fVVgF NJo?~ŋfYp/_wq9B)-TqEs7z7]xc?d*e-Q{yzU?KMYﯧNp$ VzK!vvDKG<, p^|L \$py d"-xzmW7޼Y~6R# wAf3Y ;VQ#԰{dL;_7}4ڐk{ei},t^Ȩ OHϾ>ДcIG;r5] O*I+ yaßކ#Hy̸ D#ލw™l`zgs4W*Ugkyi1giBr݄ br7~^el..N{FhtsJGGi9vvsYۅaGyK1J%JoݪAqڧo k^RsD7 Oו,]WʐoV&n{ϊY 3a4p4iҤeQ GYjZ CK=9Ӭ@z'qEf;?`~MtqtS~%)3ZRa7\9qA~nݪrUWfs2Vc2߮~jUL %<;U1/vQG&uǂf˱83ecYBE-0AMW3݌t4o/OBrMwT.$li_,'ZgDi}]m޺o-,,/Z`{OlŒ x..,f,b}ڈ]Ftt`_yE Ʋ/"2"jkAlY3ʬ@Y~=77e MrE{{GWwXV*Mg#p~d#=}7u3]ݽm+p&Lp-eia40Z&LeHrzJyZRUߨgٳxz4onоoYwp>x{{o5se[|9ŋ"YԌj(Zϗ^di`3eJ3g0Z$AHX’ 4"W[۪..[ֶV_X[[Vڢ^ j-,Kb$ a$df2K#C&9g0!?9g~9<\$^+Qѝ>/@0Ԛe]~w Nή-vYKjӋMސo8V/MCyfwmǛOh׌=jTR'i):TZZԚXV%xj? 앏 Ni /&閛L,]W^kupΓ`0TmHV0FLéc:+RSztX̣Gj_{nmmDnŞ?SM|횜~4y_oў?QUzHMg^߰c^>FLo6\:< 168*jdefFL!v3"Ȯ~({h;Fmh<Hutxvާ]3nXmT^!IDAT2mz~ٹZX[ҞH쇈[nZ2i#5uxmD"vj番lsj?8:mxvMf1tY4b1kz<-nR;9|/];f-q.jo?u7wsXmԥ%źeVV 02⇪O>[_"oT{鋈掙3wD+t2uǃ?|Bu9 mx'zV}j^ʾwN8ڃiiq[^[#E"]{?:݃Ⲭ{u$`H{tʌS8`02dv| H4⇈~d2fgeƈDNh'uthLkx6ܼtמk=/mjJ7AD:'y-L&>sfuze?o껕UVV)ѝXpqN)(rm7=<صO[UU a|^xfj 13b⇈L[o^nn++5G OwLMڧԔ޽? 77g0=M&k~?^ty`0,ڙt+ߞĉ-4d\))ɺ [DrNw[P})nk0>}ݧιW}hNNMMYtye}rۮ^.?a݆L&cqY^6gvj8 xڿm[+?kEXW_>$e`Rw yӧXz`Z<\{K&--%D/zrTplCW^>whd=:wٿm;ۼ^_w'9.tr@M{P}CStQII'Mȟ0nfѴɓ& nf ]]}t gz}t3M6m:/#8~YF#@?$@?$@?$@?$@?$@?$@?$@?$@?$@?$@?$@?$@?$@?$@?$@?ɻ:~nFy}s+C1jjTv4]Tz'?q#c yEdb{F )C01 bzBXvQ 5""Ff֪a=ɢB?""$Kw ;!u {t{ˠؓ-w=38uz.+g4SJ[ 7_[/7oZWzճ_$[r[TEKz`hm;^S,G˟d~i9-::6Lٯ18rͽS-wU%9}=_ :EY}Sv-"ڭt9NògZ~S:D,F媙VKբan9nјs߻ёTg(}yU6t,Gq(mzm@oD_k_{Xƛ8 o{#/T®-w𝽳]YkCK.pꑣj~,O5=R鍮~D2sLeMHCS`Knw-r%7T6Hn-wŨ vz嶑 O9pl G|/{D=cOOV}tkWeF_DD3+,K?S/o :Uo[1GTO,;==ṣιmSO>]z7to>2{ᙘfT_v溕N|"m nnc4ԁi."b(zigBnW̜wש5jP ga5aw}#{7LO7R`uγ%"Wg}A6v>UG/3ZDM˃U{3CD$}"EDv{_UV~=DD-0Hko٪CD3FHHucLG:P'U20zّc(tug8((Ee1:f "}~g1ˢiϮ _k] "V1APsYAf%yA""`C%JcDD""{M"bX1=Gc1GDZ5=~fv#T""i JP}9$o y3#نfb@{m|I5Ʋ[ R&ag&Y/w;ꟷz[R`wsܞH@ c0trNUN6"垛R?9 xpoo_[rZxџqPCIʿ2y{}GvsD,nCL1{?l"".WCh]-HJZ qZUD 2-""YJH-dO{UV"9mq]P<*vë2XDG.">p94t |]D7c6aUDr,=Gk轞a箐SD̦<I̷H<^*ZDT)sc;Xח3!c]`]$""v)̥fQ_7=ܰ/ם!y0NJ=MD"d!տ5s>iԧ[j{DDdWMS,";}^R:^-wyryKjo|O>v;=um>%}Z6C48>w!gDP671me+7tq~Ajuڠ(%b[E٦E5;^e%殽> 8VGut̵{xsb멭;<*"!^US0ZIgԠ5_juڜb*M 5s: >5Zo=cVMĒtI'֟\];|""EQ nx㇈-mg7;WpGKr3ͫ.Ii!"V\][)RX`{xa깛 n4v R+N#XʂQW hÃcncקnSE$'XRjOyYF !4(Ψ_MC$ GGue-aOv|wWmC)$>#6!0ZY^ekqXvk/z"y^!M$,c-v}jr5$Ӛؓ;}J^np )ƳonŠS7ee[tlY`Gּx@ f,Z!z0Zmq+{?wc3_C=9!-Q͎)KN*ѿْbII Ѱn=Gj\"n,-.Tď kl3"" ,P\0{N9Lw@"48 #e#b()q0\0'_Wj02Zz; q A A A A A A A A A A Azm3IENDB`flask-openapi3-4.1.0/docs/assets/image-20210525160744617.png000066400000000000000000001455161475153525300222220ustar00rootroot00000000000000PNG  IHDR,+\K1 pHYsetEXtSoftwareSnipaste] IDATx}Pg?+/ lBPN=]rXsvv+[rFҳQ[ZulA訅NguGv gB jB$@^~C&&뾯aw ww`J@%E\!E\!E\!E\!E\DL<Cx<d#3MH.c0Ls829L6^o2fx<>|8L\VjM%oǒ6 /aDq͙Ct:0`p c |>_ |8YLVŸ0[͂Vt= 0u ~w@( @`ᄒ8L(G0+nh>44ԭꚛ[[[B` /utB%|S~{\S]XXX,lv:7L,DC1#xiӦ͚5gpqj~ӟ:?iTTT~~!;##Εω/SYs5µZ-qnX1gVkg$:eLquuuݎoL=6S2+ w 577t:&epF[};vئMXn;A*fэے{g̶`j?v#p5S=1n j6up8ݛ !}h=40m͊0nO4CeS&x1 N#ۦwiP)lpOH4C) "GeZ9C8;:c|tw5Í҉8!-,aژ&[kxaÉ8M1۬*̬N~0=^ 7nd=o*Ta Lׁbԭ[2 `DfF?e­t "99988XdWFFBL`e/tBKP$$$T*G:u*999i&I&[yHw˗/={cB|||FFF||GE֖;܎;T*ڵk/#H"yiiD"q0&,ʹپenYJm۶=M'۷ogdd9kTurcqq1JF,{z8}F8zB˗e2a]lK$pQq$3.~^0//=,c^:XӟC 6m13fϞ=A*N_Y7~K CB=E Θ{l\ژ FEpM{ySn_LDAI׽{֮OEsR Ͳvwi{g ωsc0v`26pMݩM9GO׫$ |ꯝ>qݹJ)Gܝ`@ qY6NޱVSyt}龤=MK.]tjZy>| tT*UJJJJJʈ9\.#McD1Y[7ojjjp[[CG/6kxv'>LB +%%D*Z=%pJe0 ň{4Lz{-[y:ꡂ0jR4 ĭÍ < VPR=8f-"U|BsMyNHًk"h%cDnT]:5}4\$DQK6(l:RNE"(}Gۺ.]X#y*Tc"Qb,%9 *)laڞ$z[c"n4YͰɭ[ED2~~+裏~Uk]h]lwO+I[>G֝RPsHR\ںdNoe I궏>ږ*;G?4~yC]}#xNGG[.6Izj_'Z7(/FEm%US l)ޝ5t{[wyd#p {޴uNѺ%eftHY[ \}p{[~o-4?ZSP}(`XONNf9<MͲmD5uW]:|Gc&""Iڱ-A&l7\}[ :]>p~>Gi=<)))))ɓx0 L8joʋsT#lgrΘũtxY9"jV$It\V*Q-5(F2ªʚzF&Wi%`lXWϓc9ivp`Ҵu.l[ڻ6 -J4TVݺ$- 8tMvHUS.V\zgyxpUoko'"ĬmG{#2|Y3̠f-{BWk)ڒ}!Ѭ$+%߾PUv޼ ޜQ^JՔ= 2F~`kGI{.sqw{ѵs980Ǐ7?Jv8l5p۹-U2GD8f:.N gIK Ybk8yu@ĤЎ֘-hQ&}>ǺjVBzyuH. Ҷp^&%YCn)4i+o(/$ȁB=de L!tt0קQ$X)Ĥ5--QZ4 IQ-y]Җ/JIf 22,ɂ"٩rM姆pRj٪S9^m J1$+?PTd'p"Z%%٩K׆Y dEמVU KҴDen="7Vő$IfSf9PNc-޴'HfJ~jӪ57:LMG!| 뻵@Qt ixpl甸糜Ŧ]邴 :~ԴYnk~nR'" _"]5Hإ {Yi?[lGBt֜#Gٴ$*x%Gq$lςgG-Α#Gi4|NuG.n_l| ?\慂l S X>uoYڵk׮]S-xV_ڠH`m%.0]l$IDDDcY+є?&hK'Z%+UV;xaҊKVő-~^L~;vӂ-WzuG+߲61kGSp#d" As>Nٻ`0 ^7 :Nk4FOs}3"j0AY/F͎ NR ňdDRZZ*;gpq@ <x<;SH>0\)/ߒ,y/Ǐgť b.H&4MG#3gx_b'p5nLZVΝ;|}}#"" @ `!ؿ<'`J&16јZ3<={Zf@ng {«0 lO݌Dtc%|``@Ξ=G?o߿/ X G^ǻvKɝɍ 788800*&opRڪV5V ` ~7O+ s7'@ p#ȇ<5@J۴GB8/4JƤmkv 8!82 Ə\5bő\ !Ƌe궚ÑƉY6MQ9`\m[%p2.g5r[K$pDtg[ژ\ !V a`l q7r8[ Lc@`tz~``@YHGpZv`0-[&pw`#"qqJ88Y7 |>חw`۫-JxA Ncg7|>4jp&', B(>>>l)q!~B88e]#["Gp qa0!=0M7B88FhXtZv@;0iYS=>/Pd7aK s<¶3 }}=O x{|Ui zJٯ}|Dׇ{0p/^3!{&N8NzVo-|^70lLKj09~R @? -^hj yzz&~}؄·0 {~^ {{"_\}>ooU{yy`8LQ@gڣ{ݠ Гt ۍzv$YO*#^;`z+CXt.<wa@ @R]vFRD"H$R411155d"*--up]w9z(mݺ>rpQR\\| "zWXGW*jMG7Ƃ^ J}||Zɼ(dv@n!4!t:]SSӤ*jժUDvZrȑ}rU BHOOH$}T*u{{{<Ȣ#%nӂٳmۆWXXXUUGDtW@Tx<N7mtkرc>h ݦ7_V ž?Ofx}}} /&p~MCf<4gffTk߿ ܔNkmmAHHȴimii,,,tNcJyyy 4BT*UrrrMMX,.))qWwcoii9xZ={ƍCCC??~,mQ:DDYYYDdؕpO2ݻwd*tCwwOX*~\|Nw.ooo^{ŗ^d[[:ujժU,;v ܌@  :e +kזꗬD"\v-˫7H+..f =-f?btڵlzqqV_xw}^Pqkٹs xֶ{YYz8h޽DvZ֎ٍ^;&pV "PXwNTn| ]tvve)}8pouӼ+6osJ~rBl޼r}E.]#=kgο11OpH$N|ɉu)(*yeVV)c{YMG򠎋(5#ee3-nn]y[FK$#&.e:xGh-=|CQ#ڍr IDAT/:hܥ/ujC6Ҽ_:'vьSuhGqZgnCC*[:5Vv3V޺O^ݵ2+')Pm0d۸dWv(kf7^?ZĹRfqD{.{꼫Vf'4{6Qӹ+w]}(6Ɗگn|cW7/W1D%%Ow賺#=3_RמMK~2"g J{g"UhWi-f׼9qܾ}[T`VnmmmllvZcccaaaff)jkk3.ao1QRRIIIUUUUUUIII.nQ { dV9}ӧ 794j5hlm0?~M4c~|, >gU4>>dzJJb < >CX JwgndwT.FO=.`?OR֭`'YS2^|E[ZZ߿ѣz;ˎ'Օ^u^"hg{^!n_{>{P'"qk!üC֗Od=i՟dsY'"7>9dOܵif} f-> ܒuJ^mOϷ`!|D ;׳6LD2r .6Q=\wz"\rOk׮E',Z(+++88ۦTϟ+_>;;ۓ;)Fƴ 2IIP\vwޡepf2{8jru:Fyv^Z9 <!u3?q_ 7؍,M\b㸦&Jh5j >`.]4qXn]LLLrrSjjjܾxe?244tٽLMMy͛7ue8'^٬+fqD]͛GҊ#n?)(%D)fW #/=\IIy6^IC -]r߯杯M1.9OYYoyYВˁ?l׫4D\\RY: {EyE/"p(vE(֑7`x:'HdkH$JJJ*((}v|| 6_.;{Ǝұ[ ,<|@h Y:s F!t& wp\)G4XrX 0f/v@<kPYY?Rr޼yO|!~8/Raaauuxp'|M>R5j?Kn>mq+r"Z;ʝy K(vz#whIyv""HNw(q6N~hc5l" fN0?jQv~޽{2lpcR4 ֭Wlm³@ xg:;;QD6v3~|Vk+zP@w p3+~Z{ ccZohh縉n۹;Ny[]dR. hَgZx$ SU\ ^}.DMMhIl+g#JwfeMDDVR"v∺+_izeU)ы/Jxgs'͘i}D=ԩ 9ʃGybV/w~i B?z4ڞE7Qfk(Vl,:\u=,]}Y9[(8h4-g?W'u܂;"!'$ʖʳ6+ vܢF-\YvG_;Ehm{Pj{:[53+ZIX@rRVCDT_[OxMÛ||ch%{\2;=jhVUtv56O;o|acP*..6+_*fM ʒ9h4ӧOOOOK@MDݛ(/6mJ Mdmh4VƵ=lSSYf``ן{9c/tx"H T\\ƾf L&l%.j* jjj{ ijj:vW ONNş}ӯ #"z92ݿ˲ܓDAS*$WNFHa5v""ubqq6WͲqYDrrֻAQK_ qҤ' q/[}Q"""r~3.JzuQH<ߟH<!f~Y]{@ ܔ5kE:jegT)>͛2*/Kz[Z'?ؠ]Vqaؓ-@ݙg@4VnmE,YW I@OdOW=[rIaa!k͖@PnvR,,,4g~Ǐ{rDzmذ!//}^OÙZK'˗Frp\M"VVWvnӦM1c+\NJOiq "fs<{ѣG+**r]mu1@CW cBDyyyqq/ n:}/1h+mUIAePn:(!ц+Gj{}=W8Gi沬}~\tc_64=mwJ ֙vdںshY[GvJ'F)o+d/sD6m.[aQq*_'"h8>n?uJ"45ŧ:xt|(4>PsΝA5j[(2IGaD9YLDbM "R6֙l+ɧ,3{|+otWQ𰧞fƮ޶tcO*++8.###===::u,"""::ڲZkll4rY=<>>~iiiDTXXUCBB2339*,,t5#""XwS'K.7sqp\No ">^ES1N9؆M =B%` DL7gj_RY빭^XX%J'ef͙3ǹtl:@ ۆM7z^%- [T?TJsS'˶WUil=ˬ:E>!3w*MXf)aS.~ZiH`kk/PQdʫES7P4Ewԣ4Y>#l28:nl<"GDVng?7zt{;=-zuF睖 u}m=F2Z3g ꤶ6磡WVWrskoQVgJ䕳`pl;>}LTgff{6f_[[{LBh>zxCCý{X繐9sDDDL4POHH0ni̊v6&9s|94jM~vYLYw`K#*usLL [ XK6F:LB1114KUXIJڱcǼyD&͛o>wX#\6)݉ZZZ.t>kiy J+JrDAY+:kOu~%kzX/Fخa(.`/K͖774EJmϨDD-fw:%*r.Qkk۰Ggm/D4?ύ90xQgG=ֆgО={v, 0qcsW(maft D4X?碥3#":k ?4>|qs]_9y=]p숎FO7$b^C] ͏Z ;qkjw<޸qC,gffD"jGT644bc,)wzC)˫ꂂFFxQ1'$$iӦ?R}<xm0X&^O2k,j糆aX)!~`0x- Bn˶|XN빳;J %%%آiX{0 jjj,x߾}G5QPݻWR}}+=쳏?;#kWJ6Cv NsDl{ Gt*U7ff&" rCQȜzHQDD3gٌMD$d Gu(J=Yoɦ!mވE5}BSz5{svγCV ;o ^/afI z#/F[gUUE_yռëH?1.w͏"(FJ\[!=q+49vg:s>N~0XNJJbݻ㯿L{ ,7 s1c=-//w`ւږV~|jz\.ONN.--e/ tk6~B@"6z=_p7wxO '{:k~Hc|^nzacǎH$-H޽[\\յn:Ow=7!Y=ܸa{3qKD)//we'SgJKK&؄+ӵ.Sk{sM_6囻'ID:n.45YW~l4?'"xQEUsa!+ UGC-)Y!G(&˖-'r!?>, gOI[;sr{땱VmS"뽜 Iy`ΞMjSX6ci4GT_HDPC4#.puVvj/taꌷ>?G5"ύ Xnhhu mĚۭ,ZTTĖ'%%VVVf0VR9Yr|޽+xyyӬm ??'.N1ga t:{/YHch4‰1X"''Vf{zotsg%ŕa]JJG}!ORu:X,=W_8,ebӴ N57o?̉.k>MEwU7'6!YӨ2V%w-VݭJD+c-2x],y I]:݄y5lnUSsC3E[,Vnm!й!c8xG޾֒h0sjzl,aqL=B>pF#qWKڈ7>qd-訽eƞ;E 4^nT7tPv Qt0DGEϥ; uXI$wT\!tWG? "g.'kVw#"tO4hѢ(;;.Z} *br||7Xp5[t&ϟ?︐LY&pִlRtZsر)Rev1TO6-66Vj4~}}||^BV*#d#"">?00 N>x2cǎ]VReddZ8HR=x`B'p"Zb{Ӊy ŵk׬. I 22['. {LTڰ94%(?.6%de%yD!%v>NID~WTt(ܩ|%<:8vhd%Eݩ8kuߑ܈Z|0"VOo̍-vN3^3WdnO &"MUIxVmf_2ۑёD= \BEg-B ѻֲ< ͛MID)ܵVvDh"Jenn.k`Vkkk sss-Ƭ#:jv ˦3j5#-Fm?fy{DDUUUv >>v7`ck}@^|_֙Ջx<|8N(tEvjNDǎ۲e !;ѣG< 3fLΰ njMgLep0/SKzUɡ]۰ sC"M'/}cgQOctӹ#Y]LC盆ߡr(*QrJwdpuemD(Eڬ~|kţ:xG~+vQGD_R|JC-zsV /i\$u^h>\g\ਭst֍tZ6׏gZjzHɆfQѡDյD\te06ahdzK,GqDV巂D"QffX,f[ܹ+((h4s1ۿꂂ\\yOTMWjAgqc8=|R IDATJ#큒Ev%''xz{{[n|V}A?o\Q~ꕫW._r+W˯߸~ZۛŽ8:L!‰hlMFFT0pb󖖖7oGqs$%%ySuC"z-!vtT. h "+W^iIsR ZIiy7`!хol>W=ӁJ7?nfQ|hΝ{Limၓk[ƚ񀺵Cw4D\\ ڨWljJ0g;) |CIjjGuf=-JMiȻ@G驖/9(􌁎'BO'+0[k/.Qڮ8j`")x55Y=g݇|+?:tTV8nΜ9/oy戈OJJ?>kbo-EEև-88xÆ n,YCY{UkjjȱV>#.xDVhI>S{j[]]]D$XƆ[ʯ߹s{c7{B Coo3~Dq3X9X w|lC~r.lٲo߾T*ժUh˖-}ٟ_}Չ'mk>z{{?s[Mw.歶ukgο1|y6/*dՇD'gPwZ*9QgT)v}R9a}luV$t 3/ˎF8}|ݦ%ꑗUD/SRp`ጅyNW=(7m LGuGbv/(z[}XHQ*wiomt%{KHq:[keԙ2׷(4(8xaFʫg_)wJ492(vobF|:͕%UED?I?5sW4;;-w XҩT*ڞܵ(UzSFYUC-~l25ot.i }a[zN/7  gr񶼇ڪj; QQ[e3t~-=E"ѢE-Zd^,aRӧLko-h[m96aV0 ƗǦL4Ԭ c> ljlV ? ^^^kȍ BKo6+$^.ezq6]ףFU g֭[6'8uTFFt:]KKJ)d(ZJP8xggϾ>cvG͞=۹mܣn{rS6$$ #.NȮhJlod/Ů<|z~DIm;t,$&MJ{92؏3,*MY2?vC Cg " nQю_nNF]Nۣђ/8崷soc(zM CZ秬ߙ`9mJHۣ 9m{=\:?i5flLb4n30z[Dۣ fJS3rX1|?QH|Vt6y& 00t۬\vзVC܌Ѕw>ۨ QS47^G$ 6m>3:GxEnLca,6lQ 2Zt7o޼ cP+ 0lp^/ Z[?}8#ξ/:q-N' oHP(GhRiIIdOC555i4sT*UrrrMMؾƽbbbJKK' {`g]c8oblMswj:22׿z?׋D@sS꽿rd'w! ՠ8=nq1%%ɢ'wC\\Ɠ.qX;888++xcUUUYYYfff``'|r=Y |Νw͙3gO90RXWmS4R{m*//ONN4x{_֮]{15 }P `=cBtc4x11QP$''gddoBXjUrr2M]YYHW%$$Xm&<)Ihhݻĉ#[ѣ'NݻBCCC]3Z\\„*W{Itv.9 ~@_Vi(2N:ރ :79ϝ;w)VSa;,,,??ҥKL: ƪnWiY w}_UUeL~z-s9rd߾}T*]vT*6mءP(e2Kw޺uT*UFFFyy9܍֭[guNq:ٌqn׿b={K/*XniiQ---~Dg?j0ILO]၊,FO F Ԓ\jB\Xuk}=DQVV_4.̶O;yFk 0ȲWq\\\zzLTZEwt{۷O"43@GV|t @ h4Q~ nMP=z5̴%,,lŊ[l!yL&c/zN/t'av{?c~VzY|`I\:gkmme[sdZ>t#ibqVVS:yHY|yWWW@@@NNΖ-[\ОU>|X~Ml>Ƌ %K̜93@<~01_vM._y|ս3$d_ IJB ""ץXb[lkޖ.Z{նںVkZ[Q"aOBLf0If3e&G2sgNs>g/^p-YƋcCQ{hڢy︸qEDDdee1+X cu܃g<rϞCE.ĦItQⴱ́N9xp5@V,11>&6a\[[[oI𐐐{w.1cFQdWO(J* M&SllʛWTaF0,3B\T.Z4mdw˼a B8åt񘘘aN%zq]mvu:^/{._1aB, | Bjpjkk ^y+KOJ ivqG>1!qk{c !\s%!6#{]Iqщ:;:fd}QòZJ'((x֜9)<1\`xpa3 N=!vEe2J Μn5L=ivw\&BP( Ct)SNJ/ !' QB]]yYi]mmkkdR(rLXа ))c=d8A0<p/rFh78Gn]եho8U@@@pH0HSx{V2 ?m]z~CƩTT*o@{Ox]fy48F0 4pì}޷խVN-N׳vpo~ 0Jf pïdxF{oP@!zFipW `8VGﭗ^7LJR.ϰZfbƞzl&3 x!=yfl6*cf`(F~Tp+Ydpp4߂sGw+CG0u5R*h>ѧF!ťW%>0Fm6 _w:g`I'N3=ȑQp#7{~j0rqA ?oa$70J0;B21ex^B8CCx!!!p<B8BCx!!{{u*B8pB8np\e7r D"l 84f 8DfpUzM`0 y<mxfB8BCx!!!p<B8BCx!!!p<B8BCx!!!p<(=ph4WVW\ohnmhjz{\.dJZ TzǯɢlpTLk7b4I)}T!ɑ1ABWU T)*,*+2,=-)sjZZj^eن-%[GgvLRNZcWn !(u8 M=wƴI#*}䈾'Eg̎Az]\Zv)-@<$%)~쨈aI{fO+Z?G':B5WB8щgw|JZ| Lkg͘2}cwcw+dB,{{ J@pٽy,lɂa@œGkF?ZKoypWv8zⴷGesgM[|;ѓuxƊ1\G6τp 0*{.8zÁ'.8Yꁊ'= ɳW*~;q=Uճ 5ϝۣpZv|ۣ]v|ݫgv<<+v<ܤ=QH!]{rZNY]{rܽjOZNYuO= ) o@!FOwKY8<6$8R^Qg_H4 T߾fjdž1tAs[J˫NO˘bRya{wy{ n6[;|$Ẅ́QѠ_h01>U+pQѓC!fѪ !޷wЄYS#B#U¹+9%'G: ZSѓ5wepȺuU1 SyB{ۖ=lҴ6?t+]*&~RĔ)Jak^0Ζ|Zl(@M"M Qڻ47:y5'QkTpW M>&[dF]T D'aӉS_Qx%.` OI`Q]~VnPTTReiӷ33R}S`!7f57ST Y:8kǧ>i7%/){ڳd}_=P!2b76|zt'Cl[~Vy9xҖ)a%GZZZڜUN:iK7_VpPRtZRWd2KI"3ERhefeDCk:%=dݷ}veok}%ΪY>v^~Ⅽ0L+!l'-}B#_rӄ;v5THtqpz2ś!>~ ޻)d!&G9lB8C/%N[ys륷';+Z6)yɢ fGFd2Ug_joh쯟^7MEm?:=&Te/-q]}l>6/~)k6/LBqGO ︹h4)R?MmIV^=zWWލQdJߦ0OqЍ.Lv/iTg \EgpQ6jW4o5Y WCNOX,RsޢhsN7/zoJ!+Q=f|0%pdnMlUz?o~ݕ6G9 }MZ4/:Ss~qoTr#U%njW:lB(M+HƊږ-[ 4u}g>qU>6{ { O$׵욷T>{|Oh9nwB}SW>/}go:gYJpw *mR%$g6ɵX,.?}yPշ<¯>#^?Ph[GВ)6ź*>{hB4}@Qvq5.kK*xqcN.ܨ&Oc6VWɿg{?~+K蠿/}k^"`b{ MooM9 !:?-zWWL^aC~>]ֻ]'+34QD9o!]h8v܍u'όxQKkS3lksoXIXi;й?;gS N{3}͜mSt|Ra[-,f#uz?°DOuydK?5Toޅg5O,HNsE>O[QPre'vJĽmB8`_ įr ϰ˃?lcдu ⪺S}wbB!2l ?bD[/URLLskCd[mHFe;R7M<M]ݲnOن^yD{:;f͞=[mCH&jڥ IDAT }3skCMtwNoP詹ַlr\`BC7ԷY-67i*l?U>/Jι/x6>&Zx!jk˛[wxHt7~_b0v%pQٞ'g2j38d)@>˥?t(3~K+kio-|; yuk^Mu!UW_:7>^>ɁN}so=yU_rtqX6~meo"v;եqn9Ǽ5Spyp6 v(s}2Mb8QKSE7Ko_S']8ӉCl2&hOo}WoP*hOuӬ?\xtu(JWU./^S/H45ٵo *WUMNK! =Bp[[SgZ\JkS'rd^sΙ?>. 2aࢾKxKQ׳aJ˞**,h۷';Gᛕ1iBo>~3~8RG prWRVYVqѳK/hmk?u]]#<“ V} .M-cheK$ Fy u+O)4=:/ɗܧK}(LdSuSh Öf$OYo.4:#eVFz26^8_v&m>wx/ko{sWޤR,͈um&B8ǹZ[S#cc&I`09ݪc {7>#3i˲򪮮6hQ|~N_C}ZԱ~6Bf椰˧j98Q*':ݍsCm>P߸;6>vTBQQP<ۇc,vɺs.-^D.3X<)),4$5%188Q!Dmҕ='0@}òNwty`rۋsWvEc0V}x:.:"",sZz)ST*[FӗU+.+*)'}PP@Zjr\\q1g #\w\QYqIyuM]Uu]S OzZJTTD)) AAN-_jj/=%`YJ2f|TrRBniGͪk-fq02L nmz_[xt&<)!S҃gfdƈg NT^nH B^#ɂuGl4wUقŽEZg\VĤM:._P eL&KJUH~$e>m=zYy]ںG.ypD|ozg„^xd$J!HKٶPgbS ~qjHQ$OitB&d !} jZϜ{N.8oYU] lX.5%qggfiMfg .)ks d;:kXD؎ޏCWXj1㣼ƛ[Z+u r=_[Q !N9)Bjٙ!b:pla;k(BBCC`  RHOK:{l_'qU"}fκW!ͯ<n%Z"[f_ΜS{/}O]s7&bzGnBt-i TΏZv X{ѧ%)UGWUkSOلG%5tV!d!.CԶOòlUonyގՃ . ,*O~ujskZrڅW/Mib` ?_pf={EbB)qf^RQ__!I _{#c2746?3=L&孷p"-(jwJAFQ=ʚ8j|ɺ q1.fRjDfGgWZj|Wt 8+b8\GŻɯ9~^xx6wGG&2%luzç{'2.Ҵ&n`Ե#O,eM/wأia>ڝĹY6ڞx=m}VD0X-67M{]}J啵TH 2'r⒊&_\Own~?;??B1 RU]?Oǿ9@Ca2`{Iit3??}$&K[{H4]gkSm띾kؚ1)LQi פ!DWY0#3B4vQ=s@$f&%E k,,:UQgynfFf܉??x-иԸ`0U GJRIOl㖊7((\qKӂL /TԶ\]ǔ\DgK|31Ȼ\2^ = V7~G\X^XT: Ǒcy{՟awui}aO=[7?7dD'ٚ++*K]#.v|rD5G卆^SsIM``@HHt|ivYEHIo,:HI]E~]Et!(=T+٤huóC&-_]_]ٺ~t[{9#B-nݙ>,qL'xИYKu]H#Z!.77#{3F봇g_3Q/8sW/fϱdݺ_ذ6M..)߻gV8+{[n{߼9Y/<ӿqppt?z} @ ͯ]r|=wz% ݒǃKTWV;1 j=uG?Bk&&čX|U>p~勆/\Usct}F1SSS35}G8;;Jq̹O9tyxx3zEmn?Mp?۹QN~rooX-zW;rw칁d2㽭N@[w57#B}CAQI 1=tEzj'0u>Ϟo}G~UյN/ P߼zwdْ7߸+ %nqm-Jԗd 2![s` K{+tlwK/oߛN'yTB.;8L&ƥGjޓfΰSO|nX~Jn<)yk^z-6Ѽ̌a B.}=wk0wxLmq'O(,*u`0hg~Ƕl6746 bKWxq@fL޵'g疍9)I˗f_Ssư'Jl`lK3G䤉dϕncXJ+>^S{<5s.H*ոݹV ƣN|\9nq[\bdˮ_4)5IY3_B}_q狱jh5]A``̬iүr:bewq'2ޔf(\Ɯ1CoX~QgsGVHO;VZjRd'o9[4\SsKyy ϝip#!AzzvL@¼ߏiD'Vy9BIIq1ҝ,7S\q`7¼UE2&}mɣm!dS&'uo{I#rw}=+g͔' eB6)|ݚ)=+'=.o`0rԔĨH7&ƌ<_-ѦE7z]pVj{">nĦtj)i5M-]M$gPr;c__D4mwwCc(8xTX5\w/7s(2Vu߁nsעHJON("b|W T*xfONߟ+ݒh4+j.74uh4Z4MI&*j*4$(:*<>v|RBR)aSV,ʶ/:7jOʴz*F㝔 '@4?!z; 1§NI{F#jո NBxCCNV7:C>9-98I2oy#m--;01Ǝ>|^k_ߤ/Y`M˼8yfl靜V L&s>BGgh]_߻B~].6{ >)WGGgZbhh#_w[ :m\׭oKLgbեjWGgA &$$(00@[lFC""r*Mm^zmͭ>o6?Б\Ͽ#kn^1͟7?}ԋ hvVUz]>>rS'Z9a1`L&| Fכfwc6[uNvn{f^LMIi{l7?fUxD+u:F611Q>dnss ƣhf}}~~RཽhtRIn !f( ]NMInG}N|Xb0s+[vc&;YSdopPRgA2$$H*džn0%ժu#"loKMttڊ޻-?ZRaF땵=rZoj̄Un<_S_d&|~NЮ8_ݯ+I7_!MM-}757KmU&%Ļm?XztMC|-U++{b' Tc&9[sj;#'\.S#yY .GggWni6'ƪT=A[|'l tH|xxg…>>>wݾzRjtҊcíVk_X,{N~W1SLAQY)\i\]Sރm"C'9{)JեII2UUn3+kZiy!D\ J%.wȪtq0mNK<)),4%~kמDt!ĶOvM]Fo頵>!&88E}}}ٵwa7֨`3!K;~򠯯RdhΆ&fS&EKA'&L+.){䤉N-}Mz5*2|jF,(( IDATY3J/z FŞ:!ĂyJ'EgN_z?]byL:Н)(vBnlj^P]]{̍+ cژY.ػOw;,};v;mvsfpPӥmnn}jkYsIfsfψ5qL6ono[]\Rt0B⒊t3қ+\\p|Qg͛3cp \R;;iryqI<oK!b長ǎ[]_ů/X3g$ɖ_?oB`Gz-wòŎRߔɩ3IՑcyPXܓgG-Y4?22\[Bz{`sx݅NDONʫʚnSPX|a(`,K!\aX^{ ϕئ5ZXT/iB\D"g&&-:{_khl~F#'ycyץKfLv[np'/ߝBX5yǞ>!&zþ7;sٶOvJo">u٥ŎwUq';\y̞p[' O 66{L^v"skrO)JѾl_ddLȄ:]S`"VBjY3nqG>b|s쫜cr<2"'&r7!m mgY,7~?=- |n:ⴙ ynQUsfe:+@α7\7$>+ e2kꫪk\O.̨oԽz}uu]UumUu f| mmjJZ,ƞջ{ݭ+_ ηvui^x5g 892ϖBWU !c8-7/w1g.7s7GM߼{4 !.}=wJKJ+xa~X^WF?!55QMWȱ1d̄s\? ۑSqm.LL֯.7_゙~p7S~oES|׾u0fj滫gEfsuvv c&ϝ=㞯><>9g?y8<̍dot}]+}}wtV,[[wfЂ}NTsu%eeۄNBfLⴜ^YzEߘ = f?OޒdW-9w䤉=AW OMIs?_~aWU<~i*FSSv-N^ykX6[ֽ9=;5%1*r֢HJONt=c0:Xf̄p!B;6*ׯRU?hHH_zjF#m jG3_!!O<'& g ׽׭Y9dknaFfӖ%/TccSN͛=ؖ<8=]sDk[p(QnQ&nq9Ybt[WXfehw/__5+Wݸ4p(8[ O)v՜YN'H&MKO+.x_q>|kܸk N`zwgO;wiS'[sP.?W\V[[/&88Еܲ`m!&BUu홂ޗ0: 0HZmw3{>[Xڦh{N3k3&ƍN/-s¢R{6Z KOK4[7Rgeee1. ciO8c!!!p<B8BCx!!!p<B8BCx!!!p<B8BCx!!!p<B8BCާ)hz wZ/H_/jÎz^p+-5U[xq V0-{t`X½/%]/K͎h!TXJf0=[7Bgee fl2B!|j?WX E!||*TKNcBFFgB(}j4tB8kPõ[iz=_?3 [4-S+6^"϶a OV ?ww%3m\h Cs1GGhMx;Wi;|sEUUPд^j5Oڟ,JT\*Bn~Vf#}fاܳZ ݦ--Z;tl>oIV!DYvsƶU,F,3Z*Q"B+Du!Gۣ'LfXxqf?͛vi|vbUOR̖'4mkz|[ۣʰ N"΄s>nO+b/N%?`|LuǾۉ_!5_*߻5i6Cm օ\\i]նUȮLŞ[ڞn_cOק:Û Oikj(<+N}Gvq6n>ŰFPV!d y wɀ;xbY,5skUo[4kaڞV0h^ z^![{]*oUoP a*lUaOPg8t!瘱Zu.a!O !O[W\JBzյh!tr̕ \6nš_3o:XMB!I] V]0MQ$p!O̰_˄u,gg_^-yߵBaɯ`.ě!*TJPKpm\1T{_b@{urBX+BBM 24'Zb.D1BamMCZ{hO2A !]T;gx'n2]a4\wO9%P!C0\PjBvE˅E8'"q[)StDUN6B';wki !Ed<R릎 usZk^H2A L(B{A;,/pcV!U3+DoYmު:4ҋ~P`QVc.tgP/֐|挹^g确N{x]V!𗱏F /pYZVSF!B#|0nZ8J'[?Yj1嫮Qotf: }:r-.;}'x\g=^â5ZYD#hfBX9X\fB&F !D\uߡCǻ0vYX41ZU7Y nX%W-5@ŊNDŽ6ԁ k^Btj]kz˶7/}7m&O6=Y&7k"}z~;p!T'~jqiv\!-S>NaXT\ZԷԚ|z-*.)ȯ>nɠ6B gֲ"*9XPZ Bxxziڻ/~TbUqdfrh6jc}UX[ o31>z a֛f+{{FMute1>jUVv}}YW !p<B8BCx!!!p<B8BCx!!!p<B8BCx!!!p<B8BCx!!!p<B8BCx!!!p<B8BCx!!!p<<}m#$ \aJqKHȱ RKGV98X@gN.]R:j_F;p@ABL@vlNBY޿dK⻟pppppppppppppppppppppppHf}}}ӝpvKKK3Itxd5٣Gh^ XUt^L8DD@DD8DD@DD8DD@DD8DD@DD8DD@DD8DD@DD8DD@DD8DD@D2zpz@B7 jq-&f򻾾5_lwhW r2rq׮=xk|d@7\K>{w6v_{v]zzzwh"t'WpZW铟ѣGM9t6הU#zq?zd]էR#|}.۽&#`&n#1 ##z n8tXz}?/qDo/1@7O4^~Cg~Vb׍L$63͛'= 4Ie2@UKip8C$25@д1[W81@wSF@DD8DeOU~{U!ܯ:>ï7F]쓃{ᓃ{WW[ zjxt Ҟx=%/W>9tw"+-m?S;ތfq-#;%}?q-s?uH"|XYw$L~0ݔ2ǬEOyObLt&Pن}{ycBWԆuNn,c9c<; o'I̬^ȇq7ln7)m̌Uz~ISS&@D8t޲gGy/xl7V}Vy&39mz):U۳e¨ޣ'uwAߜ{ri_ipD8tݛh=01}Tmg\&wվmjdc6RD8t+o";/v$hG( 2bG\Ճ'ߺjOOn_gfg舡Go4^p_ qה]3mPo9f+j`͗GBNz`@MUBHFGB!3Ll~p֊] 6oVYUsve[WghyM'KhJٗkGZ>߱R0_߷máĝ#Goel]mg~:~w+>ï'$":ގMó{^0*74dyݚMDwBb3v̅R~nܳ㸧Z9fŘ+ww&)o]kv7Qwz]{? }lo(dYQ|7Ç!PȠcŸmyuUC& kBagM=p@=m\7bN:igܴv6 IDATFU~ұOHg;:t4ehƄ!^5>\P?=CUϖ={p_ܲE7|7?qm(wp{+BWXrepD8tT.BN>.hQ?N?(_{oѻ^wl^Eb=\8 O^A`Rb6z|`ճ{:šXlm5{X;:iEzuӾ6t 7 ΂!#;w2BOǛwM!1'}Q!ǫ$Pq3u5!2}/i7ׄ6~_7Ǟ^_¶{k@UcRg&/[7yiq̄@CM՘1!1+_~~Xcissss[ە-s^аyWܤhtyޏ]P!sGL1QrSk}{]17}oؘ^q+Fӫarg?yL ;lh:>v75xaoU7thI@Gܰt hR~#4+{#ie9g z'ݫ0]pszϒ1bBvR[neBj>{qv>mzu~l{7j~˱?Q?y=9,Gt/ʚ焳zUAW Lu.BYiyTó[>g~ib0Y0w[?ݳT󾽮ypDU?M~ƾ֞_qG@9/~pG>8.pj{q14CNQ]٪뚧GWtpamz$^{'NշgO q$6͝qwFeP:05D8tk*N6Wo)HnUru'B֖Ohw~ώy^`W9T&yss1, $]brp@^G}\/V7FfݶDަ'cՎ h S~#oШHlީ}iu4ܖshNo[1Z)8G{|̘돽xةjc^֬cŏ}3j[4u+s:EzTaIW׭j}cקLh{۳~?8I5/WC9!+egg5GF|9g-e~t*yHi]=8GeMW]]8.ۺ:d[PKl*@"x'3hTɯw蘾09qƞb9oxyûz'ѣw2O؁F,G t63e=r_޳CN޳^ѹ߅pOʭ~Y{svIb:N:ڀX co!j ppppppb~U[#f2Қ?z G4IeL%8ܦ>‘MI9)t_֚ .?z_6M%8"!ѷt߼W.?zOG`yo; vfJ1 =zȑ#GY5A)LȻ.LOOF)^UU HKKKOOOOOtp=~ݥ#&&Ӛ:|NU^M{ MGrtFuuuuuuG8m?c^/6"?###333#####۝rt@tfѣG=z4q<#|$Owg4j F^:JɷM-p5'9[^%@V__hɷ !*M˘mup!$ӓ#X D8L[Sp*ɿA~[:]ίa{p1|Uppppppppppppppppppppppppppd9rѣ!tBzzzfffVV9ffff&^^ՑHdfffeffpReddD󊙙ih^(iiiG2322ӃIQ'fwt8_M9䗌u """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """" """8-]7}ZpߑY9pvM_پ~ͳ˫ntQ{y}ֿ߬VC%?}%k߻Tv{/Ν1+ih/3WS~~wc/oj|kJY!Vtݧ]!m !0ve=ۙEYb&4<گr|NOíKF֙MKfݹ򄏫.y5O,::g۲;dլi4gu)]o?6g}'Ky)OdRa[#o~)˿M̽z>W,XGϾECZYׅI,ݼP!{ยcVltom!2(oh<NtcQC߁fuջmY" 9?cn8?ݾ;3޽^[E(zl%j6-nWϧ/{kϹw;}S^?蔑קz3h\v>#C5o^r6TYʗvAV^2q/ma~~/eoiٲ.}7^knX:iqwZZDz_:+/5/^[s#/]rf҅Ϸ =` ]vqqQa/viۿ}R<2Yz׷NeʺbG7zCOXxgkKN͐kz;~fⴙ?TƮxu+]:sĦ9꺚-~K%YQTݜ=q⻮lHNJ7zPGvgm/ق3'VU}U+7y|5!\c7̯.yϭ=%o(Eg)e{ʏ9cYW-X2}|sqcE|`B鵯C^yqSqȞ>g7DyuCjC]GWZ N|NW…?ޡ/-wwy~C#6r_!vT 4eJBشzmE6E7]Wt쑬oy7).Ytp~PB}->Ȼ! sB(]js8ÈSz64 2'6aP!\f/BI`Nw se˜~:;#|ФXƐ^+n>fN9 ~Ƕ>K7yʸT_u}Ÿt{K#8/JlEu)ɻ߹9-ގBѷ.n(h]%8|XaM;/qZ~Ƒ_NX 7_B\4I|ڲvgG~+,p Nվme>j6F]vqk{ڏ*<^|݊{C(t#38@7״q'_jO?1qkl.Iݗ}/9o_ֽxIɶpu;m\]|MW-jW|PSٵfѲ cS#CO~EoC|H?iƐQ!l_wV5 ?@'SY9>~tQ_9tdMBakyҊR=h䉮1ll㥻?ݹ%Ct=ƐT|W?j!z iw:'_x(v풥O|a,Z>8GW]Q~u[;Jo)UN$[w|𛯽K*p3ƙ޽s;B6e)>59&} CXOChXZ\?e 4۷ [5mT[R,DrÝ[;\4[7;Cw*'sOb$zNAgv5! kOi㍍>yC8O)d7 rğ,,|vck.57etM 8g;Cb'\r}TiGu§|om6(~o97m{OxS8ar' q~Avʨ LJ[]ӻ?'Zq2c ;лϠ߼[Yi`Ş?7!Ԯ]2oћoWO}$5)uxdu7MJW>r4Skocĵ޶~MJ6%ͻhfyËB7Sqo|/ƃo<ށ ]B[[^ooBѩ?*)ђDW=OKy=߾rm޽|5#,e$GIYi;JYy}nWL IDAT2,C9ӷ2 ߺmM)k. 蟺߭|YZ" 7Dx zSMrB ny2ꪛB٪)B>pڍZsC+?֝;5vbȞ`+_lUY!vwK{cSB?w8(nlT;NiEq=K6=)gȏTz'eO']icڽ.:)/f^N㝒xɢS`K}wϚBM{e4wBa!^،{y<ޔK%!ؕGpz2qU0uIc!cN+m\?w~wV؀wMNږށ?'߸\gzls!9SǶe+˟A5ݝ+fUlj%oE!ҺFhzkIϳ7+ -/V?;I'[~tR)𑛏:)٣g̿o׬Vu۪?y! ։7C֔K^[:ap ,p6΄XֆR[cʩ/H^"gĸT1 -OVՖ;llӊ'\kWHqxEE'S٣AMN89<.e)fcn~boAzc!P[<џoC\P-~hB!^eW]z/Hy{9,~e#S;w #ꇖ>5\!d_uNj9M}dFQ,4x-?;CXP.IiV O,m[Vͽm_(yWM:y*E/Oj-VqcW~I˚Z>AM-ELT֕{V -[xC1Zys&[lٲe?;~]Ӌڛ={H!dO|gsƝR,<[;6nMy*f g9³FO}{C|:p}SXx:wAӌ^+6]}«g ҧf|dR9k䷦>n(:s.ԲLz ͼFWFwzߚYI ' z Rm㵩O=BFg?Ⅽ\:驳G_ /V/ PD6`6b$>; R竾?f0g>?w71[ ̈́'SvI^IR#\ aъ[;vZJ v=*{12Aa6G*; BIRӥXI|.)TPW'@3P%),VvMv 9<_(K3ZnB}@LVRx=ؗ o WQEʕCIo'kQfm6sYٿ{ 7!_G^T›>dTJdNP`7[l Yx7@uH%̨T( %Q/_nyU"˺Tֵe*׭EFvHPI?ʺHJ1${"ס$g}r"TNn)*h庭q9!w8PRm7T:e@Q@+ߐpńG*inOWVqY!I^.W飾OGfs#?[確FyZ_irШ.)Vp)j5%ҕOvcn_'{S%e~$y tА*m]a+SܶXu<Ң#3VаG)Snq,%栩eU" FtKv>rRg:J{rk}aa:2ۉ3R%rnUoNRe-R)HIks}[eAKm. A7R5csQZϚR2j4CԡAH|T_^ɓy%*e]SNEx4qH+Lr}/e->;^QּrRP7HYITn <>uSMQIwrlIc̖R%uz˭+UJC ki^ 8jpPSc5Qܨ) !Zs 7jLCwqRSSijZ~_k׮wmڴE 7lKWDD_H͔aWjj^1 6 wܡx\.1&˥xqu5ܨNjj"""$XXDDRSS%^p5Upb&4A2n拆/'_0-%KQ/PW܃9K½(@jn)|q SQ_$|)*wZnK/lPp&8);C}Wr 'oA%htKVp8:B}gr? 'ZnK IQYJXɹa/PeQnn!H З_qWߢ~GmݶSyv7^\׏Gهrju?R\\V}<3rݵ:wS6ik5~!#+{ٍz󇬖XZ̙Su5G@Wp-w=48oMG6j&9eZ=3g:,X"{ʀm̍2afkkԖn6hrs$Ig-Zb9-(XtM[@@ɭH jJtKv]|7Wvn!#Iоq0=MyMZ^Un6mަ3E`f$GЭN}Koh鲕j:IgWևZpNN?mnzug_~;3eջg7}x;y}G{a զud\w('OϽ0]{u9yZZ11zwsJT\NSthەiQQ,;efy:D j믹Lqq6-y%6IɯJ[{;h^WUʶ᫯~銌4~Ö olI*mtt>{G|p_(>.Vt.{N1㲟ɣMPllL Zh3gj͕$k̙%X1ͦ[JAa-$%''_*(,RLL x ]{եjѢ[I=?;2p0 -H[vKƎh-NiW8wzIue~xǬ荷?Э˯+9U}WoA=$w-w.SN1l%kO z#٣1_- Z~~c'>OSnnl6;ou嗕)(,RAa*ݏ?7?0C(ϯ )ӏ? %t?> hZpiPR週e? jU*((T||~<}֒aTz G+ Qjy(,*0@hYRv)SϾ/5{(**cOMu7y򛊈ׅcF~ckuAڽgJ*a14h̖l$I}T։jYR^=jUھcnߩO$lN;v-QB_YaCwRo]8]6M˖֤P3g}#OSBB~rttb?5{mڼMs-Uڻ~wo-[w(_'Q-ۻM ~6tֳϿ̬lw<%vMgHS:yzҀY_kP&bcthZQuW\%_a.3Z#NVjot*߸U*77O/L~ 6uMѵW_}\`^5GխkJ̝PuN*oʮ=ted92NLL~srEDvWХsGt/Xb^#MduhFzpo`c URRf}U+3+[^|j2h̬lߩ0ڹkKQ#䊈в5f1dP?=xT~~/\"=$V)''GƍSNNNegϮY0 C,["I:! SXX~2E*l:T5:~VR:tRTɃԞڷn\v ;eqy?/26tz~3v@=гZ~6nڪkADGS._-[U P=***+""UQRR+%''*<B-NW^ݵivICxfޫ)/"u^w0Mf~MWs~Z ݮH2l-[whOsʣvk87Jzvt]ctIYv^{wRo=0vt D>9MQebvTYn|_ Jl@u<Ԗ_1(*:%|p:u_RAa|>}cuYg>4Sj8bww}W'Nt]]P^iPa޽hIn#8U-Zm-Ѫg׷-ӆ[4{|sQ#O3TwoNCzg$ԆM]CڴNҕƚ3deR~~1_j]2>@DEEsLis[ArR+~Ja-f댫"//_{))݀+أT OԞf0uHΝ;TRSfv++ti硬q\5 WaH˖6}fcvmΛ'}@ E 7 ;VxQ_aXLPWс.{o0sFNNvܭ*3+[V3].Fq-SNna$)wQy +)77O䳯8ޮ'3+[oRrrv+wV)~Y@W_q5y}zuWJJ*l>lzKTtt1LNCEEZpN:sx)oɲ~`5 IDATο}Ǯ D+:*RyڳwO47W+''WS_~3((|7 jJHhqV}Mɉ S~YWx_W]>VNS?yӹϨ+,*䩯km޽+!E'qqڹ0Ǚg~t(ǫVY5ӯx)>>V}|>>K-_FR4'Pk"92 sII5y9 ۭW锠:ypsƹѧ1;[%(3+0|ؐS:cvڕGkۚc~Ԇ} mneuMQTTdP9v}G~u1o'ԥg|ܼpoy~}{oϿ$%%&mf2I :!NNԶm*kN*3+[4m^pSmzr{{c$T3l߱[ÆT]ٴe V٠;&IGfXIlG?lNNo 5!|f5ev^p~?Vs|wH᪠1ڴN_}HÆ:Nw-[K^=;Bݺ'Εtq8񆫂v]8f~*Svkp~<__xҖ:={tKe_禲[0<:봠}J;8!6M_v~inƫ/S+o"](;SsphB9; l@m20?*11¶VUPP(kE}PXT$ǫ㶸8VQ[]qa<`˗CxTFS(F5SSZ [mޛ}6{>a(P>ORґvF>[WffF] bj :Pq߰j@ranPSaaaUkyTzj{ 6n]pjn) ^ÍsK p,^N|7R{]6K#F@3w^[N^z^[N{mpf"99Yn:W{b[.M ̈́PϞ=7V+(..ƍ={p4B5C@3R D@S*D@tJD@UYa`C.NYqFmܸQ,lH+߂jD ҀO>.V\4p#111% _a `i7nK#F,pX4 `i7nK#F,pX4 `i7nK#F,pX4 `iҜE50Znc9bIftQe+.6Њ:6l$%Шx"%U #xk i[ 4 `iv K5el6[=^]@Z5!@m:Q{MٙQ桍J5xQJv0WݳڎY,)_^魱$P\x@cWRdUGO{mc }_{rc4 !HJW+`@QdlRt"Pzhr$z&n&fS VݩRpظiffpojOD$pfP+WS]u9gji(>>N&= 7,[_,<6&ZS>=Oϧ/^;wѠ}uB]4Y' ٿVZ+.^^H99yZ$]ݻuV.|~mٺ]۶٣F4Jm.}=tsޒPMF14-K_-oIUAAE۵zڻw_^Hqڽgg?pP<}nmC9yz*((Ԩ5漳4mJ_F.]t9Yon1n3 CKGo+"BCO[oV;3ؽW_|.Z]S4WjCd$I+ɯJ2G=w}"WD ?ܼ|u^bHmdMҀ~}p8=F m޲]xByM~1 ;ef]G|QWP%h0_i75m[6-IRJzyJh٢F5ffҖm;e5dP?󁼼M6lb.[h|f^#|ڽg_PmeR {ven?4}mn3uN=EGE0 eg?ݯ(K|~Yz$I"8-Z\O>2Pfiӣџ.3ZxsnIKt*UBK0W?nig]z~ߴhr={r{0 Ws-5}DGZWxx.x<RttP_{L%lJ)**VII^}9Iҋn۩hӖݳ{h[Y_~'I_WϮJAQzWfVzB1Vvvy;viѺ񆫴oAMvڭiKt٪:u)IiѝF]:w4^^#]9BU\\'EK?I}{*:*J1z:u`(c^=$mڼM|7W#m;*A~Oճ@OyKӹgYẏ>nzt0:Cpޭ]֣ON҂eXDG`f<>M~A O99yUj&MyM3?F|lj,k J {nQ%I=WӦ@ mw5l c7A>Qf*,U\wWllڵm wݬ~V@@˖-8Kr:t r{8eb?C9AB~~ai 1bhw@>גJ$<%Iu8!h.WZ''j:p0K7mUjZ~إ|  WDDK p'2e g={忽#ǣM[SfHQ6.Ѽs|Rd*!.|]?ΝET>;-"enwc}p\.NNzMcnjoMD))eӡ`.#o+c9PiV-u:*ҥvm[kӫ&XP>TbMR:fY~sj( ?ޣ Uëfdr8@h<)5۩X?NiȠ~櫨n ;w[R*4+9Uk}ع3CPWa+WT-Sʵ2 C{?>DRN$I*((TڢegحC9:of|i[\. WaV}휠3_^|xt@Zn4Dy%J_F|ZNTn5{|*e|{u>/m=٬1j;lԮmm߱K[Tj:M{mM{msؘhu풢;wk붝̚ӄp3F I$I7nу?++BS_|\m۶V}^ /|?$s֦4핷f(66F: wV]tR>=eەZ(3+ƭ_ZqW֦toR$v<֭+|2+V$uNIIjT 4h e'vz쮰0WUa;oRR:oih mYZK_zAA__UL7[fY#4c8]2\;AUlݡ@ pr:JC~Ow7,J_sb{>#ߧ**zFNN/Y!I>lbf`2ۀAkièfʷ2{Q~Pnj t҆M[Ƀ$IR5zOԥ4imZ'鱇&))> p "&&ZÆ uf`nK#F,pX4 `i7nK#F,pX4 `iΌ<[2Gk5PfoA=ȟG(+U`!qՋ Fݏ8m!Q^?StKF,pX4Fyv7^>nw@Qiu+F Y)kؽ\|i)rBX!F $l.N}N}Ǝ8у=O/ǟ~in7ikZ(PN\<MF Wqs$)c>]YC6**vkyz֪ʤ)iߘ-W߫EpqоR{w}(GY9A]R\.>xesIS^ucC9ZbR:SVTK>9I$7jri)4m[ڴyNuJ<$-H[fWdg('7_y䊛)ګFQ e]S> ٻ_-$I֮߬wwl8IZLY9Mڱ#vÇ ӏ?@@7jkJY{nǎ i7Ur#%$?>Ua]n57:哟L֣ONҞ`EkkTMG溛J)K*muq3<٣Fm{5|.mFu64jlf|+CWzXrLdffjѲ$f.?ksd6ƕgSv14 ?8gwYu$[edS4i:l¸vah2&Mʹ8ig@~L ii`3MJ;6XA(!lKt?NwNKwZ5}Ewh7nG#Fp88Q[[߯Viwsza޽[v77֦ロPi:tZ[[uandi2MS<ݫZٳGuFVI"lT[[{JP7B+;H17nG#FQ@ap4 h7nG#Fp8p4 h7nG#Fp8p4,&P7nG#Fp#K^WcsKN(]Yڰa$iϞ=MzzzgIJ8ݻڪv7(i^WwﶻlFFuam޼rx&^W7oÇlws،ʍ477SAp4 h7nG#Fp8p4 h7nGsaYVǘ H89#eYPtrF!,ܺe*}~>k˲tET0,pKQcc>OisPd,R`{?5j4q:9pNާeY /@`Pԃ>9p("UIDATe)'4icŠ4d.ӑE }\ioѣ jnnƍUQQ1-/:rtQر#9P(2pX'KC%I'+}M8Vgg$lAEE6n(I>@ ñ(=wPF; F~D`p`# 3dL)罰mnezv.sYxYr ^s`VH=o^S ׬Ct-vl,0[ * :N^$= Xz[^1|Y2!5ѝ5ebf)`*7A C`cT,j?^SΖ5c:]oU˳IYՠ|;YPR$@ g=!$zL6n{F?[y]nu۴beo]oϝחW[64ЗN.Oe.n$O{]jOJI]gd5{uyt@_v^~'-E/ڕ?dJnZ'ߞ约r{H]ߒ/It[yo3Şo&z᤯/j-HtKˣv{&HʽkJ*/\|S]f$i4ҏϿ˒"E|!I]fr_cD'[{,ؐӃ̋9iLԉNJ{WBЗ &$C_ZcrO?^<\ 9vIYњź(I)ڱ6}.*sƻӛ=_R$Xѡu;P'$If8fo˜5|Bn_nC^8.D 7ܯonNΚ-_ѡu;c푤Ktgh8 K8N߯S aw֯3MS_NuRi&?0JwIYZ^-IZW}M,Txl^ NLj4:pH Et "ciDH> ''Ĥ1|Y?>Zl\Y=!C#֞Ȼ?WȌ-ŮbW˗gh(Uk뽛b?Etn "7loLLUط~öXG(mr"Mٗ 5%sfOVGď?nÍF!]Y3׫%`(ޫOپmv~oG}R~=ۛ|0VQ_tւb@Q9vINJo.w@z]3 Z{_]f^yW;ަ˺0^ID|Qo HSP1vSOo˿z%<|g.yJ׭ManZy=z$閛(6~m[}]#>ッ@`T } }O.E’Q *䠼\UUUx$Þ6nZ|ɳDgBwW c]o$̊/>I6[tca8e]U2ҥwb'!SsIlQw֞0-SZnon|=15^WHW^~N>rn:m.=scNXό)8xӓ]5.@ @ Z Y{ؔ,uaC4y`YKljWOxݚVn#'5ÎiIق)ŦRlv=޻)aZVIfUÔۛԾ'‰dx˭ʐ۶hʆ)ƪ y}, Jϟr=I522A-ZHvƭe;Ʋo66OWUWWIs$5::m~"I:n-Ll~iIVYYٴo>IҞ={fn޽{,9PXW\цOZd鞧[6Ȓ,Y#IҒz>k*Rw6.!#oپ4o޼G?|l8W¹ˠ0 myB?2p8p4Ht2[mnrHK)un[PHê(|#Ubmڻ)6iUSy:ݡs~r*5,iH 檕]8IsC#مc檕O_zjIґ#Gb79"IZz5:ޡCsı]joP{wiP˥jnVy9#=nrTr뾚3rE|6 -,~n[---LMU֩.8Vőjx5j_ҨY mF˥Kwtwjpf! e.VެK27-[}s:q:;; qsvب;C˖-#DLt'Y6U]P`r]n>yU gRC尺:(ZաJt5'p[5|7eYϝSw4_T>PD ÐQSUVge1͖yrs#zt=hl>ٞPd{CU^)`jXc aP$|V*Cj AHT_WF#% Do ÐV8&aaE+1789c,תHeEЯSC><9tQxؑǧX5F w<؈ڥK.e.̤GUkT>^="r5$@1MHgsSyϟ\(ʉS- i/I>-k7ȣd^e*ejH*!ŀp(Rܜ&;U'*{|߯@hKJ|?h1a4R"IrKJUz_Q__cKdžǻQQ$ Fv)eTn|,_}~I:d%ŭ2$3l)ޚ:Uhȼ+ b]U uZ<ޅKT_BtP(ujjK[FMvUUe)YSCbLK=L"dpĢ@iĦiȗ:ۀ `fw&" -ژ`Qp`#Y8H]!nlS^^.I'pp8~IaR@@###EJɘ HǣE55?f\jjx"Rl7ĐaHG>dYv70 _$"Ő +pXAT1ivec0,@,F_v!ϘG#qa4W-Ph\r#atrZRnGs2ز2o8v,,f0@ZX!ISLZNp{sf5n+ K5ɟAH5kp#(ٲ+R"9Ȉ|eI,qs !E+pޟng5k癮f5Z0TC$ddZlܠk#5$`Zv>P(\k2(Ys5&E >||䌍ɲdYƲ~YH]q)k@ SNMJTp#&dz?~LӤY%4$) P2o5!|KKSw1I_/릛nҼyiؘN:g~#KJnLx+b.ZQJ~]pCRNG?I%II-@a$Lu:z#?vE!*p#n)CˆIG0)m0@f2Jr6y5`J۞2ɕɖVHVa2RVh[0 )OidS(M7 e$y!Y)ICI' ͠!]7Ĝbf!q 02{0R)*5lJpD,Be2E.]QtI3+Rf^C3rH 5  5j‡邌4M7ǞxմU$6@Ȣ "U7%#O3KF]S"=%\Jn*Q1e)coD),0'3:Vr㰌u\"~5da5]`DQV[[[[[ H8,333̆]/D,9mNr,^"iź^(q=b qzK,{.B掩2z\0bCؚ!YYYYYY# XZkc555zDW! ۴iS{VDI׭[zЁXuuuA&6mk2?BN{d2;lU)Tdz),}[V2⺐X5پ=b b %xiJ{֍2=Y4cᳲMOrQ,K%)UwXf,]$1YR=n!n%OdW,ix#byvOB=ʞ !A:A({.4b]F;&bTC(Uzw_vE"*i55X_XoO^ߧ WOe GNVvo1ճ\O48i_M9OOo;W{Qڅwx[fQUeyrf8oi+B(.ܳ٢>`?nvІzl<"t;,\ jJ>\#2ںY񗺵͝glFNCΧau۾cKZ?j>}[i.{>uw_JѨt3Z]8[GjĚBvVkg]qo ީ,fg9:v_zDgeBᳪ· Ivܾf~mqUG{mZݧI;__Rİ/_mҌ?%VT GY.9ICl3&T/O.}:g/~˯fcə5ZM.Õ;9 _&B\}ҳ%)w߼zZwdd;7 e)u\B{i%==4O*jYVnD7N<V٧pgܿ^7zAG>mV/ϗv#B]=$7dSK ZUCw܎ bѱ bm$+ˮ<2E@+$bjYׅB*=8}n3{|Wa+R2޺*Y6^i bfʶ 6 u_ن{ǽ>c3ڰg^ߜֈ>!TϦ\33rY.^6I] MtWUS\w浳ngVMaݏn&ܳ̚Ns{NAVzG/me+wv効5!,>@"֯W᳟5n*+- bg;hZUib&ᆪ-+gw뾍֠泪-WZx]g~n#vMkli;yg!yg|Vڤ˖Uo`;wĵ=v8J$Z?mTϦ왓3;#%nܸ1y+Yuaz7,em?_kys^}cglMo_0m5ə5==mʞ=sfV.̺]%oUY{lVW07+QBX67(̦]._rɦv)msڪA,Hl%5|BBaoe\f(GbծؠM}/Qz<>!JS71' mK.bWJV3}HvUFVf=홝_J?M6c3ܐ埗A _˶4u.7}4[пX!ԔƏKKkB!6a؀-;BYlʳeB>xD3 wkh1ɧ3c ?kDw|XYćQ:^DzkS@uU7̉/eX7:.H^*9dgJJ`q, !ؤt !^h/$OY?2k !^zmK<^!p=YBS|:AhW; ?!쑣ٕ<utB|FՆwۅ`\:LF+sc꾽B^wsֲ5o_9n3Ď:k; 6˒%3|/=tI)OL !gΜ,{JC^4_RoBX}mnvmOBSe쯝xBX9랇k|4 vXAlS[{v!بsNl%k͠IBX{#o$~̝_DIJ6嚪[;Ӛ7z3قDب̨Q.qo|9j>|˧޽6d>WWUn6[rB~vM͗2sYu!lXw7gI^_ל=YY<㜫^Zӯ'tm^.4'©#C!T=8. 6ޕ¨seꜵ!9^`E?yne pu[ե+?Fc5sf7PvStpKW_sOfO[ ]:Pf^oT!Gz49vcmżG^j.gd<\yO}uO/|\|eާgӯ;G=v̫}zUO !Ǡi=dMٹ>G?;jվ1蟽XQrw.yn:j޹?RؿL|l8!?liMt>;dBuMIIGN|_TׄгW~a'xQZsBٓL|rFaz=`= >ko7x܄0jDanƭpS왳 @ȨiIJuuu!͛7o޼9~BħU'SIm /#b͆MlB/Bظ%TBL@v5׶V̽7.tOMj}b፩g9zlᮍT=ݼ Pl.?OՃɝ#tD>fSٱX,++++++333 =UWW~.}r"{nVmg>|*XfFNvf, ֢V u'r?{Gz}W_0)w wW#жO]TB! l{h<'52;7f{/>g/z"@##dfdd{ic-el't@! Z$bDD h-1E"@H" Z:e"-n@uD,+qt댉X]#]ŏ ]gL Sރ]`Aa).NP7JXWWY9߳a^ %vS bgev ; $:k"0:1 mΚvXFFFv❎b@UJ]FFF !X)JD7Y[#(1gB,i*KBu=6 v]ViD,kJ|k F2V~`W$#233ea]#렕qXff±!ѰniniXhaCDB̚ IXMސMO !d,Q(й[¦' IúR(QSSӮ7ݼyM6mk2?BN{d2kvS,XR;}2nݺu-++;ŪuHBuuumڴ)v+y]o Kda vD,Է%w%dW,٣칬3֯ uۻ U׳ i’:rdIBF"FeN:bķ+fff6a>8#5z/bC4֕P,buuu0J ]. P,!9kb!JW zqXD,ɹXÞ,FF`]2 KD,!9K@g峚4$b ߬1Πa Җ5w @gCI" Z$bDD h-1E"@H" Z$bDD h-1E"@H" Z$bDD h-]47h-1E"@H" Z$bDD h-1E"@H" Z$bDD h-1E"@H" Z$bDD h-1E"@H" Z$bDD h-1E"@H" Z$bDD h-1E"@H" Z$bDD h-1E"@H" Z$bDD h-1[ڊO7lؘ2cy{}s{Z]%+?ZSlÆ6lhM{ /崯$b,]WQBB3GFFFZ*k?GG)߸fA.![Qr]Eeݿ崗Nϵ!/~^ 3 1NщXE!}:lDtqN6lB3G픈n1NщX7KtZ]>IO"" Z$bDD h-1E"@H" Z$bDD h-1E"@H" Z$bDD A IDATh-]@Why4lvai]5x՚.-B!_*͉}6 pD `7'bzً*M͝Xqtڀ. j7N<_=d}k}v]na]YS7gq@Znʕ+o+W[}X<^Z/3Vxࡇ7_^l7}8ZnݗAw}1}7hР/Bv{Κ\+Rv >+j~mz+DVuyUTT\|m8_ׇ֭[WQQ{$=ۨVlM&qX!3V8Ko:~u]ϗ]vyV#wy8,p]wׯFݳG9-f*]ۯ/gaSyyG!#F6I%/XUҒK>lMݺw ݿ0dāŃrZ(6XkXnTЭ8dȐ| {;C,,1w!#iw^4̻+q OH=Taۆv˫;W, !/>~!ݖ{V=[Z<sg7d򥗎)leO}E.̝̌ s€Poomfw\p`3~Ȼw?٥ jSS "g7hZU/W||'7R|+B_ k; ao59perխO-_kjmҧ\%V,Mw̘檟?8W7^}ªm޵jCNIW^맷AOwaEjISkˤF\OwQr뙗+/eZ&VK5-]M7M5SUyyz5~;g>IO ϒ׫Iz(&Ktd߻}ݵWsڿzhROkԏ+xs]5|S֗zwU7oLwh_]cN)\4G,:?o}=5;. :ve8,vBNqvq^+V<?˛-+{2Nɭ~C 8 véx\{i#rss -ںQ9ሣ[Brfə !Z-$boq[nܸqofbSzCϹv7Vӽ,t! gՒ%ɁXGzd)8ph,V+'kc˗N:1/XB7Q}zaK=!7dok?Xꓪ 'kmخZ2cEO;z܄1 e$ѕ<}2=3{wanBp9qF咿{S*:<~ G\9&7lm !/obYw퓗mIe>Y]Z,Xk+׷zBck6&|}3.Jٷl_[b}'~q[PLyγ&pͷpA۾VkͿi1//ͪ]a&oL󵋯搼NZ/.jAmLu*]G,E槏'OW\|e8fXӓ׬.Vόm{f^(KK0):3 =PTo/UKoJO>ī+P>fPQvaJKBpa?QuG-MV-_1lm葼Y0)M}ᕭk]}qGK?3kkeIXg^qLDP,!qT잳&_藳T6]G^Szo;p']4~r 앴?iǻmINaUlCf`'|ڛ/LyfX_. F'a%O+n|hΊs~) }a'f9Ǩ\ԋ+ڢLJK]˾rɲl}f!-?yj=\zK̂l9i-9KuɊO!wXWj91sS&mQ*ZU_?6| ?Ƽ%+\pQmP㐯4V4\zqGBMyɒ%k+*_p~1s!lMMr_*K#ϼU+fvvhQaϜXWϏ'A3g @IqX;'blh!gv=iᔙ)ZOV,|qۡ^ǜs+?}t랍e optP̅Bxo|yzffNUU-bU-,:fatguQ!~MgMnXQ_w~q;搼z W\}RQ'zbCO=c9q;*~fޡ߽>;{9tEu6g"vWùSXN!/rgm \zu;0!bs~mUl W:ckv[qUz xu5!}i2cvo>oDN:BapKVoӳϐܞݒ\]l˂= 듗BޮwT۶j/YQ^B-{Iow"ڃ99] `GH:sn<%ֳ`F0氾9TU؎!{!Q{v$\ϛxޟʿ軇t▱Xxޣ=zOK~8{λeU0治ϔ@$IҨpء!>[֒)+(-ؔՈ[? $bi43󙡶j7O}4i À6:1!^U^9wiၾ79IJb@[:U]fS5gʾ5LS5'SPtИo__dV~pvYZ.=bIf^AoQs-\p[o/,]װ+xEyS77`UXNNk[rt?p˓Y9+ofҒ\t]X,]T|kƫ6B冔ݟ}ZUUUrr]//]jwJ++*>lvEEEYgÆ Kw-;u&k G kndmU輲M=73Xq߻-r/o^».6/)ʊ <ޡAB=-䁪^{l†җgxlxӫg?b=O'[xpyӓqc. ?3-h3C,gؤ}6]'UTTtI-:餓x l?&;g 71MNZ>K}7NJ.^ނywyCweZaAAaIxM /|n]+^_\;>By&]x٩3:ЉW}S/:rF-U es-Y%=L6-?!ԇbO#em[ktrm\.1 Z3'p艗OlL^UϚ[F~QR\1;Ycy? S[㋖n9{?hϧ T8pxb5wn]P YIfuXʮ-ڒ >k6:CUU)mYʫ!T̝=/eGHfXN7N_}aM8> IX'ӽhaފ;}X'W4߼UUQ¼'!]4ꑫg/0_ N֮.#o3S_Y8+-b[>m+ɉe?p-wO}>eɧ? g,}_.%@'GWO.I흗2'qهm+pY{*M~cxΙ^5Ut-cNUU-r/-k4|oٿpK;袋&Cӟ̚fI:sfIٱ^[ Of^~myY_{9996y)_ڨU[U-:;% 'qXCʒQj[;rſrْQcw4ql?XQ/kHhhq3N{ԥ?8-f}ϻxy IVr}UՖ%f?<{,Nͨbv&_\V[>{S'|6ploaje4X͹oՇ45Mװ?itCT1ξyiOtF4f…}+eѧ)l[Yp_ {V̸m.(6KxpzU+?׻(=e3o9ytQ^Ś<_o_(, akyw\[5V9ȫ&\Afz[(e[DLngyyt}ftqh"6E)LE-}m~ȮYjߥZFZ=!FtCZ ȁ'fVq{0sb`0Ι:~fw[O39 "VFܖ%'͹ܒ=ӮT{a{5v*}dؒ*ӻ5j.xzwcS,~˼K{yzgIz^{[;{ZS Ջe]Y5 ݪ E3 uK !!M,.ST+m!Ajr`Jθr5T/Gz'u_,>ޭk'C~P,KҼ9,2SLїI͜k.3ݓ3C!WsOssOc@229~ۧM4.3c**'\6i+߿v>$󖙳f3.չ}?;7.l_Yh\n8s.CR9uڄI'\USK;#nO0#0k\a~Q݅_9r1{F=aZ}񌪑S't8mƫ?ѫoe_5Ȫ2OgYn+-)~vy{UQyw%7 TrC%=fl:4eXB[kiX9rnuNE#OES1:wXrOݶ>F9zј^T5aɭ_~dsl|`_k|M[Md/4?oѱ\4f`o5ul>uqP=ٖCt+ϭ/~9`1>`jrH՞3{j82$qS۾py:peX~bIiz!ֵۛ6?"UЄpdcQ$:k sQ!6 Gu?Au4ު?vl葽I(}< ;IRգ'g\}]2s/⏟hѝϽܴ9 $TV[sȤC-;Câ|~߭NRg~gsP؝$CCe5]ޝ1W^)q]ׯo{fcظE6}o+]߶m3$5ﵵ}nժU݅dhRY5ԿN[;0r^z?r~ $۲[_g&wUZ%Z:؃!$>>>޵_卤Ν:.¨W\x͂=kZkk+}ݫt>ލӋ)a!9+8BHM|׻JO>q[ǿD9lr}t{E9ls:>cƖAMnprɓkꊊ ME_͍ENOs=?Ȯwޢw}-X`콶4Vп2zi{ŏ7|m Yvōj;Zk.룛{)Nᦏw'fѽ3}w~nߏL)羔n7dd:=*a)qSk7dtzT:[3:yE;baM{_[O>%[5?W|'{mw.],mOVg:0ԧ>u(0Y5YFӛISϛu(J-?M%SϾU#BҚUG_/nj}<VsOqKNhB1!]$O>d7*z-m=EU|jې}vwgj1v}nPɛǟ}ٓGW淮W=Q<_['O=navw}QIAPxiGi5?I oC3jIFRTO>:ϋfnl{[^_4ԣ~YG#GUuk?.S~d/oڻSW2~\}zTy~\趏]9[|SsX!9v-|cɺkܼwZi}{ٻ/؈E74Mf77-V]XLc|UTkGvsr8zGLjD*=zOlyW88=~qw)0)bNa{vï3WPS%K}qTݻ[WƆ]۶[)a#~>M3|4S;Gߚ;s?Qr]Sč{BL,= m[и1}ڳ]SJhҏ&{F,Lkt/lѭwO~L=sѡasMھޕmJ%ν=,֖#:SضqcƖ뛲s2)иaξl>m.SMm#pl*4mhdrj {9> m,76nօƢY_BCjtf%GB( U7uYX> 0)be4fƅצZ[Xp״M)4x!kglc=jBhOZ!|zg]Roɜ6qBUb^g 7?%=<[m76ꛞ-wJmM[lآԜ k& x=~9b̰5aU^ \דlrK>Bsߟ|q¦E?}F>33ubf?wY4yV7/ҩTRBذo[އ_]X?/y8ut:5" !E{=%'-_L+UI2U]7:7Ci]ӦBњ' 4/z^{C0Gl)䷗~sތRYY&酮 %ҕ. ɼQz~>R{RI<^15?63"ˏk?.7JR~S/'_[/X65:۾p1l?h] !9rɒ%VM c[Jި7dL:RlmZ&pþ޵Q3>x~I[>[='͏v]KkX}Mj~hUa8+t@/Y&WO[󍛚^$;+Ye_L>s;|7á@  b؀ZȮ]tӭf 'H4fE?W__uW~_}xٱ3&}<,'VhZtl(߶=_vU˿w W]}76UFk7n.֚]ǥ >;(fي/%Aܲ?CE7}16ʯ=UvKǟt~՝>awua}{>ޕ۲Gqi3SϪ^~_Sa~~5OW2iluOZ_|jS3Tg\v<~w7[u'Vuý?=QwKΞ<2e/z<}G(~u;8-)sN_ \r>qn-'&5՝W_&5'U_u~c؃U!lj{YXu˯{"?Spj9ﺼ0AO+¶-=M;JNh9%+/\[M~erο?JnW{egϿ;Kғ/Х'e>u5ܾ恶=rmBF ME3 .ZBS'f(|n{#mk߫fNwt1Gw5ƦCS?#8FyK&l*^n2b`+nt'U{g>f%^,>ޭk'CnCd_7wH|;I'IE} T^2pjW*쩓{jۦMLw*F_X!$q3λTӻSqE?}MfiW<3ːUy?u„#{u oz]_{x3>տyâYbȝw{2身S_ڣ%w&o3vTh*ɩ'wd{T׎ ڋ^|kHn /axݮ]ʧCo4p6[n-/|/5mٳ{㪎9:~_O/Bs=0.+|y7WΤ:\`gKC*\BejSG:Bٲrso~9 P>/LyIǥӨ^|09Ka{aܯ̶{4\% >{h"60O-徆|qV/,˖{ fdeƝ^! k7g6ǹǾU]rTeE~k&c.)t:Pʨs{Nh7|w6eCu,OSk!t-?l,Ol$ &TݹOQ^ߕi4QE=9O<?sQ5O=Mkׯn*&WSUٛU}< ;~LRa_(?IBS<) IDAT5}u-|7iio=fnA b^N;!Pw4>Ӱ%԰3377U=LgliO6oP?r?pr\r7=}]ήm¦j̻v+%q:LJu5ʃ_/_N=ӯ򽆒_% S>N87/?U|P﬚@Zˊ&U>gzAşuU[*4?rkxz7O}gAq ɤ@rXa]sԟߩ햹/*4?r/,˶v8a?]wGiB˲/}z~'٧%o㡼Ån`na,57.|vkqa﷬^>[G}֭ Ei`Pʨk|}mw$}7]Xqu]kvٝ_\ڛu/,l2xzwV9ʋz?72g1vORtzT:U o~e(LGӣz6e7tB5uh3߼nL˾t{oQ&7,yʭE LJKMnٝ/8~6w /phD1&˨aɝ LR'6YSZd*V\)ǿ1?ލ%Ò\;{x%K˖}=5_oY˽li7=uNjSR3kۯZm !r oE)%+}+<ޚ˅.;}lzx.z@9kl*:W0g\ۃZ -sGQhWʷ]~gio%{xqWOr>s!?bO 4BKGinI̺L*5"Ly'o!:_~uEC=z=y{[piizϧ:Ɵ-)rBHO},{jCwqiC[ERY5iSz=>/=.3"4Ԕm NjftҩԈL\lj_kd)?"ɜ2?t2X> !Q9-+ooUh+޺w];Kd:=1BN 蹍m{JFO(3+t^؍=)}ɵrXȮ^ƆuH.WKx!%gNe5iWKfn7~~mTunzCG;ٹk؄;% -k޷v}I-3g>g\r+uNJ/r~@ p(X59TdœJvUy~`G xz!ϗ[{u=)pK[+Wn7nYj[?tN4C*Pv7?'jyϵ׾Zhw_"6 :MW\%'_:*YՁ}<=Pwۅpu)U>4}cqCϛLJ_]>K7}=2]L*4%X*_.y=$t~\=s[˺nbI*-MW+&^+8X59K^+7Um봷lGm*=M. ɜYkiq~TǾdMXj[}˾ f׵I*λl\8 #2{夳 jx%='u5\6X2)ڛ>8~I'|Gy6.yq5!P3s'9/|Ϫȑ#z!&8 0M+m)yMoj25g*Ƨ/CrzF';*'͹dϾq'Ԏg6t1ިH*M~bzɴ_fj[՝&lx%O_AH2SιjMKm~=RU5ۜ5p;Elh-d/,--uڦ\՞6}DO/ҭ _Z<츙SN>ލ_hV2B¯/`J3+~#]dX[G#ߧ5BQo޸.bWvl/瘵ޥM5{__V|Io8v"2=vb-5|gҮsg19 Cª2ZZr[rsMgޚeil;,ǗwVEC!?=SqNNEzzhdhZ|}U7]\w'+2g^:sE5%iמgSjY3q3按ǧr-VX1sk_y'M'IRI(в%O}IgNT^XO͖|K>m{~ý[}ǿܑyyϜ^>F_a*Xr[z|4sK6JOcᨰqWvuz崏[RxzFOn{=owi/OߣK߱⎢]knnU}M{d-kwn!ەK~rIw?Oj9ݾмr7GGOg_/] mrY?W78#:t0!&+]3=I;wؿ뻚ӻqdȯ]pYc'Cm{4bnMWߑ$=cWMvS͙w ȣ']9u__wV7~?@$ph#6 LgiS&ˌ昊 ͻuڊ/~N -[fΚ}θ*OONEzydQ/?\wÙ*05}Ԭ.ZuXⅿx*C"5n[C2"S7=& }x ],G Im.>wJUw9B[TmKn/FRuG.?\^B;_UK5ŝ 2Θ5笮/[ŏmIeig{fݡ7o]v˕O6im?B>mٴBɛ;>'Q!myŭ{FTN|>~ßTeFt9+ sY3g޵{fZBH>?K.;Zgc2T4ݥao:ҕ:a@7ر#3;%Tf\< QJWեzwpEyK>grp75%>ߥ~RYYC]r%7|!ݞ(VQ0x 5Qp)bE .qQgMBO3܃9bE .qQ"@\1⢈E (bE .qQ"@\1⢈E (bE .qQ"@\1⢈E (bE .qQ"@\1⢈E U**M'q!C{%eX$@oI$"6dI!IsI\E,0|C{=:t=A+"Bx_o0`%I׿ܣ"-5|$I «Z=;>Y,"-b!!CP&"@\1⢈E (bE .qQ"@\1⢈E (bE .qQ"@\1⢈E (bE .qQ"@\1⢈E -W_- jkkk*** $ɐ!C=A."+ r]kkkkkkPHd`;vaU(vQQ fW^ye@Ov+{V\Elaܣ*brp2I\ELX RF?{%e(bE .qQ"@\1⢈E (bE .qQ"@\1⢈E (bE .qQ"@\1⢈E (bE .qQ"@\1⢈E (bE .qQ"@\1⢈Х|OCf֍8É3C85 QIGF|~g$ӱ֒coyzٲ53)uVEJ&oX/-ۺE>ʛ.S"vd5{.]X39'7:evzμOOOx5+rX}ﮛt wQvf:ὗ_~k[:_,xm9=hmvZqC`+s]*+uag F˖:qa8mM, [ R#\isNr&iھG<<G\#JB䖎cJ r{;0_vʼnID"ٯ?yBz\8٪kbg~ϓGZ.qQۚWv78YG x1FT*KãQ"ՄBag{-8oW4%]<\*( ;mU0x Ԟ[|Y}*PE#{ sWdǷ;۟+>8oQMm@oo@!S($] m]tQ*<>B~OP9uK/j(]RgWW yڐ[PP/x=J(9uMqѻGU@ﶭ;so8>ܺW,_0g8Rߺ^9L55.iI>_tv|\~o4X,bqX[D/UQ*sbeQSJH̙gTP%KZ'NMSQon{a}3z꣝[e7?5\K_·.[iag?ʫCvnyE!==|W&b}7|q~!D՟9sQbG d|;Wek-,>\}jw^GmaN]˧n_Z38 [˟@vw!dCWʋfN{OyپBn;8vA!m\l ۱=k?2=MAqoٍ!J]Od*m6a].{}k̎p`k6ז !n7NtH?3?s?7wl.NM߼yܗRw<7ZoNCP?s,&O }mohB(=[U.)+W{|OW r.oB6qUFھ|ǽOWeqoǃwV58kw?q؈~`K~}ﺷm0Nr[TXg8`oi5k \?<&iW_l;Jc>zϗ'zֶcs itre+2'z}e ˪'#9NGK׎NoZ[(e/_ !mڴgm+âKVmȼ|_}{$7,df2{#5xdp);޷x5]ߘ8-}\2]ԼO[GVu|#ޟݲ}U}DZ?-.LBn{'*saˏn=_Mr٧rE2?zKϷ {LwA9X((uŪ>|bWc?O>2B7߶fI܂>4rs?_м%!uojl( !?U6sC˚"GQSӒkۆ BbOLD0?f]kC"Z?X)?P L"TtI}weRںe[Wz'¨]=NTA.Y/V*0\:ta_X,SK# !Q4sӒƺSaoҟX7vT |Kfnu]$jWܲ0J g7EU͟>Rnnk=#)\y.kj,Ӛ(}i-eΫ/o*Bs bɱpNm[VuBQWo]` 4Տ-6ZKk3 $׺ݩW{Yozv<__(;8zX#qX&Qw -?w^uoysđ1I^f #lݲϝq},C6VR+r8,PE5!ܶ* }|UØ&LϷw,\yvDb釻*RkW6VO7M?teEgz_+!~Zl̪?^MBѰꚱls{ǹɩI.jYyGz}#C;b^^rsÆ OL1b%;ot]UK`'T-zO(|zjEjp_qP9z՞>𞾊7?= 8ŗ{w\Y|B)ʎP* t|3tcfߐ7 hhKEdK3?4/NE${v^}?_,ǭ&R˗4fˇ S1K[YpR7!LU_xyWv]Ϗ+f?I*7n%ՈRiفA@QӘg^u-ӟ{y=yo^XvvyѕMqU[o=4k2wcӦMfMLĬ~R}w5,m|D⽗"4\iwWgEPp4Sf{;Ɨ*lX}Ňv-X?_z'2]\mx5Qzaw}}yZ9s[+^RQBx?W3[dLR%'6ٓ{nO݋^Sm]};[w Աxy^ž=Fsj1lLDDJ]~qCǮ\q(P~{_|0PftWȕ?mly]yŒg V1f1&ٗDy`-{ߑ )ӍV>@q8|ﳽh?жs -?P10j+\^uPZnWo~pTcSK@gakɍk6-bS͖H%Sj }(kb`Q:xbqp!16ŸG5f/U0_t9k3O&.&O]y"/ݰr?e+3/{:B29A1co~\|ySS]ѕ\k᪏%5Z3?)KO{Oꛖ56.@OO׶]*B92ީ}莾--]Dܞu<^~m Bx84t6 å+{:1Pֻ?{~dqyĜx!pm|hCG뒅}::N'4*oۖ4/^ibEԂиWo/-vEKBѕiow~ɦTv? ~~i:Q}yef\W4SKW-MԵ'z+H_#y G$0c ;4އ:SLBgt"VUO:HnNC]/~,(vtnYpTɖ}4LﷲϪ4߽{Kd"DSk)mGGD\Ge+t z 'W\<B%4:2 @' әXV=|)%L.knKߓo4dsXpE}S8.lmb74EioǺC׏r}ҙ+NM&a$>n cyV׭ 3-\zՄ'u}դjun6J5c'.m:|AC_ɤ*OsZj=]]ںCK7Oxֻn|֯>3{ttzBHX͞hQGVݝяM]ڲd aL"6 ZkiLW0+o[V5-풍߷Ę`N]mMI\r77߼_i4Zn`p,;ΨCld1BxdDpq mFrlt a@sB(eBzuN5IF sQtSD `"Quk?"58h^" ó%Җׯj$bʬ~f;'dm90\*Ĝ3^eja?" ^$bċD x/1E"@H" ^$bċD x/1E"@H" ^$bċD x/1E"@H" ^$bċD x/1E"@+4Q c=3f S%ʘ!JĢ(.L(c+;3|I)!"cfHܹs<d<̹sv/N[KBgubI+:k{q:hsFQT*~ vwBMM͡Li1MBgq 8k8/1E"@H" ^$bċD x/1E"@H" ^$bċD x/1E"@H" ^$bċD x/1E"@H" ^$bċD xD쭷ޚv8>9щܹsBOpLѡPsZ:щXB.St(9㜖Nt"v !{e@7 Q ({%qNK':W[{ނTߟA(pR." ^$bċD x/1E"@H" ^$bċD x/1E"@H" ^$bċD x/1E"@H曳u1E"@H" ^$bċD x/1E"@H3g0U6mVkvIc8eTM[X"d$$bbM^@"gyD SRk*s*%b_S\bL")H 6m(oX`<8#@H" ^$bċD x/1E"@H" ^$biWvW"KxIENDB`flask-openapi3-4.1.0/docs/assets/image-20210605115557426.png000066400000000000000000000704221475153525300222120ustar00rootroot00000000000000PNG  IHDR~Ev IDATxTLHHPY/H"r6l.ڸ%ҋԭ5Rw {%eucZ1zsDWi@PWYR(Й 03 *9 3|"v{̘G2 dL!BF2 dL!BF2 dL!B63p9d"XC&pkLcJ߇wkܸ2yI~t+`z^: `z *d0y%_?ëpS%wt:~.]1L,}3S|!'2j䫊.}:z1F: P\.7??ܦI$9J\g+rƄט!N^$R,,hLW .]1t#`@PۆW2YL &=AG=^v_#`@wIR,B&]N:6ӝ-11LЪ&#聿͜}1BW":{Ǎ,rk09T0&-d3 FL.18Jjt`U~T aҺ0a2F[z}Wح:WYMqtW ?%u?:#IJH)r65|vP9:r ?I^)I|&$5 ŘL%F/H"} 9ǟIu*5u^ulߕ3׶ƺ/_3M`{/I-8mVR< P}ug[NY"}}jUw׫i8YTWUguUOԪbN(=*dlNwqM=gtL5mA{7 ` d {8BG~ž?$EIK>ߵ ]&c^>?WSK&0ɾʛ?n:ܦsk[[c|/GkU8ٯ{WجP>Y#rw[:$IU7i´Pw6b9l3j+!SN3iq<_f1lĽXCZCbLL4+2eMu;ݧvkkY%IԪxYw}ClSӻl^dcUSUl֬ջn)B/Ehϩ֮?/월h/~Ѡ/Dv׆z8a2GSŔ_xcj`,BsuBPgٙZo* ,RzJur9̳1ZuHvHL!+Z eN^9LtS_ z[#Sg~٩=~>%dлuJ߈ϗ/cމE+YL9*B=G1qU1h#ia0)T0-U+J-yQ%8B "1ANӥW, ,7iFA3$]u~9}:naԫ[?\{RQ22rûL:˅ɠ9&D*&}n곩i{Vojktj_z$% 1۬Z: %|I)(ȴ[A&}9IqeͦVu^ҭf5'AQWtIۣ?hj7B41{Q/(2rXMPMd̛oU3i͈5ŊyfXhD0^*9+?+$ilN;mkV-{bG X\R}5Ѥۓ`J=J6YNO5WVTZzU$"p\UL *Z9*&LA 7QcCAJZ?)Ϲ7c g4nwp{kV_ju?֜YFI&MM{Zq[V9aڮģT&ZWVgTcT݋8IѮ`vᨶ}s|'6Tד;&IiNS$$[{*7jY?_˵*F0bJ_Dw%nSӑR=\:c8(gv0`\(fuX2GCn1>g3E?6ױp4*&IzGE^3^E^IEWEKJ 7+kX9JR={Mv(K.t)喔W7x*Ӫ/f/Վtg2(6u}LOmQ=ìW^\8ih7UMoJPݭ%U0~*4H{BoTvU6(%uQl14١Ju*iR=!u#c|ՎA`f^T2unikպgN̤vj"%'a,v)1LXަ7KU\ݪݪ[Sf)vN^.IuDc2=^mRlPְx.si뷖 6Q^AK,]kЮ}ͪ~J}TMxrd9+O*JPyQӪQɘ 0&T2[Yf0Á{軻5ZB4PŴ:g UL=]6׬݃Ɏw7z_mPǤԢ]4VUת{*U~dR'FcJڧW(N*UT!bm;TmQEAE;0 T1e}*~qWpvVmQ^{_ RE묯?2z$P~vۋ{`~Ak澯kakk0y*qLo6{ϟ!7NWxX#f$`.rգryfoPI>:X7f^)(ުCJ$)AE?64`HohCCzwZ*&(6ϼBT ݯ-T1={pD&ԁ[kYyT6)7Oe>٪3KRt7~urG6d#*5b]S*n+LKew?u|8ZH> ){qϥnC\-Zw9STЛnOhR?zk{|Psj_oU_r#tZ5vI2cu;:TWj zjRm=#)F{( i|$;Fȧ YԈ )b^;, ,EN@!#jUIOHREW6սUq<]oݵ.ߗk7ogڵDEc T1eށCd$&M]RVՃ[kU@gvڣWKU31MwPߟ%I6@DVg5bk@IJoAhI~$?j%JFtG4^]WՓ4C$w:u37c=]T|~6SQQ6666]K2jæ ૅBOmp*xCV]ێrU?0l@6(olq({v8L]QTMRC;׊PڻsҹBZAii.:_ g*y)*Bܯw[3e!|xFm_>?q0)z2LI4ƛ,Fy=mUNP{~Ben[Uj2FUdR0T1M4n]uQq`t0fd*O TS utzv_PR̡?8;׭nwa^ kACjsu+S{}/H qnc2D?UZmTJ P3^Wuw#ݕ^aQ^?㋄""f!bj*&)kDW1I6j לyQƞlyKcrmlr,x?,A|]+ ?SaF(Ѐ V1z`.ha`뎜baԠ^w/=QPêK9\/Z0r!ʏ$j^}CFypbZB88pzܤK+8ls=rɫ7 M[TyjQt{Ɵ <(3 :!109;qZU=kC LMY93߷A~/VK]9{=Y}ZYj9 ׸V%'cnKUe%-GZu|02["?L?r ݓʋЋE(#BEW3S>C_ɉЋ~0vbjեK?I ʻCg=Tzju#sQb4H-o_ea6u#Jv4[js UV7ZtvZܑ_0cZwüۉfZPQzTS-_M鐭YuO T {Vu;r)[ T5|mIfs Cq)u/6نUME .!*SkR\vdW:) ]^:p~BϖU?E5-B;MC~GAjkmT06,57v[|$ F)vOnn CSŴT/ Z-\:t8gj~,I2BsVɕoPZʍHW$Ũ`J%Ur jC^UƋ`Uݫp=^'rl3]UHvZ玧)}@;:r|җ$2>V+ }|V;xZOgz6RM>R&Uw ~YiGӡQ2 A΂*^٪gv ?_J^$YeiOOFlϬ CjyCOuMlO 5qs]%*Ec] ĪnI\Y}T8-߬h1O֓'_Ub|:uٙ*yP3S־X(뷫תi_Y`P4c\zŹEOUAvю !P TS_zClûj> _#GDT1e^1!p22eJ;v7]9`o?M/K2ja[㻴}YF{*ڢ 8}:WUV٣|;LѪ۬Ufz@u~w*>ptJȊ-M ɬUKܯ{T]qt֞*zxa*EY&WzwDCU乣jA7dx勭C<3wD뷻~+eVܮM['ڹ%hJoQVlMS~6yVj3)yMlYh T/'T>b稸|(.J5uz\71U^=\v;Q +*S4?ÛZT1mvHF2*^٤xpuoIg'},6i2wXo֎epZa}^*))!ݮNI蟩 ޣ+-~A33l6:.̸M^q?5e@﫪?}~>hOǦC5OmT% h>Ek+M*f[OCo<(ynr>Mi°&%Q2rV(oYN,'d.mּy^T }*gڑj]5HFQC>6SQVC&˾*>dSza zUڠ-9\bJ_UK1a6^s|]v ^E]+RٻiޓhL>[kzRU9ߤdQV]լcd0τs6u;e v>:/$cNlyIu-luRQq;s C6Uf IDATZ嘝;2M@0B0&rzN(cP3l=PGm ,"rt;}ve,f-Kh&"d7c JZKe+} Vc/&qyelwd?[_+kL>E*>bSr.?} `b1&SD4[oU]}:էS|,ڴ ݅ӽo+~&k!nxLL{Ҳm^0Q;'Rm}3G>dFnj zo?igڄLU1IoӪ~5H) 5˨-ҕQ+$ <"~upw)F[LE;MLJ\3 wj[6o&ť(״vl}ԣ:zmDdOTUuw9FwMRhӷ;]no>lM4lvI48ivRc|CsV]q)14Pfufͻ;!3{6p>o91gv/R`[f?lr8ݿM2v ϙ׽~7C﵀wjwݙ b KwU~kmҔȤn٪CK4SWyQ5fPOxR%zeZRC=['4m{eUuںGrJ{^oP͍&e,۠k)U5_ZxF/ =FsJ~Me̖䴪5%noeޗ-K_{]IE˓7M6ڭ?;3b?EZ ʻ;@Py؎U֮>W1VxϘTn86$i{Y54qyenG;T~VR JIP7Ԥߩ@5;qI0;AߟC^Ҟنo *I%*ckvmfP?>pK7rN9JRK-Z!éAw/R$%G$9d9UoWe[ٚ;%p6Xs}IN ӪT\ B 5oV -65)Փժ9rC6k3 %ƻ[Oh3o⴪*;eKY^g=UuMwK}{?v*fA[ljyu;n\m7+}{ϨxsQu^mͪ~rL뾮},Q8˵*i,꫺q\<~_g]/SW5<]EagZ9Ըo~Ѧn`Y{F&cYYK#$ouz&IjQ{ _miQunzjضk?[`g %{u߭/5ZWާ5);ux$5k_lIZMץ-H$(]Z1~޽4Ŧ-*?ztN*TizTh*LP43wJwV߭܎M5;uT{65"e/dtpHeW]j9 0, tWf/UMhY֑+jPvٔl>lyY[5˦yi'JΐϫӪ:huolᰪ'U%8~Tew ֬Ϲ+QI*ؾ9i̠)QuCoסD3,v:FIRlIgaxH6*xTMCzTU?}N`;%hVmZuncU٬,Ow+TJu9,׾3h>l~~l:H|j=?4`uzj끵[:T:wGp.\E^plUh]ZN~OQK"}!X9;+$㟮tf^tZ*Ci_9di ׸D~*Y'ٮ~Ym~[:*PZY׍cjUcWHTH+ ziN]+Q],Ajlrz$>m~ig|`UrE&(wT 4FA__{lj笚3u|˖E8C&EzpɠY6+e2(@uT\H\_n4o_-UNySh!iaoCM^Uܑ8P4t )ޓy2dm4ӽ8-?ifi ym9wb7sXwL_^t릏Ek]c1,IqpP65XRwUmrzY87u޼fzcK`yk"c2VIJ]WQc]_ Pw'hЎ5㐼zOR7*v}*XR # Vwg*>"2AwJjtbRfexe}>m!}hcC3^:ցnc;/IJtNRgn{edPFN |,IR}Z@O:YwZS+g\ߵ̰}em3|۝c^z6nJ2{~dĀ(L?1]7ѭ0UD)MkBCPŶz쯷aG=wT3L桴w =pnYƻ~4:.IYY͸ϙkm{BM]Z<3C&(?7%;T^x}C IsӔFVJIYKjQaMfH TU-rH^콭\y<e6I o<>AՅ-=0a+d6:7'/㯂*G%#OUުH0~ΙGjren|W[pOR" F1u?QV7 _ɤEr?w*,n<&d (??1ѬTyQ\fQ--m]TLw{{%sM# ǣ<}>& gzY<9O'◪dR׶xnF|0}<"S;jqJ;=Y8v+ڼ]t}+~Eo<,7?dIY+\jvrh[LKgT^OeDI7G Vp@dwN]~NSgwA?l>s$ɸPeϻxJ-f ~B~^9:$c 48Irs ЍVĤv$͋v;-KRhA$)uZzmoUŷMY9s?Ũ^_rbL? #ndH1QUԪ6V=jqrÌI.q:Yo6F f_TؚUx:: ?bT7f9+:ɔQE)UC4s%6S:ʵNܲ> {2>-_<_>#NC{gt59 c}KGD<.Fn \G$R w$45H/k)鐭CZ}2{ Zmk hr:rB{ T^&Fw_ZNknߡ/W%:X&@ڠ#GӔ=*^:T7t}/Qh$6N~W/ճO})RU{Gl"}-8:/Qs:ZPt^M|SVakkUS:*nfZ;=)s6`0Lw%)AK;O2;^JzCg8m/+նc *F -}BђPSFU7sxסsե*2Lz_~@_l;gyэ?GYYRflrtTMGGYѠ9q˟V3qGv ʣbkdWy2r|mP :TZ6l*GצJTd5+.J[OPe4k+ vnRjN_ި.ն'JFo78 ZKEN᰿fl噗U吥5{mxe9)aW _joUkתvĬ *K_9a:fnݬo!˩*b!fڲSysFn?Rvi6j?د&Rōc, ⇴*Aս=1&bo/nƳ[T [jR}/Q|WZa${OCZuEM+)6uͤ;zNl6:uGc;*Cl|O*Cm6Iߒ33>̷}ֺpkm!3KZI% EլN-*9PʝڳfMߠ.<cM*KCw'\!)1C&׈Ӫ|eإSx6V}g*,&xV1:ȦWw9,[Cj҄N:g10]}:fI1 ;l.[oVzN氁`z"d0hլU_zI$)EEEK&vpI]=+p d0ub*Z3(kOU d0LKPw_5LLr|@}6fd2Nu{"d0uLKQ͘G2 dL!B6-fY7]'1U8yYI9AuDgU˹AMw)5 |ܘ`7ݦsHnHSSbZLv\WJ]+6$(Sa},߭󯗪ERjx,Igt`g$eUhrׇ7q=d1rmMuIIZ'4zޛhoՇ6}-ӼT%MSr|G_,dER47E6v &ߙ'mg$) cdpJ2dMmQK&j޼MUb9!|q?iIޞ3JXt`z\ZχNZo7佥d4+w&-O ƴPTgu w+TǕ>;տTSVt&JRAfʈ ΄˪KY*UoZP@1&X9{iYzG_~Ug;&MtH2(o}?uy\tTF|s|65=jFxe-^=frʎYn޲_%y0;gγ]8mT_SZud3*wLe/NS T3{vw&dZ%hْ+`2KoV;t]jꞚvv IDATLc]WȦI-٪^l&Lf3y%)!.kXǵG/|KeMW{S8tœ9"wQ?fgr0A:&ń . KEZ< m‰ j b)5Ez|(i#wE$r\LĔЙs3G$khNPo|98M1Ό*`&(3ZƭE)QK Wg35U1^[ZlRy*=EouWUg2úiVs6ڨØSX%'+5AV5?PCZ["iޣwJYVIˇbPߨ{ w$sD[RQ%;T]QjZB[4KϭeW-ںyOYz;sY/nц:t&}eGC"|9ǧ-x1E(Q$սUU7%?~UzsA$j_]+׬kآ/G(+B-]G;WhGcI?2BoL]1RaIuV5kXiRckS~eL4tDT ^Ǥ x8>ޡ_VM5j2Gpfˮ =^YC{.¹cw˦C7jCeC]iڭ4^~ZOHRobMU9-˝ۢe*Tm҇X5ĢtCș&mZNխ]hҚ~NS\n%5gW-gͮINXe6ƪmA3U82zb]>`dz]w{UdqٻZTzRy&ٻw͝Au/k]~PYTP8Q3U7lU:M(ٵxWεdV1Q,~ya1p<:;ceS>H =T#5]LwOxe*5l7&IJV`æ!mhv~)C2ݯj;2[,`,٢N)x=aWZL}SeSC0%pU} mD޹X{Do-gZQۧzVgjA;_֦?5ɮD]W}Lj6Ѳ_=%X`fxf$z^M!嘬OLddXr{$$XTz|kJKB.rZ6Mԉ:Ew/4{G"I)JfP%+ )_{A5c-!R)*y|`PjָAǐɚ7=cU.~ytv6az0iKnUO s|Gu:gz ʺabġ`N]oOߢv>mjlL[mWMewZ53c^PF1+)ʐÊ=R,:13jμE $<|̬gm{Z7eE* M*Z-ڷʊk*\Aת4:\A/׌TC.3˵fnvv1Գ:X8yu*O6kiaΊ~2ILWz*Im'es4*?L?s Ңl$NN)u u_/j}oTy@{K4{\ =,i=C?)#r=M:u:rQ6ݱQA ?8k,*Z\Wof \e=cVz:x@:}Yw,[o3tHVzϮgӾBˁS7eZ#wL^ Q,H0ً*5vYVik:dl,>g!SF04+hG!kx+&LHwi1IJӌ)і} >pIԄh$IGq.*o`)v775Zsk_1"#g *dc}ϮЦމV4F_8E3]Miw״Ae Yk%{frRw? ju :;əԳc\Mpl<; :LWWNP|r=`ָq,c [MT"-1FY6}v4R?ɔ.e dAqEY&LHN$it]uOne˧fdKjRp"_qaX-n[$I]}eQ LT[ æk{y gܤ&*|U=N(Z-].MYǬ7Ldv^^~CC=tϴӫ)+JtIJHsVI\Y ,|>'fH2%R`m`}Zn%Fh/&FOI׀KHy7,sEyU4^ZްNYY4-l YOD}/ŝϺܮ=*;mTˇ`?|ŝІ -Zʇ:d}+of0S K&++j"K4K$ްV֗ 敍Zx[>+bg[EƸh>,ʱp7ÐO}xxgʻy]ԞljtLODwI53ثMHzuk82f0xnh%7IR]:- vY_~5|Q*ݛBx*+w`p޿%r=gfhwm_Xeo7w9vpJo9N|\ICm*4WВ]W]v5uU,2s7p^V,4U4Z^UeU=p{Z c2ek=*.vulܫ6jxJ׹4V͘̚s$ɮioXVMWfSfLQzp@;rzU?WA{FpSKT܋=cJWhͯ:l:TZ++1G 5rLu8\%m hP[YDM[\ң֑wy'5WӲҠH[Tu)Ii7Ch)ay^[N;? b-m߿Qvl~:T]{L#oլ7w.(95#cʏIռT(b6Qk*׉2C-z<6"æ[˴bȓtiUVY%]N5+SW(?)m.wo;\?:qG5dhֲUjtT]BajWcd5HRLz]KR:דh9mwoԼ(}HLg5ekЖ6d.^ǧ-ZdVefI6.BRVLl"yjj*ezsA$v؞_޷0uП*tO]ޮF dѬUԣK-Sͻ7(Œ"sM͞[h)g)3SthU *o{?ʟVF_\3Nn]⭮}n󆙦%Z9?lp9ZP&\aKRFN֒Z:5?Ao;g:|ב3nV?\F`/ٮ{ѠWU}geΟV>ELS88Ptw/ե.9N9rIүestMv҇{\]Ю&54+])f_o3R){k>.]ji2G}MMNOño~nՒV0?Ǻ|%}19'ZnE'OzUR_3F?1 ])L˩l6َiIJԠa\ឈЎcV?7)="s9ޢl:%s} Ԡ/(v\:w=by픑s+N0Wv;wiC*#rnM$qKMz40MY̰$I&Rr{?d#!Qlɢڶ_*vlMX[(ccp[目+8c&$*5'r8߄ľ-^ޗWԜ19{&.6րKH\@0\y9(If͚2Gaq༣'PgvaF]_gtfݐ)q"dJ7G@=cEK4|7@\1\20˝G9 ^՛ w+' #daL0 20B&F`! #daL0 20B&F`! #daL0 20B&F`! #daL0 20B&F`! #daL0 20B&fIDATF`! #daL0 20B&F`! #daL0 20B&F`! #daL0 20B&F`! /wwwǫ1dv2u{@?sݭnIx Mj2uw87B&/%B&ܓ5\[_2nJ|o!kj>L! #daL0 20B&F`! #daL0 20B&F`! #daL0 20B&F`! #daL0 20B&F`! n9ȋ2%|·5}KUX0M~t& 3|M0\^TnP?OT{z=bG>qnLtTA6CNҐ0Q? `͍V?T΀>=A羅թڲ|7ĨL-ՄS5Ez?hZG}&HiBE9U/쭀@55K鹼îJ}Ӓ,g1˝II&^阵Yˢt%#rN?9=O#&\q?.IѰd lQkmoF K^)]A߿g"$I:忹iֱ:߱nS'?=p>lQt%]f{|2K(C ZC}z59 ɢBw?SYLYʴ-~辗ݮ!¢kOʿMOQI'WVoڮɢ?"xq;Pu/$]\ kӟ/cjm{=޿n.}] n[~o|vm{"_<u]&zfW7 6 =:=9̧|=1.ь,iKٻIrNٽɺ Oހ)HܷЧoRs~3`S/GdKta;Uq0tZw<9?y>w%tވD9U_\SSuz{XI=BO&?'~}^ _uUIC5 :xBknN-֨Y{–l]=Vy3ҜN9"6C[^ݯGORwkײ'D ʝ'Izg=ctlzft⾽gC>xa7uUAz4z#d\[*oIoԣЄ+Cuݦ- ~:sU|'D='/ZLV1׬jw{5In&\.˦/ײ?w- 2&DVC$u\XޯUI?e+TwDMX)-뎥ezc˻}͍"E]%|@wT4mh}z'20URR$X S|xD[_VO$MG?P<!S~u[FO]7QX"ߚOYeY ]zjkIhPro s$-83{ $) wH~Z5]:UXzM|Jn{Wo<^緐M!}|{+U~I2ߨߩ^Fܨ%]vIp$S^8oo;9ghİ몟LNkE{` !-)oƪpB2_Ɔ1dѰᒼ*)B^n%Gp.l?rTƠx9mWVsz 6+>|?٢a鑿kt{ԩSmj=l%If ˒2{ Џ200B&F`! #daL0 20B&F`! #daL0 20B&F`! #daL0 20B&Fþqw zL0 20B&F`! F߾6@0 - Ƴ AB P߾Pw.uwwKݒx_y1L Ѐ.E%Hߐ|92&.iwE)!!A //@r4@)!!A EݒDO&$iunr&~rދKp!c2|7>B&F`! #daL0 20B&F`! #daL0sfEmIENDB`flask-openapi3-4.1.0/docs/css/000077500000000000000000000000001475153525300160625ustar00rootroot00000000000000flask-openapi3-4.1.0/docs/css/img.css000066400000000000000000000003061475153525300173470ustar00rootroot00000000000000p > a[class="glightbox"] > img { box-shadow: 0 0 10px #333743f0; } .md-header { background: linear-gradient(to right, transparent, rgba(2, 80, 197, 0.9) 0%, rgba(212, 63, 141, 0.9) 100%); }flask-openapi3-4.1.0/docs/images/000077500000000000000000000000001475153525300165375ustar00rootroot00000000000000flask-openapi3-4.1.0/docs/images/logo-animation.svg000066400000000000000000000057301475153525300222020ustar00rootroot00000000000000 flask-openapi3-4.1.0/docs/images/logo-blue.svg000066400000000000000000000010771475153525300211520ustar00rootroot00000000000000flask-openapi3-4.1.0/docs/images/logo-text.png000066400000000000000000002324461475153525300212020ustar00rootroot00000000000000PNG  IHDRաsRGB IDATx^y|duU/⨣Ȓ {RTڠ̸#cQq}FGqpةtw*MҀ&6Ȧ( kKy^JC*V{;}.|?     Yd     "!@@@@@ ^.!    @3    @3\FC@@@@g@@@@ fx          r @@@@@@@@@2,@2    <    dX0e4@@@@y@@@@Ȱ`h         a /@@@@ @@@@@ ^.!    @3    @3\FC@@@@g@@@@ fx          r @@@@@@@@@2,@2    <    dX0e4@@@@y@@@@Ȱ`h         a /@@@@ @@@@@ ^.!    @3    @3\FC@@@@g@@@@ fx          r @@@@@@@@@2,@2    <    dX0e4@@@@y@@@@Ȱ`h         a /@@@@ @@@@@ ^.!    @3    @3\FC@@@@g@@@@ fx          r @@@@@@@@@2,@2    <    dX0e4@@@@y@@@@Ȱ`h         a /@@@@ @@@@@ ^.!    @3    @3\FC@@@@g@@@@ fx          r @@@@@@@@@2,@2    <    dX0e4y-z~rdn&m4M-Ma\-ގ   Y ![,w3YB5Y0{*E 6 :ϕ   $C0{ Hwpd7;YS=+uA}fū63(   @R:#k͑suu.3s*   -@hMI.-֯%ը4   )LaW}NG]~\v~N_mz@@@ ν35L5Ι./Lx   a /Ȣ@4Qߑw9ooE@@Ȁ`t@oir]۔jYjyM   Apx܊(*-Ygy!   в`t܈(*Jzbk9&}/]gr   @ vΙT JcRtͺ́ǧO7q"   0kYSq![P|n8ϥǧƖ   ]H@5RTBf9?ۂ[@@@m ` @z&6ԐK d*MGr    @3\FC G Hhu٤K-@@@ }3-( Or?(э9\Or   @f3JA tj'~.ə>pXq}թ   e,oH@#,0­Y]/*   @;G ʤ>,xܞd1L޸(z0ҕ˯} @;۩Y G^ȎI'qB[FFﶢ?a0##r/hTyKHzϷW{{-* -[x܏4g],|}ulɚn@ [71;.ZkηV}ܒ ²Afs ڏBm=g Zj $` r[ZUutNeWo4/.'(@`NbN|܌Vp̡v@6y+$+$v@;5ui3 }^-)Fh\}gFރ+ձ"9,@3nR |hצ" М`s^\sslvBV:r_ᤩQIK;F ?Z/uR->3׷m ¤߰(tT_y{|T#G1KƋutHGC! c >o ط td/݈h`?dE~?>.\}ܯOM@ijr3˹n﨎<EM`q)MW/j=ԢJ'v7߾3O 4^)J/./lߩ34y+sP> 7T @:yu_j8u{NJgQ @3hq-IW͉/ʛ?_-eAjE';`UR'b +SޮzjN>[JjytK@uZ[2ǚKcp7NjJ(;X9Mx;%L_./}}"l8!@ZӺ9FfzhIwr^}ZgL)ۢ@a&>ܖ03}cja;`A &v54 F J9x=)?|G|%WOB> ٙ0;dȦ`6T H@ ]T9ťO~"\wʾ[{ ޴ ; o l)H@49%y!Mvqs&`|$jtDcΙ @R$@e Y8jċ.lٚL> h Ƒ* @\qR&@(&?*;26V&Vd!. `o?7*ÃyqAexďF5 `S\\] <" ɗ[0kVf4S۲2O Lgli@ {)!y2@ 6p,1S}R .Q. LպhP t`.[%\ҮqEh\:o\|iU6[J:w}czvw34y+soW=wDu 7T @FA a/Ȫ@arLgguU p+'s&r<De.M}Z.j.Z5An ƭ33)@؝{gjb-M‘=r1cf0Sc{*vsU0ohVY1G 5Zsob.HXEl.]fje᫫cnT<H`Z7G Ь`b\8\hzv५_&3`X-y Vn@YfŸ+P(U%-\h3{XGd#Rg\ZSoNLD uD4 qk= G/)V{>Eocѥg(7[}/ o_tdEVj= ЍݸufF;CwMPEgc;Z.9KjSXik[( M==nV{7ͽ*͏@{gL@ vD,P(U&%E\r؜߰'n]yl;@8]^:ڕ]24`,1@`s&D0%M PXvd/=n_/پQW'ۦOD;#@] $[0;N0:K*!ɗȝWeIP/ZU"C#%~NH< ,@27"@ v. 3:Q N~)@> 7v;VR2(@2lS"(*I:0|nu'tQ]G% )La@1y<@ WL<5/")FTE~h6 3XB#' $T%t1@zJkh 1o/}:F lwVO#f  Zz TnH@a-^iV~+@^ﬞF2D-@(@ I }!2o]Wڦ0L/= ;fu̅Q F-J=H`R7C_H@W~2$`,[R1 `*֔& "D&d Nd_wpd7;+vՄwV%]5qB%L"R`H 6QstX x 34yY!۹'sY:0KdS0N]j#@ zA RjI}ކO7Y*3!@qR(@ĭ)(*i.ǁθgTmy@ .dI LF NfW0+֯*~##n ԭ, &r-4  LRh b t_]_R5`֕f C &l! FKaC/֞]ztxvJ4WR%Lf &@%@,u$;6TLIӹuM$U0/Z0jQ!e|HVW/6+un.Y}&kt Ln  ֓jt@a~YW a/Ucj]m0ȈWӞ 7t;.~׏kNI˵) oߡ7vY+g426{o7_OgvKʹD*P(M[GZbX[-;q0g>R" (qymgrp gߺIq=&,:6+;g™+ݷZ4SG.ڣ/h<fz\5ӒPڱM`;Ck@7=>?wեwJ",~Pf4G 4'I @ahrb(Mv ߨy 7Hyl@(Pl5o>mpfe37qHUҞU~~+=f䷄$p]mͦ#PXtܾfvw|dg$G6r~fʃ~q*R0G2 xįH@Tݒ!mOn: :'o###s>p9;}'nwݴ>KFn]\qǭۡhz`e8s-Y&]VM3W]zs- 羹Ë͛Ujuȍy5}PG @^>=Z!fL&y BkWZZ*U7J̿pcpڵ0>y;wEIavte6 5?};/^q+%35Ϡl|B I7S/w! l @K'Y-M UKߙFhy$ Bj}^j3y;o od# M~,:womr/1^ W؝m CږyV;M6}P)ImYQUj.BY Ί@zKW#>Ι._λcsSF+ܻb sowijTw IDATH.V|5Kd(=첝}KT{|z:Uߓ|y\Yݑ‹[ZOZ()ܝ_mAS+m ` @KC?pd}QKtMWž:EhY n0U<*Tf_x"+d{WK#/F[+ "ٛܵocZmū6knZLI蝾Sb?7't@[GFD ʃu)eߟ.uXsC΍,o:w_jw%G&>'v! OM@pOQkւK=mߏK^gϜ-@2<[c)NѸn݋V{^hD!@ؚkn :nqiů^Y,`?|Av}2󯭮>,O[<, fk#ܒ=YYo/@!( )Ȃ5FCh|S-o\:D L!`[* Mˤݛ;wrIJ3? 3TyUKvٯeYmLulBx}@ҒN]//~gs&Y V  C7ȷ~/?)WO ZJ* ֲ~@ 6R>Eol衫O\zQ+7R>&i^爣wY?zQ5 <<|^w>"[M6{ٜ='Cs, Jz+g] sp ju}InaxY[폛]F:V_/(+ЭP]Y/u&mq٧wF5@y͸ #ӂ=/.sW;zQǹ26?\y;c/ܷVқMzN;~,֒L[^_Vi[-h;!p W5ԡ;NI;!SN;x+끓@g l,]2HR,1YEpxJ:4<|߽οBۭgڥMzTxabFF<^?y\+ӤMڞO_^_&)~6rX\Yqyeڱ8  @MEvC`/;PT6`4 IgK>M ,6C|n[;zjOC_"҆Fmڒyu=qU]xuͶ~#$msjs@<-_kNN)*K%}]qⅭu]R6B]~M^jKc{Ե%;Z-owG|cKN~^ts~@  X- 6BiNH[i-J[f `a2}Mۺwڿ\8*3q"Wahr~+DM?o;mz[LvL;l gNR|P;:E@ogvf p"@-fN"(&#١crlC;3|.ږ6\^y~V^<#']eV) Zٷ.s._\o;.<$ڽWcDYwaooYJ׿̹r?~VsѝS 0q/]*P-I]:~{6X^8m<QtkS69F5[oux{Swhs屿Sr_♢.`-kMګ ~]}`VW'BrՒbLwwz$:2 ʼn{;8n O*=p4< 4-3X9L7}#74'#i Lٲxʥu鍗?Oƶ[뢧TyI_nYjY_݆ C782k :?[?/Ef[N:e i{'3qkuMW3ׯY۽ @c`+;XYow4t_2 czvk KJwwF{*:|ޢU3a`/\htPzyS75qɿxͷkI K+h؛)k.Mt>j&?kꚁ;5@ :,@b~5{P ru'Gs^;,tЍqu{mQ&XHo/R =}f><@lA##a̦ᆬV_R,@0XyL&yXZR#.s6~לZ-o}+7? ,H@Oi6ܓKBO*^9uOJ-վgSWg9-U]:2ƹ~1StxƬJ5x!WJgV7456akon$[K'Fޝ+3aD<C?KMZ[ {gFU_yfIM4j`MTsQ/O  @OrXNCq{e4tJmU3\{^Oh 8vj 7dY-UklVgMYcvcxdiY&dAtx[s\aW}:՟eBxbdoe;ev˟h6gLj SHIHCX(M&$:fg.?p 0q/].P(Ut`3}|cЖPzt 1_ tc5#?V8?#/v/wPɡW9;ʷ:^gwD}HP K:,[뙮#FOK-XLze͵1>t_o]Z/6온R I$ds !ZYϺi2ҏv'6.N*Ѝ`êP8M`Kf3|.%RL^qTe&l\wKZ݋[{w7d=sr>+nw^(¤u>V=?,I=sdsӪc7jT@N vz@zKktT^[/~%U=lsScŷq2S'τA}7&>j뫼6ɷ4ޞkN:<>q]f/;-{;{\<1;).zJ.HbXXQyB#: 6UT FI5N0XY-@ TNJ5#ЎS|O _I^Vl_VYxUQ~6|x G UzY4ީ-tZ^Yݚz+g(>cV-STǽj[gͶ-oI? vn.i+& B](y;zގtN5 ,@TDl9r|H<譎KwhG!@#1Oo/Z]l s;̯6nϼ  kw7T$U.S#wQ{65΢_Bח(\7Sy3I@r@R}&- m8:zS@ .a(Weg6L[ۀ#ӷ7BN/LJIDz8v٪gr%=!'b1pWNҮ1wʁT1 $!X|۫p?x?=+)@ν58jp!W$$7[Foo? j(#R;j=^.)qL2r=n䤤fw}sW%yN t:, W(ii,\]`VwE7B4!+ې3rR/)ss)]k^ t5/k8:wKGT-]W917V='W$ӹU Fs=p2Cʜ@wܺ60dq[ձmwf]0wVK?NOA @I7UX^]8[BUI_sZ4lt*Z\U{vZI˪cvF Ⱦ@`eI[0YrZ L]z7kyL w~w1~SIн%w`휉U41CRZOiMmݿ7%M$l%uC3d|!W\j^2g#俬@Zc׬x5OZvIRCDw|p,])@ؕkgh( }KNIe;L ܖimm I)Fn +]>]?G8`m.EfpTȁܵv%", S+/9Y'MvZu4'1 w;yz`tT@xC B?UK n(&v54K3+m[ K[LZ# < VBIbM;=֬x  &v)`B; v-32߄i&#[R@5TyrUnt#e5x.W sZ_5r Q&@/)(&)RvUr!Tӻ`.+lN{=jKܵDO}|wwפ%{ b|_&&l4V(MI^7[}/cpO]w|F-3zX[$#vK 0İΛK4kS<WQһşV{kQmVV=;gq-D)02 O-@Kr3df;çgK⩝<:Z {V |c3l_7.2h=rUo lFk3Θy7Hm q/7Wwz:Kjyǒ0`0>o;k?كp?u$*$SҼֻn,9 8~.t:^grI zx-- E]lFE=CWN3=쭎T MB0kzdz IDAT Rز%a$l{(4p͋n޼$9p7^{x"ΒvOYط.ɶL7td+;Z%?? 7<>/2dY0e6 [ҧJgZ {O%L%= ee^`ן,S3z$q%.lLG|SR.c(h߹3nnwGOQ]S2˾oձ[fY s 7# kFdw]f̮6㒍ٍ@uOc<MqEvq}>=0?̞&ioI߉ S#{ZyW;Av?r 6ko\[@MRvIkq;0y;IxG _Пs7s^'I=8Uvs?5t?tIh|\{H) ^1lRFlMnaJ \ oa{rmo7mKڑ =[?["#W^zQ z ߦ&}џ˃V{,pw wAR?w0'txϒGHa8yʉ m:| ftPb<6I2-cTNQXb~N,E y !PB@-  M[[TΘ+A|W\4=_IGkɥn#{R%YMۥўjG O.̈́_Y݆K[Ldr+é0kvx0ws4=|_F\/@1' Е=CkJXWC; #t'sk0wyYfZ*ys6sZ,Ivɶܤ溶vܜA7 5=S%&c"@R#?)](oq@`%Z-?<۞.}sٙ[a:C3R{B `C[uRGnv^XKW8) ,szFF?̇@ 1Rn(&&+ݡ1.*gD8zvzx軧bRH=Z)x/hp&(׹ {' U澸oLT[oU}/ozne܃@ v8 C"OQ;5Q-i@_+&%v\٤@|!sfU __zMK_'$MY dl1 I LF J_0cDz(}yO"/LL frmpH2I WQ `c= YHmtBv5׼i&29x%`ڟ@G}$2-PXQyK- gfM­҅]F&|tMhy &~Jn[ 9fxZt_BhL4iHZϥٚEʵ_ᅬ_x@ nO+P\Sߒ^ܝ\S-ߝ3u͊q\ N|ĤCxr.bCBx&P.D=+TEp`n&Y0ǁdZ0e8rmmApʾuo.@ &QC'Jze26.6tvCqwBe&cBtzͺ3%u^~.=Kg l P钱MQ2#@U2xS?9}'ߔyudă'[OɶI#=+Wjy }Nfg*Ibr\pX)׻lXҮ1ɾ=&ZDu-~ew@7D2!@WwuwrCW vպcP:t>.=A'kLwg 'W6_pU{p>12ՋKc{J$wvYw̿! ?iaJ;!@7ks"(8F]q8}Z6Q) LrG O.IMzZi H]o~@0q|{P/rF##a<~Gғwg>C㕧w歐K>sNY'VJS~2n};L>xIJT;!l9h@a&>ߦvݪ>Ƭ < Fg͕9:\n9o.hrxFuc~DwzB ߜH@0nΓzP;ҕ/#뮎kqMۍgj^qXwd1._&*Y=wo)[FZxnvw݂^,.v/oiMakrZ x& fu̅@DkapIω$e?y.k'DAK?PWL?/JM-[wr^`%k4ϥ :P̤ꨩU+:p1Oj[08IO θs*(\vHEijeձs2G=Y?Kc{Դ;&={uJs d߼d jߵko7>C&ۻ}D~p:>Prc]W짒(iQm6Q>/AZ0y-UNqSݕML^r/[Zg" n Qa=xK[5L CF3C]Y]3pÙ/;ox0|b.*1KX$5?[KNM:;tR-*Oʬ#/Ǯ=Iph`9D ]hPphs[/, 0CtYvs ;Yi~!L>ZAd:Lg8Ә윅tڵ;kﹿ柹fW1g-~(o>"a/H@T9A܋Io*332[syq!]so}\ѩ7JG"lSq79ӂL:Jҳ9jU7Ijs4[$Jk{KƊ7͑@ e)["@OG,T}a@C n e:) Y_EF%}GT-M\ߚ@\y~nV1\~=W}v3İdූ( W쥸.@ ?s]q3c)no]|fn0Kwfi%`݂urmR CU鼶S\Wjy:HNy{&nߴeʬrh'9'@>kNB qIo}|뾆b:t`?PJc(m&✷wE916[orv+|¶k5"ztk6US`cK+N^ c<8IJBiˤ+1E0)~C[㡡7lȥ-[?S#?  VLH7:t>"@bSfhBwpnaU/h6.mw'%M0iIg?Y >o^_sp̛txa$[PC l{[m+ϵ@ _fs⓪_ N{'vFNH)m l2G YCput`w=ꩳ,8%#j?ձs;#Y sM,cčt#>aUFSsVUnURueY]EI#5^*IF<ʹ6jU 6m~hA0 ˽_j| ?:>PME4`][,`ahcr{3dU?tߋGZvxkM:m-W/~mAI -^-7߾eqhP t\u}3I KŲi%3III (PA7Eil&i2I[hТ`-RJfy^Z.I{f;p޽irgZ PE]eh AW ìCaawR}cR;iZ_zEk['D0|t ז{rӱw 3=ƣ\H]X( m+ }\_9ϴK˰ȵu!C镟+-z бn dC*mn+u6L֎` #š J7 ύ޴%ef{ppx^r޳XU#^{pqWiepoD/ wOH+Nu ,{͍~9"&.nPm *rq1Xs3ϻcyhzJUzj/]ޖGjoQNp"cP /H!`.=[Lpi%z3KT+p dC}uNw k}~SV[ DlSn p.cSe^2_s8R0}BΞylʲ"FsU%t#ccur[)B5m40ڭ PW^8,ȶzBcy ط&GJp ڎԥ++ߵu}|.dRZ8gvXk` 8\<:~fe}rQ\3-07k8P p5Q`dod~{g)8,¦+d TCJ4~2J.qDQyzVX⧮.Z*^[{U|啀;p@~숾e÷LWyFTx<)(`]b ͮbSu`( Xmr}W\=v+Mz\V| 'QGB"2(22F,FLX4+uF9} 좁ѵYCCl7Rq8\UnGu\8m\5:\Lj̦Vr02(N(2\U.zHE-m8,{ܽX #\\Mwr%4ѰR+a5NiXTZ[6> V =*8B[2oVL-x}յ_q̡+kG5x;T\A ^Jcf 76ڏ(NzO%0{uY/0ϑ9/6Q#.qMj}lk}8T y\VNI0΄9DQCXPjcjY^P]O}f'"6'_nXUgp;%8tSfK8A Tw\i6z6>휥8* 2*]5e0O80<V8U8 V Kr“ C|IU苆 ͑O/%@Pbg8_bK8 ]nC+*ݯu&7"?sR{?tŖv0,C*>}39̻Wm@pPx\3:*z'g䠤 M:։=+EɖN˧X~;n,J i88>W-y[s\n4Y)[yrP +T~po0ǥ0} ZO7VhG_ HXj͖ȍnKLNQ:Gx>lhXT1UX 4UUӣܭ frp۷ʱ6>\2E `o˵tl͵C D@4e@e4:v{) _^r‗ykxύGSkWܮLz-+;4ӦN[]stq7`/go@w{"?+["Z IDATuٵ%jn69Rj/yKg9w_ tfG\9)@La u9S c "^BO8pX1mƣ{ǵ~HaM</ِ*ѭǼk/e-j)*v2_H6WyvCB>?sbc ,gN ps ;.?z2&@BvQ,^`;P u_ȏBGE.sWF t|hߢώ,x}՘} tdz2| ϜaO kȅ}9ϴ(j^XQe4BPVƥ l3 U81F. (c{g.ϗہh{Bj`\~@7Fg745-Xg Cu_Rb_\o#_u?Ϯ37xo2~AXWWv#8'\e-P@8B_RK1wY ? WOnm / uؿg"-W Rg1?9Ie,%LΙ퉒v{w±% /ۈe =ÌޛR&撱].RmYU-̡7ȝG_sAfW3H W9 WxpYpVp ww 7W&I"De~to.EMmj*\סF}jŰ8p`m񽣭}Nۛ[E A 8K('p}}*QԺl^͏z ^ }83~@YZx njzY&Pr>T/γǍtxJU}|~cԡT~u 5,{T@@i\n6 r)yB%T{>^Ȝd<ҕIbͽ:*0@z>h`o`ɥ4)  }h:V+=S( 轢͖uz 8'lrJRG[iT)p Q9pth]5$JzE6>-7/`;tX k 9(kQz%C_Y qX 6ahϊu̲[$9&)`3DOշެ*" :" ~A~ ׏q@vՅ<K*"UUmesO10l[HfU" APp# wB>(Qo҆`gZ ^niP][#q-^S _^d"%[V?Cdhֽ-zq\ZYj+z_UHwgBRA\)b@ˏK[daeh5\FWtY&zX6 а(!UR.ՁH V@_@|h&+EK(@n}cp]b ~kWn{RCGx%`𙲰8,rw T27C$T"/@ / pߒٱ{QퟚNSU`o3AA!Ul2 bC>>lJTL@{1=6չy*_[+=z .Ymjlb~vg5 ]w#\^;T6z.ӹZ({YgX"xR!IT5'5ew KKYUU*}"@ #7(hPL@;԰hKD">ϯD[U'Bu Kq%@g}K`@%w w$\6[Ű_̑U̮C|0_;Ǻ=:AxҐSJ?i.L88 L8_ P)pso]ͮ8̍{e-ݿ3MHRB맋y9`ɬE6~=!kt=tJuţ=w=,0ڠgIN6W-Ú(o[X/<Q(eqm '6 @"6Yv) O 'q888Q-mN8r 6h*}ꪶggbܥ} 79;ּO-yQUIsyk׍ #ЂXb)W;>0>z'`;@Vm1Q:Gdu`/EV( zȂn0`!ac`ǚE4gβ1Vp0U.x] \j6G9ԞC p8W -P!`}AP,uTG7{k~)D  ϛP2ڕ"Ϛ;Q:9Q03pR.+_3n}} (yY>U8زp"_P|*ƟۏJaabaa`̽;GoZen܀y -짘}Ӂ@透>cJZ<﮲2?tz_:~e.9KW\iƣ sy uWxC[ƕxִWhہ:Dq5fnpU}w? ˖[R0#xzqbjMFgwS:3S$`1u{?Y|-`m~ 鵭'Ctp0(?0@.K#pB J7ʯp! Nì~yggow{-VrQ 37?p=c![zG#?P%|%`q#3:*)/p$σ(@o ӷX2euyo@=A4˲轲Q2H. xf[LushPL>7ѻ{(~ ?j093}0aPcP& j??ьGj G).MGMToAn@ pcȽ@e]EpQ+qݪ"t5Gu+_/tj1 bM~+f6GjO?oUVwk3t}t7A|9=>1P*>}\uI/`w| ?qG=ȕ.3Z?w9]OS}zCcx󭱙kYR.L;flN8(P|A +PK|Y}t4׮Z2ϲ0'lrOK BO˸+fLe/jv ͑*J+xum5 eGX,Wx.j00D} ZWgp]b>09.fst)UF%|!>-ӣed?XTGI]IJF>ƍ~yFM e.0)0}v[̖FGrxPCr,RߦOwgsN|F@e,q]]a3=ޅ_2>8GUQ%v;3 mڙ@3 G;^fs#:O=~+LT)C%Gu<8ːf޳o*зJ#0)`qO|ϝ=$( r-PY\#> LS1zB_#K:3 /ru| ع^R}?P2=zU)2 xԝz6Ņ MVR`ӘN ?Jezn~Ѳ0wUk#wdf+syy5ҩ; yȡSϷ3,.Wy=(`zmR#@uZ;<+SO9YT洈')DpL@*c?U~NӢ-p-'8V)VER`?s@fsU=*5PyyG 0W\TW}pJP]r/m]댁nZm"X(? !@UCCCuh/9W/cw+*Oۅ9uڒq_oxPm_njZ.4?n p0SJSڶ:-:VժcrBHk6۷WTkrp[3aΆ/?*%NٱLO"9nQOMw`_9OC>*zn ݻzGI7w/ݱԘrx&`Y*# \yo<u2fcQȉJFTq@;Wh@1$i6Gw'?ڧڷы`c9s(WWp۰ttc7ORCbGɖHS/6Yƪ(;sg(HS,ϟq?*JeΜߍI0?8Q%eۖ6h7~~ &nKcT5 ~c]LWuʨ/? VʾTNп*,0֘-Un3ilTk5n@&&5Z^r;iP%8Mx&#Xx9Xmln#A5>:?¦ })) 24Ƣ{mkzFGi)oFO048(Ke= tT`{δ׾19\?>.n)@yy}n0(@|8=>!p:K2+0*ByDZ_R' eO<ȁBV>[l_UYbj) j?'wUޣ(Whe#*MW3!` W x>Z}ϯjP@ pX (+Xb?t^6 P؝}p"Uzev&t*xUzAVOZ";݊? ύGQ(@ P@ |@:~_ElzX(@ P(@ P=@f 4a9v3xս9)@ P(@ P p)7Q8Buퟆ/s{.Mƣ7(@ P(@ B\3'P oVeD)Bvn(@ P(yP'NVў$ܑD =O{(@ P(@ P~/`(p ӭc6G4'Q(@ P|$R(PS-^+ԋ\A P(@ P~ @ cy =83~.f(@ P(@ W@Q`Bu?@aW7h{\(@ P(@<0)PhO ϵ}k(@ P(GQX* I KId=:lxl(@ P(@ y4LBK0>k6GhL(@ P(@ yi-";DfB~(@ P(@ P! O P _#(@ P(@P B"=lZu1x((@ P(@ P 8,ȶrS/ʺGDp֏? xI P(@ P(\ J KFѷ8P(@ PBh37I ̬[2ǐ,+} foY(@ P(@hZ͍RX|2J/7dK)@ P(@ PE'`ѵfֶ`Axtf븄(@ P(Puy O>q(2UtD:Z?(@ P(@ ~p@(X`J՟'[/a,(@ P(;}D XBw)l>J(@ P(@ d&`fN\E x,0{^1iFj4%t(@ P(@0)PUUK~lwQ(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ Ps IDATySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+X(@ P(@ PySF(@ P(@ Po8M+RJCC1T5MM  ::9(@ P(@ P@n8̭g٫>1~k!A DzdGP?ʠ:`B%G*ǧg}4é q (@ P(@ P ːs,+2!e@05p TQ*D@+@b#`S"-/Wқ&n`.0<(@ P!0aQ遛'lPd@{G[}k8^3x'NJG׋g5cցS% jPQihXxB_gkϐw8ؽLYއ[cQKғͯ kpQ8lv¤@:J: ]A(T UU@>mSR*CyѲyEy8H7f6!Eo @ V@wA< [b=i&d;N0u5'O1ZUlrIljp}⣪>`AoLr$oZ}硈~+\(}{N|#1<03'ޗ 0z_6z^C t?d6-P(@"h4B_0Gzylp]* (+uP} !x"եc=jp֡C RG9 ;؃+7q[p弎#-E| 'W^rgn:G'Tq6SY){]%@<(@ Bɠת8bo T>;je-LЇ!)K"-ԅkKƽ-PDRcɘ:Dp2.cy$ 6)}zt.fF%T0KB n .T!VtVU㦁t-=P(@7±% 4w"rCo:%np7٘m h7 /Heuz`Œy/Ҏ_sfkԬ)Y(s_}}V^nQtCJ-zunav P(@!9鞿@pg"+JO::uy hbA\낀ȀB%xQ弶1 &ŒGc_=I@Om,lH\=9|{shA4L( P< 0@+["ˇ^(@ PsELߴNn7QoAvq? m]gnηV ѽ3ƻ⣈Xe6|i Jg ^us*=ی"Ϯm7~BT/2: jWģO(@ @(X 1.eVl Ec*%|ޘH>П:l^ U{8m\5ԺMY'kԚ1Kci VF쫬y9<%B.*?}㚦=!(@b8OeM P=Bs?-ᔪGF`٘b5]Z-ۋ 'Re:;7e[W9J`ԩo?]I2(@ PpS Tqflb dy21\ya0Ხxn?d nɳ*8?.tAm??'(@ P ?Bu |}a_)]!?U_5n*$7Mz1ow9P@ {[O!{(@ P *c3h T篬+ɠ',K?O9I]r9.+ߚ @،'񵮖H'(@ P*c[]܋T:fռ"X^5)~w@swJi w 03wUsf}Y+_N P׶+ dU6Xh]zk82#_q`>;ծR_}f @ wf:84 _GR.(@ P ;Vza$PȯOP2w W5}&w xdWF/}9a9.(tKnk60<(@ @Q D JO&*^,(s@c_Aˡp̌?W]ϐ#,lX~(@ P`xgo?4ez~^\!Xh6G9] pXƓ)C#.~:vnK׷Ŵ|kI4B h~;Nj }rQt"t\Q.wI P*;իT.kQ<|?H9)Yɖu>kpcJ1;+azQWKM9,ש9t=b͓K1baqQטȷ̀(@ P`JVSJ?hƫGG[ʗ! ou:k Wរp\o)/P 9.9=>1X*7s3 qQt`ߒ EsTCz\RbNe, P('pf7hOaƫaay[yۺ W}1l\`_/05Mzcٝy3ĐFom'u)^ϫprp89*Xlġ  TLI}Kmq:e.W TښtN7 )t/Vੀ`N[0(@ Px}Ӂ};|Q'ZS~=|7)?g*Y TQy +^?|sNrE^P,r!D{^oJøtPX7c,#KI {fRs y P2ɓxt(@ 8&o4T[3^}cEȻ Ft̃L.>L%W8TC(*-XwDpΜְp xQ=`׏kgݭK7^iX~Pw3]8#Rt-԰pK60ЇBg}OopmɄW^>MU?:͏$Z_2[j~:<(@ N`zm` x䛒@^U;lo^יo`x5444[ 'ͽcӁ@Ę"SrTO|x]:JzaQ?jƫ~(@bo|uxgZj*uDnݬ~[PN9s3_6lsiSZP.'Zn?Qy%rIa:;Y9ByV^29}GSӂ~{jf͒YhNG^7ҩ#?@5LB P@K49 n6zy59zsF54l5r_>A`dzmpP+=Yr:pWp]T3?U _6ä3[q25@)\}Q^cx P|%pFMYYBU~F hb'ُU?X+3:;#}-tfJȇ;̩zGYb8C rJ/8dKõ(@ P3:*)}@K:)Ql4j&%^bƣN`n~GfbY?0߈_YQ%J\9`Yr8|;U!@Bp:|/WraQ|"]a΂O`-3X_*JOmMplI"`3DE57+.(@ PMٱ{Ia7'>>1Rp%rest[ 9.gгNNƫU0wO;]7("ry?Aco %RGdCȪxƍ b^q (@ PY x5.~ϒ2!+j^xwiÌ`(j,;"6"My2̮gTqBҩ[2o8̥r~nhXؘ*=݁`Id-4o$p&/Vo |nd};͙dx;a* PP,a` )^sEՁ#2DC+:8"q>W:>+|Ɂ?' 3Υ̥sOzsɸd?,/mچy~y%_RB+ vׯiZ9*Ǧ-~e70>(P(Σ4`-==)T6 A~[Iȗ#?wq@c{yA6ȏs)`.EeS*_2Ty)<}Xp ̮m7\%'h<^gXK/~;^ 7(@ P`OX7u <iZг:} B:ni|FSv8GUqyҔGckz|ʡlk؍k)1VбP,a_USE~ҰtRnٌHyjR('YuE;85 O<";V-iNYR[ 1.jUUǨjߖ{p;l6-z$0Wkw^_Q\T "<hQ_^+1X֭"~+(@յ] 1ܼ-V x=~.dzMW<-% [jebx/-rWs%uEVVժ՞-ds]V޷5}gn#뿄*Xodև69R;Cy (@ P!PK,`{{Ho %~ ݫOXVpֹ@ rdzm@QfKres%ﯼ/A'V%k0mՒ_<[8Mu*oNs행7R dC Xr=Mgn.sqX[Y}zU A <>udK)@ P( PH0mkTߞiڧ!IJ3=&u"͑>H0?u4zՊdsn]|s%+TW8ly"Nn_YCw^*X @{ C6WA.x@( c2^- kȡ U M ƶ=VwOOYEƁi|Њ @ IDAT6J&ThV ^Xe[J6X1`4okH(54, l]:.k30*jm Fm~]TuWȕ.Vi :8ӫ:NkS٨ԫAW}Th󣝡X~H(w?#46n]G0k{{ЌG?Q amOA>N~`y|rd)j0 h6Gz&9ScS:) 7[lVsg.9k}q?5,**{ܪJwvD?mʺGDpge^Hb/g~L0?zMw+݇5{R]Çꗜ DQz%C2Q~v=㘴yB#z\́҈gv< `}،GOAae(T6I (C !>-Qgo[K)g@*METW ʊ7Ʌ#zЀA@NU8@K乢%nVȣXC}OiZ7>P9pCqԘ xb,c`H)T+3t@_JlU1 T׆>C.+lg*s@} eh;i2E ~uPQA@q/lbfdJiTAEi;3=ϝNi'{_=sy;3s`rlzv% i;/1xBkO7Ҧq}l:wŬuL=s|puplCci|c(j%&O&`tf(#g0|%z7>&;4^V 'AڅV<ʛ̕- (p0=1q,Au)uyP;ppC_+|X6 Fck#Ʃ1?_mN.H=?!`;u= $YڊO2RE$[r}.hx[~z^WVus:Q Q'Lw\=?zF/j]>h l)XVq'>h (BW'*5Kͨ>sr6#3LtziΎO/]}kV%W*g2mh#@Ks L:J7՜l̿FT&g'Xf{z$ hÅ 8N^qㄚkTq0dV;~o_fſHȤPjިVk͇[f|ڏ~XS!e D_?WK(081E6O |#c?X>Eܺ_3V<'q?8PΕ[F{Gޔ ;z4[No2ч8UI";v;KN@/klX ‹%G: Y-LA;vz3`DκJ=ZBPS(١bq'FүlPةGf e!U^O ٷ\hW80EinӘ4!e}0#u=TDx07HW˧zsrdl @Ycpz̗X 6w*o;bUy=\`ZQ_@/Q0GSq>'…Vܸ)ܰ#4oZG<ZZ|rѣW$v(ܴ!fZ穁>y_Iγ ,e<˄lJdG;`hO|ߙIe_c_ 6vizACk3MEA(+,2×fuT;:г3Rn J,n;vC0zT ^ |M6?ΰ̆{| ̀=҂.rNg|\8)O1u^>P2C%0>x Ѯ*vapZ*rXBe;mNЀ2xV9(u ګ+/_"++dn y7`mB,35ru'5vg@$xc᫆Z_ SyeǾ pJ,Q^Bv,3|uyU*p:o~ã9}Q,?K\q#vݜH˶l|ee}TI^HDGQW[K!iqƞK ft̜ htOZ#.opU EM)fTÚhMvݧ-n^)}E:o"K ֫`ar),6<r(m"GSu/e@s+nO,o, |YIK`-3|nURy3ߧ){Q1ǭqL1K%x)RŽz63 c~qFuEƎɴ:*'/{Y.gU ΃~vX H$&r~f^vh{!e5mBbK̓ӉBT˧֮ɽ cVՉ\7siZ 2@L?S :uB)p+Xlb@m!$l˸J7N-ȃ&=Z|.x8&ϯ vlနj`ـ @T*!f6m+ wa@P/i$sZz%Έ-<|ٞl}Ǒ+D|Y:n])n= S'/l|R;m`UjnLbAV`n`3j7(m!;m\@}r&qٯrWJ VvOhIt<4_Vz4u'g@o J. _ ͔j`::r4>uWb 9S ą}"`2m_JYK 8=0u'Tζo0&w1}QYҢc1yeOvNK&!yzT0@-WzY wPSz-J㶺7?+&71U²U_75o̟@h,¬'h8Z\z,_Q\ ?W/u %PQp ,16ZtǬLpfĒK Ւ@u/\yAlAlCn/k`_zT$毲Ϧ.u%qE&aXlaX>9f%ώoӣ@N}\9t\_q9s'zzO G:X;J4$jHln%g_d@jF~<;[a̸.oii+,3}NpM:sϏZ9(4_i[Ns88ŜMMBbJ}7Ռ{k߀!ϙi˪d"ɻpz1όַ J,شx\9((Pܪا8償GV!3rVN xbˑV+OS8%v~k0YV-3[s51<ue6Sqm3dѕǴi}9EZ,O:43H|*c"Sqwb4QwʍA?ʘ+RSlHѥЋҹ˧Zz}qJ:a,uhwt粣xʊkTMml(>oUGz'@_Zz{eo|AR٣^M8 q.88Xe]s6|8*ѷI \ "g2"گkNN|vsu[&wHr.s3fC%遽&;ƹ"ve\Ir@IvK4X燦xTs|=hĶ##/*3n6 3 sB{˹HH"c܃= |3c~ m.)tCd [dBC[+bp3 DO0Q7Zm,6RKg~GǬ‡K`Ḑx&h5OzzJS۸ ՁIgQensLxc>%\CkްP(9HR <7mnQ?'y,k#r>* F!)t5ּ} G}Y'gPx%z| T7j/P@HD@dlXWhz(":A6E~6; ˢ2@^hOUW`}`Ti/8}O*:BMԒ~ ?Aף/p>xg\]G:/W@&28W?k'z< iz,zT 9"1Di J >|tt36:.ճꢩs͗΋`]`nʋi8U pX;vCP͹#M#] @/XϺh*.8+xË^%_M1ؒ-o$m3EU=B ̌7n,![`_BaQ 74&_i oh`CCjDh{\_&Wã5:FxOjhgZS#=:p0پ>G=.MQK k+ا/8o]`uYn] N.2>1^D i/g9)؍;]]q7jXsWU##DlÈ1a<cñڟGi0SgiQB#eԭ芣X^E+H\^R ?rMԕ(n[Z?}$7 5ݵnsV xn:-W 3v-ťN[67?[Qyсp,f͹S% `=dkZR*焩66)eң)g:dc\1 J?J,-Rg2(c\@ .fƒeΫFJy!)p^usSdshz>=VJ"TD}9nں\Ң-{ +UZC鉳Ӛ7i]wp@5[d4N?ňp#rˇE^f`jT)Y w'_<9 &BkWgG@r OԮN[hViѤsPsiEn~@Udxhyr 2\3 %z|a~n)CJ徨4Wk%T.:7 uY{.Muŷ~K& _ea]\ck:-{bJ}ښE | @Pxl,0ܮDE;5= hn `8uPz$y6?GkgpM<7Bc- 6;xKi?s}cz`"xp?Ƴ 2rǚ>7uؒ1ˌk2 [^qUKƭ Je& T`!lycqkro0peM 5k6~uBstl Ѝm1d+ӿ ACm}Sb&O&n1\JYG;HPdOpCM]?@Ò*@V"ܖc҄M?gVIvCY97𩡭KhA ȝe-!J(M:׷.ɫiUW|_7siY?\wR nhsƻ&_v}F,i躴Γ @N IDATAé}4u[n<7Xq OFDLd{Jvu֑4~݇B `vvLv,o43@%2L&u Yf]% beō'_GfFd8Ef@>YfZq&/Q!5bH; ЉnG_ÿtۯJYtgùe@tޘI6KHo  V/f ]At]7.ģG:t未}-&#,&HL|u1k [×Yf?E'ᕅekJo{9m\h CqzɈdPߤG%#,3ϔP8P[%V&wKdh'dZ@\߯UXGžmCh9JD_*6cA$`-% z$e=O/n=eY?GHˆ -,YEu"ZY{>."Q**g5gp\G;F j.tefVe~ =&n<<"0k7o@/HMDtQ2<\ȀVWJt]ieגyk WZq5y:OI p2VYsGp/_k_;!;&Ut~ 9H#{@8U0eʎ > ֣q/&J8T`1ɯ$M/{$ =?ΝarKLumOitJ+k+Гz.Gy*i_LQHϲ;E}xG;[eOz]b(Zd - 'cDcZ 1h +uNUգ7%'s`_Lùn諧nN4nJưOȘߢoGJp1~lY۬W\9+%z]H; h9ldscs954{M{{:,=P c8U@!Y0X""yN翤<,B9r'1V<+prrӢ}T = %clV=g|E0@;2Q '> "ot)|*? 0;-%I%%&7K=̳ӻo!@Sgot㷗^Rn@7X߇K~j2-BQ $iU:z\?/mo"k{ꑮuJ ,YolžyN^XMW[f2w| >mfjh,M_/$O#E i% ʴ_ h},y:sQiҪݬhb/&?sia:@1T}/v-%P 忙@ }ǎt5;y&q 4YOGռpM}'h?Z _*d=-Fo/-n==?dHr,i?M? ٓ=`)[>fƧ]:F9(j~iX曤 DoN,`XZq} @Fv~ G9^ &X]Jv?Nv~B9\݇Ys"p˘g7Uv@m⋭J%V5ܐNW#Gz" Xܘ4ls:O5z@rf#4^'\ ߨQX f邌qB$  c8Akx܉0I9t%o|싱!ץ:cc>k:mvǶlm5]}@.^ݜ7MíC!)3p{M̍9 0!@ko;Ps~~%3;vCPs% `%-@qj:a/ ̲7$/'\H2CQ?X+z,u*%@DŽjh*MBf0~XݯRCCjƑ|@x_#$ w晤$JLG=/ȕ`gg[hZ]q*prJ,-E.^G;aJ yFAtAFyPOaG>λ(|9 ]v7,k=ᛨ*,%*¶thjͪLthxB7vӪ&!Ἑ.K'BAmr<^YK rM 兖i̸o*i6δ1b [^)M a2 BR.< vU*Q[@8+{$-{ S~0G_I7f_8 T+;7CDV*^ph kA9.%ЬNsҢO!m,]'! ^AE{i͔Lu?~1A7 +T W ǟM.pZОU!`]Wi}qK;9/b&e @fsbn K9tn %sBp}suKd/f!U0m\M 8SK(05yznǑ*J\ ;$-/LjP@%zOO u,3,I8/B<~u1= c[C[0Ws}L&7A?ʘ+\Mgh*vflpNx,2=Kc 1໛h(;Q Uqx: g,ʵ @,jqi5pF11#^_O2NaW6+^Geq7l3\0룩ڰG M,fFGN \<>u1O:ּk'|+ ?^'g=G>m6["ז2@Pz0r\@й.+ʍ$X ,՜vL5-0c*X2%T6E^qPFud!3J? 扢 pG%+of^~RRj[ r=+3At=3#?20QjOhL.M^ $"ʽABrK78-(*qSh"Q">ČP]uX8 :л(S6羹,VO (P n21WL⡃T#V>mfJ-|ϸ,ѸAhr6.)D8#7/'\}nI~m,pҲ[ }+nbT ] B`F pd齕7.oi4>3S&=Z Qrѵ~ -}K r;/5KĴ5H c,=oZ .)P rVH6SE 9mcTD48?i))oHT}ng?SɏN03g]aOɘnVM. k[r .H'nv+'744Fl#1O?+E/vz|Ќ{;0JdьiTu8%Joqm7Tz%zoM*6Cq D*XxX24c'h%T4F01L7]58,Әzul&y~1?# 23a~2U154Ŷ i{Bcӄ]$"r퀍KBN]*Q`E%d;-;%z_1)9n}R0Z+kR<䃫\:ze lLtSN$-12f罧HE cmտL!Ƞ>i0SlteΔ}Fl6-2F!첄#gVAr`:ūk. K,3|we 9#n3,b=c-K ZKR'NM/hHIypWo AƉ&Y c`%z\%`JO@%@r.Nlj= :g16!K cyiL#e_&mP .1D+Tbr؎6&x UX=y + hx`ߥe/4bhB?JϨ!2(>ASL͆ZÞ4GvCdݙqP ~t,׻9]*qc r Y q_JΕ_;fuM,%v-[#96;6 :lڙ5zfQpxAqfpE.s_Z.h'z\J5 $B]ҵ+']`f-p(75V"rt]#k?(vZp7Z iE~~9#q͚3VKYr SI5ӨT2:7mn wF$5[I*c&W ^_2ommiiq}Xs%sm!jg"RdJ+beU,Q@XY]OeP+Y =isS|v5ce@3 ĕV\C}S '+p:d;}<.֧7,.{}N0v0xزYM:ך`_SJ5nr[ӾlA ggP63v&3V",,@%{|>Kwү"S/;=3@Ќp)zmaa@}`$[nl/fܘI_2*2P+r+eMx7> 27%'SK'!"|!~``V-0*T̐?}#Ƞ={>){yA^@Q' %7P`~sAwmO]hrNV 5/&9]PID*Зd{؇-޹[+Ğ'8#qnl\g7ٗYtlJ,-$I;%oPՙIď0X{iW t;G!i5-[0nǣ@84NF->uLYl"j3~wD0p}*]{ΜϹ ~sΝHϵc IDAT!Ӧ dAx2-+P Ͷ VIQ+AN<)\.{9O /I ؚ=eقF%zBN84z#¤E衭`/fVI[@b3e%w @ u B@i+rNU-3wAhq9=j䞼p@%_v@\"X p\z葎@/fZfda3^Br8+#}z}j?rP1jJ.UЄdC[g#+.ҬңҠ 6.kѸAЇkJtjǑr}W Ό[;fuO)ЧаXn縕"1NI'n%TG 6-<6=ҥ _ bNWBR 2qY_|OF WD7wCpkNԏx{8,z,u1&:b-N48/wQl !/#`=UŵBU`)204\|37~u|+F JGgph,هXԔn~mqIe@)fU] ;f.<wKեg.0;c ȪzE6L!8]eệܳkпAx@X3aSO-T`T80Pnxl!늴fV EeJ(e],y51)\-KG74Fl[` C%zQ+T Rz@owۻ]P/WO'rs)! +n-"k Uf8ޞԀq@+K `㍜6S_۪WSg>./!6.3oEeZ*pz,u-!\7-CGQPƅ/iA{W&] U@ q<̀樒,j c KTa8a}3 Ɛ'yo[Nlj= Ql(Lp9+T1*j׻>.o(jC =M'%}ы]rOYϥp+%VF5hRq! 4Z/ߺPpNX !fO4:*zC=P˷h-J&XUX]W#.&ƟDj?YGS?1uQK}C'B+n$rP (W.L^$`Wd|n4not%gF,9fdDEAriͩ >%l#lr3-iE+H T LE X Ԏ`3[)r#:Hk0N.f2CW#+Pc!?WRh Qi#p8^}4U`o䳑lԧیG$T;s8AV񆰟!U!)3P $ ۶/[R8=YM M@ Vt˾)ɤUlo:C^bazʅ>@qP`d)SHי >fƧ][J(K}`{h}f{8HL]b3$H ۲}I5X DbZTJ0SaXEe'?>g]sG#+`ktqQh%ҫ d@ Kh'>Qr :}ģ{oV5>JK |ePͭ$e Jd`]ѸAO>J̇%7@fqtvyWW},eW1.֎`YY#PیJ0uFjc HfhD\Uѧ=Lf]$9g3>$(ɮ.%ʘ/5fQ{9 ^U}8QQIixZp+l^q_UȌ40&U?R •  '[92UiY^D@YW9jt֒sE|Px}M?|+&O%ÚZ#ŀU0PK}QȚBm 6-BV*9-ppq k/ZzrժBy4_3M'kM >K23,(ɮ7 <]qt'';~ݕAɓ pʣ4L<#KIC "*"P`ĩe%e@vM%0hLS؏oT _(c_d_˧]1-}oHR(5xJ 6ޡJME P+;]N~~| {\}F˭b+]w*%/YV"|:P@a8&I֊wXb"d^~c7@8/7CwUc=$ ڌHo>'[u{ 3)3(ObP6߷[m㭭r^@W9?i^}>fWBytBwqx%t̀M(ӗ{YthnҖlJج5ϯ+W#F,j#`/ i9=Ya?y+0/D7 X t ?#;95!$9"%Pa@ B .W;;T,ezC #21%e*.)Mmj5|drjAEՒ~> TFAC%],\(hrzҨGX*I!!Wj}#x )H5JQt4 (ni+يI%_%s=|cݯ& ,͘I7Q` BU` R#]6<@ᦵ}W^ - :mۙ<3BZDt<|j>m@~_ @kW!9 pn 7y}N\mi:bl2.fDOΜyߘ %f{PPfWWyB7OMrbH TʏU _rȰN7p2h0oib+psapnLBaU _77ը| 2>I+~AUI7Ռor4ym =/#Fqj:a̗I%_%nܥ/XyﭸrG TʏեߙeGwV>*bhhH8 ֋Y%T؝Զ{.|R X61=C*#Rzm +nD@Z `yKyTf;F hp`o{y~+W^ :i$/b`\. ;=S ("+8}l6= (^0;H,o 9Q`YjT`Ք׉67Ɏ$U$eݿrGC_ŔeM :x=lQ@dC>+c?SmLu*%8J 3ٰZ7-'i{8qhKh'gc֬] @˫@X!ǹϾ%ܚ6CT@nQe@ jO&=G ! ϊHX \!qz' g*n2. iV yT%F'F܀XʄǭqVzL; {2CG4!V Y wf_h+3𺭑lA ˽IĀ]"Z!f7!9, (PcoZ:b*\Me'x&>c~NW@ *f,56F>ʇ (}Y[GbĿ}G?0@G vn2kEKodL&'3s 䓙s #e'ޕsXsRbѾp\}n}þ:~}чhkݪݗAN8_@;? h$Ҿp"r~ ngzI6٘6.m6՞\ї}T} @碖hkl1H $R iO}aRa>Yv\D 8CJ,Qi*sWݳvBupc}/VgWeh"-=wAdpW߳d~0x2ϝ|+o Tdo*F}K JGG+Z #w|Kq9$aek<*A[-ۗr3-T*JY݇9uǫz]k6x[l&eU~(F|̱!K ]?sp`^~rI6Y`^ƎDz/VǹdD$3zaY$}`ܸtD9TfLlؒwI(.r5 <;׷Z #t<3!֛d|efe:TGLwB+ j}{n_qcM7T#.i|i;o)dw$znF /#ښ9]U{MdSFX @8[ Bois``Pg6U3`GY}KN45 З]T]!Zo ! xD{nt+D1r]? N/!QHbO4}_[DVVvte;_ֹvjΠo߇ܝgl&;,OB-û?D|Ie%&U}]1Ǭ@rZjSF"ٰNM/6{,Nœ¡wfGJm}}fp/iI_5py2'}kt|SB%ݦ;*܌;vag)kfS/%c_1?sBê0@ma8iM/@ Mu)i|+Ru>j2.A)]@`o/~Ҏp١}ٽmfٽY͋·uVX}.@hU,2/WCn/o[$2jp톖ʜ]AeY>L!~HSMζ;j79YTSDڽj1)~kw 3>Q5;Ip|Ew~-GҰuo,QRȲf._4]o0p/t+1 GKN5^]GZOBq_ڔs]ݴ`,]FZId~ds8\wγ_FOIkt*w&y/RHk'P\X$Qﶓ1_*s;<{qVcs[d"ϢŸ,SVtk&]kH+uF9nwc2F@>ams _5mܠӀ5/9l FZҷ@~l6Xl pf7Xΐ{w͘x 9Ғ)*r=mRj3,E0H_u,SWSǛo[$2DvWm`+-Ҟ9%0Vo]V=5=3ۼXܷ(W{Ŷ{8|RF95󊴧AԟT8@ʶT4m&`<Θ_U& 0 2< Ƥvj9{{F4OcaXCDl~ UfmE5d59^TvH[1Xa$q,i墦V``8DZ3j3/b5;0Ns<8|/ɃZFJ#I;:12T}]D<,p~y+ *7/8?._2h7q.@Y[`1t#9odx\(ߐH_'U&;Їm4N+M Oqz}w4n09jjMQt|r7bkHwD,(V5r`Qb==ǵ/?(7mȥv2+ |SH6,Sޡv;?S׊݈&}yt@5:ʠѪ}]_rc0>nQ3A;gOiQp{ק ``ݾiEn~ Z ϜulȪ[ d_*n.CCth[8NFcOt(R\YL j)t5 pyYN5w@4iPNs:3,j|_U.-P/X좹{YRkM~S~ygpq/Y ,rn`1rB֫z{#o, 78z@&|ݶr_? Z;::n@g IDATGO~ҴX㳋9):YlCM UL_WcdfX7)Pk`5} ?4iS9vg9ګf"-{L.Oş C3H14e#[ o-f3]1ws_ `5s8T3N0,|M V>H_w 7 1`g0ڶ88GM}d*jRH"U@?0{sV fK D0:7MݾoC؂J#}˃(oƣӦ}<(4EMdy^&){F$2s ߎdSU8y5qTU;9>[WB^Q1 qQ*/m f. ~; Z8W~bJTc>]97S{n&| ]Зju_?WL[ Ԋ6!ݭgϩP M _)B O>KA@q%Ujh\.71(<=Wi=sk`@'碕lZ{Oͯd~ȝv*ZYSiw%+D.{k72բJ$v#Z^WRr󻻛m sQZFܻC6?־zGZP՛bl]T`?xh .{#_ۺI_ ({GR{ҐHGWΫ_jpޗl̖e 4.N@3_OD~M>l`(*mܠH!' 9n٢' 99k~ؼcɾTt;#g8^3dž,uw*M$z8P[3sE@g$Ps | +Q{U:>sbX޸5uد<#J;pn }KdT|V9u \eJ~F_7a DZ2 xQW/љf"j1t-/KbqENSEiIni= =-p aM.4OWas<-b3=oŀuZ&}(nO!*{+Hkb̦ EMK4EIwv.ӞV|h‰ʾy@՞Ws )[pa?n6$АH_-7 y(;5v34<,_@#  u VG_rW(:|zCG;n=N?8"gr{@%^[䩞.ANvti2d' ^/jx}}Poˆhwa-t*7'_n^z5-PSxPHwirPgWCF3H[1n[FSs7^{{lcy~8@IG,X`b@ȇ \;ykY XO XةƟl bA> jFXj/3XWg}jQςc]>6SSB(?nd~ KЌt_ L@W@t+ 7/Oş(2%n >fzQֱ3W.jkU֫iu +NHKd߂`+6trCK> '~Y?rl˼r&5܌H1ͽYC-{u4&af?0nІ@N>ة9<x/ӂC#MV'#W&5*u~P,ݝ܄? N7tUAv{G VWg|  h[!7zHx=tVޒ kG֯O?xvs\?ߍ~;ϑsi_*~/Wޣ5t0 )7!lxV۾h) O0r<ٕD֞֝ =ޟ&7sH ~޼~c5zQjEN5zv9 jIXƻ鴧#fK[cays;e[#O!BuC4 s5􀭟k^XH$eSCsޟ:pF"ƐJwBڋ.+5wQLy$Rzx;Ų XAsfy:%"2/L!Gf/_c M.EGjhM>oņ̲펊Yw Mz׿kY/ngKI pF[Հ<௢Sq}oGGGw8pl>$="]zj68 VOb>lL $ eZزΫ>%"w:*Ӧ-ӛO K]D,\s `v9D]v2O [J 7"ݹ,,oΠ1/r͢|.}\ngBߴ_rƦRyO_𓽬]h-wݲf1rΠwUcͪMWl2~wJXw~foSG/a"kx%pd%@A>S`re30Ғ,R U^]ֹNM`aw/p^Pe)çf ;swiYп,Ժu[:; Ӊ}~yk۸pn~wpGAp_P@ / sPx^]p{O-C5كu,C|뵅άMόV9Ώ$z>W6u^\wng \7aG .r*PáݷI|DA@^_Tzѝpt~<[=u&@VسjC&FyQfKθmVJ3RɪdGiP '=BxcZ|ݕG)csz0 m&qt!󭄻AF󕇆DBDT 8kjն3zіl"eX%1kb;qCwc/Ǧ|V]>ZWO^j;bGNwh8( KQ9.;0'gY R'3(ŋљCՎyeYs,#hݞ.SAe/YQ}kqU wdnS>``5T\HrbM Hn8}ɦT!*1 5v$L5+)کM Gߦ/^]|K5^ 3[KufO`YX ^APȗRkٻ`2#T=.rŁ3/]V5/1 \՗__-*DŜcK%Tg%I }TU7L(_~3b4y\:T&_کM6 g{`wK,7-Т~njx˵gѣ Zœ¡w>YDUlDX_%{PT)f⨺6~[87QP}mY1ǛTT ٗlKhHd$/^3vWł A' i> 8V 'TSn G[?S{ 6҅ T;|d K ](=o{sAE6]\J4]s 8% *zͿ] j g6gl]*+ۓ˩E܍~>˹}c~lk$# ggrzF>Ge1#J0T) WgU`@L5]xo[.V"y`NǸ޼ p|sZPg'" |)ei0U:@D;\Q?x%8zE2O~ 4$ҋh5ٞ+x6vOm5Ye} c^ag()yT9Ƨ[%ښnR[L1x1&<5j#\ozsi>a˽V>lx~m_4Q(1)#Yv's0aUCi^zB7FeZTC䖢N*`Ci:T%d:h@`.rKWpRyG:/e& }37E׳i8og w-, G8`{yOnk0?D9:¹K^_ߦA!+XM@?r+^ը5GT~ )Gӻp ВM5>~IBcmsqE d*>^ڻ-@MZSZ_vswl`8` "Ɇ1ye*mX5] E$3q(5jhh'j+_ʤJ1Z`"v*~6X#hkGRFq6leQB^y{ m\,Rć~j"2{%ݦ_24_M0/[Wy|~.79=xD{ՒcU#a?:n[VGc=D._x|`cTzc.Wl*~6X;MKBAN^\w={%~(B| wGBU)lI/D ѕ3℮[ZqcX)|\Bxp?dTU(wEZ'C=!i;ӓjUVdic}0ʆpn܀\(^tz_5޽~ =/? Kş0+{-IWz]wz |/6^eQ/rcS1tϹVUw q(~ǿ l ܐaWI<8苵Jе}ֹka>pƐMr3>[w Nc1n_ @7fO`Yxt$R]A X]կ]s c>;I܊^~_W2s- HKDkymD.̎ŋ[<ƥv*Cn] ɑ(i{mvH"}B+xȤi~ M92$leoVEB62Zƃ]|xue8rDnyrq6qASo} {ZŠ)uT̽^cX28}]^mTTyeۺ_2@|S`E_*~0VSAy كɱRHx@zԸY_*n4~j٫`SQaj(i=fr,"x n Վ$ҍ|P7k*[, |fyB0|HK$RG|WHKp9@w@-tu\i ni*![Y9 O(lhDVz~9MW&#i뀁\~;-Gڗ/; ttX O4z+s D0 xfӐUܠ3^ÊæmٮZ5՘/i ֮|hKtZgn4S;\`V^EB,ȡez%xHU+W0e,IEqs[7i,Gp]c^9[:;3JOUAȣh\%y1&`z>.CI;umlT4(J@*T`^wڲdp7.󾶻l r;iDYӼYm'L}nh} &@y9ĄQv2a ,pzIuP 7^hZN]ef[#M`5xlH`~hKtSY?&;ׂs @)f/s;ق`-(N+"бYXu{/gٺṟ9K`A:qj3NooR] ڇF3y D=Fk] ֓&C]0谻:|Q`DH],]=sHyӫɼ!Bŝ]}? ?}{ɶ 5b&DblA*gg*Xfms+e%Ќ0VE}uyݺO ~tIDATZ_Z?`u/\~oVITNUG,Ez6ƀ薥іt 0o:֕TϛzWevhC>E:|'H~Bh '8֣*@W(\eg&˔Q!ZᝀAb;qW㝀u;P&xtc^oCeV{ϡΐDaYP9G@1޿O, eg_`I( 3>Rr`nBT9@_/{8Mz̶9Lb9"c;۸vgje6٘YGP`wY,GbN{]Ȑ@FD3i/#\lg.T6dX,:A!o5y )`0 ݫ-?pTXCzo0[o Λ(D:Į=aʡrߢ{*1iiIVP (׽@O8z_ b NQ,p ,wrYUE\R Y^-vW@qŋKkT5RXܙ-<뀣\vxWH{vIfQ!}3'^/0rz1GxQy5l,ށyOCgQdV̓G"ٱMrU1ExN,qA=,,"[fU5|Hd~5=O"uVd.|h(׫{= Y@u 6 ekwqd Yg$/J;Aj^6mH׊-QBN޾6&p-z}wU%R(@DД~XA,3XUu<}Q, Jz ϭƦ/껆Vߜz<"@=\MՂx >. (@ P?#fX˳ϱWJ(@ P(@IY 0ߜ-R(@ P*E`I}0A P(@ Pڠ@0I(@ P(@ PeY )OmE P(@ P,5_-((@ P(@ P{`kU &C(@ P(@ `he)@?(@ P(@ PV|أ@^(@ P(@ M T*D(@ P(`H!X f[(@ P(@`XYR` yaP(@ P(76(P `9 P(@ P!`Y~ 0SmQ(@ P*K`e{K= 0A P(@ Pڠ@0I(@ P(@ PeY )OmE P(@ P,5_-((@ P(@ P{`kU &C(@ P(@ `he)@?(@ P(@ PV|أ@^(@ P(@ M T*D(@ P(`H!X f[(@ P(@`XYR` yaP(@ P(76(P `9 P(@ P!`Y~ 0SmQ(@ P*K`e{K= 0A P(@ Pڠ@0I(@ P(@ PeY )OmE P(@ P,5_-((@ P(@ P{`kU &C(@ P(@ `he)@?(@ P(@ PV|أ@^(@ P(@ M T*D(@ P(`H!X f[(@ P(@`XYR` yaP(@ P(76(P@,نHy 8 P(@ P@M 0`UxmhH/ݸZ 9. P(@ P@ 0֙jJ HL Z!ju)@ P(@ P0#Ќ+RW"D8Ό]MϚ(@ P(@ `h֗)@5vlAGHi6KR(@ P(`Xa`/VS=oOy](@ P(@ P0 xۼ~ګcؑc&P(@ P(@ Dڳ5R.En ˆr+_2N8(@ P(@ PFeq G } cDXB{!6iӁO^u%C(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P` {E P(@ P(@Oz"(@ P(@ P WkIENDB`flask-openapi3-4.1.0/docs/images/logo-text.svg000066400000000000000000000314721475153525300212110ustar00rootroot00000000000000 flask-openapi3-4.1.0/docs/images/logo.svg000066400000000000000000000010771475153525300202250ustar00rootroot00000000000000flask-openapi3-4.1.0/docs/images/openapi-all.png000066400000000000000000001327771475153525300214670ustar00rootroot00000000000000PNG  IHDRcQ pHYs+tEXtSoftwareSnipaste] IDATxy|?{fv-*(h-"*Zi]UVԟ>Z*ZK9;"bڊx BByDŽ%۝I5\W+O^ VXXءCڿXUUUEEEyy-[oߞr5C|t9RuO>Yn]+V'Zz%}ҥK^]: KJJ.STTTRR*uw]XX(c?PRRRPP*wݱc&@]"h) iE]"ZTXͧ[*f= sn^A2$иrss8/(@kƤʪn,+yñgzR0nM쪫Z`A>"y_׽vY$5Z,++ z4rAZ.$d" "Ae# mݿn-ի 3gΜyeU{ի1Me_|xO< 6lO?t7W,^" AH$#h"0JiՇ~}ݗ9ػwﲲ_Wg}vWNK1wܚxҬ 7u{w ,{N7|WoUW]u뭷o S6_"ԇX4~. Z%Џ=؄ ?F&MzrZD"~7k駟KlْŪ^y啉'p+Alڴ)+|x̙3ᄇԳ5KKK8∲3g^{AZaÆM>}̘1&M[Hg͚uu8p`뮻nэ{wZzEO:G?Q,6mZCqT\ҥ`1^{HPDbCN(ˋXoA0cƌ &_|3g|xiii -ڈ#~\s5SN cѣkl;j2L"m6|F e?K& HZש{{^+V޽{;{zϙ{Qbh~^{7x'<8b4y:v7---yrM: kIX@'x$ʃH~,VuDUÏN9k.I4ɋ/駟;6s𩧞ܹ󩧞Km @zWx̣;;NkJ v뭷p ϟ?eʔsc 3P~d]#W\ܷaĺ;V~:vȯGXdt<'#?/٧},ˍ^tʠϴtf/@m4d2(_A_.I&h4H&d XT ;ܺK" MDш_@#.((h:iMDMKh4ڡC&+@ǎ]{ KJJ4e?۵klWΝ;7e)4tRXX*`K.{nRr!۷Fhk׮(5AآATUUq򪪪lHYYYkO$l@KD]+gߗW93F[ % dA }`Rqs$I&H$ uA@SfȠثt3-Yo#YC999999]]#Qb&t -f"tH$R ]-@zb^^^;vD";e7"eeeٮ%LDnnnAA["(//Le՚r@ftTK ]-[4-***++H}Aպd>υwI@F";v2V#`0:Hd#t'}q$W~,{ۜ#H,KET52͋k%ss,y0FT4ajszsGz08}peKu?Ӎ>g-Yn:thQQQ݇~UW-Y$A2yz{[+[o}衇VXѐؓn^Ms^{m (y޼y'tq~_Zcjc~z޼ygyf޽{U^xN;srtDhL~?\\\|z? ڵkC*++L{mq~'}m~ӟbzT=۷-[z衏?c߾}oo *Hlݺ5KCSxS_M7A{c;i='x◿#eee:m>}ڱcֵ?~#^}ӧ}jD"F+++gΜz=z|I$tK7r?\9_d~V/.%/MOۋvgnu_𾚗ͮ}? g ;vx饗ΟgӦM{2/xxiiƍCo[iiieee<믿>nܸg}6AvUVʕ+׭kh)|˗//%5x5פ>\r=(7ڧ_WwuWYSo_7MI/.--0ay+,]4;fϩ o~ضmX,ڶm۹;.@_6lؕW^9qĆVW/~箽>۷oŽ;>{E2C~EbŊW_} .|'x̑;_^jU=nrZHdcCNY3ʒG≯7>ӨwH&w,U#E}n+5j5\3zŋ[neWZe˖jXpa^9䐅 AЫW+bܹرc_>oL&7l__gqF*86lجY6nؐn 999IN)//?lNA%KN9}^vꩧ;aL&)SL<9'''//+ ;Z<_>udj0-zݺuc 9I3zѢEgqƝwy94JAuu~ܥiٲe_l޼!7w=wAk.sp՚o~РAj]_9_{.]/裻7G+]ظiS,5ig?X:ύ I>3nt.\ѩva/X69kAQ#lZ`/ΘjkR'xg'??;|S?O/C9ꫯ>{R > }sSO=URRҿzdɒ3g~=oOL=5xm}?[kStf>l޼y E??^zСC;w|gtAm^xy 34zKq*b.,,L&h4ˆԃ {kPכW_}c=vI'5VAu`&y]}љgYm0uԩ>gΜ˃`=^tY86{+ygye?tJ=^zz~td,#M& @ <#?kpǍvx\9 .gdz}f^nT^<`@z>/gnmΦ&MI랹5#'u>?ߜ3M}qfT:s] mۖ~^nݔ)SO;->(//ӧOO>ի{AjdժU{oѐ2='x׾nݺZε+//_v?{lOߓ%QzkfȐ!}{;>SO=7jA&NxtMsL +Vo;v `ǎGyd<O]::L;=zH7 8S~駫W߿=ޠǯ_3(\l1Sˋ%zЇvX7|_6x衇~GpVNU}&|77кgf/30#٩F3&NcgLm3;uad&M5:; Mw_'?̙ꫯ6W_=z`יiӧO7: /d˖-O<1؝x≥ z^z}ᇍu#G.\m۶Loֈ#:*j[ne{:믟5ks=s-/?Op@?Hk_ZQQQyyyYYYQQYgURRRQQy`x<СCS]o߾}]vmjdС?^@Ϙ1䥗^*ea"H-[ הS:5o0VN,bּ{ݟCy 䮣LȄ7^`Q9Ǻ6r\WG;Cgl =,Hu㰉7 ֯]z|)qӚ5']$Ԭ#=E]tGm6!矯:3ڶmlٲ~;m۶-jĻ[&'p… ܝzꩧrJqq7ƺI'4bĈO>5=3bĈX~7EO+.._K.|eɒ%5=#`Cm}ͱX,[_~]tѸqƏE/X,+R1ak٩:k֬o~sO.MW^/>y… 8% Nk>>ਣ׿zYvkgLVOW>uOp^d@^WG pɳ5sw5kvN_r&b͚Mƃ JEkjн{ uk6yY{y J55FP'GmL=(-->_|?Aǎx^W5 , V;裏něp X쮻\ƍ>_r=&OIא.:tGM7x)ҿ Z& . ??ͫ֎|t#JwH[ؼy+**RҼ曩yyy'|rӽ8pB[߿eMݺuKm&֭[jdܹ8^zQF]wuY9pӟj@h`G>.V3|?7 g`dK]tCL2e„ ~nַ5lذwy_jo|;yg_ܧD"7,**J}Sr*hF۷o5jTUUU;#H29}u^AsΜ9~K.y |۶mR'oٟr)z)ڵ۷m.\&+gY}ͳύDΝț9med^9_ߴAiu]X6/߃MjtӍ먑}ҽΚ)5<``W'u^]xqp>#Gsݮ_s͂ {6/o 'ntnM?iάs{95[mT#~zqq 7Cmܸqرdr7p:RZ*Lv?N'KujːzmCX,V^^5ϛ7o{_~zD"od2?#<ꨣB:nRahѢԇ&C=_BD܆8qbݧNXsFUUշ>'9<M6'?ٸq#. q5$W_uw߭qkO<5\'0gz_~^{1cZRguҥK_yɓ'oݺ<ûcZcܹs׿D"j|':4~ *FԘ']N>j9k#''N;mѧ~aÒooAaÆ?7o1bDjS%K;lذtyyy:j`~ܹsGs2dnO& رcǎۧqlm{m۶;vʕ+'O|94Y.Μ36wK,OҿҥˑGy!LťAκuw}>˃ ߿/Ν;7pMڵϟvڵkAЭ[nݺ 2w Zh`SQQ?K_R3܂ XxE=j'S2ܲeKlꫯ~Gzu뭷6(zޭ<>jKePEejfЩto>ACF0t,}/tDf/ksи={z4byyyٮGEEEEEϭA>?ѩZnk/ eTPo۷o!޿A?]luP-L&MvJ&D"Q^^^UU%}nZt{pa%36,4T3AS߰2[̝V ڟc-H+)A-sЉD"ḤB#}y,FheT\YYp—^׿mɒ۷oohB4s<dE^~A4k;Z-;M4@jhCYzẌ́Y˳= YdCa4hf!jhCYzẌ́Yȉ1dM/@zh&B4Ț&^;dLtC5!wh=,@fB ,D"lW/@zh&B @(B @(B @(B @(B @(B @("'@Ko۵~%;\DNɼ6X^xEb[tƜ-6s?^ۺ.%7\tL޷Kd \;d@Ut?fǡ'8dN~2'?Qԩ`ΦU~ޚMR)6ՐF #x-d5 `ymʎ>{ɝ!U69V.yָEYN5>Ef|=v%dR_`Hzz>gx֓~[Ok #< iTJQEQ D m?v'ߛmɜ?G6L2's?ǎiܙؒȽ#b{xaaNpJdR?`/·ns˖?LKN:0s'{l(he7hz}f,wb=3ֽX&-{'ʎ:s1/Z򦞜kNȫk+z ܻˎ:ZqnpXZ]~phQn5`Au%~y֤9 Oȧ_u%ux M>1zPmD.r@&z}K߬뫾TneU_SM۾OGL뫀iPȘ#o_>6vG][猺ZYUXRG z˲>glum]_4C?;?ˡ6@) w$SRĨc'K|VDŽ ZQcڏ*+wlZj_{^[\R)nKsC"N.1 5_+]OY3􃆖+ .=noL{=hnw=4?{eyd23$DbQD 4"R"UEF]VZ姫_PW(~nkZmXTjD! $L29~fX<\k|lTj 8FE&_z*TEc#35'>ӹ^~@.(K{Y֑?5ǖdL/D -b8@/SvZ-!z{xρCu]х &gH 4@ATǴ=8-|ͮx>A'ezFcտW{X\=]*j_BQ $+.\ ) Zà:~=%bA9eA+!fU6>~:juWE<' @=~/rt[I-/FЬ)f6]ix;)ϠR6#2֨5Z8Ri> H>ȅ~Kno4E8{YK58Q{ھ~]1|l7 {v"-i}h6GT*93StdRG; U|E: @wB P ! Ǩ>Qu$!TQ Vv8x|zF۠H!#Ct| \H͊M^zQ/cTBw?wO4DYScVm @ȂAM Ù];Bk%hUB]h@1 &uMbSULVzgRcRkży Ȃ7HHV334MLJo2Tg@rG㓌ExEPm9sH+ SLS{T) )*Ct 2)[Z͙vUʴi7 }^49?ÓCc*=~eDJ6g[ZTjFz5 biQyPsY$ 4@tGpaՅoc9)7\1 zZjzh1+G77*5+sVz-gYv5ǶU~í:[~7y;r!(vZ{/s|+M*.-Iթky  !gklRNy)OfH񺜮:j^vZEjƜa?k|gLG/gs[,c+vϺJa8I1>CƤ!&Ǽ'q@ȅ_qܼ͘B : d˶5QgR-gYL|u߆oj&eON@wA|v 5u`[Z]3Mv#c(W`nm\4J!2,c WO4^֘33mh\dP -m!ՁEH~ y01‘ g]0Ԫ9rv:҇@*ʒnhv_ƙbkV=U3jMhq_T s@UM n>w 4@AR]vU KFwɡ/H8}P{ڇ|S8ϘÐ ZvymF !N{t=Z-Y:߹)4Ϥ j&u E9E-bGh)ͺE P;/\0Z<+ BwFl)d0 q9]^H-^mWϞt ڱ7sgvN8w ;Ɍ)8ʢ(w0 C eo ɤ"i-T`ԧgeɴFZ3_*ӫosÙ6_eCNó$w#cGWeq]H'>3U#صK=ШUj`0`? U*JRjZ!2o|tP&>= ZRw4@vA&{:&o$c/Ņ]oXh颙wA% ~I2l~,"R8^w4@5wzd+mT姪3 !NO:B{mڍ!.7]awH]|JjcRPG PleO_:a6IgҊ#ml%{w秪qt&)^#"# gmm^Ϲdn p$?hrY>x>,jcT{/=G3)3#@rK7KRRS$ηNu5⮭"c~ @-}σkOU6 {N 2;~>[/o:oD@k~&7~ @.ϫ[ڃ $Ñr䯤@|;;/{LVkiTUn@2t;~55g o;.GGm_w:t;$3h>#RE}>üπD_>Uz`qya]#~ i@9]NⳜ퉹L-M[[4ֻ6Jw ѠHNt6d7]6vߘ5@kC ;g~T3Q@bBz3v}PT>iRխkAn^|ιd+.W/;Ft:>i](T޸ rTI}y))vNO|{2RąӮ67'wz׻>}I@RBw@w}j#Ӟ髩ɟ.=~FIqO 5HT$m?mࢴ /J4-MWEy8}mGA;IXߒ{wDF/ml 5+O jBߣ[m4:ݙS5r o/ &-6^ 8|Mܥ0i.NO:xCڱT q>ҭS,wyk=u G.]$(vp[׮u//<7I3ǎw6>"`{{)B.wgo>kW?oDl?~̑;uO;Q4eH4h@/X4HOL)*3xbQ@JO.wí5onZkq&CKgEE*XRvOUKY"0"~Ow4{o鳖X¢≑h1h˖{ؽ׿_Y:fȀ)8 f.Z7}wr vtm\𾗞[&cgkn~狫6x\$h+.ʳ{7^I؂\9sW*?wɠaQXZ/)}Zeeuu&ZpE1`0_0vlGL[~S'gB;?mx T642yֲo^qE5 9sȾvlRwsQQ0sY:Ⱦ^[H[λ([/4{;•Ӹ]T沥eT|{X/j}{[旅#۩#tYs^uŤ Eřz!_|olm}O; !eB!p rYax;'&{X^{rSORV!S߸~{ _ (,-_,v'VW,{2gᢊ{k)B_㊛.3hmX4me{ͼ] $}MVzŤzdiEupUy=Nxh d[rڮi&!LS= >$xLJ*6Žq;<М}U 0kڣkCs)B}kWiP] Vlqi\x)9^7Y+z(i%'g<nM'h:^k߼%gOw)>[^eW?P8 0ZN8+wSpͪ+}SKѼo}4#U3z}s5P/;^ shD!~[w5iҸМY13&gQ6ƍ~4tx=yVe !4g#L~۪dw[-e7v͛<;#!K- jn߽s!y{89kRx`Wm>uUd1 enǖ.eCnXQ3|5m}O+ unBG)z^MG0Lϥsa|~꥓B鳵zmToxM/i\TұwΓ ϵ[yUFly-OĹ~ nbU˟ٰC/ܳvggKBٕC )?> m"Bay/o]aG͎ T`ȸY!YrˬbB4}w[ngK,BMbUxe$S2$luUN*Bd\ҵ0˨^v8/?Oa>prpɬp?avn0"^w7! af-<]nuycy\'>~]{*{>.Pl )kv.ϾʕZ^L~qٖ~?J3NZW|}rvw?{(^qb),o~kybʗ4{j1KG(z5kB,ٱ~*+Ob?0lg !2JB]i@b,Yt)cs 2<2[oW7}񜉰۾gݷxi qLtE92m\q q{|G~9O~;p|g,Be4YC/r )#Y^;,i$?k(OZn gM M8o|eUK敍M_)55SC{^9s{]8ߌyB/6Dq"0r %BPofx*ܰ'W^׿"4`ٱ&/fEM}Ю^D4eُu5~w߉n z =⭮ˮ(+ jF[𯟸$>~qU5^9p(49_E@gU#y1؉o׹.Z3J)>D\Y'ܵoNѦrہ&!KWY鐼Zpt1&YmwqU -ey[:6sgW4,5MYI6.}azi$`EUuOm4~kwV*;u;<׮{'LQ%=O((NXѤO+g儾< Q9tW_^yZ-򾰭^\ʙf!e˟s֎-myD!&Zo_x֤lULluM+giK.?Z\B7=Њ~OUB-YvgEGUJG7w0ݺvK+!>:p6"d ϊ6zD%]@xB?H̊n+?[Eɜۗu~hztS1N*m|[{ IDATw}mϱC~.kUa^20,{~m|( K\᪬ϯޯTU|I}ђuo]ͯW(,w}EYkw("jR,ȞU'7U~( Kk,Aǻ߇җڽsvtVW9|4悉~dz~cc&?Ђ"VL_1nUƋ-*B7g+5[CBXJ(]ҧ\cH~k{o޺!gU<9kzE/)2a[_/{x -O͚_ǥ~syƕv\fviuUwbf!e<Οk}gqϖMm=USpF^ךm6SGyaݪͣ ?ܸsckNk>z>yvb}W\!z`Ss4|u%mӮ^m# ՕuGez<.+w( NC90@(Cc.w@9w0ҩ.0:@$A 4@I@$A 4@I@$A 4@I@$A 4@I@$A 4@I@$A 4@I@$A 4@I@$A 4@I@$A VVktz^otZNѨJ%w]@@}~x\^;]WBP]P F1hٴd6SܵR4F H o].Wa}V'-JCP%;z0R&Sj1%UB F(ҳ]vG{吻8߁N;@!`1MfK&C)ƔTeoSw?;@Q`$j܅IMo0fNdrЀ$ )5& H2 I@@R`n@F n@( IJ@܀; #P4@I@$A 4@I@$A 4@I@$A 4@Z @FdRRx\^v!KÉVp̬@"s|1] aHBWݪr[MjzWnJ1sNvNkuz6v5a5a֖3Hț/ܛ??1㝱B4|Mi*`e)T c&ՒjIoC z]/ PfݰG{Cŵ:Fh;aJy84ZN7FZͧ<.  /,N7z*49(B%PEu#̗_+wdoD@ P"+ 6v9 No4&s)5MwߕmdZOiH@%(VE|{>LꈞC4_4_G=wF"SR2r:l9Ӑqy=.{Ujɰg2;1z䉚6 dc0$h8q`LW,Gg N=ns(PsB]ro/+U #(ɜ8ܸfmYSLfKFNLܜuW_0@Qjh6 !mHA]j]v{-,:_OxJw] T"89c|t'\>/ghtF6V(BZgNXIa?PXwmg8q 3}b)n/oGY/} 39&.dZmx] v[Lv֖S7N,W=tOC$ʠ#^ H xXw<Y7"`a{\,wKs7Fhx.K!76b0aX@}2h^h jA1{#(z_No @R#(KPtD3_ӲW0:>DM&n4j2h@jc~zc9Λs;o,N7]Eg2=3'd`8}SRcQzA(\D" `xI7x_員4@YT*un.n:uzX8=3Wb$9W|lL1otdЀLcW>Ճ*!>?y3 (F PTKv8u7:9=dN`th?e=DV5c(ZE1S1σAD91X6&@{ٖ3 _gX 0:1'>ݣ11"uySuNiNv5g8F 9}.g٦v5a%[[2sT*"5-SwFx\0;XP|nQh?q /@Õ8/ zsB5pcO|Y=gגӾ>2/>3Sj}ftUqB24@q @-gkelA%#vwdz}GI)ΉvLq)Mٷ]q<@(Lu{{2[Ɠ}bp6 B @dNݷ?ϒ:117@ %?7zl6gٮ32bϝ2;_샼,/oߒkF(hj̠ 2zW@$KM Hd]1]M5Z90 )]L{y`t#` Nıtny A}/$ NAYo(cE$cZ@f'*юAu26(-S :}) eФπtRcvLŜ^G[`gL :2e+:3՟,Egv.gcƝ7sKn^6kDw P ǝ !juh eСX|QE9I58SLsT dwMh 1jU*a\mj!h!D#3& 0@t~Jj={F Kprv{+4|ul | @N|ǭDl1yӥZow3&!0pۦ%B xՐvN8w3.AK3t3dЀ kF3sU Mᄁue_-e;K514@ʥR)T3Ov9zIWdwꔒfU#puER-rW(!#B5[[M}o)ufMSm0ZOFs$b.-Gvݘ\9c ӳr9}w>牠F^,V*:ԕay=q@ .g{s cr ;,wERɫ+{|2y3E? !r㠧7a y:O7|-Mq9 ]X 8-+3cB9GGxB`^Ȏ!WyėML@{՞& u55 䶃hij7ǔ’ÐA때q*tic޸O;ܫ@ҵOo1#Oz{L@;ccҏ|7455&xݷ^;sL9a߇CUvNބ=״@:WkNH˾1't 3ρ~;6+||Q]!s܁>Ưƅ&Nm?/s'O9{ !6-▉m5!OT;6 / tWW[@J@tt(?ݪN)ɠO.<㽎m uC̠'8{no({D-{ N*o:ۗv‘Vs蚆ix 1!~{ޟ5?=g_p_5s{w}q;gjf|BVށYUwZ `z>H qP]gͱO9#wPֶ?!obnI< /+;贳ƌN ߖ"8R{6;_7OmNCo<;{{evwF74 榆wAo#JK3SAcSKs:::vT^WK#8bu}tvϢ1kPjόѧ;]<n ugȤ=u4=?FKcCug& IDATw)(:7"4Y5CptQno{)e0_LQu/g4Dc^>[#|Hf MB #ݘƺ~ti;~>9~¤'Oݤmk՛?@aH덇Z'Ee0j3:{|{oi^nm ND=nqƺoogigɝRxz>7["wT6(/ FcvnYu1y{J7ښA]@wuo禌<*55|gS{.>9mm?]oQMH 0ϭoo0 fL-kș81}󦴍?ixս^AF[,lX={gi<BH]c3'M95=#=J iii;: gGZ>}45|獩{wpW[@Y!;eG榆 Ѷ[ɶ@C!lOhؓaBYy}^_n=ic:Θr&Fu& H]?9w#7Y٧>W_轳a_q!榆&zccXˎכ{}tA0ں6m!¡" ';gǮm7SpX'N3qн;fTvs,Ρ!惆cw3=Bڲß58e/Pw_gR}{?HO;Lj$<԰\'R޻n0vg疦U[[ cCv`/a4 Qft ߸ 8oʀ8=viO;Bޝ1$ u575TbSþoٽG Q~Ѥ!]{9!ܱk9kC33l8 ¸  Ϗ Z㧴2u  řښ}8'&?0IOlsݾ=TAlP^~V3H{oѪ~{;G ɟT=np(y;p &L?yj//R'm8V<;Ym 2H IP_[#c"+{ܩ}B榆-,v7ab~Qp` vtGG8lQ]gRfǶ{d̬S9Ҋ͍3h3D'ƾoioZH(KnqvH]hH{8r'$?0:Wลa:b-}~?RqB$ dfKvh}8艓$30e{jٹy>t8kڵAKߎCk 􌌬q#k RCۓA;>Q,=#;x@$X3K/~/爦8cfFhRB݇'\Ğ$Pᮚw7Ҁ!3&Oo=9K{-'!mC贴kq?0BH M 8yj;PCdwREژ1EgOBx=~‰ ϐ5/=! is:z{H+9;~5RfsS %47?qR'Oo-CYa, 'TZZ1sN=cs- =9c3?B|rgn˜:BGG7|ru>@hR;ffebZ[۱˜c0c-MI J֍ݶnl:Ot:#-qC ! ս'==c #kydF|BZ|kg"5/G @jzL:9SݑPpySmemܝw՛2uFN*H9~/<.'wܸrݝNںwOv_Nrr{-azӎAgaWs̖G3<9E~ !k!kBG!}cbb!V7e_hܙ{0PhRQSþJ@;yi'O=-ٽ"׼w֟ L0|m @$DB @$DB @$DB @$DB @$DB @$DB @$DB @$DB @$2}Bz/,KvocE@hF Mx(/ǸO1P4gW‰}6~y$u 8i.esO]==}__)i]QbrQqə]<%?94+_ם7)=ѱIg(04E (Lp(=3kbKf0uZ[cm]YYT^%ϹK2h fWgho箦CY $]Q?lb׏{_߹7yjh!`_}{ϝYWBH/[Tx K]W\-_jΝ˧&k@B+:=]NUPşS0^ݸ}וfϟ{]xd 8:e /oٸ Uznyv 8O9y7|鮃FTyc+ rc8ZbkLn_a0ԂFiœVՖb4)bMBaO9wfŧ?693>|{˿}ߞشp- /એO?əW6=}k?j?9)!n{}/7m?OBSaΝY9(.Hfs|suJg]{畞qvA^/C]S]pv9Sr{Px'eZ?xkB(]xaٔZ=} Cˆ]*3C=ppuHf9=7Q`k%חΊZq˰6 E3s¼k^{'W>G^\ !ݯ{cw1Bhkk?IvRT)g$ ǽE֋sC^gN3^wS!Ny[ =/II*C!͹n)?7؎ wk6$ ,-c⫊ 7ooL8´k sJ&&8'^wka1B[+?U]ry3o!|shέK-$ay;gc߽K!q^uYʼnoݰo ?HZ8Cc{ Ca;/fȹǿYtv!T?}U_`C-W7о/ ! ;-\+|rPP~Ǫ$npX߿zιu=-LUt&;ߏ?>###T1{ᴩ==T4g忮Xԝ>ƚZc!_g'}no5ZC!h-7P('߽m^σe5ԵRzn'+_Jв`˒^skEC!tqנEණ ذek`(^e{\U]rXtz+JZ^1E߻PM fO dzcfM;٫|9]Aq8K?%^2;7,*н~ CizH}O,yuyl[V0HEsV~ef3[u,-s3C5/.쒟l!̒o{v&3,?BM//.폽'dOJ܏[q33C}ϖǿ //?suUI< k^q*>ϲK78>=|s]畖VmGjUtĿz;뷻 5hշ.OVܲep-?\K;˯Z~{&(qFV%׬\W|Ë^縵zu!sҥꑕf*Q n(*-ws%-gn>2;e;ׯ\0!7 XBhy枛[C!⊕7&+cVް}%XnC\=s@0d̙;Sd+gCo]sϝz}{My_>ggPPk՗+u5O~Kzew}̓?,,˷_{v?5kz?QW?f5kʿre|_f -z?+ &K !,UXy_\{+>o+\XNׅC(*B_)_'7N/~BsB|püX_;ܱ~W;z,7xW_lʃ&bޱKBHzu!pACOotZ<DF@0^l=[3}s}&y,mc~As>~𫼳fvSų=)#wYvk,A nn]+|ft/qB;7,>CjYv\ps Iżs3MZc[;[ PG|WΊR(qӚgN뾸eV~ܯܻvݽHg 0_>83m!d\{ t4T=\9< Y 7}w1Dc/?,џ0b;7Wُ_|MA>7ھw+LU{dj^|8JlM/#4 fEהκt>o}r(gˏ_/.ꦮ]%/O;#GU/ H鬮h;sG4eέ?zWaDڱ~Ug;T|LiCB{bW8#/а^|3Jf>:0'v#58{ʮBYLJ HWkϭ4#)8mvw>Cԙߺ%[xzL a?y$Y;OIυpI/>A!] 'a¤B^9jMWKnX003\Ozͳs)7&A勮yLJ?rC(([T~LΟ|R^^΀B2wG{q_ki,} 7vnΜM;Ba奓BMU{fݙUg+/ O>Be3,7l{j\k0pJ|#o>;NmF 4MKnESEĐ5e,}횾@$sSznajs!:}UaN[xߵR~^i~g CWܿg?7Ơ>w9.z5u;O򪻯Щ3kw߶pZ^!3] ߴY!O\wf\yV~!mSoW_]\ J*ՄP0t uP!7 y]_neN:O;ip I'`T{KO]%!9}sJWho~kw5TwGuк?Ӛvk=loݻ[ɂBٗ|K Elmmǿ !_VW6ozrP{Cm͖7Wg_]KFꧾwZ^BǮ^v.ZoxF^%+BXdBx.%ӯ/ wVӊB莧 M1CnZwgl74݆;W==y!d̿և{񇭱BMA}l+|̜|hkJQAÑ{]u-]]6oꪥ3y*hԮM;8$HBOzUa_ĩ$_XBou /VB^Tz!!CnoL*\tp@ 6~uBY/\kc[:sgz 脂s?xtf|#cW[/.omc];oߚVJTTXTpyٝsî:w=BU@8g/ˏ- iS]sKF^yUo !PPrMz#C%Tħ; ! |r{}: p@srˮ_5fsP8W%̳JXZ;ܟd֒O86f!xKλ qӬxUyʞ*=u K=BMw~-87EN\skOO.+ !ԼT)t:K;yfB~0`{Ľ~ C店SoepKR0e%*-zTrusxh] .|\,xv7]6O˛xlSd_{p݊!:~>򻝿1J|ۮJ ԷB~ާ~_|¹̝7|һW?ۻă٦Wk}f;z* B;>gYњ'ߨ !C5[]mxH5sW ^?a]u[vwҊ]1t/>~G6"e+qI%:*&[E?h>gͲ3'e[2yFHMvW$hYSYq{o/?;7IY9Ҿ祇ReўUwo$?5W,׊!EM--ZZveiw?_g׆{^yp!`OnD؞W|0XoX*o[]~|0b577>:OUp!YfΚYcۆm7 Fͻێٵ*x3.; ;P_ᕭor;͸+a{%q)Ya_GSn C =MzOY󊧝7B뻯l޲-q uT/]%!ͫ>qs6 ʏN GX;ߏ?>###F\)Hapw_yY!A|4  YzЦ+X|IiV!7&DrC8 iش/^~~f!ߜCnz[?S_>ϝˍB% WC2a3 /Sqᜊob!222{|b߬Ά0J U۵+Dnoؾ;q͝gC*tye\5c饅3B뮷_>[!Zh'{cF%Sp 4@ 4@ 4@ 4@ 4@ 4@ 4ё.@:!;0BFړH]Ǹ;$zԡF4pǐ4Ǹ;$zԡF4־?]u PCwH `@} Is PCwH `@-c\HCP#`DKv uTDR!#1c4 Zc-E;$zqC@--zP:!)bM zP:!)ZԀc_Wñ!u$HH 045% ZXt1C(h$d"8fZc-d]]ñ!u$8`di0]TrKz u$ܒH `@,fK@䚛>63+33;}،cnjIOGGǁڲ5v@{u,wRzW癩TwQO 0J83S:;p0: HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDO~0 􎚍 }F@&+r3fLzzӓ+{ӧOOOO3onV ̜7]te4Dk މ H](^JnC.zuB~f4P10bN>&ɔ461i|b ~J"6F0FO\ND$˩?"<2\t#d88eMF1i:: C)c5"bbF&!"B>/q,q HH0uFc9NbW 9v2VW2&SJ5!y\ &&SL&sP Y+{p,rl, ܒ8f˱Zu*V2V#wu'q,FEFP 8bYF_L8QFaYVG&p_TTDz F.;Adxsr< aaЗ ѝ 2VwU<+tO`R>QZ_׾T^G=Hb 68Wu00u{pܰ0t}$t!<"|reȢgD&nٻMKߝMѕҿv`A Ib C=8C K6 O /`RIݨ oY4_-sOpl $d 5+(HN@040N i;;|u9}\@pĜnwﮢc?e&n}^gM${BUHB|mp4oN+aV@a`W[V1H o[5uZMFGEa1^$(/)Ihnc,>%dJG`H)ͪ4zv;+s'g.!IOT*RS,EW?rqGYCA%.<<l T\Y)IdƈKgLb=ko  - ^vMl]Q E^iェʎjujԧ6>=sE-0fiꔩ@V9KGGVy;fIedea?:/[~mc?^TxO-wvIV{r_̖-["zG2;qҥ^{LEEEz^+..k10U HFIK?^\H6+PC" ?:|߿uF7> O6%CD{zÓ>)U*FeNLN,ѣ[+wDhg.~? sgKzQVF )BP\(ˆȤqnD?UUTT4cƌ4ǎkjj6o;Z*//J?Ν=ztnnҥKR5ٻw办z… BU7o˻{c5{qqԩSsss6o|ȑǏ$pAJ9 (QpGt0LH* d$~fUCC8A^ ӯo"G@9LزH$%CD VlFM|jќ+a*CW7fr./7[ZZA?[>?y…O=}O|O=唻v?~NNcNNΞ={rss_?mժUG1{#G_>*{ͽ뮻fϞ-rr&F D]Dѯ@B+EGG]Bttt`a._ݪ%!%bbb믿:&&F!+ /ҽ@ 9gsq'\C  r^8#tm<~٠>Z @JɨjZ-A[ߗ9Q)k}Y2#qa(o$:_x"ھ}{WWLee%5r2G!?OVtuu8qҼ׿ӧyΝKDǏʕ+)))3fػwoKKk,S2EEEEEEʀWzJrtC xd4kM6---//ʕ+w:u*y [{/?vuu=W\ywüXNw!Lc;'-[xba .\reժU+WtǏH9}wޱL~t'N޽{e5+Vhjj=r͛SdBV&++KZM`#8 1)U žO$[@24= 5b mYoB@lyov'@enu|_w;=02:Fk6{{‰(!UƤq bF-Ttʟಣrss-[+:thȑG=p@^^Zٳ4N0r{nŊלl޼y֬Y:rHEK.=y򤐏Yxq^^?l5qNNNzz'a3fS`yJF H.lx ;C ÈK#Ye'[I `5PPZ7C)UI0|'m?Ȱq+̷VXb )ʝ){p#//B( |-t Ѱq?=x?nw0z"R#E1qbzp ֧wn[#h.o3`oRTσ=}(!ZPF?yNJ[@` A l)H$^^(:>d3J"[N # u~Z;PHD@~@0"1b nxD%BC^yEQ]$wOIČ>2$B:yb`''e1Y H @h Py#w[zpLS2~e's,C^%{}IU@ Kܴd>f%PBkt`ȑy A8Q\`&n DĄL@=: 7w(o2`O=L'ӘURA KQy\; I@f%!^2y=hQgD@b}j-^g$qb"I~y*2|XXX<s6qt3Aǯ! f> Hޱؓ4w]iK?[`q4q2+ܸK8*@L\X|0<2ϥ!Yӣ ߧqϩс/ܡh-!.3&auc?0X8`T<"7Ɛq'S2>t>œ4 IDAT1HNFpcx ReCy8Xn2RVG&q9 +,qQB +{$*,)1!vF=N8$ᄸ $O)O |2$ 8cdBZKsDz,r<,˃3X2ɼ`MCJWᆩ@a8ye9eM,k4M&I###DH<:F)d2_/L (#8&Db; FW8Lod:IJc!xe2/ Ade7׫$ 'd &R{F`KWgG{{B.7>L67C =zd q[=,6*Z-ޛCJ̐`aZ9d2-w^COOXNTrpBh`M":[xi4N1o`y^cWWDZ?67݄#BJУ ium86Z>o*FQ.d2!òc ttt<,6^$A:o)//?w\yyN#{gɒ%seeeQQQQ ^zw%{??.Grn#222RSS###.(11QBkΝ&99y/rrrEJyy9M>]BۺVd2(1dJ%˲o߾}voO/Qd2hԘۺ[ QzXo,1z^tDtEkj"JIIb:n޽q_ƚ "^ Fc~&==򽋢1k[n\ps]dP@:uo% ڄ7t:̴FD111b$}7|SrBzd2L&_8_~]նM&9 T*cccSRǎyeYLf~ռ4BP(GOAFO^RR"v9P]]m0ryJJJڹs9"f͚O>ݜjZСC>͛ B2(cNdeeK!+c7(# Lww_|Eq?ƍ7b! S__}ׯ_&[dɿ˿w1mmmv2m۶@m3CPfǎ;wٿ%K ֯_.n"DRAV;uT󏕕v&:====7 fL&^2O&c8KLLLH7L ,Q$#LA˔LQQeH˲!t>shBD,Ν;-##h4>V<(Z˓?t7(ݽ{n!NW1ό7_DVBKKKgΜIDƒ˗6al2$ԩSZmQQ^dAA˓+++ŪJRA>|xD$<ؿEXÍz\a֧B ]]]in0LOOOLLLLJŲ]}ذa&KHGI($9uƍu:ի|Md,,pm\8lX cf9~Dsd$==}߾}BA'1NU^^X(VVFĠL}}ݻzq֭[/Dp ;m۶q ;NP^*((Ʋbu?Qk|+k֬y73t2dؐq(u*qx!›_8N&,o8#{{pT&1b]smd&*22zXlTڏ \:裏 )#%cE.?>66ezg%%jW^]TTI699dBEީS,VElllQQիN:%V'O42~W^yez~|H_q[_sYJynݺu 55u֭oKhǑbM+"9sf\\\\\Яe˖C ClҎ;hB2“[Zz{{1})G"R(* )sˊ|G_fM@eJƻo'[ly뭷ݻw1Sә=ֽyߖiu]m]TyFO7{]ζMKZw1ss]롏O|KvSΦu+2]dⱏ³n)YKsrN<ꦜMe.K|z3mv_▅lhէg4-B`ђyiJhbpj߶f>S\N|۷F2##"ۭ/BE.߾}NR2Η&̥T*kkkx70 AVwuuա6tLV2կ@@~~>R2.;v̘1Dtƍ&;v;w{:%Cݫ!v!/˃b/2mذw D}b+((~qy,g~__暡G9) _\`œS',~z!}.uGO"R{r]%rWpwmd]{쵛BE^z (aÆC c.oܸq-enfNۻw/y:w녜ܰaK/T^^t֭"u9ڵkL<22gٱc_|q]w >_E O}EZѣG9LԖ}VPgnΈU죜O84'ls%sos]ᦩ|hO.Z{~t1*"U۶n徳Keq:7QuKsL^=2bZ"ÛP.D“k7]cu~8DӖm|l(%uT=XXө9x4_ΊrbXv@0oa|HH{7s1dӭj%f@0TS⭧36W5gtNF0;v'#Ne zL X[ !"s,59%YXUȆn^TÝ|D2:NH|'Hx$66vرr< Szwu:/)vh7{guC8(>itl͚5111˜M!n-(( s?dׯ_uVUUƏ?w\bIwՇW}dIUS?{mKS2DCts7i)h۾4w:¨񣓻 N]nJopW'7iN~ԗ!1 Y4s袳)3{_(9[tR.զm: uYX(}ۦl%Eo_Xie4&m֫Q-uOwle[Qem|/'gEXcߗ n|U/5f.ʴJP$-/UG&y1S|b#j w>suuD`LsMQ\b@z}ccc[[[[[[CCCkkkccUJe՝`ѢEg&Nct-ZVj4:Fc~5&&ƪ;Sϟ_TT䗢}[%c";A9cq%""^\y޶;{FhàAJWT*UJJJmm` ̩St:Ӄ.عs,Fs7|ӷENf.I|WYǍw ףxkҏ<̀XSt.;t0\hDsl/V.InWedoiTd؟]Wvs"ڸbjR2Y;3g;%&3^d0hd"+jH6v$.唴TUojI<éjXB7s3hN ڈ'bl%ɹ|rAA1**!!!>>~޼yvW{KKK``!"snFR-Yd֬Y~ `Ν+//mIqС_WcS7m{9n_zvG QFcopd 'yy^P0 #(c;/aY6n 12fƍfǏGBb̍7LRRR4.|̟?_B$СCDTRR2ȃ"==dԩLYYg?bǏ3(}|SƸN\~^l&?-(j4B52!ǖd+.ZVpٶNթ3-.\ȼIjKvna/nYh7a9]"Q]|U&Jޚ0^j : Y>IU#\=/Ͷt4\S9=,3IIq[N}f3?j>o.lpB5aPMPv3s/^dVbe+O߉U -qԌ7G*:x@p&/x`!%3a„DJ5i̭L4rj?LPfΝ^TPPaIR2sOrrrrrrzzzLLk׬Y8!b2U*^pQQQ---  aި(Lf2%l8S(&AQh4B'"EEE7.(,NIIQTb!(s=]$KDDŽG?HDƍsg_]u9@a++ʜ/;syc͎p?rP)L^s̤QDpJ#՜~Zm- IDATe*:5WM @DD;omVXUOn[>e!,[gYdhZx;"ٽ;j-+u(nH*`0/_|B3iiiDt~!Xn/t!!%dɒӧ{4oyyжsOat:]JJJ~~صh4 a"1bxmm-1r< C51BXù&""d2SqNW^mV-((hoo_z;g0т2>p󾼼Qeh,(( <u;9|YUD浈R‐K_d"-pZb}CEW}QubZZEf:vc2,y-o,9MHT s^էmcgg A*-l4iWLַ%ىZv"cM['n6Nf\(:qי֮]o߾R"|VFP]]]QQR֮]N!??ߋOͳ>z68q̭$v9Ʋ&sƖ1LFn0 ˲Æ 9rН C$t93@ׯ?|XXZvԾq+}݋/Zeex2Vaɤ?YWZѱйsN:łZ[[h]ɓ`tg^ݔ[BݳE71'IvKmucݟ69xW:C|W/ȝ' W ޾5S[;C45xH7sޔWj RM{|ƅۉH9*표h9_|"p㖇;݂ԝLc {(Փܸr̙k6o7[ԣLi*"j\Pr&QiDDUWklrԜW%| ~\mkYR=i-+ىZu\<^HD oYnR:i-OS|`o=Z!"eDb6}5%%\SG IoybI>=j6'-pP)3۶aO6Oy{{`ݷ @č7TǏd&Mڶm[NNNbb멃7|CD>n߾5Iӿ˘CmذArH&s8NRM$s< d䟄Q)ȋyNg{'Z2ǏjooOOO/,,C/M0 dYVzI.;ַ ՞ܴ}%4'G+RO[vrO\F =][4g>-(i4сȮ/m#Jw"J}Q 9jHjk$kfMhfo y'ųl嚴xQBiAZEMJ"ٽ?jtrZd)!+]2?CuuuCCI&M0aҤIsa*&&=&&ƋyNn`!deW&33S8 FarR1[z{{'Lcˏׯ_WT4“  )ǏqVQe͏m=z1{IOO/**ڰaFOS1 7n0 **))I2}rNȔQ͝~:S&HNN^f/,baz-v*S__ODƍ2}dκMoMu]9KcRR5f|J4둜)vjh,,(g;v#FibeK D ҭnU9(|JD*J5 9GLIHW(ʹ4j"& lbZFZ)m`oXwAwvas&9kU\UcpXe]u5-D=-AbhkID\qg:l"+㥦 ueeuDD:hcKYSDbڔQAF}kc]]sCMU]CC][z1M8)2}>Ykӄj3QQ:%:[ٽ?j Gj Uf8l},2 7nts'N|Җ-[2Y:tȋv240+S^^^TTN6hذa'B9a!M 1lL&SSSSXXL&}w2㸞I39R2]l 1hCyj`l5kܹSxcۓO ge4Mfffaaazz;P[[˲JJIIqs4Nb4I(evZ=jwءD-?6(MD huS] gNI-;ӭIJ×uZ""͘TKDɯؒ'j eL!"gAH!|@z4S?[rṞ1ID#;1Zg9f6"~uǎan\pOW (T 28[L=KT8w۲oXGSkcbw+hDžOԜx SW >˝.aӥ}>wŪU h;Bm;AJAܣivڎ3Y9̹їo^PPaÆ6v.̻sÇ[?\7ge4MVV;Y/20B0Z Cd9s?#e20DZ,T*MKMM5:$%#8>߿ɒ%B'V&''WVV:u}͚57xbe(66Vʔggg#+5sJ&222)))(Azz7|s9eΝ;G=+**lseee ̈#ׯObs ]~]X9X=K_(ÿѓ0œo$J_ ͵Ƥi>%닮Fg8&6v &ʴƠтPL4q$UYޖVG -u-Dvn5J|b&"DzDj~{Mazʘdѿ>-*7cҒa}.cO<5Ёyfevٰ锌tݺu=P2dȦMf͚u֭[JKb.﫻wɠ,^;ste֦2RTBf};p8DDDDD;1)1**FDL5X,ST`0aBc?aD$))闿eY2"}Yf_~ر?|Gu.eee~ሎkB]Nk9rʕ|A`ۗ)ApR6o)_zn :_|ŭe8,5rh;7_3$#"C-dD 3,;yh=3uϸYVyIԫynI K$M?/SQYD[1ED dL#e 3X.^ltbiI 2p&""'m/1pжmʻF5u9j: >#URSn$LyK}pX# KER-6_$obK[m˶Ďzdf]ߩ2 (JEDzT;qjIKXe|p΄N!V$"}I("RXx ڠ6?7A-1M^ΦZs9|=[sX!SbbO)=vV+_on>{f.ZĘ^KHlZ̊9{KHg}^SNUneoVDX0mQWV ؙ3gN8x~xVV֙3gڿ`ju/Z(ꏎ@ o8NJ75MMMfs:FQ;Vch4AR9N\\RFѨՆIQ2"zyY;wNDbcc;oJF'jʕZPi֕Sxx7("JPRʶa{-)5E%9QSkQܷ.{ts2"C~x^z//-#2C>"陚~嶴)mDf۶7 GO6 Pԝ6a;$KķO= -hpEM+Tx+= ra̩NIKHS0 x)KdeJ/9{lKltvJG-|3fLXƌȨj^oϝ;w􋣟d{vٽk]޳wOrsv^Vt:!Ӂ2" /(fϞ݊W͹s[kzn}w4O}SPPpAP᪓֊HFrr˦~^DHn<^c9tժ㨹/Mc}ֆʎ5wvHܔ\{3UWZIg?mܷ՞K v&ȅ]/=}bӲNV8T}-"ƴI<{膎/b޸| ʔ3VR=ahpFWޝ#bL-s'4X-[LŕwĻ/-W6 <_{wzΨpd2Z3ʵRU[N\QS]xbڵ[{wXm.[SqfתW6n_ZqF)J%nX'؆3FL P,_"iNθ ssƑMZ%j/XqVVa)"bzgӉTsJK8Fc',p2T٢|>-ԺCjIV~S2={aLG/fG6]:{$^Z'OϞ=[^^nDDh4lgϜ{ζZJ .GDkm:(mEMeă۹sDknCg=o޼gyfهjˋ*++2e̛7k\sw~k֬yG6TUUի*"wygLLL mosO?-^51wm,zou,Yg]9[ϴ~l/ADݺb1L:/qMgşZ:9_X.RUaihR1}_Qѕϳgj{lNNNUeVtJRhZeXN~u2?/IINSYsqe%IGXe?OwoΞ=[pi4Yhѐ!CƎۖ}ʔ)yyyC a.[oӧ7|\Z_,))ӧϭB&!=v7IҌPѮW5:mxBOb+%En q_صF=Pz~xZ"g/an"Вrvj\ФoE ç=E؀{i7GhEvkcZ9<]e/M^z~HϞJ]7`"(gL>G|R ]Sl/Tp.5& t.{?9hƂi"/G.%Sr-e$>vڹs9szk?i8Q%裏6|k;wn +--UTjZU(Yf_S^7є;k>=-IHH¹;f̘!C>|}p/o,)\.g~bJp8ZmUUC r*Ym3vFN5 NOJDǽJ_cǎ;6tJJFuɔDGG:ݛqرM6uҔlXSGy$,,,''_Tূŋ+)7oXG7r*rNHD |xȑyyycƌ={v)S3FI}ah"Vߘ1cD?i@t% K,Q`Zx5kJJJRRRf͚ŋ+;.-Y$ȱ\to2hH\ήȦE"35+<Q2f̙3/4iҠADo߾-]S:իWWקQ|"5ԩS];%#uaȐ!Jo%"?C]`{0gDDT*әu$ʖLH-r:zR,wdp:]="5ӆf(PWсtfc>×_~gy7|/55522255UDݻuV%IO umϞ={޽ӟp+ ‰2}|p'333!!!,,L TWW8poQ&y睷z+d\ ".g'ujӲC?: WXm/Y"bL4eee_n-==T~5̙3>JKK M o"&LXrٳ_x!C͞=[9Bf<$&&.Z6"=Dc_F^%+c4/p"b~詼6]:+Wnٲ%??a&L7o^n$)WN/JJJnzfGxǏ BU@Xl?7SuW\uwj; Sϴ?8yz/VX,Fn1bD1/RYYYEGG?aaam/TV)//0`@BOBUH7C/o򨨨E͛7/gKeϟ?w^eSp/rnus޽{e%%% /–-[wر}9rdRRs^bbbfΜ9y䜜G~wJ>}]uUCMII RʽNPN~QerH/mF mq@gb6~/.aaa3fXvmY3f$%EEE>|x{OLLlٲЦdBeȐ!KMMW <\rjM3Ejn:~ %AQ@`t2𩺺zӦM `R?>)Qps\**'ԾݻjjjNl \Vt7N0@9Q*Fe:3gΜ={̙3EEE"߯_}/'"(ND_;jeeor`0G6R2AXPrEGG:UYYYÔ Atp+*|???pH]ƟЌ2FѨ3G0""FKAQ#(=yejAgqϳT\p8vxcqt&22jXzz\AʈG_%.CPQ2/I] :>~p^JbƳmٌJDjZw .eF oeϠ!(ݔydʠ?g^ZVR=r9].`^}:>%eFcAwu%U}BPYuUU˗+++kl6 aaQaaa.҆]XÝVOp4:Lkj精f\gͽ컎{ Zl]Rz%DEEz`Xʽ`HdP~*h4eQd&\Ta- NSR2ϟ&%媮>fS:NuuX@|AtnlX WYa/TWQ RPwT[3(._ϲ _[#e`e..q,̛/zAt èokJFD\EvRMm/I:`dC4pX.W:fQT_uFPơj쩪K,U嗚ʨ5/]t@LѠ!,\Dv{Yu4.3P~It !*S0:DU+YHwIA(mu0bCV?;yI*ozl#Y|tM踆%=DL)}ED\sd0V W&~"9|Ӆ|.+^1pDDν/g}-;V ɗSY,,"]V))""bm+/+w\)Re6qa?Eg|[69|>gV`{t)"(ܞ9ؤ%@c&t\WYDN5{$D;8~mFS/qwɌ2hO'9//e1xO #ED/<߳J[zQ"2*d4DܒY[d.ӲglhPR/ǧ_.k^R e/""-{L\ R4$Xij:GT4Zj*0H:t:8^qT?a\HI5S2 A%$|MHZ!CdzIJ%.SF薘wT?%Ĝ =g)?J1;c:WrgBE"͏RO_^ovi74V]`Qp5n[5 ϻc}No2i(wNε:9+w2_F(VL,3KL""RS,ʞ$@#?-5wjT]HXq{_.sjd22,S4qYBUN'ߓD)2fFmheiYXcH䃙mjy`m]6ȧ e ?QYN'AG㙶( >$ȤkS2Ead,"12ey#9u)ӲVD闸?H)&)'r8oh)%O?Y{0{do>ݶIgG@>^&/9E 2ޮQYbHhyh,oQ^lemZ#"9{^SR19tVFkЫjv>[/]QP ~1>I~%o.2dʝM;&޻'g&GEDrwo7rЂKc(jdž?,͕}hBRő->tffJoHEV-YsM;cC\eһ;.}י^|dOLMf>:9k-kUQ_Y9syOO˼2Fo2N\xfnoԦdDdNӳ͞3G 6G=˜USҨ\9 ўq3ޥk{3Xzߕ묏Nҥ#Uh>n֢۾_>IROcqIiN'˔~ ""@>VR$(YW^0Lh|]bԜx1$t}<_2S_$Ȕ[Q,Z-xA_&2zQTW,5a@(Huňܛb8gddY%6Sʄ6%m%GYrFƽpLDLOϽ!F"wۊy322ի;r+DnrT﹚~3 W۽Ņ&fdddܿD$f^Ŕ]8y>xDO}1fZqN6u~p,}?ep{9򍈤Ϲ}^𣧦>s`SlT2'k+ꝿ*=uCw&Kot u#6 9,̽c̚I"[Ζe%ʉw,eH7Ƙn#s (ݰ%PD#ٽޠ6,_{Nu(#":18uv[Du n7$_QJkkZ~XP|SX-"1׷#"_/S!OPE_kS2Y|WoXZR2-_Dy?˶9E6}랚=cZ^~+bCQI`МĈ^_"䱿:i%)""e}ݱ=>-ZWo_h 2`,H-]Zӭ_,V05G7h2uiAohQ=í~5FhZCxRc_kKUK5}י]sID*.6跲d4fVd)RɱM2Ljl>4&P53wOƧ%ǘ-[a# T1Tג47wLʸ;nnŅSysÑocZ^!tw鯼27M-/xĔ2_֬hzhnӴg"~dee]uG?$V$n 9ނz[}{L!FD 2p:RZxqG IDATX].ш #7=m^-;.T{3+c^c*=fF[MEWeluI:w;fۨKJ7Sӣ۶HեSM|Wv=⸴MwO7qK]^WO3UdX_qR)>eƠTסXaMC#kϸ;iAS< _ή$.S?/?H1O/KM'>h0 +=sZ\jbYųZv^I3K"ЩSR ֨Jպz~xer+`hRKvݱ曚?K{6yIBGẗO |,}dX"r]S'9LDDb[_-젌_aU4ÌWk\U[trDDRF>YyCL"RpjK3HO٦*,1ED9 m”9?-YD*rhӗСC{DD$g˫ =R!"jVϽ!Fl[^z>' t(wj{DgʤF!sƉr߲N?&MeyRֹ-微 t@ k [ȜY2Ş\ ޖ}f,[hf!ϰj 4iYMUVS vtRvPNǴbqߞ9"EDi)z#ۖyMgJ3|/sSo=}YDrPG E$eڷΝ8"EDDR2'}v{U<RۄK1VLߞ/ݵv3s^D̻礷ry|V>5wJ F 0HIaVݠ/;"F/" 5W}ۼ"Ňb940ȸU2+$ׯ?NsKPe%d=)ޯϺAkA_/8_ݽaʾ}OXͬɢlc\4J2ͭfڐ>W9'di,C*˕LxT)Wb%"ս.im:}6?-7zm7Ęl+Or?xNI17]z\#ްbX IQ3YgLޗ-nS?KK7ͣYw葺jGb*&ԫK>oToNݩ=y25f5t5I>nfLl\vM ,ȶ.[ڝ0:(yW ({+$b$s\Nm=_'?'6)|[V:y~Dt˩]GcnDy3eڜJUW)"""q|K OO䔒IJP|v4E{d[RX.")ц)*vD$ >h=.on~X[A͘:$Ez"{.G-T[4aV&wleYy**Welij~2͚ 7̟UЂKO֋8l|k%45a(o+^QɁ%S;oR0-oiqu=3kU9𷕫WśL֍+^ }X>]y#z[[q Y7"'Kc(`9^Y=wDޖK!VF㰳.kr~%"H8EN|q˪2.)OTU "2.˜|xeɛ"I"A26~ arKz|j7ArL$;~Q_,F-@p/Xa 4%juuګ]RdYJ־2=/_VV93]",5/C+ܱW;54ymjĒ)K8}SrX8#}N-S}o*ܰޖ4NYx}qF?ދ nUͿ5ڨpÂYb'ep]KRpBd$NJH~4HDdeZv2KH +N˾}b SV%\,N(XGuw S\k*kl5 Y10X"jã"5Zrr٬EJAlV[iqW|yQxVIɄH~~~֛rtArC&?2놧]1,%WBXfi3І}.Z)11QD*++].t8v}W٠*}G5""b7BjS̹%T/9 "$Y_awI呟/-AegV 袾ZVѨjJRZN{u H ON1)GzD^pz_jPuE7+ct՗+v@C#ϵ᭳V(tE%lٲ{$g6Hͤd.pÃ3䕵~}~T3;=/IJX@5xjZnaN2JDf@jxxW7"uUΗ4rRZn0+"zF_`ɯT/2aQ6'?$H[M1Ť `ԌWJvoZHk5(W8)mbDI\lfI?!. Aȵo4E{Gdܮ(Zl^ ,ol#+;KD+Ջݗd."2eWD]wХƫ~6.=9Z/"b(9u> u]$g̓w ucF^洼9?t 4TwqGJ9rFVWUWVkFV^f5keyhȅ64 7I߈/J#<<\DC] Er9NawŷnC#.?$:9M6,. ~]Wj4ZRTVvK XG'kspXxZ\pxTl.:V_s}zX*WyL|95 Yed!W1bn_isD}]ir4Sh%"G]{k'tsߘ[7QVGDtU{NLLQD}Ma:NڥWYKee&}o|Tߦ.A[ޠX6t:}+kjj s!]E[Y:tmz)Ȥ|R:.ԣUѦ(SXQ+Vnvu•Ϛ^(^DU&j CGQT`ԋ.t)T*JRU*JѨ5)Aio .9|$"J_7; lZJqkT5vպ=T*Fp8cs&}Tv?{*Y꟥di{ZeB&ue[r똓ĜAf tXM9^ԢNkb@p8TUѡ/-T*=uOG+;~FϪTme5u,jpVI9NZm4 $F8Pt/(#"; `)B]B{q8"El J%u7E0M\$;lYW ݗ.ׄRSSt:z}\\\XXqT*UXXX\\^w:55?0# 碕[/)J[˒s^كj ^Zt:VT*˥V].JZ[cA!6x:K"ZRT*Z-mk-IIIiK5Ņx}d6nZmBr "塮&Nnl6.r84 =Վ-ѽY]aKJc8uPqm?atP<#A8N騣qh-nJR4u L'v)1z: 6eUD7ݗ4rt*̐A7DaZh4wIQFӫ2nnpxlcmSd:J Yt+(\tQʭZ Mh$vl-s`}7.]uɢ[^apؒ!|̟<A쮉 2 ~QPܴySmӦ ͭuc8Nɚd͚mlGhP -eH 2A҂ iAP (@Z -eH 2.hKBeee(@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH Y]@ziiih.8eT>FJҒHQCPjii1L&;.Hƛ222e(p8evmmZ/Ɩ7ty&~)DeKO#DCKƼmy/NDY8vA]X+"3Z7 viٮ$Q/9dyd-x|u27uSVh]Pº쌖'?HdY憦ή`zfŵ'N^ZXݕ=>> Ն$L~))#TNF˗}Meff`Ktv:=o[DH Mek)]8pFvҒL&c L%)iqv2GTੳ a8:*,M8:x;$ZqHN_# iAP } iAP҉(M iAP (@Z -eH 2.vw\ED̋;਒FAL>{l=c]}/>76-CX 6rrTFl[ވ9/tvAmp""L?j-]'L4K.]K ,2JK'"{IK*gg|~߸Z`ӸĈbKcv|裏Fѣ?|%tve ~~{dKӟxeie++ff+=`o[w/r\n5 _x4&̜_y=#8(^Kff. Q q̢7?c1bRb~V@˿ʕ+1co+C ׯߠAz葑сy䑍7\|EEEX#>(iߔtvgDDlx!n. fy֐sc@kzq1Іj_9#㢴("bKU{gokyղ6pxג̙3O?\pҥ%//o„ ]tQiii3 fZ|yAA+(@':2vg/OjQ*h9gا[fĶ +z Hw=>hئ >79v獋6Ƽ]?l8Z477d2"ϟbŊ!CtJ%[l9s̙3ꪳ:c fAPI{&ַT5zēwd+91< 9"3""z}2zMǷC`U Kw߽h!>ؾN̉m~2F_ŰGf3oƿ7fժUZ꺶vܹ0(OӣMMM+,XonQ{w;k6??O0AEOMZ´O+=Bc??yXX+w?6O ^yŅk#[:k`?uziurǍL$bӲWfpmS|Y1{:\/~_DǵOƟ OSqG=4"bۺxMYDq|1idDx|{fͿļıU1U1kqӍ=r/w^9߈"j݈)XX|,6Ǚiwhl[O0~_Qǰ?}"evZwtktnqUo~ v.)Kd"byrJTƉ9{F|>7S;}131锈A: IDATkȉ.7I>XpgL>e̘'.#"bł{q~m|5lgώc9b񇸒;nK/^8"ᆱ:+ DbCh~}"buU֝no]vSDfa]7LDD|P9"PH'eg:5?{b19q[o1(""ce;M_Jڽ=gdbrO0.f|7Sv⣔G+>h҇#6***#SN #"L͝Uw;6O<otV1^Giߔ{!ճgDDqS؈h9QOM8ptٻ/vqgWs(fĐ⋗ljC#3"rb'Dm̜.!_/GGsb/[wl^sjvM./r21cƜr)EEE֭7oުU;ۿ]l٪UgĈ`uˆWz>9{ }*n4n. qy>ۃ2]sv_s;b¸(Xwlk+V[ִikժU͋SN9c="l_}^r%e˖un=FG|GRMMmD~~yS:H=K= YcܐW㰣њSQ?Ěy#~cF՛`|m]>}D} &"Fufff?O&s̙4iR;q=裫W{wN:N,HP##"KvvD^!s\I,>Az*KG3{浥#.sCTc2Ǧ]bÌqȏ.pR^{.ˢ.{Dע{ٳgGD"3fLfffD >dK,N5,z:V^ͩ"p&(>i}N}~ڭ;dʵs9?g^[#2\:bX%>L}vƾ5hi+gǏ-: iF,VVQ&| ,q#c)Xuk6kjjZzueeeUUUGI2dHqqqIII׮S}}}uu{dbvvvii뗕ueڵK,ܼysfu8;'OwV//644v|"2dȗ1cg\dɞ|??pٮeʕ?>g2eĉw|YiӦ544zcǎ=H577d2"9眂2&Lx^{UVu0uz[lpiAQڌƟ}Zqa.uS+}c{fgFD47nǥ ~ mP:)Ί/lþF{ٟ4gDl^/z9Q42&{hocq91처vN#> 1aht>. 5'"bռ52oŸGcDG;aUog!HGD"@ihh/~755e|2=xnS*"_~lhhx駟}s=ooZUSS^HeDbɒ%ov}}k_?~~sW_UW]5f̘תU͛gy 8ުܠ qLMo{XLijfg,<]^Ǵz]w߈}GrŦ匝n>zi5pY܏axw{reʹEɑQJߊ*Wfy6%|GLS¦!V, "bӦM?~lsg>8̙e˖XdM7tu]pJ444g?{[3+?N=jjj̙3o޼d2ϟ?on=-***nkצ~5jԄ JKK#iɒ%֬YzA^tEW_}Am-SUUu]wmٲ%H\{'O>ќ1bqC'N|뭷"b֬YsNnn233DgWveڧ~/>}E8ffDɑ3cƗMiw\}tf^,XӘ箏߾]W6b_-_^vADD4WM\]]]w5D"qN:O>֯_k_{饗~ӟ_nhhh{?3wEԔLmmٳ#"Hwyٻ >|xaaaMM͂ *++O;Wե.s(P<0kylר_Oeg/y3=՚=&i:>-y Wp}䴥=LvvĉGqm͟??L{C 9SQSS}ݗJdee]y啗\rIV1??:s-_|˖-wu״iӆږVUUuwR2'p-2`8SWz衇9sz}WXn߾|^M>=uPIIw󝽧dZegg_ԩSS<Z[&݆ Tle„ W\qNl۶m֬Yƌs1||Lff1cRz޼y֭;%ImmmeG:S:^2?͛4u|.)@G˗744;)n!???"***͛|'On1C {7^z}NyΝ7xcQQ>;/"|T[__S;?NDs9{z?X78U… Sv N9NKP8¥2{ğ_|BgpPeggO2k׮XZZ$w{2w{/"^2;:餓>Ϧ{\}gSד'ON?%++nYYY{Wح,//#F!HDDYYYiͳ[#GL]Ϛ5-{'|q12򷿚1CnC:O<ᄎd333<奪jŊ{YWW7k֬Iہ222.GDuuuEE^WTT6:>Ou;.J]HSSӽcEDII7ܫW\-jkkgϞ=ztvv^h{z0̜9_.`H p4Pȏo:aÆul7oN[ ,qulӧgd_nnnm۶͚5+Օdܸq:tiGg}vaaaDTVV.[GD<3f̈A}[pacl36,"6o<{얖CQbDKKˌ3~ӟ&D"q嗟tIfk2G#Ftxn^^^*T/SrnllSN9cvvZ"=~ŋGDvvرc;N&(ulS]]… ;N*$LtM jP׮]Si(++[n߿755Eɓ/y8g_֜Ͳev{8Ѷm{\Æ ҥl7pMW^rnǬ]&"鳟y]&***m۶)}ڵ.7_}{7[?UT3<-铑#G/"VXš?>c\sUW]oGDVVe]vW;t #_н{={ {ѣƍׯ_qݻ2~͚5?~իW766^zpRFDssssO<1uf͚}˧MА}Zt@YYY*64lذҶLݻizxƎݮM+**>J8mڴiÆ <4x믿SNK#  //o?[mܸR(((ի]vӧOzٲez]ګW^{J]UUwߝJ?Ø1cv={vzĉm effN0駟N&oҥKۘitҥKqp">|ő#Gp8gP&;;;uJN*J2dȐ]444^){N]lٲeZ﷎yyy{J{.:A***#SOmҒŋo޼_loPfwꩧyÆ ?йehllܶm[Ddeej U444477gffhsssk/Z@۶mK%~hݺuwqڵkS?Μ9:tF=\2LsWtly}ʧ?髮D=re< x9:t֭_~{y溺ummm*m۶6oׯR\Rjjj~򓟴}f͚ :횒}Y}---6m5qӦM˫>[wMMM˗////?ÖvMS(ZZZ;K]]]w5D"/~q̘1kjj~xꫯN9R>ի+_~˖-d;3HC#Hcc~z(L~_2eJ[fmذ??^xT/Wr3$+[o9n;!se[23Zzfņ.`_zfED47m -9+#UCKFh*Qt=Me t.]될ŸgD+_%\կ~{^21c?O wT[[;{5\|Sq_5sˣ999MMMѥKTx≜메LD7nL&瞪/l***R?>cqw\DԔo]w]yyyVVV"ю={v*%3iҤ3f<{A?f͚̭} gy'+ŋ~ig04ʃ5m;9wkطƖkrwҒH$ZZZ222̮[595dܿgFFFFFF+Xe,{]]]}}}DE^^^nnnmmm]]ݖ-[g> uѻwh:rlٲ%k˔niرYYY]vYuuo]SS3}[o57wK3zwl#GX""fϞ}n=3g?nhh8/G?Qmmm[׿Kqq}K_Ju]w-Zl/7UVVMSL9"";;sܢE^zTuu沂W7g(AcKƫ~7\322^n >َ -s]oUsέtvT̥6mڔ)GJ֮[n[^:u]TT1'^z֭uR9=z%RXX~O;??k-,,sw}F5Ú5k.\'Nxߝ6Ș0aBNuuuee偬򰔑q1D6d"fɒ%qꩧ9p~EDEEŶm4C2ѣǨQZofggzz+Wv9p(ve">+{tv@ IKKKUsniiI&Nd8޸D꺳"m޼6 e˖;??vzΟ??"ƌ"bƍVT铛[WWjժ7v_'Էo߮]s|II 'ۇկ~{^2|'N;qOm{RVVVSS'x➊iaÆ[drΜ9#GpH1f̘{m9ikGk׮M =//_~~e]y]gA%ƥK1#O p v<})UeFFF2L#+s8K}i$L.q8 Ա----J]:vѢEV2dHǶkii)//OeJKKw8OT__v=8$(p~L&S?1R2֎2Lltrezkʔ)jf2N9= ;FK/־꫃o+#"H?~Oq &̜9__vmǚʴҁv`͝:u{WSSS^^~}]}YY柟{Thѻ6c=vԨQO=TDgl.h֚Hz#)+ /ᒙ IDAT,3F:c42;;K?+Vv--->lFII2tذajժ'xc9+V<'tСCx'_5kR=~Ą 2%j̉'o e{Z3bӗ:""xN,///++|3{d~駞zjD]vG""H\r%{:w)%77K.I=#ov<@*sI'3v4z /0"%Kejjj"'p‰'fjnn>P+MSuuuۇu֯_غu^޼Q& c#BbxuH$^|'|]mWT;;oܿN^|G}ۭY{ihhѣG}rg~+Wlv---O<ċ/999_җ逬+3όӧ)]vgN]s9漼1cƤz>@|4OZnݎSҥKS֯_:c'phR:7vuu׍;6L{ソۘYr%ٖ#~FqUW%d29}_m+S]]}7WWWGDaa_ޖS꒒Xt鷾 x}G*4iҤQFeb{N:0"_gW逷7n\իWϛ7.~tӧO^"zǷnժU1p=fff~Wׯ_Jմz#";;{РAv .77ۿےo._.]7xcQQQ[ȸ袋&Od׿7 %SWWwp ˗/Omw- :OoL;/_~ 7{iҲ`o|S) /+j5tЯF;Ν{T#G߿2""?zYQ7RAA#"wٱNEEE*(3|.]n655׷1bD~~~]]ݛoGyg0`!yZt.c)**w3mڴw}N> .`СID>q>}?cc+uW奒( 7зos=wĈNERjjj̙3o޼Bj}񴋡C~O|򆆆_=ȑ#'L,Y2^xa͚5Dbʔ)SNn׎5jԨI&= w}m6xbŊGD"?~|[zKff ~d2`T($m-Yo'?ٙwެYVZ7pCaa!eomS__mo/..A5~s=novD|S:c: W2#"׿?;;{H$=ܯ|+={lYYY]vg`ɒ%f͚z衇:EĠA~>^{^˔^zp gu!81-+++XzuyyyMMo涜-sFDIIBQZZZRRxÇrn-[UÆ g?#̝;_rko߾׮]hѢŚ5kRAK/tk׮;cǎg7tSb+1|{СC=c^^_o۟?eʔ= K$C[~_7rss/T{zh/QVmmٳSsNAA2sI][`r())Ϗnݺnfee]y]w]*R\\~w̘1wz m!Cy睧~zEVV֔)Snd82.ήhKpԴzʪ:thiii~R#@Ջ/^jUN~~~iis*---k׮Zpaccc!CKJJZCO|իKKK?-kiiٸqc2mƥ_~{[nM$yyy/lxA8@J{2^ -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -g<ŀ &@+EvVڭmvN;sfٞwdz;9Ό]8CzxBDi"D^H@<!}|)yH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -d^Ų:?5WT│ 3?6%Ά/.u9tuQ2#Eŷ]:X5K.F!07BȌN/+.u=tAhނ/^}jDCV${%W_{OZB !Ǐ]b8E?e&.{a~s-MGR B0d!byrU[NXyܠa-om^{ɿ)}k~K#D$3˿}.ٟCe+]"^?eMω_7%>ԣU]Z۸B}!LvXSS}EOG lkrpwS^?9wblH{*46'/;S&DzBhcB8v@7G&r㑁]l0ڷl4wrHm_:Bess/OyLTkeϊ !5LQlɵ~W*_yB!MӒJX[ /!BKʧ7)YF_GSdp,6qb1?뮂Y !^WT0ڷnޫ®+^Xlk P8-myRK{9aѼaTRA 8{s49+ᡒ-!ΰ,yZQ헩ڲHh |*&3(Ztߒό)ZdNgJ&q7:*N-/?-9T؆}Ow}jҼD,xmoS"mae -(Sq:Xsے;@BɍM?5)ўR9QW=9[4&V,}?}jYκƶq"Y =_ޱ)a}R5%Ƀ ZN]uIٔVv訩]16fZO#'kOVX @귣t76S"mCuBȞP +'zuGp}{͋-)m'=__QQBVbmUL9"BKk_~@kGU(Vt'qCu>A*T4~~ӄc!T[{,0*>7Ѵ1OkX @뿣SjeUmL]O05~SKmʜA~*rhmu]H )aS2!ԪO.cY]9iyS^XmKLion/U'q16s;:,[~Qm+bayKS;vsi6/;ھƆH^ޔOQccBȊ&B8ԻDXڕTeb瞬`BAGRKgv'oˉd"D4N ۶I}N̶MOP_Ws1߂2';įWOMd2Ʒdkv4=㠧xѢ2dATm*ؙ, !\:!$kB!o 9QG}/~N"Be668 eקfc!Dbaw2LȍEچHW|@Z뷣گtIOsL!0e\Ⱦm;;[FOggeGD!c;*[ ΧڃW1E!xlP!- !]j85B>֑کTivٖj%_"#PC۹;4 wy49+' Iud>-9ٰXKZR+6Viھ-cgi8'/B-Hd2dRvi8\ [n|D5- !h$+'WTKeڃQ!96gȒpUe|TS͒;O] !DHnv{JkJÏ2dȐIME!踢)!2i j +;QQB$:>:hu۞V$oωdɋD^]K h?AvRHm$;3-|A|PS/!!"ܶH8QTf7kɊ_;ՔT#;#'4uNWXjcbnmvDJշBf4;'u  M_P&VTvdI2o~O7}sYXuZmQh0֖iyi6H6vL%cgL~~9ceUmGE|R[yz:’UmJDB='۰B7Ρ}=m@B~y !x n?j$ YA[QJ bc!j+7jm ! M̹y{M[ȕ>7&ɪ߹sg9cCu]a<>(ѱ 廬38v'oˉdDBV;g3E"m!sX@ZנLU+uwd:dfEcY7k+͓+0ɂxNc=ۺiںtO ! Ȋ%rc܂_B[{ vϩ9xO>SC ?0T}EﶷHcxRO;䎕O)BYY!ۍgBի|ryin4'R*x@ WXIMkO tB5} eyq19SFu(s2y"L+D}u˗_PYY٧|ҩT>5Q_]2 (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ i!R@Zwy{ѣ/ÇO4jԨ &7nر y͛7K!xر>8qDfffvvvvv>3f$n~A~_755rax<ѣZzI,X:ZZRe/~W]uUכHgiOcsbY-o'SRe˖SLMիW{=,..Wruם)RSS/^}immݸq/~"ȸqBǏ-//ǯ⊯}k_җN)ȑ#oSvz!++k̙_D"KKKo^ЦG`Λm/#D[n唠,E $gIޕm-Բh'N}哦ԁꝛ7f9Ο>DnYP<%'6MGs^ pڵĉ֭4iRxF]o׿۷oߴiSIIƍ/^feyiiiy_xᅡC~;߹;Nȑ#oڵk׮];dȐ3{Сwjkk֯_aÆ{lg̘}_2UUUgϞ0a)x uD" Hg+(_ VmYDD^AՆ//Ѣx !kD,1VP477\rȐ!C-++;|)q 809͑#G^wuv[ssի׿| xdOkk_xD"mɜbĈP FC 9baȑSLZZZ6mg;gΜn3f81h IDATU?^RRrĉ3fS2d)?Hg.4׏T,%;Qܛ? |w3s.&z˗tdN4-7T<EMMMyyyaa7\UUk׮ 5.\C >7/!>_1bďnS2]eff^0Gff7ÇWn8qݻ?򫯾zPYH[SǷSRuubayno'~iLmNMD2Csz-n_vS#9dfΜyW7YreQQ)Z<1B'ɵ4k]ma='/6K)sZRkNX-+0f]YABșdAhO Bh9Rܵrن~k׮;# 8peee0(B3fL,;|G}t2---gotF#F8zhDnnneee2g?{AݻsΉ'^؟9v9y3;7x{gk}d}~C FKh |*%&3(Ztߒn: X9)±#ot3 31Z_~T[VqwƒotJǢ*Ȍ)Zdε]*2 yWQ)n#CKeyʕzk$XlݺuTF۩CGԅaÆ͞=ѣ%%%gm_ZZJnhK߃2YIT !wړ2uέӊ"9 r߂1=wHqDV!dيO-+YBa@$1kaQ7"nB-?1jJ[d?-2anu%q ᄅ#ɲ-}?} ;74xaawWҔj+PC*_3-<Ӓz.{-[ꆲGZBcr_ pQԔO}]v]*77wY|RYYÇ{nym۶ >K^J߾?J=,mHBS6>1ypn_9֔\^0-"_Ͽo)ٚsbm)饥BHUc(;3ۢNBKr/q暺Q!(V:,qC<ڹ7,ݥKKBj=) VZH-߯}HESm%e!쯮ؽ9ܿ餵uݺu Æ k9pniժU7n5kV63篹eذaH B͝8q;^{+##B9ی;vʔ)6lصkM7UUU{6mZ"bL4;L)J\vx튎%{ds}格)yyS n^xג{V:sϳ(]C2Gd[_>䖼S뙓=*US Ck;t7QSrOdB!X|V Sn~edgvTSN`3Ta))±Yc&!9>|,͞=naĉoFmm텚Á xZP4׿>hРz.}eZZZ֮]{ĉٳggeeu}4hР[o5e˖Ǐa777ϙ3gȐ!^qzX i2rj_7s2Q4ݣ/^hEM"ߴܹKϫC{b.7Ex%eGۧmjlw:QcG!+͗Snm3*b8 ޞ+jigJvtPGG"wY UpJMyW!+p߻kpAڵjƌx+~ӦMdcǎ!y]x̙3/C}O?|CCڶmۺu&M4sӟ]s5۶mۿF8tЖ-[ӧgr KE'|.Z<˳!: 97%do=Ҹo϶#`*Bitڒ7-,GBf$;oޒٍu5;+8z yʕ!srRFFܹs/_^RRhѢ#FD>mf̘1gΜ*4/f͚yNj/N6-3nݺl?ϡ7?xff?jԨی9r̙=܎;ƍ8w1z{G}ѳV#r ]f9n3(Sx;ß WOMd#ƖcU{N^cRɭ+[W9ysoVJ"C<07''BƧ&BM2c;[刬zO͝? wtiP${Ҭo[~賚'4Hٳjڴi6EKKKyyۿ޽~aÆ_݋bx6o޼y欬 ~_IO'R?o~\{?Lfz饗6lp-Ƿl6]pH7ݫѯLM ٽ{{Ғdɒ~锝}ܽ{/y_~W^[bΘ1㡇:cʾ^g\Ç{-Z_W_}n֬Y4݄ D"裏Wzٳ_|ŒE1~ss֭[~7_һsc@ ~Q펚T׵d-BN7yӿ;xaΕש;mW-)͐csΥʪڶ7 r)gmޱqEKt3}9űc2p577\ĉ---޽{BW>zF8p`4i<%iذao455=S>o:u7lЛcƌ{wޯ)//7&ɪ߹sguhCu]a<>(ѱX!+O1fO !d%|$+t؉H$;>&6"~ΝU<.sd67]mV\mI&=#=J~ٳrڴi +33sѢE%%%UUUv馛.Ç{~᧟~o3hr^`MJJJnγ̙siCH}]~Q&p\sN%_ +oj#35">716v2%R_%N6Lm]Bds/^4`J^n":"NBqߞ;:TeQs/ZWz{Dd'f@FUd}cSwZ)]O,85S'V[KS}2\IԾ3TV>StG'Z&{2ں#ݭDS;˟]O o.++FEEEi_TT4|𮡍?RX,^333+VTVVUW]5{{[mwy±c;r MK;=Hi[ң>9TriDn<2#6 *ֆ0&gʨ-\+r?]U}bmc&?mϓxr t;p)ڵjǏM &L6mÆ ۷o'N23xг?~=裏ۿC=IEEE/Һu늋333KKK[ZZΝ;p:^4}ZKG¡drz%N@o .+W,X0xt`ӣыrhi9e.#֭khhk⪫={vCCúuZ[[~o߾hhh?IAAٳkjj^y啳ɢh4e˖͛76o8E^e.#ڲeȘ7o7mt_=D4WZf֬Y{oFF,WF_y啷zǏ߰aêU&OH$.NקK]'3{ & MaÆz+;;O>䥗^/~5nܸɓ'G"777WVVeeeMMM XxwaÆi7|nob4{?s.kx_W?O r...ްaÉ'̙C=3YE"GrO 墹yʕ!  <裏>:kh4ҧOѧ;R/u p.;9rСCĕW^yiZxUVVe 2dȐ#G^B5R iAP (@ZȼS{;eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ iAP (@Z -eH 2A҂ i!R%ɊB$Ξ0aB^^^"[֭>~CCCCCCuuիsrr͛~TBP /lٲ%DMGB8rHmmmeeeyyyuuuuu̙3dWƝwyk`嗺.?jHf…ekkڵkWXB׿>tЋZ+կ^x1c|[ߚ:uR2!kv555{M&T?2GFP& v3f̷Ƿm۶~חVUU}cǎ0`@+2eJee޽{9wIWp9?22z뭶oudn}7ߟJR+**^C7.H$lٲo߾kfїr>eݺu! _B+VXlYcc-[bŊƍ[pa t%(pI&ՑHdmwnݺf͚{Yf֭mϏD"d?/??я~tў[}7YWWwqjI.***BӦM!?~sXs322MqƊD"ѯ>|瞫O~2rȞo۶mҥ[xqV֭khh[ s۷ݻ_rIX]555O>dcc#Ԅrss>n߾Nظ}3fu߸qcP?\bW\ћ7n,//={vW|MƏ}G]z͛wJNuƍ/<۶mv5ZZZ|~wuyFUz{RW/3Oyu555_~˖-.\x몯/f͚'N瘟駟~g:묯ׯ_0aBa]W2}xǪj ʴuwNPz$={̘1k??ر#s=Ç/9r={hСC/jժ'|rɝ/| GnuuZ~رcaTg?{7eʔJ$O? /sM4K??R[nvvvv]]?g͚5?u]7iҤy˗?3fjmmݲe9֭kg?Y[]^g4hзE | 477G똭+V5k__fggL:5J+Ǐ{}g[ZZczRK/B/})+++##cԩ?я}87vTͫVկ~U[[D/{}c?;R/]Ϙ1㡇2dHuufB>裏>ڵ#3<3uԿ5jYW&1ݪݺuĉoGU&0 H ! 0i"  ʪ(޻mmnҭmkV(h= M ` @ I$ !7dBy|pru;Wf &X9sp>OC{NLLܷo_yڕ+W9>>UUUm޼YܹsN M7ݴy悂9|vM[VV'c޼y IQQQsݽ{Ν;O<׮iM6w!Cׁ~_?#7xc1jZ&$$L6V^O0!,,"c(ЍL&IgΜL^`:◿>[TT3իW9yKMbڲeKIIɔ)S|`8qۜ%!!!AO_>&&&==+SN-_tw#G=ztV+VہͿzo])W_}uK. ! Rj׮]uuuvw#}{E}駒NEZ+@Q-///**{BBΝ;7!!w˗vx7Ͽ뮻L&Ӻu~n߾}޽GxŋK5ݻo~}6f[c_{[ni7x#44t-߿]~_[nM=zTWWKjYhIbILLl}ڸqcTTTJJJӶWUU/$<ф]v9sbJKK].W\\\/~gկ@ѣqqq-R^ʕ+_|E_겲^Hᩂ_GJO/XfM]]ӧOWVVի@eC:.cƌjswI&i̘1もT<#7|/0y͛7o޼yhѢy IG^p>iӦYfի'|W_MMMڜ$ooN@yaaĉ;(((صkWLLLS>|xڵQQQaaax8p`dd?|A]]݄ |PN:AO O$%%ݻIP&$$dĉ'N f&1SLi}5kH f:q /vyެ3fl |h]]:td2=C3g\|'|r!z͛7?.+::W_dɒ ,8x}wxsСCXGmiiZUVVov]]W_}`0|'On2; @7bZm6ذaôi$7t5mڴq7lPQQaZӧ C*]yfӾMm:,&1عsgBB•W^y!Olrɒ%&L;wn+y_;=zp x饗V\xFO:\`#'N<3y>}4ψ\Z TUUw_0U֮ڹs/~t}͞=`09roZAeʔ)#+++))iРAӣd2S2GMewg UKoz7nX^^>eʔVʧ~z֭Oc_P& tRI#GW-zk^+!(н >||ҥKx޽{K7nܘ1c>RI cƌ$Ӿ??~/+t:z###M&S' Ỉ'lNA*//gnݚ}ϗhI}; o߾f,11?!C/={ѣiCCC&L_,**ٳgf:l+W ~F$ on;:qZFQ_2έz K/-XW&,,lܸqc8rҥK?nn֮TWW9(...--߿VI̧~z3gvbLjj_n޼xmN^SSSYYٻw9?>>nht/l6(z#""_X,qqqǏ3gN䇤¢6TTTLKZۭs_εJNN^|A4(z۶m4l0_Ǡb {f?~|ɒ%ׯz-zׯ_d/%s=tY&gϞ%%%fׯ/2TOmmڵk+++G{?ŋ}݆2LǏ8qy].Wrrryϟ-dff6h~F]WW6 ?UVV;uuuǏ۷oN8|g .}z޽~,IǏ###H.k 9߿3^z˗n׻{~rM:~ڎ"l^Jqe$p\r6sQƈc˱emm_]ҭ:bĈ7:??8fM2e]\^DDM7ݴk׮<Ǎɖ=#G7n֭[_~_~O?=v؆#_{KƆ8qv'|rذakp-jS ILmmu몫[\^hr?`o|mBݻuVIyyy-j>UFFŋ ..{x7>C?6EFF\Çw}֯_~fv\\\+JիWNz':;_wcr- UTT˫/69:gΜ￿&PUHHܹs۷iӦ^x!99ɴX+EeAkƃ ߾c BͶ4m˺[Çt:8P\\LءCvzʛ0a’%K^۷ٳdee{kiddd.**4`ӧϝ;iv˿F9sf1A6TTTm۶1cƴ>Y~Ç%[oݷoݻg̘p_xe˖]d2;{m钏b{?FEEy3C!!!7tn7>ݧOiӦ-X &&&ഓ&M]fMJJ 7۵)<<>tPqqw͙3'`{תgϞO=Ե^tR!i͛>}z&=X+E2{xNm/nVu|Ϛ.\d}G%K]G+b 5wq6fXs~ʆ˷$$)33R\,?|fff!_rwݖ4.]K/~<|m6iq֚? cSSG#Gu_vz-;JJl޼r. or6n5eR2_'d>]Rڨx_NU\l5H1kl֩&;q.jkv5KwvY1>ݰ$زeKII޿G~煅'N:thZelᒤmdX `%SQL}IS$Kߕt.a0#k4lMc%;?I 1%_|mEQSj]G,)q ki~{tDgi?&pqbM)?88aghZ^a Ug:)i U|"@,X`k>Ë]"(35{LEqAq-yPjFL͸2F _8s\MHKf5JRIn#KR_)i) 1F.RF˸ ˹n熂fsh@ڼ9lnzRko0̚7^?ܱ)kC/WSLO6|ʨ 7~>yꄔ~z-Ծ?krtKc:!ȔI.y EWNId16ۓSQ>SKK5eK6,h<8)ƩVT k".=oechShC.PU"W8$HF$66RÔ_dlJMΚS,~d fkꬻ3|9~9ֶ4A${dn9{6IJ`KO謒pdحsu5MZp[jK_/^z-r _Oy5}8^+IX )~Tr/'{*"d$S_qbfHX%/<-D!&i|jցQ7Λ_#O3wSvN36!eɱ&)dmi52x{DQP>W񓭿yI^ߠyH1y@l>g8'즊G \F\9)udQ!>>-0KJq=_WPX~+l'&eL*xms=$O1g=Gd8)849oN{0 p21c{,mvX¥X[/0)s gIAڊ[±vS;>}h^Y1VrL&c| vn~miK\Y/ޛk9k>$WA+[}s c7 lKXGkouвlg|g $C5E;.HnW+&ش-eL Q'o^|-SW6My9Z{ɘ "o42wTq'žݗ osyvקd|; Wz-6wr|( S+(6>%SԧdZ}JGYGY-kS2)sΝ jb;(/iЈw9olȵY\?6^pvq;$L#בOd1H7(t:MO9kMNc(7#rB1[%gGa#bs2wh׭f;p%9GJ'ZbC%ͱfin*jU:QDd\eE ʤ!1:CX5Veva]Q~]dβ8.""@#tv#OEig%>QNvq2J*8;5ȒE 6?yEQn5J2ٓzhu]^Nu'"""/m t hkKKuњʫ[/qno3 \܉ACL $eDr&QMRyDŨ6Aђ.{rP*$7jZQ IDATgNi^ݙ빣$56ӇHTtuszqNO>x&G?#%)ْ llk8>.-#)G-7 ߑ2fAڀC⒓mNw74ر_W's {Ɨ$5p>ni+)՞\[Vf@ZGudG;mtQfhvX%,ISA9K&Z-dbcw;%n)7r]1+Wܼ(Q\)L1WwL#N%EZ'm΃G4$+vP\X+,2~YzʊVjې;kcg,=R|V=ͱbbz 9;Rbd,Y;2KHL; ^c m-**rU*W K̹_۹sÅ@]LA}ꊝ[[d'hj~C5e4fo,p L o>U~NauTAR1f=wqm[kaձFߴ֤kRʅP^c5A1KNQݦ6.depS5)&а]ΤiU6$N>\)Xls]S$h 3[]|t.Kblkߡ;i:U܅pQ:׫OUB]l[Uy=5yBBZ?W_7OlUEbW>/joΔJR5U)>[!i~"$ݪ_?ұ yeGjFI?5Z:׫o՟}ܙ:<{/]_Y·X'yzFz먣}e7)b׆F3飷 n2Lz$izHnL :n]i盵.HîOkH옶faI%1gBNCOo8s0oMVν$IVjڿhF#?]?=ZW̭0L|sDvmTyFy}H-jXdKGw_wT^yf="=*F=LMS˝tiqXie+IjԵ'i0ȓfܫWuuknV:S]| I7ꆻ#G^ 4vFk҄׫U%aɪqk;z<-i]|m}_BB5=ulSڠOT;*j>jeK֨)zgI+OOgkeo^, -gKz췊Zj7SaA 2݌/%#)2Jǽctz%TuIRG8gF)4L!!YjO0 /(jIrW3NT)-V }bݤjժp~}_购;ZW*/2?zY;T]eiziܕ2yn=4F>JN=6km[>}'t=DhzPQ>{su*)I 5^ңOl"C*OWzT1᧫0)$Da*?`Xj~2-S·hgZv< ^ϕqz78QXOV@^ @7cJIKU+Rv˒$UtdxiDޚv}&IE_ж1(mY!DQz[SM=sԮZ6Zj✦WoվSU$U)zϪ(̠5+i+;]Ώ9\l^OIpy^NPy;gT}`w!10LӒoJŇdj؈y?h/2̵5S;Ӟڳ@Q|f?oyYl3S{N͘J:)I={)>o1ʊUUq>T;Fw^IMAG9 2LhzOJrּ u!Vi\HHy13bvX$%e|霖:\0xFo>}Z >E?[t$ Q׶6r&.QASnSXg+׫H}Ra()[??[ԇ]uݝZ AWS}r^Ih4p!-5WRs}3:[ml^x ^UWIR?Ej2+W6r6#IisԳw'@L|I+jq>mJ?Б/%iP%n43_Ng:htu {J S-+ֳIҘ2zj=xj[i|_åR1IOCGTaay:\?=%4t,vtWl͔'+WP'zVo=LXid{lt&=eL?~H57kAL B0o#M ۞PF'OT)IzϺzv EF)v K״5&o0Ig;>e74!SNIږv~$ Z{-謁Lƞi^mG7v eN.NIR2"c̦ntt*!}56&J;r v8IliW% !&y悒ז4Y]%ߐoXc։;v=SlRW $Ϡ!AC&$s5o2-.vFHV~Q;6d:$N&g\an^OJ8yٱKd䊢7vZ IJLƩh\*e{2OdMsCÄ1G1GwMFJ9o5Ox*}y;6M]pTQ Fkc>7g~$[ƃ}$ OQ&p5V\c,Ӡccc ~euBx< WeL4BԌm$U867YY٦ pɰtK}7-pl ,'kgGde9nߡogm<\;2F58t-VtH]d$]i\Bٷ#Iᖔ-!U/."wqx. 2-TӼY$ɵ} Z⩮hrz:)>:$bm;Pڱ1#IFkZz+:;0[J^@wϟp8[ނ>a4osJk\o諸ٔ}_f-beğ6dO%Z}%IΌtBEdm(tXKn|gdNXx_5|.u rVT%iGڧΘGTW|dgFKjܹyfuG}6E$osֹcg#L7Hf;4ͬ,W+oPwLɖ6ǖ\o]8jdg>UsS6gg tkUg[Ph$I!dϦ5e.ǜ4+#ɹ43u $y 7O]tn#E['UX/wٶߞf5JĩCYZ•iG[I2-SG;wlڐOZ@FsSi r9wnZW(gMΕQ楯goz:-iQ<%evHك'\>flegӛ.̅,섅SFاL>̋+q)7NMIDJlMMQrVa@wT^m#a1QFA~*unB4 IDATWiGj+JJJnu4K #M iS1)C6_d $bge$7,gnkS',yU;N1ik[.:nj5IfdIt}vA] &d0&K-dJ&Tg9Ks:.;/?_,Feh||}⒒INX6e-kruzj^0sۊK>tkYR߸QҦvz Hi$d5puf׸xA^U Q!|A}S$ 3_k'&krZ Zi)Iњv]\E.$)h ] tO%/ 7_ٞq߱˙)SmN$,c3&^c $OqYKbm;N_(G_ơذ&Wmv{8N]Qt[/@7*vTkr󷭇 ??xJ_Bb->ˣl}?EY3f59Yurmμ=k&X Hݽ4%kŗ#qd&g=9Z ئ7 fo6tB.|,=$ MIi]ǜNnI3`}NCP+Mwݘj1Ik$kr!3og9.Qh=IxA6}vojAFkZgS#Gl|KXIf鄤EXܷsF|s52UC2,2ST^ENz c醼 wKgK׼e9e6l¿Ou\mLₖ69r}vNk`%8?vkJ蚅|. epY (A\@P2,e wUw$+ B8hDAERvc؃`gL>{?vN?yo l@:+Ac.?2+3B!]0~SB)_vҵNtkCz) c'(B޽+!#f=ϸ;>{Z}kݿo I !+~?vz&nƁ}ݷCwN_G^<{ox.Liߤ;vL8?.|S8uOPf/Zͽ n3GUk7._[^B!} NmJ.{n;,8+2Sy҅zmƎ{#wݭog;O!m5_”v\p}tujK\Jc,(~ٽv|W{$mG^Z~ޅ[MS5}B!D˟#{Z/BRs_\>7w,}̤%_e{AU|MkB|/_Jc笴^}?X܉g2̼Xw„7eVUz aoMm!гܥ){oj-ko>Dҵj[ߦNs| 3zPӹź0C2r y?jLY]Xs]^ ֖Nʄ~'xKrj& SU{W}&qMth࣭2m$n}nUMs뫔>Ƿ3_#CMի]awW;ڪ;Cp.Ơ5ㇼ`W~wcb/{eUwsFԧ>uR;v,X .h72zKeff^}GeZWx6o޼pI&v~_(m:SeΝ[jUk^$11)))XlϞ={}7zH$RXX=--8b^xիܹs3>8?MMMڰaÆ mw}ڑ{~;߉D:J]~ؼysiiiKK˲eFqRGo}[ggYf#F; ++LV;322ڽUSS?ٳg>|~BWÇ?{iȐ!7xi;V'oE^x:ٳbŊ²e 333;9iҤ .[~}s힄4`N=d7oO~RQQZXXXXXx455ݻwŊO=TQQW;oLeœ'OꪫN$}2 htiii=z(**:pqR gBJJqN4yWCyyy'N]qƿ/zkH}۽UUU?!peJmۿ?o+l򴴴QF5ꮻjllLJJ:֭۹s~zK.hA ۷'''wҤI?VZq_W[=j+7| 7͛7o^eewyLfff}}C=4jԨw( SvرaÆ'^uU7o>/fgg?oFw)MMMC6m ='mE7|?ycc]wuwi+\?OSO-\0gѣo]v=#vtNPᥗ^`MMMwWtb>O?c]t˖-yyySL6lիwuﲚ_򗕕_җnN3jԨ{'99۱cqFbnaܸq/nm;lj G@uu|%3fÆ }?bԩfjmtH]yy󓓓/VAXlժUMMM3gʚ6mZeeYb͛ǍOcMjԨs_N%KsRRRh4dɒ-:6nFz݅IIIl޼1uuuGȤ_իׯS)sp-_狳ΥJ$-[fĉ+ҍH$r뭷:ڴiSwӎ܌ӻ-[.8zoذa޽qkllܾ}{a.4hP޽o~袋nGy$vmG)xMs]ixx㦲)(42_VfZR!憃l۸M3iҔ)rfeSW]oW֢v>u۔ܰkx\ȼ[5?Bb$-9L~vT&(**ʚ:ujcǎ]~] ]}6`͛833+=N&Y&Μ9H6+пS>ׯkO㎝TWWWVV>`./ҷo߿o߾ڀi͚5/RAA5\}8GeꗙJA#^^8k-Sowsv.̊4TlVm䬑 ps.i7}&seEnR흚yܹ_]$#+oϮfJriyI&egg޻w &TVVXj x'|j%ꄁgϞ+V <.k{=!! %%eɒ%uuuqNjnnnjjJNNNII"iii 8x`uGik1p]+yW^}+?3k^9+__uxLfδYG~sSs o1gu'̜B(G:'`3.kXCe*֯*.^80mڴMp˗_wuz^7qR=t:.xN8'?ĉ;ik7>S'N7n\gf7(nݺ;wp ?ֈ#Fe˖Ӹig߿{ӧOIHHHJJjii|ϲL<#<~Tb:t3yL:WzL" 5<ǤdBe˟ӛ%\qK67&N/RLnM]wiNfK/g֎;6lؐ;rc9*--=;wwK bؼy:yGT]]ݒ%KRRRMvl)##chtժU:gTk^ziҥ]L(Ӿ+߾$g1!4`XFӮUV'ٲ}S1!oE;V?#5T>1g4[,n ġX,lٲӧgfӀ2==}̙6lXdI~~QGΜ&M?HKK;-XM7tj3p>BJJJl2f̘vY!S.X`Ŋs0`Y.6`*))y E 9̂Z;J.~e /U3caF"}Bpi6q̠]W{gॳ ' +ʷV.[^NmyWϚrqNV o\i:!̼9/5zPKCm/,,zS%gN{ԁ/zNM(**,((hرcV^k.?LH$==}ݵeX,f͚h4:uv 80//_^nݵ^{ZԔS50| R[e׳Fn([#Ee[6͞{]ޔk _sdk<'-_߽幅wܐuJmsNn焲ŻK^^xm{tqN-]K}lNQ4]xqKKKKKˮ]N8~ɒ%ӧONsSZZZk;wN<\nΝ;C{= 6رMgB=.} >kfdddj1=Ag6ں Yxs>PYz&v҇/,dmݴr3ֆ)ó{h1T/\yqNcf5K_2eTVصjѱ'.ZFܱ.^۵5nj޾[[T;BۮYfc;vlذaĈ?OrIuu-[\r%GMJJJLLliibݛx80EJKK;:v,Yr}V,{e˖1}_eggn:qĮ-RQQQYYyw!(4`K< ̝RYuw/?AĶ/Y*-{쌹_w-s,< n۹¦ }]9Y .o7R޿P/nIgX}o8L^{7C 8iKRW*4b˖->} dff^qhtXػzڻwtLXl޼yݜF1bDzzzqqqee).URRe˖1cƜ𐘄iӦUUg,ޞ–-[뻶–-[ZZZƌх ^z饥Kvљ>Q&+|HDCm/>pKI i.^t''?¥cOwIYыvyc 6|@XQ nbmu9?]Ԗk;* ȼpc>3SCuŚGn3>yo2PIG΁233 :3'X~={Z{^QQАW]]=z=z\qi3r 6[k:Xl͚5htԩiպƍ.{F=x-[uSuu+iLqgDڊmZmZhI7^IwVFj76G^,N)ܮWٙ-o7S_a"ყ+-4vIuSK/KnFZh?XS{\7\{Y_ؼysiii~~;3~С\rΝ;׭[wԭ!CڵoJJJ80lذĉLC}ꩧ6mBWKOO9sfg=mٳbŊN&HZmiiYxq4'SNyOvkVZZڙSsSEE#<F;^zʂ5g:(S_…^\YzIY5BjzV8#-rx ՉYySgd/lzdqtDR"Ysg*z2)gJ3Ё 8ŋCfJII̔Yf%&&.Y'Fѓ -Y$%%e'[8SbnizԩSGotԜѶSBBB7*IXqYeC̚։9ò2BuN -]X^SNu,83*Z힙v43+#5,i}P}H[>$hQ2~{/~ɗB3u`ǎ6l;vlg1";;7ܹsQ/Yd͚D[b~ƍSN5jIgZ0O>d7ym>K.BVH.j̙III_SSSTTt;mll|v>kG.hmԫW~8G2a͖wjǟ *ѩeNƟ[ nV\gܹ<&>mjժnd_WOB*))ٲe˰arsO"a4s̔e˖Jק0{9s攖~/-O~jժѣGz뭑HTrӼy: sֹ l4 >53^[^֩Es3SC}}gx (H֠OѲo_^ј10/W Hn֑])13-aj犫{͞[|$TUU={ꪫNjbBB{bŊ={uw̙竪=R桇я~D\tE]|q뻱9s|!̛7{YzuSSSGcXUUUX,|h4:mڴ^zԾ#G3fLii͛ONJ$;owy{;??Z*???aOWG0kYΨǔC٢oȟ8??vF ~YeK_vPszdvruڎs2!fYt_ hعfqՑK+7M(̝2'?HK#6\&rc^ٟYp̈́MB[;pR}n@7*...//:uСCOvСC/䒗_~70`@[H _B̛7G˻ˎcǎիW755 6{I.rN222v yS\fe.۶m?RSS.袋.(###nٲw0`˿gϞ=ׯʚ:un>s 6+_~y6o~󛹹ox'x?~y睗[^^^UU믗755E"s~L%%%gLNŹ tѓn+o;hSIQB3rԘ ZΚE״!wd*++ݾ3n܄&PQSdSîYYx2&Muy^Vc?eԢAw/mn7UTExӓ1glms-_fSYu!3`'Kܼtq !'_WRXۣs<$.^80k֬.]ia9sZ0mܸqǎکHHH;vb=VNiyY)$E"Mm[paQձsNܬB$Bh[E흦3pʜ/#$pxL{%~} 6#ЦZ m…]uuuvjmXD.;/99ijj:p@yyCZ 4w TG뫪[ZZB JOO8ɠ̩ݫ}Ӂ>9#3PYY3*;#:Ukw9jϤஒ2)As7(KP& AP (@\ .e 2A₠ qAP (@\ .e 2A₠ qAP (@\ .e 2A₠ qAP (@\ .e6؁]Ewhiiٷ/46-[ --gp 80>khh 8b-{*;N 4,|u֏ʽӦm[Ś6olٷ4nѴmkri\>"]GkXtՕCR΅h\jt⌿^BF:v@~<Ҵo騶_Х/H0e!1ߟ ?ZC)OM>eE?=q߅䳹oM}MoOHKkwLm/-UUTأMmH]gAIBcN%sQr={,>655\8?vdLJBH'umg &uw!1'(p.J/SF_|iӎZ*K9+)芗C,1Is6v8EZ/Ri'р)kڼ?i./ yi/M7&f ύ?ǖ݉lH-;;8eOڜ.]]bKUU! ܔ<Ა;xW2cfDZ4_sw}3٩qk%/4ՅĤpcʇ>to\^|XCCHNN㳷&^vgBHṅW^<BH웕kRf|"$vR:TMI<ݴ846&\2goMxck; qIP\z-u,NԵ'?xR?Yټa35~>i'-v@ciC$hDHImܴ1ABCI @;#)3?r'>XP]jޙߺ'){P,mܸaM_FXMM,m{-tIL^)O2uObG5mXo"#Ge|Ijjׯ{WI#FGq@%sWҠAlԸzUt_zN'$&&\o~ƆO{5n/BH;y¥W;Yjy;I>]-W/yԙ^b--=HޘZx]Mon !$OwvZ;;w%Or.y>}cĄRf̌Rm觹q+=nBNsnnxnak6my=io|3¡!99!##ew4,k?沷Kڜ{>}BBBR΅??y+VC|o|3)лwʌVNGt >N)(s-_狳ΥJs^ʕW%]8OƎ,Nq2mzBї`ƕ+"#F%z!2v\˾播2:ۀ)!##|7o z'|̌3fD"$5^9;zьo]BG&u\NBB+=1e߾ľ}[kV3sMONIBZZBZ_H}ȱ)8ڹsfe6XqJT0y ^6`|sGJ ---*۝زgOB"555\4,WR^ټݦojK48kHHB{7YYv.ΰa IIM[CM[ ; Eښ;6%ӑX]]כz|R145;bwhnJy½B 3CbbRSc_sMM8Ĺ3x̔3wZKNpE2CþW,^Z|w\+! H@4jP4bXpJ[kK7Vl]gg[YnڱZ[bˆ+TTTQxAnID "hy=#u_u;H^Qj㦋앿c凞j3"OTPYiI%;ꏗn5ޔ5!BqZ~AŹ]>5oGUB,y8C҅)#i(rR3NQN$W IDAT=C>'_ HUGsHגVE5*<;5ӻu,`Q ̾l_mi]e4o񞙦/gYu8uԘv{\N oWGG &at;ΦFgKz^cժP{uRGDcWM򐫣C;A}w&!F d&j3%>'e^}ytݲj[q9e9%;7sCaB,m'DDa-'Vt١KZ1#L(*9[[eN3N4%''jc Y*.).~jr)kwgOZ˞'-).>1SM[]f(}jqJMۤd?= 0uH?Ot:?2/ >CB_ |R8^UpfFRǥ%u`]#(H`(H=F.x't7.PԮ]eN{c}_dW{wA^}ku=?޻Q9R:xkw;.i۽uHd]-BUppW}o?)dtObŻb?QdgK@831RZ )88`^>s}61pnj@”K5*{G=:}u@EaCǥ}L?=s2>.,Y7^xΩ4d3财[i#LI 3xT، DeR+L"Y J gdmiiӢD¾3}7ӜTm(Wޝe}z[ wYr慦<^J\V_Vp 09퍗Qawz\.t^\BVGjߓ#nj L9 o8VB@uD;{;}jRSЂ _ϹWCTj}DEcGχžQ+QkK{]!P|,ޤRiߡ]u6Bن4(D ;;cL:ڻ!@eÄ\W3q"Nϋs%=RB.\/$GN8hċVH~יhz{4ܗwRV!)pmꨫt T_E*H3e*w0/J *<\%.]#Րe:%;kRE)ye|q?.| y轢!QB !G.wIq 7 `!K{_M5w0Wި]| 7! Է9Dn ZLj{;,EQ")8mmEΜZo<\wh&MR jȂ2U6b!:Y+:ѡpTYlCRV~}iEGŀN݅?EcG\^EolhKVe.Ͷo(; |sutt/Wi]?Q> &|A*;Rщ9S1*8$% !4~$邅h8ɡ꣓5JmuN694hޣ₣6w*RR[WyQΐ]5Wgr@GfWk4g\jUX*(h@CBUY' ʈr]O]}E C=q~hDŧa6MuU=ۻ]gg}!:P;QPbWtfa> !wXpSN^]6*<<_!XtI{S s5\+C' w62E2k6}m}'_ƙ&dLg5{JidW"So4UVZisi֧ܙ`j  i٩DeD@bbЍ.<88%qf񣂻V${v3&Y0}ҸQjU=?=l6Ϟ9Axq=8g?ev&F#$χ'nmqc"Fugݕ w.J~{gm-pyFҌ RGO){{o~,.魟n6͜1!^/B!rRbUY|I%U{-7TKXbKnJKI%gߞ!p*SqVdxY>}H)ΌzZEQZh:VY)_Z9I'-4Ro(WܼfK/V|wn)JJ"48wG<%&mҔ(ZEBn(8 1i,51*DF-壿]B]uR#SVf]Zp]pS.AqFSgCn[7Lӹ#5M?{n4o3nGmrӽ q 087(A`p]p=@P#Ae0"@P#Ae0"h\]e5ܵpSRTZ644400pk WruttȲIV@].WHHHXXZ* lkkpB{{^rp De!)Z =xVnp e$"*j(uxZMeIֽl)=׫_nH}TT^z,ʠM5-.A|C1,0(G"?31B\>)kG Ij0DN43gfp% Pϙ"Yia[)˚=psybRˌiؐ?OzK3F:lGu0+V/+JyaZbR_`dJFQ} TJ&b\Z5Ń`PԗrI7!~v24#]KJ1Dz[WŔ8N'Bڷmc~j8WZh4c&&_ r2B!+=% }̌a9YVf|몭л-L33 R: x#p(lr:}7ZGyhr¬$CTRѲ*2.94cNN9GEžqI;;mJN{q[`U}8Y_٫sLֻ=eFNt@ZW-wzbr[-^ZoWXqE5 aiDQچQ/[plQ9*ɘ8l7.`%M9٦n{uY夵N2䬚Wqץ Er6EtkZNYEL\q47g!ȾVuR2B%i#W>f?9|uŇgk-D$S-Gmڲ++ Ƙ=fυFi¼UN)WPL^'{ ѤQ_^{*@i 5:kUiEʼ%=3Dd/6[*Vؓq$[eoiJު9Y\3>;LR^#7۪JKSVݞ̺~2ml*/q؎T^G%3z.V]Qiʹ?4/;e_~?}ݶoӸoQYjުBzXj\bʵGK콘j?C->a҂#"jrŰ "Y [ROLmRFA+Yڗ_jS32{'/KpKEdqF9]_yJG`ա5X!ft4_}G4CX~B;(b=3%>uJ.Y:c,zE%NrogE.B!q13{hJ|SvC=O<Ÿ$7sZpmzgS9jpߚ5jR{P;Tibt\@3RYQ Yh 0Dz(Oxf^Z(vm+_7ħYV+'#ڤ "Q3|DWBXH#~!H c$ ;3#vw~\B2ѢdvnSl~-LrdBz!|O?FuJu~NRCa>v?rv^ČL~֗:(㨯]YsvGm_Fd8M8lՕQ.-4Q:{#!zꙻ'*L+dν}j^^Aإz?I"DKQ1VVXTwV&]>'I,$GS喷49y*Yڊo3 56Attq!>Ef%je>s27 c:cyQNuMA߹Xs ݻ.Ա Ɖ3+,e>'Q\*㴎ϬgmL*\v,sdZ-ޭ29'q]Bhz@Eh/۩\Rȸy ۋwXpSN^hRoT? acM3)='’U&iRWdv(e ^qC:`a25I\1Jt\3dE䤿$?{ZQ8.FkEV8V\l+wBҲS/Qi :WN"iN)Ť J/bqt>&QZSanT2z꥗PyiBQl0,1F%!?\I"D~  !Gd,CpX'ݛZ)qĬHQwc8h7RR[W)3gZXtߜ8S :v㾐$H$cn  ɲ|-[<ÿ/=(>VTT\u}%2eeel3R*31S+Jr4U{1/̘eh,5;Ks{bVݞ:e5unO3K.1+3SPؾⲽֺfx].\#frsN}c~X<*&ڑkf<*-*-5 !ސ84~Kw[̙ԗ"!5#kIs>~J7 &x4559!DDDĨQwtѣG{w\ , !ƌݿ.ܯ/\(p:!!!k֬hUV;6((Ƚ]__(_?z]!nR;kdNM˘|"܏CdpQ{uTsg}t[j +3|0NE@pSe->/ckVLB(NQ % ?4ojZм ה!"F")+Ylo:5EGzN-*;N}{~AmNnzf^RPEh48-]pr oH,MIEpk&X|؝ڱ|OrO:t:nj3q~hhh8{b̘1ǏnooeYфT*!$IǎS%!!aر}immh4 G5 IDATuuujNh522211!@`3xiUdbZ5lY-\ȃW-92 zL#٪Հ/.XvOFӧ[V!{%A WQ۷oܸӨhrssWXݹ}Ν[nnW^5Hnݺh44e9zhަ_v]p‡~8::wݴi(̙3Fca}Ǐ;θ9sN4u!UlX!zC~K, Vu۶|uXe 7|(\.yPڳgρAAA-hmm}g<444Ȳ,=zw &x:oݺt=w'%%]{^}}}IIɲe}q:uu&;;{ժU~̘1[n7{n[,xHJBB$ !zM8?XV.]NxN4m۶XRM|;%hrss/yF(//_v moo~鳛ZtxRܓtvvʲ,=z{occcDDDȲ\RRK&''g>RRFt\. z`=^h)<<F1L:Νs6ͽR2G\aB  \.WEEƍݛF166z̙ B3gμ曝}mmmBEEEO>ٳ;w\y-r޺u,xPZ[[}٠ OKSSpjM^>,fϞ-8}ѣGƏ3'B[̙3GZ^J~}޽{vSO=uw,Zh񵵵Ν+)))//~0wܫ;let:|t?mxmۿo~YO8/:j**''gϞ=v]^x{䧟~zǎD͡O7[P&5+'-q& ʤD w )\A}LvkMI3Y:jTHYEqyv6Uzcgk\rihqSJw[ݻ _ʞ5^Vx[BoZdRѲ*||wvڔh]O G`U}h0K 㒇ri.R^+Л22Dh5"Kugu~ eKgFdٝ_Zfk"Ґ0mVrrG΋&kBCQv˞CGmZcrRJʬ B!Y8%^A˖󥕙#{ N=*G%gMEl,zw4ܗm-֋xlin*C|"&-// >Xmr&'V5_,^3#^ޓI[uAYV nR2Wk2ӣǶl/iNPތEH̹w-[.fb֊s֊9k;J!$QIYP*Kh0WbTUg*H[ںUrpmͶԼU'g-qya}=):kUEIBCwgTo*'ޢO˙3M8fZATZaOARutW ~/epGkC^Ͷvut-1$kqa5gdaVՔRM'QWZp^DMN1y&e$RMqҾRy[Zڴ(Q[QOS&Μ(--8ZeM!m~U(F(ɾ;Y{6i>!DY8Y:3|"ۅ.*qROhJzu.VrC=qa\CT\{1z.fN!s&pKyE;#s WnȂ2"4I}#cZEnl'uBnJљ6G^Mm~;,{T΢f~L{K:r'~=jwmJtkVޕbKuttw0 F@EQZ[[].pr$$ \VEQ<-7[{{{GGG?^#nv}o77x~nR'/I]= Q:ƥ?x4ʨ(Cv*tQ=ui?J,$~ALɻ?[\-sqڬy3{K*}tEQm6錏;vlpp;vl܌E9~ɓZpׂpp\Q/f_? KQ^{0%%… !!!O>_??c=f4-7~;pq>]]]mmmQ*jhP'Nhmm 1 Wt9}A 2dAQZa홫VUnЎIn+l%vj K|-&\9, Y9 ^rHٜ䓚s^/9r3^fN3:b*Hy_4gj-//蝒Bj$IZvc^xīmϞ=תk+<<|)[I PvX6z5^'r %K ৞zjpnohh'f۷ӢV'Llٲaeܹs_gBww;3|rT*رcǎi/\>hQC7tĨ6ka)!c&f \(g{!Z8֓=u,b^{nk}.ْ>(ם[TM=6aH]rEˬu5=z4667ꚚVVV6 +Wرg?  ^n MMMmmm]up85{F.4,b^YW ٙ zV\ l il,}K}WeN̮1.RUp=tvv~GVUV}k&o;wBdffڵk˖-Ov:qqq_W/^kIeX^Ç˲}ZQ~nkkklٲk.;444A\.~ &I_Wyyy&ɻ?.++{G4 Y>onn޶mۮ]Ν;VǍ_W{֭><4 k<f(ߐ>V+Cy.{ϯ]z) l6֕5Aˇ gtOSQe쑽EŞ|-{nO_PYZ\TٓgLN4ϊ6xaVrN^W[<1ř M;Y23+.k6ˠm;/̕o?|9{e^\IJ[BoHȚg W(RRRY~AsKr }gSUk|nnn~ꩧN>ٻnݺuy6ݼgwuu%&&>#III~J~b )_z饒Ǐ߱coa_K/TPP9j„ /bY[n]^wqΝg}J/˞M^s-,O\t\Μ9+_ٳ=W~>_|nÆ BG}GwNn+iW?|{=_`>G{\Tu3 -oԲ麛YZMf^2ӼWAD r`ff=̙39|Α{>5%%;{U[OKLөTeާ8eʔA9ԥK,X0hР9s\r?uB2d[jffիҦM/{yyKc۶m_}մi̙o޼Μ9szrLynZT^zO?]|… 񘘘;v̛7O]h4ҡ\~}׮]-[p8RSSWZu֭GyDRWݞm۶zJ,dqq]򗿼RSp8 f<L<֭[x_vڻwh޸q#))b{9sF׋DϏxyyf裏***y`X\x}ݷjժ3g,[_~5 J0z5kָ("x .i4j֭566mڵk5MݧM3g7.55>zGL"δZȑ# rʇz{ HxnzѢEV֭駟{:en)L,R!ׯ?ڵk|ɛopB)#™3gxﭷ Z/^qp-_<44~X` T'p8:OL7K{۾kXKrعݐ3gӟIPKUUҥK!C;;wv۷g;v .]4''G/p͛.]+. 8qbhh+Fy_x177wȑ>>>{>p'2M$33޻ykzzŋ/_.عs?߽<111))5js=~IIIⒼe˖-^M[h4> 8g7oޜ|rs)8qb%%??'N:u'2esuii駟~{n;׿uŊ.lm .]ZhNyy]N8k9Gm=ZK,}m7n|GΝ!!!뭷6o &H5ݷoȅ d2 /-Sch4^ӗ.]|(qI~~.j6=z^zz+W5kֈ,///O^}r%h4*J;6eڵ:uvMT۷K.?0~xkM|ҩp8;?^̆J-=HNN>}믿.][~g7lzT͛7o|Ջ'jppի,Y r`秦Λ7үZvȐ!;wLHH1(X- ,s^ȑ#< / ۷_tkm۶' e2(,,[hх A??ҹT*۶m+ճjOu:.]j0h"SO՘swb/ Y]+y5MPƯc5͑~:7)Zrnsvt_.7i!G:nABw`-;SME~܏ʂ0Rc8#]0R[y^Fk璼}M/\n cJraaa/rTTԄ zY|]ީSǏ;e'oL/扉{oczաCۖ\wIՊ_ر`K~~~ׯGF9snO>`0,ZhovRRRbb\n 3g|KJJo_W׮]nXnO8qҤIj?o{̙#j#R[ƏR[^z\9觟~{Wx/_ߋ[H?׷Ab<,^~~5k&L0i$PQQ?[믿ѣGnݤKJJo޶mۅ kG}k=05lth4uք Lc-Ȋkj?C*b˖-|ɮ]F)uݻo |Rʾ⋍7:|Mxx?oΞ={mٲk׮.'N8vXDDĬY322/^|q7=w~Kl2zhKX&s={9x$xz}ǎO>]LXDDXT7AAƎ+Jڵu\FUqc2'LZݏ=ZQQli׮'\jOV#79;"1 ڵ;wX,'O5jԞ={Ο?/ xMfyذa.{2|={x\I _ullY_ m %%%%F9sC=TUUo&%%ٳG\SOOO:tӃVnQQQ.9;v]Ξ=]vGYlYbb1cX<|vv~פI֬Y3xτzo*$$+''GڣҬϝvz x aX~qUlcǎn:th6l߾} [HIIq%==ϯ̎;FfSշLWjxO>䡇rӞ LU߻woݴ.]zW\>o߾[nȨk\c= >\lCEGGFzu+~I  S{Ru5y뭷?SG5jThhh(׮]5kQ5T}u{ķ*Ɣ ĴPCHQNFsΥ֘YINmש%qnR2Wvq*yqZM5Ajq>Ǐ=تU1DYYYZju׮]022*Wܹs5~TӉ|evg6m^W>`ɩhH9333޽{׿3 2L&o^|3h޽#F6miooqM2l66y7|366V&EDD}Yq!CӳM=3gΌOѣ@ʶnݪR?0@\ẽm۶y{{ ET*͛j׷Z:txŕ;t0p@Œ]ێ0//9 Q?{s1j@˥fjh8yrXVTjf~?ԩS{>HvT*UQQbT( ƍQQQ Ǐ.ƛ7o q.]nݺʩQQQ5βѱc \… G9rȱcǾ{Æ {7 Nnh4 .?~Fb yҥÇwm)^'N;}^Xց.]4""B&ٳ{ t%a jbZ7(++KRo>**yyNNt2{ QTTtD k|oTTTjj *jذaEZb$&&vSOk׮KՅvv]v>HΜ9<˘7:::'vWSc=tرR.9WHյ?0<<|̘1λ_:jӦM~{ٲe3fO?y~KѩM3+I5IPV*/qZMYh4Θ1モzt򂃃-KQ5֭[h4n:fDn'xַq…Ν;2Zw'Ntw p"he2Y6m1P7-FRY0AAAW\q>^^^ 9"QVVtڲ Z]>o''Vmժs"Jtʕ}&ǧz\ZZZZZrn޼YXX(zH??q:r8 ͭ["##k߿{V\Ⓔ^ .4s۷ Hk?h40d8AΝ;粵]Ν;WjZmpps qڪTV{S{p p8<(ӻwӧOgfKII޽A$5MgXE^_A2]vn+((r労•+W ())IMMۢ'NTVVY?88 Ν{^PU_&ڵkG|?e\*ΊVsƓz ]B꭮MS9iӦS:p@d;Ο?ӦM'N\z =yomb^ MI^()TxA8Sѣ43\&&gΜYbE``)Q㻺tRAr]N͖T*ۥK^_ٗJ.w&7d0L&fs.#HAՏjꫯ\lٲɊlٲo߾ׯ7$!g˫z/8 KaC6ׯ_ᥗ^ɓ'Pmۺ+***))9q# 9LWyоC4vzڵgϞ*J :xnh4Y̙3QQQK:ujbGUUU!p8o߾cǎW|饗A#D3:5/aj]v͞,'8ڐ'qjY, MH_]vR-Y>z]UUx L4sFh?Kj9}3bAɓ (((())rh#P(VR7,,^HNN0aBHHH6TGOl6:m`0effoŋz>,,L__:\zU.jJ| |6lؘ1cwl[t矏9~… H&888//ײ KJJ"""=?N3ƷͥKvޭhMf&iҥGVS(r\Dp#ӱcǦ^8K^qn޼f:9u/\b{qRT*]ƈj"z^PoAuq޽{bbbQQ8#4HqJ2M5ޥbmxx4zc(7gΜy'{ ^^^Lׯ67P(RSSGԫW/e2Y~lfddJy j{O*sXX`z3gv*eeff؞dffVUUc_FL <@VLLZ\{e IDATΝ+))իWKKKiq}S1117n˻zjݥfڵk=| R׮]RYY˗o&JKK/^ءCiA$%%iZƭa 6}ӗY9,Z:o7ol-^ dA?w.U/#? ߽{a-"###""ƚZBR 6￯!潷nJII ׿mܸ166.]?E۶m[AAHIIYhQm}''N&׭[ߊ[nZ}ߖod2ZxAAA5VžÇWTT8>`ҥ5|ڵ/Sw}z~֭c4?: `Ĉݻmܸq7n$ʎ9[oI5&vpfgg?pTUU%''ϝ;ɓ[ رXFl6o޼7p,((Xxqrrdj_~h~EFFFGGo۶ A>۶mE8}X.wh4.cXX,ws ň#v}z`0.ZmBBBRRҵkQxBBիW]Cu/Vkzzʕ+ /{xԵ1N::t7߼|xL+Wfggoo>jnٲG[nMTǏt:q%_vSyzD$|||wU={677דٿӧrKgϞ999gϞٳtlEE͛1^UUbzVպ{{GD alɓ)))үbpA7C׮]V={kzv=zkH2mڴuIqzy9x{{Kxm:W@sip񐐐PizYqT j3/^nݺ)S PUU^׏ի׮]~^t8k׮}|׮]9rd&|}}.\/ǔI&tٺuk-;66… /p̘1s̩ӮGo_Z<|i*͛7Ϲ'vc׮]IIIvgu=#B5`SNرcǎ 5ֹ Ê+ľ;uիWoܸqƍ:nܹQQQu_g}^ۼy͛_j۶e+Z/NpZ?~cǜkSNSLyd2رcҒ^z%"͜9U%_RRb2BBBA0`ٳg_8ӧO&L1 :sTTTNۼySO=%Ne4?=z4|gM6]tygYH EΝ゙={IiիYYYs#>>G}7fϞ/~uVvvvBB9n E~ꫮ]L)ҽ{zK׻4rănjbŊ He.))Yvm\\qS# 4hҤI<@ޖ^g_z8p%t1=zt[.++kɒ% QMtN:ٳgС#j/Zk׮;$FyիWwxq8ǎ۶mرcoQQQmڴYvOq8Zfٳ]f ݾ}{hhh ݻw{nifÛ7o~<|7޽m۶111ҳVII'|S>>’_`t xm:+KJJ^z饨9s47 !(p7}7JeppbuV@@ɓ|ALt?Z򪬬,..kDj|o}||oϛ7l6ݻwl$AWobDEE-Zh͚5r|l6jjС&L駟T t7X//GydҤIէ@^hÇ7m$N7֐E/B:o<1(T*Oϙ3G.Ϛ5o߾u:uXjw~~~[r壏>d2v|ᇍp8v})SHgfKOO_bg̘Q$h DEE7n "**^|Ub"$00pŊb'q~~^0amLj.))ӧ oݺe5+VFbZg͚ճg>Ac̘1.$/6z-[ٳGl{=f̘&,[裏m6[ii߿l2jMum+U*ULLKT ^{?|juQQQ߾}_|̼<Z^^O)q=gΜqCUUU~~~BB¢E;~xN&O N̙3v5ksĽpWݍGv uGu.p'@@P-Ae"@@P-A.$ɚei9cğIxBd2Hh 2Єdnwy 9brDL̐ef&xd2NjE v!% 1#rY APe2rss0m۶$+#dl6feUF~`15}aj뽖pDvU( eZ7((H`6-??0,,.)!vSDdbQ{PlQ22ꍠ ]\R2@cQ(AAAu[\K6b[#XL'/ M]ԻGiB߭1Z._%ysMEЂ Ј lUUUԟA %SgAPTUUeZmb"(LJrH#<)z+bX,6n2yĠLUUUsn (#*)CP2]sD !(MpY.@Le h,$#(MIU E ( Z2hE ( ZesGiiivvveeOvzL&VZvƷ{yyi4hZ\.u2b@S~zbblѣGsgiii;w۷osh#%%sss<=^!''g%%%5nd̙>oqQ׶mە+W5V0˗+**l6[NNlnȦF}6mTVVXV-rIPPPpi~В1 JDDDvvln׮FiȦJJJ\Rlݪltf}LgeeGGGWf;x`^^# RZZZnnFpdgg=z4777--->>^Zp*wA .Vg̘{Ŭy慄L:{h45eׯw^Ҷmە+WJ1kțh~zQT h>}jժeeN7jԨh\n0RKZZZEEEHH$Vׯ_ttnpfsy8fLNNNd̔ TPDGGO._|yS*jСLUs2l6߁}\o8qRƍoo~CI#$@Ĉ2L&s8ջǙ򪨨(++eb)((山.S,EEEeeeL&N'-?Snn]F)***//mݺ@Zm4M&*ĴiFU6pgx{{O>۲e˶mJKK .]ώ;eNgyfԨQKĩlN (n_x155+rRi1zJFFO?0<|-ݮRZnݫW H.2) ;3K^t:]jjjyyyeAӧOPPЩSJKKvB MHH r常8A=z9WVP(|||bbb:v;4]ٳfYZh;uT]Feu}.Ѝ7A ieddСCFٚl޹sgaak'W6bż<\nՈhZ r___Lvg>I<\N IDAT!B{|||t:ZV(r׽ShzWwQ62s]O2hEP6whƎܥ?F@@P-Ae"@@P-Ah2L?U @d2LPkAJ 8Bb?$4Ah*bC.WA 2uSZ | Uqr9Y l]H$H.&9wDH.گZ{cD&} @C0 ]KHl]!^P4BPT~ې`ZK!Yy$\ AP (@B !R[۲eKkp 2Ν;v pij;; AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$ABjk"^+++cXrrrvhJP 4fM"mڴiz… o!Cƍ^֭[w-?C:tjjjBYYYz$ٳgOZjU,kONNѣǏ~C~e*Gy뭷B<'|p>|^{-''ݻwkW:&O/oٲq0==mF#x|ĉOYTTBHMM/䒴=555EEEO>7tBS:wܷo)S/[lĈ-X… rrrZpݙ6mʕ+C7=Cp'??__ /?avvv4~;bؼy֬YsI'EN>89AҥKgΜ_{\rI}SҢ'x"͜9O?:tK_0лw /?裏 lr]]](555;;M+++cXrrrvVTT̞=;9c222vEEE_|E۶m 233͔!;nớVWWv?<99{{l;v[/LMM-((ر{X1s'6]СCǏTRR҈#&M|򊊊Siiiw̙3+**Mֻw#b38544L:5jӻws=w;oƌ-6mڠAvξL>=v 4 ??߸qc~~~ .BC 7n\Ǻun喲zC~D=o]wرcݢw룑38nʗzk׆dJKKy晩S6BʺK/xaa /0{욚:vϣFj}&Lд޽{tM={l:3))i[Դifw[לr)k׮={^ڱc]NJrkATQQlٲxĈ{ד}-:^bEYYY eee!ׯm۶gyftɒ%^(7y䑷zi%͝;׿ެ3o޼(Bq/_lM!TVV?n|ӛdB۶m'|i߾Ǐ߬+W~WޛEfRqqq v86vӧϞ'7999m߾}YbE8S:wB83^|ŪSs9{h:ut뭷=S?;;̘1#pꩧ^~})))yWMV\9mڴwXi/HuС{Ζw+--n}W 8z/Reeŋ~n SRR9{ <.:9M6=~,8q;uthŋ/^ܴ7x7ި/--4i_H '- 0wߍb+V_degg0`@U{~Ovdʕׯ߸q ;(O>w}w/ӟvg !,_m۶{_69svގ{UUUVÄK|͛7ϛ7/wꩧFYYYC !TTTL6@ }{QJ&Bؾ}{؝BOٳgG_|qcJ&ҿs=7P[[;cƌO-ڵڵk!544S.1%7ph򪪪=\[__Ν;7p'yNWm8L-ZTRRB0`@.]BOeIQ?رcэ۷G{MO_>Ю]IIIׯ_taÆ]_WWu*++ۧj9999ݺuk6RUUv)O4iĉ!_;o'uʷ f)))QˤV]]Fڵv|ڵ(**ZdɈ#}qqs1cꚚBVVVj֭[˨:uoYXXX__0j{ڵС~\~z*eeep z̴Xprrrڶm[VVVVVVRRңG=L.++/Nu-]4:~W^y啝bg6lXJJJ>A+6ʴk֬o45Ҿ}m񒒒(ys|c=VSSum5vYJJJjhhh` AYnnnB{=.]=ӡCAsVTT|͛y?~o k :tЮ](TVVCE)뮻_~Q[kΜ9TCx<?C555999vg555[n !D 98kN[fMᣏ>3fLNN.gVTT̟??:۷o㴲>(|UWٳم_~s=W\\\RRhѢ2_~eCCC/BƦTX,'%%5HOO!m߾sͮݶm[tЩSªUVXBկ~{9LO{kjjrssoX,sp8Kn8:묨ʕ+|x;a„fjkk׿>SQc}N544L:5߿]b!K[.6nEp-_|ڴi{./شiӾ=꾈REUUU5=q9$''^{Sx|޼ys !dgg1"ԩSt7mS}]>~dνIɄ&:w,(A%\jj㢢X,O C i߾}aǎs̩&kv !| ,!$''uY4ӭ[^z-Xb'tRѣǂ b?bŊ~544,\pɍwl&;;8=۶m˫LNN9rd ݻYf֭6lرcӳ0cƌYf=z駟0{Sׇ?޽{Grrr6mO^y999k׮}f͚,tXyE)Buu3e2mڴׯkkk-Zl̟;v*++_{^{鄳:?qj'p/O>}3G8L{?UVV.%"K. !dgggn K cǎ?pxG$55u٦)9sDΠALEEE+Vz7nݻC=tWkwK|MoOwk^`AK!zo;w_5-)%%ꫯꪫӿz ܹg6nܸ|B^uv'颋.j}0iҤ.h˖-!]*ZP<///68INNjiqUUUў%{hZdvvv6mZGy䭷JKK{ J***TX,O/|?{6a„z*p7~}o߾\pAaڴiK.mr x|ʔ)ׯONN+Z1JPĕzWٳ^j튎EEE'N !\|ŶȢ -//﮻*** !k Dcؿۿifwͪ$֮ȐtI'v^ !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A C IDAT AP (@B !eH2$A ! 駟~!3)//'|ۅ矏R2ɝ;w?c 999 hٛ2|M7Ԙo߾}4z[ou֬Yѩ?e˖E)… xӳK. !dffv<G;pJKKkӦMee9sFlBuuu'++vwK]wucǎ !M6?ߏbo9tT__?1_s5^xazzztj]v  zw>]w5gΜ|GZ}7hР}]زeˣ>ZVVB(((B֭{G-ZTSS__w_AAAKKK7nts%K4~|򊊊m6wƎ2:w}4rHp2bXnzB9sfUUUӳg! :477w_h:mݺ5ydff5H6f6l_뷻iݻw:.6^:wܽ{BqqqH… cXnnn߾}tBXhQc >,}@h5hРBaa뛞Zrˣaux<|[T[n--- !DܴԲ5kDp m۶ݴ={F֭233̖-[\ti!??K.ф˗DvرvB;w| # GTTT|駍x|ܹ͛;Ң N())$999mڴiePEE:Q(Ю]=?]׮] ))O!b(BزeK۷offf4(<ٳg 8ҥvptڵO>s̙={1cۣKgyv7i4cƌM6bUVEÇ۷Jm̅t`v?s΃w߾&nmݺ:z={+))jjj N;.\xYg%%%5a߿ G3gNQQQqq'BXlYqqqvvfŋ/^QT{qtRRRv;v{ ,!wqQ܂˗WTTdddZ*pth֯_?Onhh={v,իWnu۟~N:餤3???:nhh8L2W]uU޽ c_޽{t^]UUUt8-++o߾/޲eƍ۴ibŊI'B4hМ9s7lЩS((ӽ{;ke8uSNϟ_VVV]]mF2|̽Y;vA.3dgggffVUU&ݗN9}KAqqqUUUkguuuQ%4D?a„X,jժԨրRRRK^ʕ+nBի1spHn JKK;3Ck׮]z… 7mڔ"ԩS–-[ [֭[hիWoذawӶoezl4sG^699O?HOJPܩW[[;cƌ>(p)tܹt!JubW_}+jIݺu߿bĉ;ωSLY~}o߾={lz677 nݺhOݻ7M?~sͶAr]t0`@7ߜ5kVaȑiii]#))颋.]Yh]fe!@;6==={˵M'S!$''[j+##o߾!˗۴SRRR4Zjg}BϏ'4JmJII6lػmdҭ[O>nƌ6my /<paAA~'x"M2eܹ~SRRVX1|Co̙3`zzeҩz˱X駟lcƌkRSw)Q(˶JQ{BZZiOQE%~!$''uY)))]Ѯ~߳>;jTT.]FմБ%55+ӥ}ٿﯿ<Ҿ}N8!:ݻwǎMh vԩ#^E]5`ҤI]і-[B;wnBh}x"j}VP͞]vIII]p[wvv&IIIm۶m*8li@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !W/xkAxUUUo 555qm</0Yl)++]L'Z~?jD#(oٷrKYYYk 555[o9֭7n܎;\s5~G+{f;v7nݺuoϟ7o^kj,XpE= >=os(S[[{w;vŊ^֭k/ɓ'%eee]wW_uփZ[K7o~g-Zt_~-vE\8Al|-[V^}KԼsL֭ۋ/y뭷n+Vx%%%]][ro~|˟zoq퇦g̘B8sSRRpG/^x'x'|ziӦebŊEEEÇ;퍽G`go۶m۶m~˕ȶf͚͛7=zԩFUUիwu|ƍ玲DÒ%K>裺=O~JJJϟ߷oߓN:222(v}v9`ܹ_|y睗vugdees9_9aƌsNRR.oo?E>#Gvرcǎ#GE\._<6mi6zƍڶm***Zvy=S999]-O>Y~e]y8┕M6gϞ;޽O>7n8gΜCMkN0aBKU@KV^^lٲk[n9(3y 6\};_?=ztyy/{UTT?K8묳^fΜϯ^&77.Kq^!///+++ʛrUUUM:[ngqƁOHdɒ:<3p3gܾ}HP#ؒ%Kڴiӽ{#F\pAJeeeyy.////o߾}!33{^^|͛n)Kpzzz̆?7tS.]RSS7l0~n;Y/k׮]֯_!^]v !TWWu]si?iҤI&5~9dȐqeddk?xxYYe]eNN΃>أG¼yn3fo1q͛7k.;vlL@UU+2y͛7'''/>|.-{N=__bŊK^r%yyy=t„ ;FqUWu9:_xgygϞwuW x?޻wᄏcǎ &Mo޼9ueMaK{mMr뭷'ӥKQF]zm۶&Z[n9묳.|pw{CJJʭ:dȐ]СÛڛ"ظqC=cǎ~z…uuu W*,++kƎ痳V~㥧+8ӛ~ ,dɒ;vg84hP1c~_?#{lRRĉ^xaj":;eʔW^y8YTpӑfO?ɓZ/oݺ8ׯ_6l!fܻwc硺Ȉi߾}3L+WOA~aVt: fʔ)׮]ۻwO<ѳgOɴf3D"iӦ 1ڵJ+Wܳgl:u-ZT}l6Jի^/;O~~~~~~ g,::://rT*H$ݻwwqqqF5D"azҥKOFP5ի%%%={m?46z{QZZZwOHz%K*?V#""j˟`:u}O>dРA!!![X}*%޽[ZZ*0`۷˗\]vcƌѣnt˗/3F,ؔޫɊr|„ qqq׮]tgϮrm۶EGGgggW3Jd 899ռL&/]m2bziիW dXRRRA ++յBXD+#cg)\8ãŽEJJJ~W2LTTTTWFȑ#UvX)))˫򦻹[أ<6ٳ[lIHH.\"ɪ7o6۹sC222..n޽{www''gݺuEfff?Yf6rIJJ|o^T*/^X{j~1cƼb۷d2koooOO .T͛111-[YH"...--e˖u@V*P-[,))9|c=wkN|^rd2>3pYYYdbxƍ+VTY^  }]||}9պqƸ:,g2A8qb``=WTv5qqqÇ;wO?t~i֬Yd@BP3g 0^[`lRP?^|t .\p#:ݻ}Ν;;99:tÕ+WN>=hР߈z)}7 T*G]u^Y%A޽el6۝;wVXqŮ]vQP\\r4&&&++˫w̥;w=vǫ\l6=՜iMug֭VsXaAA'>3?uڵk׿Tw?ظqVk2㏒͖f͚j꼯̂AV?34|p77ZӲe˶m?~|ҥ)))b,˖-ۺukaaaRR+*.^'$$͛7Ɗc7o޵kWyyj###333_f=ոvZzzzDDĽHOO/..+VF[GsΙAݫU߾}u:ݞ={֮]+L&w}ewww5gOrr+Wl6[iiS~g:ΦP(=w?hϟ_pafff@ ^kѣZݻՊRoDGGf͖_/Yn^啗9s֭[FW(AAA#""N8ѯ_?Rb0֭[7c [-[i^^^{Ilt:khѢC}^|{!nld05//RK/gW_}%_l`#GTaEiӦZjBBBBBB]xZm-RRR'8p௿z &؇ vr ??!ClڴiҥK.A*/qwwG._|Ϟ=G)srr ?w&AZhQa~.jx/:wL:q͛2dܸq QDP̧zhBxx}[nVgϞ?sbH%;;C?uܦMtիW+Jhժ7m4uT\VRR׹se˖|:oڴiܹb ٿC}w}w۶mȨfȑ#=<<6mڔx k˥R# Z~O8Q^^ڷoqUJDP=:..CO=TeeeT*դj8L={FEEm۶ѣRyz1b fyŊׯm۶9rMVZղef͚diӦر#''GR j{55TOTʈ>sO<<^ W'nlԧOj^/Vڵッv_K/ݫÊ dȑ6lX,Zv>nnn|7|mX\]]R;-[\pի^*J{K/޽5%p_5+ݻr~Fh6nܘ.˃'LЦMya ww| dddlذի'+{Ç7v *e+V>ڱlw V[=;̟?n](DZZܹs_fͪɲlk֬ٺu~ح[xɒ%O~Xٸ K^ڵkʕݻwwu:xf={622O>o9み2.BV$j ˫$IMGHtttNNΔ)SOp9YYYAAA[nZ vX UGԟ^[ad=˯^*>ZbD6ꎠ IJr̘16l׿5nܸ/233qLBBK53^|w2SgϞׯ_7Lޕ (++^aKPPPjjk׮KIII"JӍ5jȑnꫯ+Æ ۴x{{;99ӳg srr#z^PTjć7o{nV͛Fw۷q|}}.@"(GD"駟zxxC{{w^j5)ޓ833rp>}6lX^v9{lwwI&+ꀠ C ٿO`N:u_QPKr//lww':sɒ%֭{\]]rssV9s5k&D"=z'|b2 T*ɓ\KQQoaÆ/`0DDD|Ǯ#?+VDEE+ʂ޽{Ϝ9STzʕ2///T*KJ >|xcjaϞ=]ѝ;wAR$,ifj-,,,//:rfL&z111;vXpZ/f*/ZS uGetJP z/]Z-  A4 e$jDRI$.ԑ  ;vرccWꎎ2hI (& 2hI7v@eX fb)---++kd2BPTYR5p4$2@#+,,l2ʊF۷Z`0 [ РFcfffqq 6M"\7^^^z^έ[rssk|6 IDAT]qqqjjoì@ (4Ҵ4q%Db:Y(J[֒\PP`2ă/?_PZ4 2@+--MNN[AyxxTj^(rFX,ٹ &''<]KKK+..[}JJ$i͘{A~ݺuKqI냃5Mfh4z>a}+,,uV,&l6ۻ.@=!(#јk|ڀ1+0rssFc-o 92@=w$''{05Lc &&f ,y 4Ae'1=||| Sqqq^^^,ļ;vjBU 2@}qLi4;Fqss{sVL}cccy%&&Z,ƮxLb),,{U6@_BfKNNn*LJ O H$At:JUT*N3LBNb]vĈ...k֬9|b0`;#n͖?:ubL0aJ>fu֞={N>}mA'NثW oNvĉM6%&&7nTrӧV~ȑ#3gΔ ٳ'--M*VYFjj~o ߿ox8_3u*jwؑRz1m4???٘>>>NNN hڹspΝ͛7=z4??_*zzzveԨQ xl\ 9;;LߑIˏ;駟ϛ6m1cFCɓ#f 첳ᴴ4ڿǻWX͛ׯ),,sE*:~ۣG ޸qc޼y{~7;wߥ)Ç,̙fW_}W#F={l6lX,Zvƍ ͖cȐ!L&Jݻᇙ3gΝ;WR{7j8IaauBCC?cvҥ/2;;{ԨQVkTTO޹sg\}KVpuutO;vlJJJ=:`fee-Yy .Z.\Xv۷?Z999ײX,M_*__L9sɞ+WFGGϟ?q 6ZHoo}͛7o޼yaaa ow]lٰa:w,T*j $&&.^xWvssX,YYYN}vHHHuhd[n@-L4K@=*,,/̴Z͚5S(V^^^MH"TNĩS.]Թso߾H'Nn&jYfYYY6h4;vl6wE*[͝;ȑ#b֡8!!޽8LLx,Z())T2qi__߈ o֭[};p\.[~hleeeiiizv߳(..޿ݻwuZnݳgOYܹ?LOO/++ʕ+{p ucZ?3~jRi&$$[nѢE]tյW^?J )ʈvt:T*ɚ7ocǎ޽{/'GnZ.KRN׵k۷o޽'l׮] RӁzUtS%%%_|񅿿o&Jr_W\\l[PPԩS;w,b"##_~YR|͈F#&T:A߿/y…O>ɓgΜݻ8ga0֭['^|={f͚UW(++kΝaܹb ؋/,*+..vuu}'*/++矟~`z~СG۷Lebh#++KlTRR믿3qD"߿޽{0((l6_~cǎ㏶mۊo+W$''+C:n„ Wpssf*܋N{+Ԡh6L7nHJJ #{k׮Wv֭UVM ObCiq/:y'U寽u:R:t^OMMwt2ͻwh4sԩ$#,,lΜ9jȑ#`X`A߾}>UVb>&%%bPZZsYf :TFVW]QHH_|jv;vy󼼼$IXXؔ)SϞ=+nS!TPP-`Ϣ[nӧO&Rz2׮]7uwޭ~7.'''///ü*ۚzjrvvիɓ'"={6""B̋\x}R8 Z}֭jF^|}}+(++VnZ*]vݷo_JJJ  2iҤ#G{` 2q̔6m:=))d2 {M.A:ƍ͚52Q%Z;;Ռ8qc8e˖&##b<ݽ>ӧhѢTj%??_"({L|l6tΝlAJJJ_XXXR\HV;Fjԭ[+VgzS@@tfff```GT:;;'''WhHSCCܛI/͛7܁j޼yUÍF;ӷoq] 2@d+VXz21Rg:|A3gΜ5jTVh7sCedd9 MyRT(FTV,jBϙ3g'O>^媬رc{;X, $;Jѐ$2gff?~СCUӧ}g6lX^v9{lwwI&7^l P/ =(SRRRAz*d<,2{wL&k6HϢW^}ԩ;vo۶mǎ#FxWU*U/B3J'TjFEE]z> _󼼼>qy )>|{ "##͛/BzmYY'ןL&SՓ&M;vc0N:eʔK.Yf ,}Xu2@PTfYBff^M׷if…b*ڵ+''Co}+ۇIRT)d?^PPp)((jbPgQ[JO>}X,ǎ[z={ڷo?pD%l>(zܹEU2Gd*]5i6e˖ /=fħd2[zzz/,..0`}ꬨIfsnnNAP$U$44?Z~ܹs+܋ OɘpTWPqA<==7o~jLxA&Nho^R!~啙ymį^3_SNհfzPWJ N>h4V~[޽->J l#[F222+OMMl^^^ð˗/?ϯ\.g ֜㇠ P/&d2Ӷ/d2 Ot޽{NXִu۷OT*&"ffeee}K,0[Νf 233VѣG,X`4ŐDe׮] 6nxlsjh4]_mժU>}juv!!! ޽̙3?~W\lw9sJ Jjپ}l{cju%D2z˗/|?^ڵk>{gg|W ,hѢXFjj[lY"""✝gqƤS?z76le64L& /n${a#III+W2d}`0ؿq5 w"ٜ'Fh4 .޶m[bbjj!!!#Gѣ}ȑ#5ƍrypp ڴi3o|xcjrSF!77 bZCCC$2@j D\fsbb~kE6B}Y={tޝa%'''_Rj8b͵iMj}Td2]rET۷/%%e]GA)BA$ftFTj悂$_ekZBkjj֭]EUTTtȑnݺ}ᇮ]=T*77$Ço@-ٳK@=s ͚5kBP_nݺ͍<;* p|}}u:]ff fyZ ~jK7Z4$2@z>/////O܉ %j `heF Q,KAAlX,eee \L&S(*J8;;T.DPh4*l F 2hI (& 2hI (& 2h]tY,lXJKKLP(T*FqvvVT \;8~f̒I& @a,&R7 B@V-ץ غXWk[H&DIH IfL2;w: a2`>?|͜y9 /|= 555555V5enTWWw9Vު2@ljj w!-ZV:11`0:A={l6Khjj*++3DP NZK:Nk4R)r86btV fn(/CP9yԩ喌Fc||Jjr\.k4x^]]ݙ iVSzIVpmW^^JOOONNn=%@R%''GEEƖ555w)2@h={%лwoFӾ4M޽ CUwiVٳ;uMMM6\.WIIɁ˝Ng'@P:Œ CjjOڙY\WWiZ}7|rvLIIٳW\K9r bB A *++}:$%#JMM5QVV6w)S憻GYYs=wqo<W~x㍬0V"_|şp\eRSS{Աwhjj+--Zw??^kXQBLL̐!C}[*++v%I kQAAAAAAL:hРLˡt},1ch4FclJKKjƍS-9z޽{Ng'ֱO'''h+3AR\\JTeee=#JCf4'wn[VP%>>l6bVnWT`]6Uva?o IDATAxSN\ѸOB  :.DJ,K(&o ʠ p7~'۶m3L111?OgΜhL&G}kZ&N8mڴFJ^>ϷZ111?Of̘/[XX=znwn6mZW_}KMM=|7|ߚLdiiisOby嗧O޷o[PP?{'%%eڴi7|sV.6-777''BR >޽i~~|PXXxAۍV]pajj .\ذa_][[+J:y޽{_,SPP?I[oM4I~G_}Umm\.fϞݿͳy憆`O{rrm0nn뮻~7***-[|-[:up۷O>)..v\111s`hխ[nΜ9 oxzCBf^HwZPzG};lC?~$I~~SO=5o޼)S;v78qDTTɓg͚蔔ۻwo<dr\ dddfff~gW*r^O|Ƌb?(//_o߾z?TUUdɒ˗w֭qǎO>O>9dA ~ժUwqAJZV,c'N\fhUUUyyyΝȸصfXEEŞ={v=}_111%//oҥ>#Z_~_.y~v111wzfBv~ӦM/B.]xNZW_͚5O>QQQ6lx/^_|mm+vyrymmmaa}k׮5kL6M<獍fٗ{WLvژڝ;wΟ?ٷ~rL&x<[l9p+ܧ~oVt2@H?hѱB:y RRR -~xgΜO׬Y#>~Z~3g1v*))YhQeej~wK. epm۶gee?-[#G}РA+V۷o~P{YdIBBg?D"i>@Gt:}ҁB:y nS1{l,֬Ynݺ^xAR޽^z};wܹsIOV-[&7o &MgٲeЀb_Z]]}wܹ?kjj^u멫?jժp &)''vc۷qdqW\iӦ}(n͛kjj~_oڴiΝ۷o_n݄ Ҏ{_ZZZ8zGӧSNoNM&N=H$Æ KHHo-^w7xcRVVV^.IT>/ {KKK333[N0aB@hIL>}JJJJKKÇx<'NhB˥h:7ߜ1==֗  ڵչsnvJA }[JJJ _ ۽e˖[ncǎn!`{߾}Gm6_뮻H$'O` (/!B;/? zV[]]-Vo'L+~ZQQT*u5z%KC"\6L/G[xN'`xƏ?ڵĉU*̙3~9-JOOdȐݻw5t:M&Z4h;=zx{sҊt,HLL hX"HR( f";;wJȑ#=*f+,,OKHVۣG}Gr)}m~4\.o%O׮Қ'{$η~FW]]]TTⱈVGݼD"1bDC-j:`5P#( t:VZcÇ{<}o߾)Ҷ>WT6={ڗZT*m_"ˍFc&$$9sf p8<ŲmuBBBzzz~~oKSS޽{njb^AP4jkjjķgΜ֭[}}}u\gΜiQFM2'xWΜ9n%B{b ڵp+NgUUU~~8NF={rZVJucn;΋~zj^=OJeg6t{ DL v\}> ZRϝ;p8A8~xf`t+&c=c]f-b#g}6dF*BMMM]]w}7ydC"̜9d2Ξ4iҐ!CZ^y啭[zժUm-jjjco`x<ܵD"3f̆ &L FIۺBSSSee={3撳q#Gܴic=w=5Bw_F`)Sl޼oٻw޽{322y}+t!:.`m&w'Tvi3'OA믇 jLV瞩S^5j?PSSsĉ={^+ϟm+A r%o5:\+'k'\.o[R}}}EEEjH4hP^̙3[ b1Az#G.6@'??_Çu&fAJbGzL2@HzkbCnXB1s*xAܹ`РA :v۶m ]ٳR9.\y744߿ʕ͟?~۶mv}<[ 9]vr|ɾ,ѣM&?'t\v֣[rClX,kh@;*JBTWW'''w^;|΋j[#Ik={ddd vAR"&rrrV^e▔UVϙo߾+W\R8l0ߚG]totҜiSRRZ/_|oR묬,D"뮻?~g}6 rɣx<_/~uDF:ze,Xн{wA^Ç_z饻;--72+++77^~'f^oqqڵkM־ud2٨Q6m$B~ʸq=Ǐ/fVl6{WZZ:{l_w裏.^qf+,,ٳx2,##c޽?O|-I,˗_~9rxqB[ZZ׿n ]vv͛7nLL>oݻwjS(ƍ{뭷233322|{?yniZxjB#M7T\\lٲt;w.99Y&o/~J${+V\_ znZz3<ӣGq_eeeJZZZxB%66l6vlcq=uHt:V߿tt Z6++@նKMjO>$77-1bڵk?䓯V*vuȑ&Mڵĉ?Ù3g&%%]lJRljg̘ѻwoc4͢E'\.Vqw>GSOm޼9??_Ouo뮻}:\.3g{1Tfsbb?<|pV~QQQ*]Ϸ_~e |dw}bcc=d7nܟwӟ^}յk)ʆѣG7 0aBAA/KvMv-k׮u݉R*&&f֬YzkHW%k3 ,Xb?񏄄YSS3~{wiwmFYpaTTZ6LFz'jkk:\ BQUUR.]*^5߱<nh4666 &<#2Ḻǿh4644T*;KQfN7o^׮]ׯ_߾m~L<ݧ$'N w nB.\K..H\UGv7ThQqqqcccN Z-6r@뚚,Yr˗ggg+뭯x<*Z F]] /0w`:={NƙNk=o\.t-&]l6nz-C}9?_;0RV5|i}Z\TiX\̀:5O@;_eJLL,++_766k]NKi5O" `F Gѣ ABdc`Fj4,D" uk`s~v~䵸it*  p\ hf.GYYY]]$πNr7~x- BB+99Ylfي;3%j;mw@fee+KII9uTSSɓF1>>^R9n6!+j:%%3t"j2O>Ep~i!P(zY^^nZ}fltz^(J<].l sZmJJB^je]_iig}P(}n!/T*(HBWەJI&N@lݺ5% .\ B.]]Bٳ݌F#+._eΓ*++}0]jubb`w!t02@2 %+V w!A .?#J-bb ‚ >MMMfj|DFZ x<{ cRT* + (LSSSyy9O끎z-jMII2vݒSGHM.W-ړyco=3e2 deep1^7:::!!A| rzٜJ +%"h~'s]'n#+GPW*)dz%֒v8mkG xG IDAT0n?q-Wz mk,s_V*Y.h)?(ߊf^n);WR2\q/ ^v,e /_pQ]"%'E&v= :A]2N3$vo3R2deO .2. At22@^Wʄ\Ĕ A@ 2 DFe5qjc2e "@D (@P "<[[[[VV(JSRR ErZA.tFRVȭOhDf644߽{wZ NbPG?**JVsb1/T*F|뭯x<̭&pm (Hwĉ{o=zt>}.6w]wСCC_`H(77joJz{G)fAv/x6lآE|ͭ[GbŊ^zol}rUVƊo^cV^]TTP|ff>د_>SO |l}YɔdZ!߁'%%XK.kkkϟ_^^>o޼S~9}fseeelS!N}>gvԜ'Ot:Nɓ5'p^]x 6orp\R2 x<#G<7ozVUUK/L $$$ 6\5 (HקOnfs{q)ݞ9UʜNgΙ^YY)i˔K/A͙3'--MV\?~<33:|RQ.T|Wourss-ZԣGqR*/_p8VZuС^x`0-ݺu|xT*ս{mP]]qO?r]611qĈ ټ|'O ;Cn>ێ;ƍ7hРw \mӵk3f˕NDW .v]*72es\.ڵ)SLw ` h4ﭘ,H$ѾZ$HzmSSBA& =s?O1%Gaz/]nEml/rAAJZ`AVVV_.]Vbyw^xNxK+®]***A4iҝwyŮt1bkdgg7N'O=zb\~RtΜ9-Nn;ȑ#;w r'ZEG\gvDb0233ӥҶϞ=_aa{}ԩC׋%ҲWH9q޽{ 0tмӧO;NBѧOC]+1=zhT$4n[*d6 h՚'N0a\~utR9bĈS>r|_|G|~ӦMA뮻&Mt!3y>hȐ!iii3!pU eEEE~iIIn~wV5t;owU[[xA?0[l)..v: 8cǎYo?쳪*-B %''N?uMkAHLLlqťϋ.[n1bٳ[ y:n̙Rd2} \Er%$$ 6,11vWUUc{W|{>}P& 2d… 3L7x`,H,**:{lCC^ӧ륡a۶mNnjGoN矛+/0˳CVx>Cf.epmZ2_~bJl:J]tZLLL}oU*UTTTW8l JGxހ14t+J:CK Jbh4͗UJrK}}8cǎ)S\T{9cƌիWl޼k A\vR>(t:΍76CFZw >|xX,.KgzPSTb… MMMQQQoذAñjժCuBa0fF._v{SS`hqXMMM}}p3gn2***̌j!K$[ouϞ=Gٲeȑ#C^ bI]bSHitl1qԩjvBFhVR&ܹsv̙:AҚg\222ry.]z!RYWWfQEtORY,{fuՂ26EGG+ ب;s bĉSÇyLRD"ijjr8*ʷ'.F0d2ܹZ2lȐ!;wt8;wlDiǎ (Vf6lĉsrr9aÆ< 0a͛]vĔwU26jNp8N8;.\ؙ%u\;mU*n7;gΜ9rH'oaƍv3]za׮];vn߾=??_ӧrtA6nXPPQEӧ'%%y< 6:uf tIPUWW믿>66TVVfggBѣGuu-KFFVx<555EEEcرVVkZv!N'& BGǟ:uСCх].W={Μ9_/]NڣGA^ɓ'[fJF^߯_?LAҥvW_=x̙3N>sCi4gKHHx^xݾf͚KF>.]̚5kժU,DP׬~L2(agϞ߲w޽{ `4ovqǏ;~%**ʷҀ*++:t!;BѧOTSSyfѣGޔ v555~8LT5o!H$&Mr\oڷo߾}nqq%K66lѢEa ,oba…{fg~aQQΝ뿜رc,kL&;vR4&&fĈh'O~o1&&F*;7xcӧϸq|sZbb bcc%T*֭ۤIu Fw}o9vX1OՎ1bĉ ",]D"?~ڵkNj?5k֛olӦMׯ }lSPտ/ CGM\-$'N w nB.\K..v AI@_VFFqj644~fANw~I^ou:iX,.KJuɵ"==A(^/^jJL&J,BXz p%H$WuDUڇ "@D (@P "AD2e "D' c2H$D"(Ղ px){k@PH2HRg HV$| #R)Y< 5qRT*0jF . W?¨K :*#>Uu.v?L&S(^Z4`Gv _^ B&eNCG\eZbJHHd.*AZD"z ARRl]{1߸\.# x d_\.WTjZR)J1(x *c4Vk}}}}}}kh4^K$LTT bhvnK8AKRĎ2G. 2j:%%l6[VO"hZѨV}[|eK8NR2hܕR)v!(GPWZ*koY1ӠP(nYO?#+NFP2b/KɄ:\}|YB %p (ʈ/ĬS27@X@Ƞ'cH \ZK3|.Ž 76H] "@D (@P "AD2e "@D (@P "AD2e "@D (@P "AD2e "@D (@P "AD2e "@D (@P "AD2e "@D (@P "AD2e "@D (@P "AD2޽VY~(Znj7Cq&?P"1Թ:uF Necٖ,ӡ1u0 eq2Me)e┢*zrr3J[=y:9G @$e  2DPH B"A(@$eH, !d2}[7seH2 !je2LkkkxC=W\\N۷o߾}[,+..O#eee(HX,֯_B~DRXXXZZ+N@(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH tQz{et' (}MX e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(wIDAT@$e  2DPH B"A(@$e  =>&ٳ?O><3JJJbXԖ}P ,Z3 >Ç=zȑL$N˗/߼ys:>ұd2YYY9eʔ &$[Ko! \pl6~K_[[Og2 7po bW]uCtGݹsg'͟%]}gJf֬Y_JPfh֭nkkoB5kֱ)9Do3fLaǎՒ裏޺̙cРAsN!ɼG:?K:)J-^_!\r%z ; .ͮ[-p0>{/PTT4f̘$;6y˖-{%J-ZnݺBUU՝wީ%8m߾W_ ! >|ʔ)G:V__B2dÏӘN;mРA!]vJR/U2 .T@>Ț_޽;_s5~NRСCKJJӞ\ԔK:N,YjժBUU= 9 )n޼~YMŎt~ǎeeeEEEiU2,++ !R#=%t}ҥ/R .X`kz]vmܸC &MA%> 48+((߿s]]]eee>/9%K!$ٳg<zPst|O>inn>3g^~y%P{{{Àej KKKʒt%s'|rzPhoo?θK/~D7dȐ^yd-9ӕѣ z;v< .L" @H$e2LW^zu26mڸqQ_>]L8;ljj___kZj ]8S/^\VVl67xwYSSSSSsgvmwX< x|ڴisM&%%%Ͽ{c=vYg3gGX,6p;nxꩧy\u]˗/fGT*ufCCKrr7hkk'г20jԨ\}777@YYY@ccc}}qGC9.Ku]7r¦M|cZ e"a!t:<묳BT>o|0gIW 6lΜ9d2r 6=E(@!ϛ6mjkk[ٳ'cn3gN!N?Cuuu=1Ǝ{ƍo!/| }bIWb)STUU,YJzc!T*Ϝ~\pAk6s⋇ 'tCqqܹsBXn/ܳnDBcccKKKaРAeee=STTtW֬Y[]6PZZzebϒ1bdaҥ6l[ M6垡rgرcǍBg{Tjٲe!I&umG{*++NBHK.mhh8N7:O(pkii},,,<~}_ϕ4ʊ+AlvժUZꫯ.((8U{č78f̘¶m۞x≞j}nʕ+nB8묳.袎Ϗ=nL[~1}͚5=X&I&;;ym,鞒y 2$+\8* ,OzgBx|CX,vWL<9N5kti.Fgt:0u_?Kmȑ]w]YlYmmpSH F. |~߿jժ={N2]WSO={ό3xt"?~?ԤRs=w6}ݻ|{.w3gΝ;7Hto!_w !Lwؑ38cСTcO'tMaa#A=>B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$m ʹjuIENDB`flask-openapi3-4.1.0/docs/images/openapi-redoc.png000066400000000000000000004771201475153525300220050ustar00rootroot00000000000000PNG  IHDR pl pHYsetEXtSoftwareSnipaste] IDATx{\w?$ *AYYVlZpHK[]eݭunQZoz*V㥶V,q/J5A.$1C $>lHf>kB? fe&l:dPtTTTTdXY ѩVgA.rL.LfOҧ#ϷpwC}kζ2銈qj w8 M >}Xe GVS@`lPw'Ogu> zQI=n1Mػ ~'Aͺ[aޝk>nUG1^tn{e15_v8/#G JD3ӆ KJ`f2L#G%@858H.SOD8de Oɰ,˲l2r J4(n`\""!ad2"2.>c,,k6FȤDd$b`̀IÌF#kfYt]8^ʘLJh@QTFL&KPY{ɰf5'K`aCͬ5m:A;aY%BRTl6ї03N{f9&&:05碣#Ye|QrAPƺ;"r\.xyFDxKe{.N^AV8+tqkf9+zts:wqq,nHZR2d㠌&%,rl/ݔ 2l2oc?u_Yt[.I?0gs{FԮbME5@xdbo2j@pP ΗsX2 G7 rׅ樓տU|vCss\MmtTdI5+g?S MҬ D—W_ʇjyw$_/28,OCy+wopBYD#s/A"wtig2v @ M+%-c}—L _w3 싯mՑزeKKK߿?իlR]]-H 1U;o X`m6Pk }!xD_ E"]|@ώz~3اd?c_[MW{8,)i]5zԐqDTTd+V/!oO& EZ򸸸ѣܷoJ򡵧~:..|b"*,,\j՚5kD4Mff)jJ\ T "D8MD BD)8 P<}^04Dg=x1}DD]6ase+~l/]Li?iG{'$TyÕ˗YXd/0`ER2 fŚNܷ M*-+~ۋBez-w;v(,,|/a֭[Ϝ9s!deBNh4eee2j:77WR!+E[*AIa%~zBHX_}x Ё  & Q|2@hx$1"E;iR`s!aV&%PMtP7>=u|z9M{>geT[WIDDN:zFSg͚u}͛7[r Hi4\Y>%8h4ʄ~ѝ %;!xIǒs@'KOW'.zge0:po!흷7+cdH"AsV8Ώ/n߾mFDFF>[l|":s"##--; |駫Wҫ@@2Hɀ8(F*Nruxm.6/,ҬPj)~Jx G9H]K34deBlj轒^W:bPβ2RHɐt2ԇ ?jT (//8qbFFǏoܸqԩ[n{ ~ᖖ[LDΝ+# )Z0)ap|tt$#y|VhrK.hWaKKKaa?쳻Kh~M8q߾}*{|ܹ{-((;::^~Ӿ.2eeeD$˖ÉLp;?ydgj qUX`boP!{} v#R2Ázu'7cyeF0'@geV._geH )+(cnsڄ!fe}{&ޭV^?t{8qg}6k,})#<2gΜBKr-Ɏ?~э7Zdh֬Y~ҥK>sSݻwժU6@YٳgQ[[dS2~_C$_gJK_JS$r;R2#.G~SD>іxu\t=п7>X  I!%CbeRGܓ6訨H%y̹߯o'E򊋋-̙;Xbhƍ6#1ƌCD/9d3NӉ'ncԩDTSS_Q~~'K2=ߋڀyoNO&8_j@!XlA`3BQ !% S06%NP&aHo֬t1Y:|v C*OQGGGiiҥK_rHBȑ#t:oݾ}ٔ Rx(hՃ%E2ґPZ =\k[ a]^`"J]Dѯ@B+  v gǡbM;#=74#_ѿ=FGGϛ7.]OXL8Ş_MDw21qD =LV涵ܲ2v*R- ) 6=A_*!2N:J7q=?@I.v byC"`=+#NPWࠋ &ߛ \geֿYBDC)I~*u޼y_|EqqYfY6lMt[pĉw]h4 :>%bccˉ(''G!+ W AUA =Wo Dp#.$b|dYT7LCq0n -[KK yC)ded,urVƜݓ63:ʨF%Y=7g%Ofڗ_~:::g~GVWWܦF}w,̜9;f?3g,ɓM IdfffffYZ-vD@~cWOAW0`<} x %#пikW@?5d<#й z,[I? s!K>6ʴR8=DGG.,|S_Wf?gY)QFFƍ ~7D|{;JLy񎎎GyEk---߶nz[Zw'3k֬'G?nK.8qK<~ÇC>ѣGاd ) ₱fI8K{K!!\<]e-t>8@p8KʈПSodܟ>`y"T|fO&+lٲnܸ222>>:wUV\|ѣ~ͼN\\̙3ϟ''~gc9Qtt}z꩹sΙ3g̘1D;q}YLtS;wΟ[r!+(A 'Bcϟ${H0 e aq]Sp[=ِI*WB O8AsUջz81qlKVF/% k֢ Νk޽;::?p@qq1?ͪU/Dw^coKŚJ:tÇ?3姟~z֭Ϸr޽NLyAt' )2 Yb7,p0 )`scq @z ;Uf8𹥟ԡq6)zV0W TV~z(ohHLdxfe8_X8lff`0 GDžv=ifeGW+TzyqtF,%cVsrrT*Uyy>:)J22,^L }o*y9? /|!o&Of|?#0vs: L{J _X si)zԶY]uVKqR ܬ.~ik9a h5 }3,`zɑgh$~ >mH?o|/+NG)xz۔E[[KY{)m\}P!Oe#tfde@"5Nb~eT*P)׍ v@o.9oC8Ho^U?zM`W)ݯk&NP/i0·$/w^` &8)upy i{?%&&fb\xy W>+X]}o $kVPv)CLH$e((Pl|05į!Р̐q7e\tbM]&NI? &_+. \ S_c7{Р̃L&?n%H%::rADѩ{k{;vcUwB !zߐK_p@>, M{(lܷoY"TQRj ?T^k?Ila<ʰB'U4:Q^ya'QJ!^ՊHAZm ֧o݂b+VXg;!?I:3}D(/?7`_-X`OyEK2_R2vsprI ^#ϖe,U0D"BP w_S ^xy!/}![C}(ؾ!O)8&bV~]XRGn` &街 BKYB|#o! d8`DATv;һ.p. B=H,d7îvA7ɜ W7x?/]0^!]1d{Zתl TzS>' Sp?pF2kE'] BR2/8~B>|t&(QB`@kg /yrQD 9J2+pXpC tJ~y8B${eDz$.“LKҨH_U$6+_^@w}| )8  u$sXc]9,?= :"FN;wT/AKN2z1)}P@PAU8߇݅I0±D<|q!b̀D>WDWsXH^J}orq qqe@+½$Y89oo!b+CWR_ϞE'sD3M ?n gIkw7C\&O +Wzu?`Yؘ/<3 㣍k7L?<'pSpZ@_sD[EK%F\4W!Ndl}tޗ1H.Fpc8 >8q,Dz,˚فHULLd2,˱21 i| Ǖ#*,)1!vF#n8$鄸 %w)o |2x' 8cdBzZKı,k6Y3˚Yc4fescSGkf3kf23#ݝ0 KʈDqRzgu ( )sU&.⡊Nr-YS=B- Yʞ8KRX)+]R?[n'󴀾o06mq.}MaS aU6)IJ۠`L"u vlHm|UaSX1e95Yl2&,c: IDATd:&**Rzȴc. "L0}0À.7o="W2Wq`K R2n C7;RB;]BܽsQrIj_;9?:M޷E۽y>BPFƅhA@PF:pwNǏ/,˱=).5.-U{Rop!8e2'AdeM﫰$ FnFsG;O3(#M!U]PtjVl~!/v$'sw%q$%qFKkPƮq/JrZMPe˱o)v2毠O砌ps{W6`Rӆ(q؛{zl%˲DzDzfeYh4#eW >KW)0QO2ZƟl-gǻ9D BK¿Ai'˴[I`̺{xK zA}BD )]Dх! 9H6zY2' ͠}C]A@APF2gu!"\0 s˳f̚9c95r\8(>NĊ qA?n]m &3+Ɉ#GPƟ+I^pηv+c9Z6 xϿAidHǪt CPz4nx4UH<%sW\DloFP_2GPF(~ 4A[mA@PF2 HM?aF&2F׋U76f@b}D`!. M `IXn< H2G!cde06x,XeAP>0+ @б Xd2 (~aq#!(*l2!(skZ D$N5Q\\\MMMllX%I-(c^x7wvܺb2L&IK/,w\hZu&ɒ KIQ%qldW-) B1hH1$Qչ:n„ b# uuuA.(J 6X"2 .1cƄ ,Fhv]ZZ /Z $2Lll,YNNNNkk22| tvv~ǎ{9rȐ!|ʕ+_+W_@j6mv@m3CPf6lݱcG^^^iieZ[[MH*(h-?Ԉݎ?kW8sȴ qB쬪qL&^LƇcXMJJʌ0L Qd#LALyyeHlN{'O:ED ,#2gٺu ##QO>F>)4M~~ T*'|)Vye:;;~m>IW1ό9W^AVBɁ*++'MDD@jʐd2r/4??_RԈU2˖-۳gς cXF\f֧Bhoo?0fΝ;SJlvx8dfFqD8 'e9b N`7|)kfr<))iP:r3Hff۳)2.c:.''… qqqeeebeeD 466z~ȑK,sCcc[nEFF+Gd֮]KDǁTF:AW_}†Dm,\7 xL XcCǡԩL{Ύv gyeYLf6:Ukkkxx8˲d;w 2dSd&:*j1~\+pA?۽{O>ɧdv؁ \>jԨ8jd4͂ ˽ȪT xGS2f9),Xtߗ^;vaQFkcǎovggfͺ-o]֬YfGbڱcLjhҤI|2իwݗw^z%т T*J ,޼EN=BTsI)K<˲---ՅYqlC HGݻw/_B&a]z1"dD999NZn]QQo-XWO 3"*))Ypa!J2ƎۗO'o?O`vb%/gf;|]W_;Whhظs]xIv3ݲмᮧ<Ք+ V.f&+;N^N<^Pv^n(]Wԯr[rG7}BWVjt[ӹc9nn1Ĵe s؆W{9#JǹhͮtZ9kh@-d4 (wSQQ1afp”)S⚛.(cݝ ߻3Uomm? YNb9!Ұα1wva;1111116tLV2կ@@II R2n1bDtk׮]֯_ԩӧU|{'źuܞ;vXn-_|۶mOlW\9r/˾_rРAW\&TmY8'\++>z ]t'I%K&RhI4Ο g7̚:𣓗Ֆo~%꽩SWSe^z==y݋Zn8N8qMhl1dhXk}u^MԽ\:]%4 )L@Hpʺ;t*CD>@g >ܲN&:8Ur6\.~FWWydΖ- #zFYa&S@PPf[n%:R1bݼyիb':HDhQ&-[lÆ DTRRax^}U>an޼Ddtvv/]l(>j駟޼yZ1\:ܔĩK߭2 s1i}ջ'mZ}O/=o?\' /(ۨL%Z%7ߘ34=rg-)ۿi^{\mlϮ}Ge&0~+iQw;}b/Xu@8mVO+Df< `s@j+++h?VVVj^)G%={4g4 zL NqwMeiľN5A t˗޽;66O>AJ+qqq#F:.2۶mt֭.\8bϞ=F.\XRRˏR":u\rZ5jԩfIw'^KWsŤ98K%.kCL!}T\:MyuCU_PXAV<)wx罿Yz6|rsh璬k\{hKÈn*s{(3KUsCQ9uCՕh DCgI≨h{9uom'j(mVF^|aݝ ҩLii˗4͗_~ꫯRdxԩLgGR$G911.G;8a:;;Yd/:*ҡb@:.77WVƖgff]QS*:`0rߝ1?利'… 'L{ .Źq'tvv9s\O9jԨ#Gvvv2//̙3gΜwEM)qdz+κ2%^'薤۵x}IΖm \2צŔyk_ZZneU+ӝq׉]rf]zrrm۶Y^A8k]]i RNmHPWk uHNab/ƹDIӲ< ؜z}ssVjMMMnjnnT*m͞=̘1c"##### .h4FVjXC^3fht;d C w q˱ 1DD+0Lvu |2HET 9rDM0dB҆ ~Rdff 6):{cXc0._LD܌R˛+sU>+4EϿ̘1CB$aDTQQǓ"33"==}Ϟ="{WfTUU3(#O)v'shSQ?/v֜P̬t/ƥg?HOFR_SNwTQzL"5Ssw˚V?aɵ "JŘخD4ۉK+Df%ٿfQ}ӗ[ &""ŀ1̚doMGkv]$h ux)GMzVw/kMDP&$f>T^֠0oU+l4k?=ǎnni")&QTz\ҢPx-L& ˲ d4"(# т2j:77D<ƅu2uuu)))JR<eO.v!/[?@D#Gd7xC)%LWUvNwsWt(sdуY)[NtjQpG\?KDUe\J&p57x҉/MDcz->m2- -D4 aäAxvj ^NmXu@6sm$ҁ*{B@Jj7ڛk:Xy)>0ڥn'"R0L&mcw+OT`,iڷc5? B$Xfuܗ<ȾqWiEB LƊ77h_v۹^6U˦拥%^*(M.8ߜDDduwެ{."26/..k6u׫0u{V<{;8 V+|xS"'Nn24vOybՊl.3@)_Q}dϷRi0 ǦLFF8pۋn޽OM0y/\oӧ8Pӥ]Fcxx`p v!!CSO3n8iȐ!| NbHAAs9rJZ`ut=D < .$&& xPyƫGfիFGEerrr>[qݼy hܾ+WxlTT9KJM [2a,BW&5QNlw5"8'~Rs:hO!RPq3JT5+3gOw(S{hA""R($6{δޱDD֘""Z[ʼnZ"iY#6u@4`LSOMK_6ި.ݷY[W$9Y+Vw|߮ˆɿ˿`ﭏ}ڃ'۷rl.mRzz eѻ]L?>onޔTs=Xkh.-lmȋ]r9]>,K!"Nj-k&R],s6翎+zjej}G|J&>s3=NW렣eH4w= 65TҋZQsP|EOd$E 7upz* IDATUkw?/^}J"{VWWWW]]T*/^,Hp())>a /ޠ |-[Irl6Y0Xd:l؀z0 c6w',a]i5hٲe{jSCѤw[m۶gde\x2aɤ?EWѱЩS9"x [nmvԨQMF,aOnY릛C[_'Z^`ه9yڵ_t~/ Uaמ C{Ų]om:p:md&'rj+MO7O6u.^1'%CDaC3~QRn"5Vmknlꕧr 1V~b*mLpjQ>e9),&+Od-͔ʪiWV}jBE̤EkOK+6h㉈j/^4풳SmCꕒ!Ҳf"Jv,HKaC'?jfA}fbRgX=TeD|$O[zx%b&Gs E&e/vIII+VP*Եk$%%:|D裏ݻw/_\rH&s`YVT(yq11q|&l2h(qTiRcKNAzG䴶fffۡF-lfzI.1B@dV{b{MxhVRN4l.VoeV_J+ κ]K+DfQ}@DiO:kΤTjOWeeb(-7/ٮ3ٓ~Pihd̞FDT}+-I82kZ_/Vu4wG.Isl7fXF8vueYnJHN-tOv洈4;f8{3vS6mԉ66WUM7s]e5gzgn;HD;: #3~1;bitݣݝ4?HDyJ1YꚚRSSG ü||A6{2>+V]L#0 9X-]]]G+W(JKfzIa zCQFuuuϸ*(h2W}=DQ&33|„ j:77P`0)RD#q ?mo/N_*===Jzz .- D=0 F)l?T03~wbΖmR +x|pRR4v뱢6(y/ .$'ݮ৉6V4%Nv" 'qc/7ټlqic{-a$GC$Fm?.c@Dr+F}S]U#yo2uB9&6BclBD5Cf4tڍj{4t>`|NLZfnu/b;vٲe^w;^ߵkWIIɱc._l0 ˗;VRRk.O 3f̠9opW&;;m܁7p@H! 0˲&ex q;d Qɣ22YS2.q C #Z`[ ,<X\x(C=YZ]VV)b=OɘfR0i1/\،6lذm6'5u:coݺ?;Rgg'I/.]`wO^W--Kwc8qhu̵-`xJ:a/tDDwSs5kD4ދx*/`-^LY㈈'9jP[[jb/yD-͜bhӶрN;Qh;~",Ӗ~_%ŏ׬2 z=Qw36磵S(dp2ধu|sZ ^Ҟx{ gA -)c=ݭ'%%:bu/b},477o߾}Ŋ.q455ڵK:iѢE1RQQQiiKKK֭j 6ٳ-K2j:''m?:y`jBa23XdSL/\. 8cYl6fua3X {;v˻pJOT#G.\OR\\߯NuA`IDEE]J7SN ,`(ejyy qn!Cʕ+677.-O{Rjvz!2=:Q撙Y.E9<;|Uwe9&.n8kիhfz;K@DVaH%48Nhoh& ~b/&"㧛9O,8^nimI0>w3~V=M;<;p32!2p-K2ǎs6ߗ OՆL2&L8p@llliiׯ_ü߫[ZqYQ!q!">xcRDGG3 c6f30 ;vgs=,ZR2nk0 `裏9T_|y׳=***-[gϞ?4AD]vl6ō1Br|1}m۶}EEE6elS\|wg6?˲Μ9?ٯo\6pv̩ÞK=M7zPzL"5ԯN}QWs$=eJigk ەYSu2:K$Se-55PlmM D4jL{95QӹZIfTڵ Gr<Ͻ: Kg6?`}S}H9~Y}ɶn\mNW"M` w+ˆ7:x٨/9nh!"R]pWRRR~~]MsAˉIV[ZZR,//ҥKGԇyO:a?(!&LرcG~~~?B2NZ/]we2 CWWCLxDxDD"La6ܹ4=ldd2hT()(J2Վ;,X/_{NzjPdG#^y5͗_~p4iiiD͛ByMGe U[ +hC^d gѰyٞ2+wAeۉhqnlR$eg÷UvV{IKD-ED_0^n$Ĭ&rj"tRK8mN,Ӓ.VVۥVYikp4}Eu3 ILDt;1f]JFt%Ovt%&)y574{0qҘQDDk/9zXm=99N~CmUQ|rrѽczoUWW?j|eee]] Xi+**OA  ExDR%"CD,2 .ˍFcWW˲r\&殮.cQ.GDD0 ò,Yl60RP* {6韤!;v Dn޼yU"JHHޔ ڶmP ݂Rw2`-**jԩ$S|S|+RXA6nYH95gw^'"or2Dq3FT}ۨKM [$ըOY]߼P޽EYQ *ǂJ4*<[ݲe骭ٮn[57tP) *` "Gaa8yx==z=}]!ew|/hV?ȔZ#'SWE"&q7l O֩]@5 cc"msf M.Lӊcm3T}fw,cE$igrKUUT̕[޺[v٦h/9s3CncEDs_;w5ɈL[ZH0|F@SJ/S/" ;q :A-Z4e6M2*ɩ&{{{'''^_XXxvI&KJڗmҷǏz'''{{keꮶGPPFDVX7o^'_ 4ϟaÆCxbM6)%uQzzM<<-KwnR2^^-`߉0 ĉ"t͑mlWͭEIʁS]]̓܊ N'""***rsr>'OS"8Mb2jjjso\'L-`VXXX^^."Æ lC_`KΛ7/%%+_Tyy< " ,->};֯_h"FmOUSSCDOͅvDDCE?8ѥS)ߊȐ[| a ]xKk??{v _!Sk|<z:+qﺇn^gY;?y~w@VEzҦF_ٍ䮍'w533fI&>PʏO*cb?ѯ ?Z@WL|D_sg[n[nuKD媖j^yu̍l!m8q]|}j^Az3s"H&h܃W&+Nzqm6"m\:|rDu7ӞȹL]]Ee韾J*+(i#<3WJՋz#M}ecI&읍KJ>^qUo(o]bNM *++㰨ay5i<|r ''- E$::K"ᡜٳ7 !jZS,Mfgg\Q93;;;Je2ZOGDDFe*ɕ} F#eȶ:(~ݻw{xx|S 03 )K/EEEM:+<~w=|s)1N[jUiijΞJ ϩOy ~Hqϧ_ q7xj1{w rO7ގ)ON͹wF  Wvjpno{U"ѓb_:]wDG{3#Z1mܗ{h+\G%{-v:V&?>)7%F+髵zQvo^zynorskz<ǜ#X, j^Vޢ4ą/KVZQJqml$֑kFůApBŴj^^^k׮=sL^2=\BBB~.VVVfgggoooט(5O=ԦMZ|ɇh'k-Zxq~AA12ۿ)S>liӦ_=//OD~_YR/0ygTZVD J9rbРAʇ2Ntu:OOqq㜝zqAF v}ttW&==}ԩ.V()GG~OOOס\=<~d˦~ttSl5갰~Qxzz>|o˛2eʼy?{LI}a}Qbb976eyᇻdĈo믿~Ҷo)--]~믿or,f߁"uL\jbx6%(zj%"yGFFHhhhG-w߭mLUs% 8q℈6տS2r9DEE) L``?={q. FWWW3iiʖLHe4*+*i77Wt[% %3a*lH? o&((hϞ=oҥK??0::WUtt{tt߿Jc>ۿEE~ſ;qKP"WoٱcGrrrrr'L0bggg%SPPP[[[PPpСs)7N>聯 zWϰvDҾk4iRllQFuѣED[ϮɦkM[f_|ư3f,XO7@h JKKoߞ|͑ǏOLL^ṍ'EF=|nW[h֭[jjI㛏]zuyy5g\ps +++kFl#@SQQЏ9aD@Dzy3'NL<㥗^Z`Aonu55+/]paMytg~~~A_|w}8sF2Ǐ_xV3|pggg1cƄEP_ պF]t) FٰaCYOOϹstWU_}wqʕ++eD$//////::O*j.WBGGG؍3aÆMtqftAæ2hv˖-`R$&&vK/ed.i_}}閎2"`oen]CP@ (`rrrrsssrrD' IDAT?,,,444,,{"(>Di ~UVeeorAL FJv=Z ʘL&OOOkW^U^^<%CP6gF W34 (î4qppp k׍ ({XeDh4,2"BP}OOK4 z|[d29:::88{ (DP@0eĢ!(0(Aݗj l^ť zlcn6c'RT*N7dБ7G888X@-e A2%CP}39EMY%^RD6&rk`а!C]2 ZPd2a2*++2jkj**+.WW_|^A]\]]=<=] Ke&cw3 * ER2Ӝ]\]\] 5I+м 2zNv =|<f̉`s@d2iZVޡjM&S^24`#6?49tl NF( 4Q/_\WWgг._NmRYђ=uuuz:d0Fc}}`P>2dh'#)sVF90 A Ә#5M`˚_Eet3sSYFJ?!kf\lڿ32_2MSU Z+)2zD\}ڥ4ʴ} bL54!%Ѓ,C05 @_FV`ue,s>+苮AP@oh~< SXeh}}eX?0zNv hlNDxksGU{a `@ ( 2`@ ( 2`@ ( 2/⋎EP}9"Ѭ AM1@b,Y2Ĵ3+CPQdea5jgJ=U].;wnذa.7cƌV2:Q\\,"ޣGz!kW>NY5kݻW̙%؜. mgΜi 4k@?b字/_n>~'DxΝk׮U^ڽ{wllӭS%>ke,S2۷o1͝;{5kr߾ޮO(My、+'11ª eJKK?s'Lɘ 6lŊDWO>v*_SZ7kXDPn\gUUUWNV6Y-(r-O^_SU-q msJVs6F^A^[߲ZzWNLL|rrr 6̚5< $$̙3ݲ۸I.ԭ+,[܉HIҿ6^9QFNw}D\DW`I=0{푁5_ʩJUmݞky]pshcӥ̤5brpssj4Ujr+K VzID|}}-Zj*_׿/GG&Ϝ9xKtuaWHU~VJ?󫢫'X7{n|(U""*'=13>Q WlT.Y9Ѽ*V+*ߘI3G{5Un"?g )g?qoU?e[?9-P-"̏Wl65;ʈȬYpJQQQBtO_! 8f9!}I֕=LUѬ^񓕔x\8;J3XBCJF64jJE,5""~TLsLF=jYѡnͧqKTR2"+ƒkvQDFF_OMM5o;vOq_&5\Og4VexoDc rj|ȢM`Vz W)) i=DDDϊh٢-1J}fg}z9%RGGtFtKlcl5)Y+pyRܾ̤+%E|OnK=_Dw;:_'RtL{o3 &\}[R :X $iuiC7ڳ%Z |DD콼<4EZVً_=&!/h"NA擪6M}&(#de֮]y[V.YqRzA9/M`CJy:JJ%0:Ūo˪j2]U|iJkkvh5`>ўܲ*sG_4)Aŋ/~ulm}-{y◗"!Smŋ/Ɣ ]Kf` Ld|뾷_8 aR-Jl4uaa~j}ٹMV/s&ƉJY7WGWfW[\''3lʎn^vTpv ^|'-L̹6|VDDWgΉi熯s""?~Q"R}E&15MKeJVnc>ݿ[', 虏4<*0+Xzmo\EnG_~,3B/FuigGY^k+*\U[k겈,JU%@BG V(C&:`@ ( 2`@ ( 2`@ ( 2`@ ( 2`@ ( 2`@ ( 2`@ ( 2`@ ( 2T.[q4..`̘!VfS]A96mPGGҷue"/wͧeZT]]gU+SNLPf1/ٹ:Y!(s*ͧNNF -H prwuuXr>>aa^)}L&駲KjSA;TsEE֭YS=z/srsϞww0aw 3**겲.L&""27~m۲W\\׬׹ SSRُ=eJIΝGoz٘>=//RDW)I22J^{`NNyUh3?(S3X^}￿fĎm 9۷>TSOE;8Є +ͅ 啚{ c/O(Y~)-6SJeذ0v{R;{-%yv~&!&-z/hjՂ111C>1us]&9P X:zTۛe-[N=H*`0_CZڕ81=7VWu˖Sz}oDȑvrt:ҥ;vԍ q?K_?rKǎ]2%)-]?yr]{78)ɚO?=GD knTZZm[GeVVz99˖%6qww93bP__g))쳬??m!!7k7gNڵD$/_zicKGP+Ohc''''+ sZ՟|pSOE7T;va,^Y{ ]QRR'~0*G r]k'No|sBLae[h^c0WOO7mZ+|gYY[W_wuub曼 O֕:@G?L&IM=_SS}]AWTu}ZnϨQ>e&n8=2򖢢Aw}1Q""?<..yyfm7oWRR~̭vcwIM=sg4&f<2(li8p9)ek̙3J޸ys X*2x&))|y-Cͧ%%5L=[DF11C-_ugn]aa՗_(..<0+F`h^x!WovBEjj怎c11Cfi>덻w1lqV֥;s{X"('/Oo?*/(|Ѣ^Y6_OK; K7g2grrʕ;U*Y"fͺɩz]V[kݗ,T?yZjύ*}b~zϏDd;gَekV]tI{DpMV֥ :W*2I}|܏2]ɵڼjVUZenmС. zsqp+Mu]2h曇s[=^~y—_+vv=\U:˞@y}\\啬Ky{wA oɫ.G@DĽ'/iݞ,q=kxY8;GZY2ODN;VM륇c5LrbMzz?fdG1-.. $KӚK/\yQ7r]Xghgdu])VwX7.@y_""cN?$'>ܭJu5{+c1e*NPa +UXX%jlPAA9W$"AAyІJiimYA)e̘!JPF7&'kƎkm{li%]t"NN"E֭J-yz^ڝ]/<4m;|ɯnӳV|nb 5ͨ3R5 IDAT?e .ʁ [\NNyەiai>[oϓ M K^;h rnNɈș3蒈Ǐ07nqp Glɓ͍vF荄PWZnNN .koƍkت)-sL[kEg̈oRu7yQ=8aO)Y^rp 4gV|kOwy6o|p픉Ѣmd'ر /zo6m0]L/,/._POusJ`0ݛgξ86c>MIќ>]ֽ5BkW.Fs; ٚzFӐrqqlG"VʹsU\@x`s槟 ;\-z7{D$`z#.n*ICb\y#=B7nܘ'?*N' $"|$SlYoڴ&sfٲF&WTԥ]ط/ֲ5kxCQQQ[G[GҼ-9sӛnmұ}Эԧn~2e;1i{NHO]fet$ IIgw&zRFW9]hbUjΞڎQjKfX .bUW]dk*uŚSʐAy& nZTgh߾qwwon6-tР?ƍ xQ'O^\nnM|ҥ+13uzպWnٹW*.\PpUIHǰa6Ngx!VB]ԤYT6XSxÇ5O? V2#3DdC #E$!q$}&I[vi& h68c> F7}-OoV%o_7_M~c79kn|:~&g+ޜ3N,Yj\h_Jf wVg KmWp+_UY9⎷i*/Mo6h݇km/_)?tC_}bS7Pg}v#!g zŹލ>-o$_Z5gdw'x㒓 VA-vzH޺TV֭]{2k0XD㇇x⫓6l~/,qMqƌ;rWݝ,IY󩯯Kll 6f̐{ӓ'/fe]깠${8Gr˫45ݦ󩽽}Gr#]]z;;/355/нlV {6eEDb$"kT񏧞Zsb gN r)O}%ۋe;wo֥>:ghog>5,""3۟h4#fLs/v]RϮٓQ.~YbÃ\EK%<κ3UDwzO5yK>:TZ+AfM5 r_$ٰaO\"%Eiqp/,6Ѵ}{`Ryg`sTXX%ӛn6c|p+{t-E&/G-]OwWCB<\\l%$ݢ˗!^^jSd2tC^g>..ܕ@;Ƈ%?(Sn>i̝3nxO;?_D1J1wFiEߝuÕJW-ֳrh3-ZT]: NӪ')gzc ͖HC~3VX4Ay_I囓\Ed -pZhx"f{fiH`޷HWT`13v/̉1E:T{hݮɏw]ƺ.I*LX([to,z?dS?0jKaкٓ@7={dL̐k ={WRSMCTVmۖmyGn lO%͗볷>V/DL&ٱ7L6DT*ŋ'&C)I/()1_j䞗w%;rHQaauZe˩wML?H_Xj۹o3|ZVKJj] <ߵZD**ꪫ l]7[u"2~Dy;N$"{7͆%"ۛ.5{t"~jJFDDg-xv$~ܺr5%#"|""IY:q߮[y@'0g͆E9Nռgw譍gxVij<hm(_|"rʛݩ<ІOϿKPv|}]'=;;n,ي&+33/OGa팏4_K/\m;zȲJe`؟3PbL'1'U+Х\$" h$(6Ï""FGiH9i=S,Zt>uEV (Uc午sr5׺&YqtoҒ$;R|,цgHD|2ǣz4P_oܻ7FDZ1"bQ>'O^黓y̙+Wǎs=uVo4:}Ǡ^FFFrj zՃ2][DDu?|L׼ED}w {zu =]FD$wlmN_p0T^k)M[Ɵ>8TݍZj_Th-6}űC+-Oڦ=|};EeyZٻo$0rQdEe-p$K,oږ6]-M򦺴;+ƛ-6K5Ы25t `%V C"C'97s{=n9EEM=%s=׏b2y(hv9_X>٫W{]kweʃ2EE$&愄?n׮pرޡ"Zj٫ymW"2.QrDٗk2ꊶmM:$d2NW]U Z#e>>ImBFi$roSIBTEkd?<xw]|GgHdC  ipʊXTy@L~V ~2-[hY &~~WvtՑ#?7."d~ٽRaÆ =`f/<2lXPW|O%GOV- :djyU~S~u.+Xes*5z֨ALwM{8m^H־~z +vyZ<ĖzaZ*ҧ:' :BuVO=^c&g_r?ɵ5t]mHޗi Ԇ$iTHpCӧϔ9r:u/X "£O:}XiFfiΜ_]6%pdtٖ-K=W^ϯ^{yie2y]yjݺoQdQѩ'1ѡCNs( Zc~Ѫ\IzNH2v$?Yv]JҞm{*/]ퟵ~sn$ɧ͒mՇ5|oP2#,ko5j\)bqk^mml kV6YR 8ϵl)*}JL[_s͕jB<"U-[VtvQ׾-m+~X土M5)W]ꪫ._ev}ȑ:V'NcE%Nf$.F &ސs2#$RB .^L):UUdkʇh8Dcb%x 7RR\[(Iڶpkmgn#ɶpɱw;$5\(ZagPNW=`>I7zl%Y?Jd qK9ۙ3:r}N۶ƜkR?TkZѓJMz!a5 'nl۶^ys+>_Am|mѢ8rО[mmvSެBIcF+xxi:;v[eG텑暖o>ec /' 7I2rl zȽ7Kvݵ#8t@h;Gjn[aO!dʢn+H~9:9lYbII_^d8_~zV-;w4ER'SRrZ%t2Pn%P5{k':Ĉ뮸 #GIn~mڜKǻ 8}ZOW^\f{u$2/i0,$x[S|IŎYYNv~n#kJ^ܦlN5h|`T`w?M=`+lcR.L'-I9YYlYؙE)C|$\gV a}K_4:$yYz+xd} ^ZSwHF^ b6o/L5#G9r(3Xd~KKS2YF){wZh#{ݫblpɇ b?]+>Ⱦ}vuikժUWUd5K (ӹoP哙8QF}԰0[?t?)]]+}| *XZ-2%q甪3-7X~<{bv=Z2egeKۿ\SZ <#kueBg=nt ǯ[#Gs߻_хSo3#}:jղݹ(aWWG'ίsg*\]Ԣe#Gv\boz>^^-s=(ӡw߾껑Ν}~SN'%4<]qEK(h+VsvOɬcC:qk* ݾ7رW&(EФǧK>:>W2t  )QO^&?Ko=dcl1սǧc`oB;cx.mp?'$@ٱ_9rґ#?AA,jw= nW>33ge-'b\]ywQ;/ƅ}Uk/)9]M⫯=ZT>9`]#b2ywvsz$e8qSW^ꦛO:uzYR^P}{; Y: ҘLǻ( hѢvqVWevju>qsM]\ӱW99w<\򗢀Uv d}ٸ[b-՝8q/q5}ETTIր|S1YPލV惠 5ڳW^O'kZ3}U3jiBЧO999gH`_":ZSh߾P1ϩS-JdeJJNo o ;^^-## HsYCEkҽ8W_5l% 5~a7n]{g۳]U siLG򹹅Snzsxxo/%{ٺ5Hj26OMFo@~UڗK%es IDATA< pA?<'xNZh;>#oB#F\'饗>wFɶmo}-놅ϞѩU: 7CIǏ?u!疕ٷh5VkгqS __Ӑ!*HڶQ8x%*4Onn}W>8Ec%yyEUfL m9pqZ|=^Hܝ8qj\}ˊz ?o6g]r.~[e-[9NJTǖL^?Ѣ[:wi"#WyV*pՕٷh}7UT˖-69u2l` ٿ??3X٤tSKriїTiժuxɸ?_#!}/ej=?~bk/"F4qeQ馮V>ruQwun}:=Mw~-,K۶6mQ "좎͸߿q?99ǟy}mA6upazzEU]L@@ss uI3 On}~~*WӟzT 04bD}kmӢ v;x m[Emk4ߚ5#nsYFo@3g}G 7`c4\ 0ξٸ+ M'p|u+Ba2y탃]RNatرҁ`Zjٽ5=z\Uó gh'G\/aÂ%'l)(8ѻw~:L^u\_Np ߿twm4[GS>9bud\6GjRҩS{Q؍jvΜѿe+,Cq@sCPY-~#}͙3ۢffi͚]mk e,.22͂ƅt|mRrĩN-tM۱q@3DPZ͓'s th{ nTp>4{Ӧ\\ˋ?<@sѢ5mk|]Rrq[|:uZR@@}6(#2͈0ZU7~{L62_<.נKmۚxQ4SڷoحhF84T@@PA4 e,@@PA4 e,@@PA4 e,@@PA4 e,@@PA4 e,@@PA4 e,@@PA4 e,@@PA4 e,@@PA4 e,@@PA4 .[Ʒg}(EE4 TpaQP٢2hY (f 2hY (f 2p6vƍ{!7(6oרͨbcǿh)'Ҕ> S2%5vcp*r}k1/3ag}oަnYbm"J볯|U\>g/3 >(v>򸼤Ե\i_L臟2"N>r?zl̋RWM iSМN[LUsg6/enָOzx+R=-9?ºudi*2Γ%Rd|kKWNgQ6~I^;f^Qhd;/Y>cfb-H+|aa}$-0-fN\jBeҳ(vU^}\n}ɹƾs^>q$U{6vch"A?,u xsh;Gڞ,Gqa)wxFZ铱n+$B9ҲldK7|YŒL{@Z-Qo J-B0GH72C&_PXr=\8{kqN+%IAqh #e$b^6sP-86==lnW͡7UB8f .i[ 7:n%c%)8*&IIJFZ$gǰF]q6IFBId =ddۀ;'"" pIkAas[-sJKu6c_"m{gH~7?=fmz>xAo?|GU,S{_/8RW>}w~/G&LY#|i)35q~q*CAv')5cz3DSRbO7cF ;??j#-lɹ=>AAQ i,ЇW-\T90:H+1|`,cVz砻̖)bhKd7z[~މIE wpϋ֘&8b$? W,Od$yç=o3hk=GRc^pۂO;> m;~6? F6/9%=e~?ʺ`]/ks6O-zf{.?xNJ) ΂aeGF~v=;3) IyĄf2hJ3n7$5awޜ"{'o5>HΌ.~K3+Lڒ bvmٔ5A,2舑csܚ͙%_Uv:q] /HK$U{?;O:pH|:zfgoV}cԒ"^.K̢Ut #;Ц ܕ;KDn$ͽT7!$)w[f{wjGPO.IgϞݸMBLmt 1z%$o~}r$#1c^M~ ."#mҔL`̒y~\DEJF%4z;WNkdl;muf#5a~E!IIuXT'4z l^66HyH+MnLR2Q=sR2t:k{EflI}c̈ʣƾdE_Eky&;fȨ%)mu¹E~|N-I*۱h4^>Q־zg$%=.8bjC/ulye\>Y,$Zj|گ"]ZjԶپWho {o,ϱʖt1{n`IC)-diS0 $־^RQP|R{s?ekZCN}}sh~GY7ouCEDzCK/εߜ~X]=@'vn1<0ϖ\p? i9ghH\:FM8.iaKw^c#e՜YE :{sJ,K ]L~*-PVIJ_:dIcWNΎ_4knBM]d\ ekUJxk٢˛GFyʯ7/0.muW+cl}ϐ^t㝰H՛~;6==ln>{}3eqBۡx#&,|~Lkf=4}y4IR3ܡUf.KqVZODgFvI[LiPde_1els\۩./es3RZ*yr)ۑYWQ]=m̏K}Ả qfƿ> G\kZؕC+)p̐_˖q?Uصrs+*IWm-? %w:R6ylGRq+SJҨjg~oίԕXBS:;}dԼiѳWCLzG<ҖM1_.9* +ߜ?_iR~vu?I^*>΃iRG-9Ikd1l=Җ$U{36y;G řҗ2ҫ7YC_/V,:n{I`}͹$7ayݮMV6ŢK%'<6z0z0^]m$8OHu /IRy5a/qҢ{%cNjz#{Y\H\ނE} EiK{yx\d3e?qmNj}+vd.=äL6K?-#iY|:Ϙ2H]p򠊏EǮ3fL+^3uFлKFoDžY?K Lnl_?*mx #yd pKZ<}ƚU=‘]Z8'wSQS5LJ2R`mkfN:ceY񋟚oSYKoZ?oWI2$ߡ3RzγV}uU ){9yAW-[Y$){Ś{DU>RsG{$˨ww8(nIaPIRThϜys$,Vƹ?>M[4WT֯J)gL[f%͝??cjt$y=1Ȋ#/J[[_){u:$u +#i2sh]?z#qXwǰ4ʱ pVήKz%a껆 ~]Ԅng=.Ӟ))#ﴖX[R1eΝ?V6zv9Mx&c{ƆaTܜ"M -;E%[<ó.1Ḿ~\s7s!2vHHƆH=X}+ƾɒDn@ٿ}Gmƪm tzCRJϔ7I|}k.Sd_brYέKo7$>ʉn)I&ll$#kR>HJRRJbIC#%m wLdoUm~u6曦g$ozP'I jvIA:6HRQwR> ;| $yY=.FJ2̛u IDATsGd[JFRLj+g|+Uc2yexګٳ~煊ȋ$ѳ_$I5&VI%k_-̕W)?٫S`yeYyJFop3!IJܑꡠ39!A,wTj1r$%nLkҖ;S^ ],bx*FFf$)ѕGM5:5m'Ǿ"%#6qLVʺwtl( d&2GY+g}ouϟdH ɕ./ePh+4힒d;}IHO)%O-c>ye/ky~ݭu^y[SSzc~] +pn1$>:tդnΝzN~-bl$%-]vv0IRRJ朔IWGTcnfhLBF=d$y6"Is2u6%9-ɻëUc5jɳ>&IE1l n)I^pˉߕS1KR17PSeiuho74@ȾvƓVK~%m]-ç C?|Ia>&g76RON|99A#yɣ/0 ۊx1l<8ode(7777I)WU+8?LpE@GWICx0Rⶦ}"I nÛ{>9."BRrۓ nYoX3-AlD%7[vsj>;Uv_jCjEٕϏRDD5*)!"EAuv0'vs2ϐ$A\PvI"I1(HjHFy[Kk|VVRTrH*oCIav+=nx9u[Z/s`piܠy+;GM| ,=->mj^'x#5 ,)(%ux3 m|G*!SvR}e>*#%sy3&JB,}"Y`GU6Cl{ݩ5qLJs*UҰ4i%5sjUr%eJ 1аszAݡfIr,}hʬҧ˦֠Z,]}%֠VaIIvdf ˘M41u'0u]̷uѪ;N$mZS Cִ}E|ؙgfǢuꊒH^ш3', >0ҭmWޜsxգ7u\"z} \\gZ}TsQP'v3ԫKXWIaCe#}c{ I<'Z)V£g_|a{83>[eod 2۹unZp42XXj/%7fǧOHsh38CB}yHuO~1ED+nvuKK/(,Fcl-ܻ+Ifο־85g])| ir}JƊw{sY|}]uŰk Xzk2Iwx$:%S%Nvf:9rT]Œ!|X6:n!Яct4N)wvqfJ;jb4eT:SjALmq#^2qdLw>19*|lÖJ+jP.Q-\27l$SwW0[\OO5ٮ[+c %IS 9>OL,C8LRfRC%iU^lkߩ?X$Iݐ:cQ)I[͖@/v;bh$l>MUqq" ~uG}$I ,-Q۾4p_? 8w|cE`-ޙ[%4d_ 171!$Iklڨ]/U:.xPܒ:*L}~lMv GUHٸ2[wW'WJԷ\.QvI }EpO$)<ϫI;n>3MaQcR(yү.` \fy4Af;]\|WsBIRHlVoCf |sv:1~ jυ5<DN+'>gHx&694$i֔j[8ӓR$rK GJ҆1;$gP]{ߦ'J'|Q9sWq mWlPkPe2V{IRRb\3iKB}l =!H9kI/J$رC&!>{sMZc:٘jC#2?ܰC22ٻ{o nH@åaB"@& 1U ) REA ^[bZnbQ,5&X L%48Kdש?&L z>#{?kϞZ%}a ,yҌII#go{MMsIJG`gq2d.;6]Q7 O˵8ƎCliluپaԠ>-ژ,pIYvؚ6BIF̅W{J9{rte.;vCnx@I,VgyefZU3'[%)dpng])ޒڢ5$?ܹgOj7ZymVRrWu3! }7,ܞqh̥pKM]n7 ֏]ek@cjp}꡵~hS|Y<,osMCVxTUDH i}@^\.ipbɈ.}:r:Ey#'n/o|fgfe/ XxpTYͯ!w E[CL[F̅I]u#nF.kiS*^"׌!dMfckܙggwXH/θ@ש n졛fmBG-ۀTR\o;ܴRӞ Yr'^qTr`k>I2r֜dl3ڰ@QUQ_aժL'=;)[d˴ǧxoI<熏;,n 2N}QF=yY֤S79}I)ɽO v$H}Cc,~zDר첯>GAVxK?_Nf+F[[.odGF>~Bϒq[HhUi?Vr@X<~}y}m~8c)Iwf3!^xmwgèG2.})kVv$%>}}-c 8@Q٢5vX:OHܸ77/.I*tKbaqw<4}MyhJغZXm_$lj]ǭI_qQqɓn~s\\H/[:6wxet/^Ҥŏ]Y]~²+ K-kΖ$uHyq!2Zaq3ּ1#z Kckޙ3,^nM9kRMmeo{%I^~nɾu\7f^%qc{Y I2 rtHX\5_>${xq$@Ih8nGFW=ɐ*<.yWMu{+JnKɄ NwctA/f{QAu0,%Jx*丢xgq-j>7 ^+t؏F-u|[\r$7h#)Ì%}?xȜ5d|B:{3eJ?ҔLܝf\`~Q2L->oO:6g"ymܰ^AKR2FaslYiq\Ph`xZ?dtu/4N)9^^Z""[^zfqc^Ybny҃[XGYk3X}*'j??t.DtKc\2w/i޽ ׮]EPPAjAԯjA&r\N eXz  A2e6v2`Qր.rᬬJQ A2e @@ (@P B.eV6v H(2ʞ=#1 A2e @@ (@P A2e @@ (@P A2e @@ (@PѲ IDAT A2e @@ (@P A2e6vzl7k]t嵭skZlG3IcZW]!jKu8 -̛~4b {}9msi[v{vtqHןտyu\NtE)WKu8 ʐ̙~+0?c5?dF!~펝KIrо)(ҰnZe>MCu}Uw-Y]MQq-<0wǾrh x.0ӵ{qFcWpVIIbn6v1.#$,,2~{;v~̂z.-(o߫2||^4 k#]jAn^7&%%Lw]&ohl&N\#iȊO'6v1kLH|S2q;r׽sRtϞzss[bO奔gPڵ\LWRP`#"۔faF5ݾCB|Niǁg$Yۈ["v縊}5k&7Sk~{KWq;D[ "Tl ])iXVY%~q~L ǁJ )rp #WV }dRfGJRKt/<fQW{̂9_{Զ]9'+’«,UZHRYj9Z)G%E[;GDV+솬vd^[:\[Ke7V }o{Ðye?d'̎蕞._ Y͘[:] ۶<o6>vl׮-ɐNYH:fkmC?<^}ߞ[x"c\ط6kr]Xں,Oo}/|?&>({fgu%?x/u`]n%rڅW9OagL`Q6}8S W,ɾsRy`礉3+^SkjJJםg6L{6ߎ7w-/#)IO߹(ӧr'^c9=NYys 4o+|AIfևƝ0S|9EC&I^>ruHc LJ>:W\j4v(~Bf忺? )r9;'MO. 㙷{'c?إ@CLDI^{{/9%:FQN((و $gM W=iCfd,Ftg#::ȐTXP'6L/M\(y[R11ZB$`ѱ7M[:3;Ѷm!,TTϏ؞Q}_ƐǤFRjAa n˩N^p[iJ\'6Li]'o#JR2aqm{;{ Iݽė tø=x}&[CNgfW/g ߦGsϐqUq6bD{kY0f*a#Ke4Jq0]y5`4|M <hǍNaա; e.X\,)jBU9W43sP0_o>})̴;o ٯ|8}$EyGtL!#^ʘ4wL~Ų;>><׸څ&){R$ZpSF{&whh@I{߿lθ3O%ٮ17i`t%ec,̞~7o 9}3) ejo-c&pH[nu[EKItK[&eī'V'½$%Fte37nS7Fܨ2^:-SE%'P\F3jHr/xh_EXdIm Irg;ο$%#)e؁쌣A+d$Yo|tt$~򵟓gu=Ϣ# )y ׏\mJ|d ֥%)tƛW,aϽ} ~;<}qNFGVVHH t(IE|U2Iѱg$ێVEy#ԹeRg'*+$TmE,,%#)ìȅ]FAV*>:YV?>'we69yVYY_: ҍOSYkf-x g |z]5,$I_q~$IwG:8}$o=qTߖuۗS%_$GY%){aG vGȑئ؁;Cݐ(I:]iB^K%RjP?*6mS:ϱm$a2(bh?ܰU"8?u]{+I[ҤL\|KIrP[Q.IҎ)Yi.sc+ftG*pTop)^h)ռR )سr3o:^!ܩ~w%%Lw]gb77sDc/m{%?]Zh:2g+TxZi7qAzr +o$ iw}J-Z5vTy&:~ĻlqǮuJ Nf~dzVyrU0Fۘ_ -zh# 0oqԱWY"_ϫaѥ}"h¯N얽=K>풢sOۚ#i}_U)IVې}&uq}vjtíf&C R>p!-h=܎%Giu+J;׷ɳ<pB53$I\'ZKNdЪ/,%IjyC`m+N8-Ia'){OO ^S;%֡POm?wc|odbh~P#p2'OV|֢E)lǟ/0vu=ո\}g>e76ls/Zt'Y }nP#'z}^zѽ 8ltE,yCs󗭚إ;냙?dL6KSߠ ]+&}pڟo@x1W8vՐT=Q9f9W{L'(>e]žnf~u1c FZ[|MʌMƛ6fTK>Q%ZIY<`zN$IXjj6QR6,?}sr-JcZV׭;?c}[Oinv2=6"=!*/TT;w7NuV5FFKŕ %&foPWfU$33C iݳ3:@VywVu(seӷV7RzIJJ3){^?olVNd{OY4=chvOn׬ߪ4 bt2JJz]Yq3C%u͏5SiԿǺ5p=9ޏ>SAדq?ȹv>oM`1/K6krNc'w§4?;eiTԋ׵y/%c>0*L9sxVAz9ovjTLϯY1{k'sg`Py|$\WZߪ&]@97zG6Z1+;%)aS2B;>j</b)8u򔌤ЎMd~fzuU.MH t$)횿Ux])ɸ{'*d$vi= I5 7UĔ;yՊѕfjm̂CK2w<,kH Yxuf1JY1%#);*$m/e ;3TijB:L3ZԡPksTeNdlWxsЧ`퐵sC&)3 x$)-6sD-Y:T"W>ZR̯%)'네|7]%5);J&@QPF[Wp^lc?@lDw]_{{l)mCSR{r{$t_A1fy\:-IYeIᔤݟZab#ҭ;1d6?ǎ|$mO Ik%)Awyp2xUS2-KG>Rywx;2$Iq-K> #C$I jZU9VjxJ]5tkߧcp5q8Q$)x[8 ?릭IfY>qH_:^C hq|[{Ɉ V$GȗGZǕ|[E""R{Gs;pf%$)ʻ1$el{~h+AoM[%{]De A]0z}#]f_SfGc0‚fdf: 鈫ښR;6%הnβUt2$ɳ/OmڟEr;W33OZ㕔-edDj\QƬgo:.MG%3ҜXtpC3gm Y[fgH<+HX$eٺϗ5]R%5ܸ? p(IE ,b\vIFj#/$&Q8ȩ ʪ:db::_ |muߔ[: jwY֯;P|mor2m~ h<<_UZrӬn\Gu[##l .YpZjsXZ$(44z/{`u8?$Ŗ.,K9bR*RWYmK$;|tj'<Ǯu|) gynK:+ᷓ^bM9׾5ErjE>#RX!8*\2mGu}+ j3pN_f,*$SRXv/wn/|:=u,̾o}7%[IOq-؟WoftwL)v;+R{XpV)+}=K/o߄mǒu?Xw6# IDATq+^#iȊO'J=/)oL=<92t\ye,tGNt-EO jek*بE|t3jamgmV/'fq״ԥG_16Q֫|̫0K-ܟ[;GD^nW,1׶6 `Xc!@.ex_޽{]JjrAsyL#pi1sͩCGFH+mj{7/_o=$ 4S[Ky*)޿tSz:,EctHͮ6Xڿg\ %8}ɅgcI_K4qCґ,_\񼒟Fh2SUQ]cwj[$#$&zJH2-])%SVLLYc;HR2*!%#)(8%פN/kص)zјAvᑫG4ẟ\]tu?4aȕ#2S ]sx2dt}|йYmb-mKY!bZ;;WVnbj)}Dijdkל)sZG$)1*uݐYCmOʬY;SIF꜉g M8!ɹf6+?Ÿ믿-[߿k׮LQ?Ύ|7oٶm۷o߻w Oq1>Qǎ{=\q}ў={~Ʈ &M\s5sO-[͛'%%%$$Hr:gΜnE1_M4޽{&My?}eST4M͛7?++h޼y<++x3g<%qW\e`|UIjڴifΣ~b_@yAAA;Ž5@ $0ۀv\;N̹Nz&6mVn6Mov]N8`,31 B~ YYYz_w{똵gAuiӦܹرc ,x# ]pA"{ȳtiAC`؍ܠLqqq۱cGYYݻȯ9vڲHgnnU,Xp[um߾= 559s x饗-[}֩S~=zOOM r+V]v˖-A=999mmm/Ǐ?|{1ֶ~͛70gΜ/###rLqqO?xx䥗^jmm |7u]ӟ655!Rd555O}('''Jÿz駟~ /^я~4)))2p32ӧ7mo{,Yz ^Ɍ$'~c+**:Cƍ/*ܿlٲx 1?F^yG?'?m۶ "OT___ii/5Ǐ/^1njjo~wsxwϘ1bg֭ L9766N8|f}ws~r&>>_Hh+0+]UmDe HMM>}zYYYeeeggg|(ڸq/ۛ5{ă;v'8~ի?MMMgͭ9|S<ӧO?շvɓ'wx_ ())9ySO=GG|߿{ڇz(^Yv͛ϟ?~nܸqwq`G}'RSS͛ѱ{ HLL;wn^^^ccz/$_򗉉+Wlnn޿CC/~|3d0BPxHtc+,,LIInmm Y^^^HGGDZcƎSy׭[ND<$Ņа{獍яq]]ݣ>Z__~Ǎw^xaΜ9]OV===ׯg͚W]]}ݻw766>Cƍ:uj}}gΜt߿?3g!%^؜_ #=(AVVV---xSRRŋS}}}{}ׯ_?a„KOoll|gn|7]]]---CՖ-[.]yǫy|+Ǐ`O<Çyɓ'_wttK+V;{Y_޸q?IzCPhݺu;,Y2a„o~ݿ/vMX\\w}뭷&''{VX9r7|1c/\l;w<oooկ~s{ӟرcZn]wu7"ؽ{UVȑ#eee)))ӧOeee:tɓ@Lssc233"'N \M,++KKK?I&A~333?OL2%|dEEO}:P(T\\\WWhѢ7A$''O8qȳZfMdI&͟??իWS2A_wuAvvv;9o߾ӧ\2R'))[n*//V>..n͚5F|| 7ܐW__vԩVd[`AR>l߾}999w_$Ajj痕>|YfZlYdn)0% ۷g\`AxOlԨQNjkk͛rȑp%iӦ=:xĉ+V Ewvvy睑HSL#=zС??dV,11&77KHH(--=ydxEEE>|kL.7\vڵk{o=\FFC= F`GpرcsI\\܌3222O8~מt!i"iG_O4)777paP(A(ڿoo9s"cfggٳ'z rssLMMɩnkklީ7]]]P( B޹sFO5lܸqfͪ:|yK̞=;+ _ZkkkOO`oO?~<;w=ܓt+**JHH BY`AEEűc:::F =PTT &DP^^3"...NHH֬Yw 9u~{ (MؘagϞ3g}%$$̝;w0:;;7mi&'']xq {`.h}W0r\A ‰3g}1cҎ=޾q|CsK233~Y__-[v oqYuuu;v1Ɛuww'v 'joo$r¢ECifeeZjѢE:G;pF9;;|ʔ)v튤(;>}71++߃ }~#ަP(*''+JJJg#;;sՑ9ϙ3gӦMGinn}¡sliرs;;;z}mذ!///:-4 F BUUUA_P3fL81ζp  uttA0@դB@yyO>Afff^^ɓ;{zziX|ի?Ǐ5*:>r;vɓnZ[[XWW|iӦ'N&&''ۈ%hoo?_h) Eg'\ *'++k֬Yǎ e6~;ORRRaaavvvg^nkӟt W`WApc\.tzzzzzz.T)...ԧ>5ss| ?giii뮻/_>jԨpqq2g1cƜ;3gnݺ5\<==0I&eddTTT,Ysڴi/M ?3g|Ϲ Q\\ѣ#!˷U$]8ϙ3,˗/(++ۺuw^A ?]%COgg뛚fΜ#L81%%"9uTkkkFFFZZ9p1G{.&LmyBK***fϞ}뭷FR2QJJJNNNgAFF`6󸄆0p ɓyyyƍ `ܸqyyy͕YYYS.MHHHhkkKHH8qqq ϼOك477;6K'OW_-//:ujxe`ر+VHII_YY f+]Uje_|m۶q/'N+ \TÇ ٳ)///\e),--mܹ{ `%t5,^}Æ l+pAW >}^y洴Ӵi"Ǥ^wu 4q IDAT k׮ݳgOw___iiڵkSSSoHnHIIٴi͛ԄOHH(--=rHdk^˜5kVNNNii뻺/C6Rf*4G$$o߾} PSSs4ӧO>}zUUՋ/cGgg/\UUhѢ]{qKII)**jkk{uf̘gϞɓ'G'MLKK?~oo+RQQ}˺#9s椧P(e˖M6]׿+56lHHHXdIt8p( [o5##w m+]Ѳ8믿gϛ7"lō꣏>ZPP0cƌÇ'$$<=Ef̘f͚_~{͛7v<A/Zh۶m<̙3ݻwʕ{TT&Lf͚g}wSؿ}}> d۶mjmmmFFѣW\YXXSUUxɇnkk[lٖ-[ 2uԄɓ'B5k֜رc?=;w,--={ v>mڴ{'A†0”3gfeeE333'MTRR2vFp)nb\\_ђEEE3//oΜ9#'Mtw?36lعs|7̟?;… SSS+**{{{/_>o޼~gff޽;n!Sːeee~=܆ N= j#"(-111//o .:gիWggg=vرcEEEk֬>}zYvۤI^|Ś͛7sss?{ξ}ۗzΙ3g޽2,X<2[okK.z뭷vw >?ǎ۸qcEEEԧjjj̜9;xJKKKKK.]:@j47v{U N,++`ԨQԂ~eM3f'?ɍ7[,_PPpwF/ɓ'71773fEe&Nx>^zM7tvY ޽;===:P2dqqq .,)))++۸q?8a]5.ښ:jԨR}}}---yLJ+]V)A6jԨp>f(=zt[ZZSSSX :;;{zz9ӧO?~W` LSN555eddo'P(tԩ挌 ā|'N7n̘1LxΝO<IJex4 W`.w97q4S{>OggSO=w|3gpMP.+K_׆룁| pҢ)S17r{gg{^ K'O\n]OOu]:K@%_WUUUǏYt?ᔔWKgSz .y%`d驮NMM;/_.3ewNJJ0<fG( \z=e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@L &e 2Ab 1AP (@LH #Νu[TG7ޘhQ0}'$$zL:HPxdW;VP=:q{kAs5_uƥ u VUU۱c-frrœ9SRqJ|]];~p&%''֓c@P;#WԤh…8+FS[VGlZ]Ss0}4,0PWwj+z{C5%l\UQүcXsۻO:z掹ᆼnsuY*"([5g8pbǎ+?ki|C'\ƍ=]uWQbb|bb>6ᇗ$''\^V%*p`Ӗ-៓ں7ިɣF⡿=-ifo1be߮mo7vªUnV[Y:LSpOƒY*%(оaHsڴ˖M4xo8f\ A]i.[`Avaazs8\AHWWMUfQѸyrr,YrfS;JJu60@**Z2R0iRjRRS"==7< 8mκω7ܐYsLdڼfNAAڰMJ 7oR}G͞y.IOO5u92̙ fOw f;{4pdǵN˖_ZZ:kܳ}߾ M>cF9D=,)ihhh߿ɓG'ΛUXtinnP_Mͩ={>yɺSsN3oę33SS.guwhܹx9s.̞7/+%%R} puuu A.ٛο 5Y? ?n91ȑm#.4a=7^yp;?\ST4.rmǯ ragO׿>oy]]W?שּׁl9{/}Qn{;{?]|8Nҗ^+)p jOvឞs3}zƗ|ݍ73kr9t;-[{dǎ>=lO>o;;{7ozɽ;w? D馼sɓfMLl|O<́+9g'?O~r ĠYKo)\뿽bG8={"'A$$įZU쳥ؚ5S/#Jss~ /:==޿ukuyY7s +){͟]ܦ5}}ƍX5}˯S.h$ m/8"VV;֭w3t _⫟ĵ?d<]]O??$A6l8aя|dْ֟}Tªoތdn۽/QC`xA0֯`RSnRNEEΝinR}`p)o}cZ/xd{{~Poo`"}kh8ol}}/7 G_|P_z{C>{s{y0)vLJ&?c%xJjnŃ|H~_«qW ذ?CqQέ7EgM6>ύVoo~ S*rtϏ~m˖ o1; srAp^8t/~_\:,+EwNN_ ;?? /|˖ho75|[1ŋs{k^~PK˙===KL*(HԺ\6lO_'zkAx]ъ`n9'%''=ڼqg=<{޼ .E w6l8گ[oq` 54Ov:t2U o}q)PR^V+zի8A83RzkAZZrcn'8nnpzfnKNڶ3}[z3g\옿1˖~7M 7}׾F巿9zז^Їf;^ydw'99epOf9s&~3 ˇ//L:m--]_|`Zd V-x;zCNG NcI^~) F?}ʕSťeeMxɓӿn9-yYkpgOO׿>dINt+]];dV/]7yrz W_쳥u۶?|_qe~|d֟ɢ3 n>6==_禧0\^sho^Hzug6yrڢE9޽ ӻi s5LX||܊SGjjy/̍7=?_%,>>n~w'>q9LH7n|Wr ySnHΝuuF}0LL_kŔ)ܱ)++__>1?n_y|ӦA~b8%/!:%;iWA8ݻ#Ǟ}XjjR 5WDxX0_\:fywXtҚ5S{v}dWWϖ׷Gze7ޘ?@Ks>3);JJ3|eٲeH8&}FG|j95/ާ_SYt_BW;>FF%~;+>s:uwF'NtTV%_N }?9 !!f~#=U{5)qqUS{(# Fetc+t3VMUU9z}\O*99aћyTWut}dEEoTF+VLYpL Vff 7{zB۷]x˗Zugȸ3ч 8pW#ͬ?e'dFJd!~5…>8kLXBB?iA=;FU|si/xYaR&(4Qi.Y2i7 VFZ૽| dE''==9;*55u47_={l1kVA0jTb^ޙWǏSNΘ22%=pz0g]rmm]۶FV*6*JrrBJJbY[{{]nMM o)7ܐe)/?ytO?? s&pWj /|ĒiX1%)i¬YsL߳y;+e532.ijꬨh4g?>e0sHJ7̑=pdeNp|Oqx#ы ::zں+*:YYٲcGpSY&LSc&ON;pDYQ9pM଼8f̅@F,AxS۷cΜfep|,ɉeۻׯ7o򉏏TC55;ؼs{y֞\WwɎK5t]ݙk93s0E7TQ۵ܽxyy T{ `Ǝ}q^MKK., &VAx͛**Z"͙33.X(mOY\\ؑ:)17:xqx@55m͜ԸKPn{>{FSU& bG{W}LPho~뭚+}ҋ;vuWѥA0! ~#s;0 IDATs~W`h$feɨ&(g<شeK%jݺ#ZpI==p7 g'U~ܱe;6jmB&\a2߮ǐ]_U6cF%!7wlt_8ƌI3Aƌę_.=˖[({$緆Pkɓfbb|B%b\e?44opRV_ӧgyG;;?,i&LWb׷?Ծ,_<0svZKCXP'J7n% (a׮{6D˗|7^+_x}qY\\яJOO9ġz2{1~1uuS#=qe ֶuu&'' mND??gLp֎2ǎvtumm&&8 %ptunTݳ|yA^G R]]hǎ>* /]Ek$[[[Z:/|H1*:RQt_Υ;6'gLgOC}!{w}t&_0sS_>bX'Fgehll츨jj*+A͘+ ATTDeR-ʹA^}nzc췣Leek_߅//?Y^|9Ғgʌ4˛KJ8>dgN:.|/[[{&3&:u>#^de9sI__۵ݑ󳒒^@Νuҥ&ONsJNN͛jjN}d~LII}kV]b4$%/[ݳvmy[[pgDIKKI=O=/Pg(7oq_jjRt,ujoGKNx @kʠvQ0…sL4+*ZΙ?ٱw.m[oռ!L)z ̝;kG6]b0uu>sYf6$vIS0=ܵbIH=[WwjJ}}o~S}{~Pĥ]nЮ]ǟy9wf꧷7;tdg͚1AlڱLJ0}܉?QKf[oD `Avz챒?xYhü}K~~=\loBTU~ۻ.}3{_w;:;/9q,LJ{W_|;G}QS됝=c6'?/kPoo}/~?s5ozBgxs@ FeteѢԡjUajjRg˖p y=۶ѣ-gO׏>ڊQ5S 7WZEt$%•{] nw|u|gG>܆ GL.i\\WJ~xmu?磓^A,\]kÆO;N:}G7~ҕ?6^;z Ti8{G[/6v911__`(E%7E|s~rr6mY[Vo]`Av\ܻ馼o۲:S\\я>hQ7̜ѻk78ZQAbbt֚W^9<șYtұcfOOo^(Ƽk(-=~}ŧ>5g <Υ5q}n'ZZ"{>[ NӸy󱪪/v43s׾v׾>|v?~e„+W^s۷y ͚|yAt詧[w{YdRRR|{{Oqq/ ߅??ᶳw?uHMM9m[ms˖-[ o`ƌ1͝غz߾n9h JJ˛??@BƦ 떨Z"JRr+Kjd@gʶJ1X-}g tT) 4NтچKK7 $Lgz=so>Ona3E:YSmG(B{U}]&9Qs !ٵk]Չħ^|񭾏$+kDy9>/~}G>w淿=z{KKjSGy'!瞛}q.L_nN=5+_{oWKWW|iY:>{ʕXQ~w~=G7nss+L^ je>R\|Fn|I9?G6q'/yG#?0(s1>gϘ?3q`|gNg>U_ -ԧ>:򂾿ʨQ@g>q_=|ibg=$nᢾT\|ڵW̞]8bIN~B jo+wȑ.+Omٗ$W_wCό=3_Z[9'瓟7' 4dOo/~zvw9'/=;Wwo/~qO?>F>>_կ?sǍhv2W)=P.2G`PKDA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2_x 0Fph[CS㛓{L2 B LXA!B=2/s  0H2B_ΜTU7;CPmXeOA(!l?<۔Sy8^x g կhapN󊧞Ʉкpp>P(@2DA(@2DapN<;G~pMnذfv^O…Nxg~nl;|'7yC]y8!Bnmz&8q`?U'yih^#+.йK;7otYJito6ݲwͦ-e9у9Z Ôi_O2 *Lm)%ROo_uo}Mʼnt202MoTy)]Nι>2i /(gЇ t@.qKzٚ!{Kj S&u;;oʴDjѝ!c0K02//4RظC붤ReFK)H-ƴ/u.͘ɣL؂1N(S\:;aWl߾ҋdBTzMjܻ}P|EGaFgsz넆 ~dR>7$tvS[Oiz,ܩxѣ;xf?^P+oʴDjчj_KDOX\S:X/rA񌼎- Cwjxq@LYTS%)|щk1q٧|]9_|vF exwь.S$ftYKR!xFH(ǡtvF3mN⼣TS!ܐ(ݧ9umOuG)ݷg2[;1;)>w~ׯרroֿ??>cgN,?V(&Btl_Zp0ǎ2u3{ f%!~sg\^$B!wtlϞX`e#@i҉9`pe zg;&& `A" .Z[?oٟޛln #{0Z^PokFph Vٸ#/ A# :=tz87gi:B'vn3n  q?㆙3WW*d'w7\‡pG>wF'N!\ݶgUFkIgBc%  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  BS] IDAT  B  B0r׿p93Cff f'}v.̠Ό2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@FɶBF `Aۯ^;sօs8? d[sӞ7w˜.,>qc{d'`Ú2B:KM nŏwJ?s_E `[[?-%b$Ї/^ L=W>~^wB<}ڷ{ uu+B1\oEکUND WԆݵtQNlXsy L?Wf¾^Qv3^B_=.Q1W*fNͷNkz6BmmmC9asq/ uO۾+ieMp43`H}bRT_ױ҂/d$}m!?_~ ND,w菢%m[呆.iSa w^ydĨ%۞~ol !ܲ]c^9mͯ5ٶMu!ۼuY $ AS=m)[ . ?'+Nh~?~` p)ZϿko.8;7"M5wn1s؉UU <⺭w*g1'n֨9;ˆ윮W┱9=\2)ɟPR>k[]={hRK5020Zk:*|xYiY9+M\v˨a;hnKLHC2#Xг7.ݴj^rU?a'ڌ >N\5&uQۇMA7C~fBa'7{uT1׶n?};32 Y-\q{~c=}ߦG۱(аvj#GucQimyzʎe]KtB({zN-V+k.\m0uwϝzw!,j5C{UUd>w{un_ŌI,?* ]4{uCE.]~cE~jsjsXVk۶b[S;=HeAhXsyU7g[oZCVᄉ7x50t''G3pp> K3v;wz=?-ܙ7{!vE3oqs+\?}*Br`}͊EJDM(7suMNeSuLGxvh3顒 !xQ WUudB-?5\`J&lپf6yvj|Ӭ]*Bk]_]QҼk/yYsWv?9n>UimmDmu۶v]w_Xժ{H6[67w\WC=!&Pń;u64Ekӿg͘WjތԮ޴/Y|u_,5:`QE۳.Q7ϯ(9:u[t튚>4]XUOk2_pENNQ'?σ+os߱ǻ}pkkWnCoZ̷ta:raUʲ5_t_~&W,}'v=ӱ7jsz?um-oڔ~ wM!CSS)Ղ4ַTݱDNSOWkhqkwgF 2ۗ^Yݾ溍ZzyIK('muW-fj^V~ySk{nug2n/-i?ʒ[ҥ+u{ *rC-ZM/< *V}oy!n]ܞH/Sո8ɺlwW-_z~U=5^qQX>h͝ oKJJe|.[핅9!жgS:G6Ϊ*l~+|˭!PcSP0 %gq 3[L1y&eK&w^19Hu$JU2!Y*)wBcʖU*..)vie9"+;';;';3M:elgT^:Ô7IoOW>cn~n}8{xUJ&0&dnmMJٷ=]hieƱOo<+oYR1;'Jn}74νl˻WeDndLΪ8 !yJU2! nxO*L^{1^5uVPYeRjfA[Ru5[/ԴkW"- s.2ň &#uasKXTgꑿpaF18/ЅC /:5FcGm]kEyN!4!}Nѕknh>ИO]Y%~C.XT5KgC啉;?ƭu>Nf}(]WcSsj:w n^W`%x-?]xm]%wdGB( # c.Ȕ5sբмv^.}vFqdơ̥Xv~Fٻ{s;%k_~gb/t`p4%ژDS )(r+bc%_lP B C2,YY9Z?~ˍ\y>V|fޟ=c/ޟF2mnv54El( o& Ɖ#c^R~kkVb?x盪NśɞPW^i 4'ީ>tO2  6eWN|ڢ9mN;k,T݌Msj|tBD]\ɦ^Wm*|svzۭG^-:j`̵K{9`)_,|giiZ>JiW[Ny]sh'&^mޔm S֖kd{zpc={T4T(Sһ]R+saCBዳTXGE~r"JW>!p[Z.xItpm79<=s~.9%ee|Խř w?_-(&l%e^gRMl]B+/zB'%3L, -1\Rra|&ͬ<fOi5 2^;/;9rE1t)x&MkھeFyy쉽Tg;N`́ϥJۓR{ wy>ѧ=lOB?~gXy)'2}:f6gfuKR֋BnE}514mp=1pw˗Ed̘1t|Ѧ^RReL4_~CDⓒ۫Ss:nm~CDvW.]Fnj۠tY'%'29<4)?[yyݩ/}픞vo~:IcLIz}IXSmLNwHk;r_i+TQxwx햢Jo#s3 '1'G gKqn^ʄIC=;A8pb+ɀnw4$?`MDq~DrڈS 4)ݎMk8m(=4o>϶$bOAz 8Ew&Ur'|p5 b+ڽ 3ʀ|W7Tm(s` (qo[}_)\>7e$B:d{kR# )A0$u+CTmz6r2ʍTxUa2(0Xz SVo̦wPBJѾ"P2`H 84QFd.]=G⒒Ч=K榏1xiC=-;! ˗/Ș1cz pgpR&L!q OYXz 1 bA2 e &@L (@P1 bA2 e &1`]|y=~K>* &PQzpr[zksF* &@L (@P1 bA2 e &@L (@P1 bA2 e &@L (@P1Ꭱq]ntH0h.WN7c 4ESz(KtVMN&ne􃇢KGH%v EqariNXƤn4 (s/e\{q-TO%"bkȡR4K>Hel2FuVok%5T-goo+ແ qj=ko.^|X}l!ll>60a|Vތ' V\|f񡎀w7|9' u8*)l/˹k6S2"VXsNѾڱGWb Y|[?Os;sO~#ֶZDSթvyc~Diy9"p (z˚ʬw 59f;\ ˺3yxƓ_;~~fȚyb.Z0hOVg{$fq˕Ζ\#@^V́l%9Ec)׽™P2ɽהO$tT|n59645ooC,e·O/^&2hf9?ԥJ@ъb/Y171[@%|m0ILrhՇV%f]WpY꼯S&4=K<`^Mu:^VGK}?|!{~4@ (Kt9EzZyY9Č6l{rGzs]Vu3 9LujΖ:]Ϳ_kKb߰uE1yv-::B6( ~)K,}.T>9WGF ""ӋEotT5_ K$ZnS|NT"^/B'ož%#궼ofI=(3dig60d}m{ͮ S?VpIp˪5=X3&/KOY/.|Nj|kU{CE:S-hoרK}KL]nm/:x3,ycR߯0s Z}n]f?[bofxF¿=>P2@o )9Wk~`8{R2"َ*h\=jƌgK_yX"Z9\7NEHɈH[cK6MOkJ JɈH%o2ad@y[jԺ>/?s)ѴHYR2"lWS 43Ly5W^SDIPJFDʞ\Pk^>to'"^-jv͟Sv53GDD:^5yTk^qVGH2 AaHf!5:э6)n1PDYysaG՚K޲OiG9?udsYt>>xcԣeMGޤ6 PBk>q@=^vҟɛdY<"@Mn8s^QFZh2f烳W,Zs_bGZsy]9C[6Gޠ)}vZ|Ԝ\1AS?;zʝZnYW=n_A7^4?QuDwR|WѼum2"c .fsoLpEЪ׳K^U[ϗյ{dﲴ潥[h"W=zu:;*6 ?s/V/3&˷~;CYvhS`JFg)޺jS>y~ъGJgjVԚSynkW-g[Ff},Ϧ]|fowSkoN6LْվjsG=O}~~u$m?g2w_,4wڮM{(+޲^Ƽtl5.ꡪs.MD)ؼdRg33[.zؓK/P/?h&M_GpE1Algk|KgdOuq/ҊuaexN%_l|*X:NgyIA6Zs?Bӳ^-P!bxpoz2wlX8#`Nҧ2"FH|#_:#SH]KA)3eͰZ.=GDyTܺqwmXak9NZ:cSjRJ5Q2V/3^9cʓc~I}m7׽a?npGg̛[=V}6r )վ# dm3d- ئ:c[kp%K2%Rg6?_$ƅA){ k;z}/ԫ<` }u񬈈9&3݅δ0m4O/0C_>_'YfIy^M]qFRP$W-Sk<)sdJCD8yl֤c.Kz&̒FDETgh{׋38zz.4 ("e2_Gn&ދG>``(uy1w ,Jk:rݮu;םnp<en)Lݠq-h7Wؽ-%g͆]lRꘑI5F8nq=9zTZnߝh2L2O5ޝ|hEʊ]c+ ez3nD[]Moj9?x$4p+"a+8vK\Y!߱Msv;dx*۔)q8V:c5 `nUm"oiV# ?E\v-e0r:#3 ξ!ہ1DD$A'>,RKDD-.%l\1bSwIET>m@n"(t]v8{WywE&eJofhfiM$5KmoO!N[/lJ-ljxvU_.ukB}Oܟ.8LCMh'+"{6W:z<]s׋WlvG4ؖU8牌"[Ae[S:e!"z]@MSV|vxZ5SYyI3VUhjsٛ?<~a顀60nr^?ȢY7*o;4NUs%oťN%egT-0'9g*}9|~3bʴl_C=ªꋮj{eeA?4QFO]V!"Ut3 ͑B.W؆.g;ݺcΙ*e+@? L{0;&t\Wsݺy͟쟖q*5DP+&`s%Z={3F[1ͻyK,Aao/JԢe22v-I /d{J2 stiXj٩S&˕nbq*"3d,X=;ktSF,#fzq[|ldCUR ƍ {1eG0'!$K^R9|C2L*}Ȯfg!c & g3<0ѮG_~͗?;CFw7%ِ2>hl6VmRU9*R:B9Ji.;s0._,"w}PQtjl{O6ePãå/Nqɨ&ZC FC/13K_Pqfcϯ&jBsڿUm""28֘t=r\~^]!ҩ~➙$^׋p~=7zؑIt\"[`iL7gmӆƤ ?1c n)& ︆',eAzk2+27 0'(K e &@L (@P1 bA2 e &@L (@P1 bA2 e &@LcлrPn{n}T@L 2f̘._,ܟ2 e &@L (@P1 bA2 e &@L (@P1 bA2 e &@Lc7~r7gҲ5+C=(|G``hNg{M z=ُpU}[Z.Ɵ C9*CSs:o$]땸:f۝N]DDDs:otw>xO] 0 'LXus!-+Ͻ_?uG6pq_+";O_wfn:#7b9s3 "jK'D$e?K|Φ=?.l֕2Mo]ĸѢ/LyW]ԢJ`RLyRD):`]A=NY^%"+nf ڔ;x;DF$4b2 ><ն8EeoϜS)id[;% WDo2,)t-"8w} LeGrv\5 BkVn naq""7"?rxyRך/1E܅gt^j@DD2GIc%]1IkSs^|'?٠D)"ӡ^[?f%tS)DEDj|]ƎKևғ1tjN獀8Nqo$h=qy:;5K1ϥ^R4ޫWO)"28>kWZι'-_{:|ڜ7D&ODqͩx eTr]yeTHcNv>jly>-d]%gSC3u'59x~]vFW"")i7WŐkz#o:`@`i[S'H-Gvj{uSpi}Z''h*9Rk+M/L|8fzoknEw.\G_J7d;cfhxmΔDD$cɝ=; 6ybxM/z'Wg٨Ɗ5/qsSWV+LNrbۺ_U4 IDATuj 1ggaTm\ɎB|SqI?PO6})y`ΞH;@+ ztiޢ=!#w~mZzd5A)io9=&ҟn BJFDy'K]@8.=}b^Dc{zXc""5r-if&"}yAD$!rO_. iFED))2 }{ΈvO`Eb;暃7m~M7|lfN\-G߯+L{)3jbrzF8"9tJeᢐx٩Fy ޮvmO-wW򕍑7h'E@kGGPAtͦҞٱi^1!kˡ5vk塵կݏfnWޤU9w{O\cE_ڝ i}Ȝi`bΝ;[դ>! ) R&FCHީKo8mQD$.^/"5^Q$۴)8ǧ{ViƑ՚P{VftԿh|+diZ$ܴcu1NZKS$'g+)/̴J,vMmx);[/"i?:H)KjYyŜIB?>=CNXE>h"3ok&"g1eH}79 M{;kI |Cͥ3s-}Vdoae/x*"2}{nv3o]8}B_K%Ga(ݦ33'./4i !2Es =)9^'?+Òoo;LsdD0quݭUJCY9BDD Ogo\Rg*Fd>[5\[=_D߽r_i|@B JimsU1N~4GFOhj_T_펠 @|J/0lފ#/+s}sӃWuÕW,ElS[>m?Zg߶<B:oq'F?4""]iqĴLKƬjq=t:M ڷg5{.t}vhMQWokMDDW%%sQDdc'hoWXz AeioּrV5J(0ZS.l{²-!OCUQ+;t3jhDžԷ1(I/ɘ zD]nx f#RYAP/"X^)lE*59b_i.XEnsT`SWoZPsݻ~ʜ,I0zJH@,3pA\KrRw-Ztʀ\iyEVdC  @d)SD"",ŢDg<noT`(ĥ9m^P#1f>vdEDjl< mZ93mAĉ|L͎'cLE–u)QBha͞/<ŜlIǑ秽b¡1FDDd1yJDuٖsdNYIIOD$0{܀0x0$=»Q.b9ym\o3PchcHzMBV_r~}Y=JL_je۳67qĠj$_ŒY(UU"gZZW&Կ.")O̔bm8*"h)Ix,p62ȴdQ~{S}Q|GJX۷v .%-ߨ?r:t*O[q]ϡFc`J>z9Hm7w/3EڬY^:.cxSh/CZfȉ&kCUD$7xu&m @mwr]EqǓ=kȞ>տW+./Ԯ6UovndߖzSO٧Jr N?p׊VӤTs}ekMyp%>MOSlڸa! OZHp;io8'UDd\f='f4?2bi""->^+"6 K/0j?5& _}UmD?͛-bJrMuO2Wœn"w)$w7%A,z7=i'EvwYvgJiywCZ>)ŘZ( G0vQ&j*̵ȏETnJUDnjQ2SE?y26RTE""g.QQ!Q:;X𵝅Ɂ;Z.\hq'T}!%5M2_|CBڲM]uQsw/KKEa|lys[Z.:E罸?.@]ٙS3,3ي/4IoYFoZtRXc)#vܞ0g>噆͆?ػH}$2}:i7\9W?p7hc8ܴİMiݲAQD4Cu$!wӁeY )Vc Zc,Zk7DƘFz<&M?j|Q BE9_1Fֻ2 !-W.]pdߵtv2aPk8}rBP -<2@ K/ &@L (@P1 bA2 e &@L (@P1 bA2 e &@L (@P1ᎡCC=[ QQ126f̘ABE2 e &@L (@P1 ?~;v~S"3R?ʝZn98vm73JdTe?u~落8݋\]2'eaPظg9ۅج@ y9>A(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2px/8 tu?.Ap 0\=4q'lLlS\W^1+mƊ?zO.jg2c^lhlH$Uupq0>Ιr¡*\jfIӶ.Yvzvlj}k*Rs۟WqD&5|fIIٌcsǞlmWƺ97&6,+t.se %3|bRɵ˷v<z%*z=oڕ\6;^yPGm=y[2ގu?lУӡM;7=XQ2ਗfT,{ <ӾiW'M(TOg*)ᚢ ˮnP%ljZ²ywZ:rQ2*:}*vdm)I;g=]{߽5~2)=;]TPT46(5mREo=]{>.MO/UP8i, sO`ue[ׯjiKcIY𞚪S5UOm}Su/3WEK׌4UT~όyq;׺Ïٞ˦;m< *Mmi2'j8bT V*u~~Ppe(]O̻@_ٺg]\_1Br0QKF*ˆ,d2?<{6d2}j^OW6ґؘ8>kCݼ;Vd8 )y?_Yxo~eǩI0L%GdrWTz'%lvJ&ɛ,h:e+*0d%3 @E #]q|gެj"Po[ŊſvL}bݦ=g7j5r[kw:uE[6m~g{ʚtcG9(}]y_sKm͒*|߯v5ֵ%I2bњfߝw;x×\+3;lXUQMy@L^z~n@%nS#~,*ں_UVh;\%mJ6U͆ǖVN>قTɍ?7;}ݜ}Y⹕IU<iwIuK`Ns>y4.Y%RSCe#7W/0P˳/g35FLyiֵ:OԤ{jAy~};3 sM*{PdoفAj}V y{ ͙l\ <%OnΚyGz=}UE~ܞ='^|%Sܦ73B(IW[q`pys+g e2C4TjPt5{rVsKGVR`O<~eV ~iqK0{06ו Jܜ$o wdԈ'=]]]{dhm߽gƎ 9O7{dGAHGvckWM7IU57-lш@\Y^}usm=AT}2RV33'l|yenד$IMT~вk{6mhy>Sy_?RCs8-2$IiG5l-ݼAjكՃ[S3;WO{t2IRrem~l7PQ;OL{כJۆ|TH@$IFmuTa[v,zWW-[0)64j{*69QkكCցFUPD=eT6۞yf^49gEOֿ|UhˬBI=0|z7̞//^ҒJ6>^yT둚;&i?uܛM4=M-kؘKR7Z̈kdE}ϪekwW_?l 6drIڛ^8dJJoL%ozOu'[nL$Ir'z{2씝ˮymD=kQNP|fYI6eeYyfH^nʊl3en}rEL/8~&;k |pvg::ғ$It:={F*[;Jgf\VZ2#([ٽI29]ZR2ʒ\ xsk6ѱwRɬԤ)Kg|S_c[Vd_Op 2pPK   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !? ;8\zN;e_NͨcQseA(@BBP2   !eA(@BBP2   !eA(@BB,wI\PX8^)?xM0ˁKoowg rݝη:L /1mj^Gw`Sw߻OIL]zng_s\4FK=;=ϻP1靽nM&_z%gMNg{dIՙ{/ŗOpomРG.p%$I4?/tqmܻ-)͵?yz݃\$I^vCͽʿם㏯]ۛ$O䪳"dۖ5cS5I$oo).upx/Q:__|ẍ$I{rs^|Sem1:_YQ}ݏU ~^3F/8Pwo{^S}}O;I&\3/$ITmȎ2p*yN9+]o5w}zK56xmx$IYxvF(ŷϘZ4!9ݽ׎_~w{3.hZ|m4!/뮻xk^y0|7*i?\sT2q=t-kySoo<7;?=7M/-/%Ο|zgnλ{|ܺWェL8 }olˏ&KM(,Z|%Ͽo.&\4ݠĺۧԹ =mBi5$GVg$I~U}w&U?X5ô/߿-I _}%yc[Ww>9\tC #ߐJdvN/;\ 7(62l`η:~?O7[vGv?FLNHY-[L(,<އ{Pow?pyIrw߻;k2yaGt> ]$IλtH~QF^ n'ݝv7'F3O-vlI$L.<]4Ňz;w??_|Ycΐw2Iz;wGܨ;_y^w`3Gv ]Px}}oݗݱ𒇹iG}Ԝyzx80 dxU7+ɶsC/7>=I _pɌt۫I7vQ\ٻ<1s TwG8kVc`!#(2(H4uPA#/ `!>+v#/ɱbKSKGNwas1_es^@D8{8dAI`^ꔉ\$% 1݋vy D=p/5` bBcܟ%1<ٙw߯pìPo$sqY,߮м?^CWYP2i61[<3@DR11>?Jttj+&""徖 ukN];.Ρ}*Vږ$ɓ6=8qGᖓ5Ã5;[kxpx ]GoɂdE&Ty,;kz*`_wOLDmL }q{Y7Nww`]捙;/]H OLq?Tz_wb"^_+Q#5'h/{ǥ5W?"BSﺈHv%(kmĞdiCj"徖98cq̟3*4"@M]{x/+Fr}Ru\'"PoO_]!iD4ڷՅ}.zϮN|a]7$o:1s>(%CDS=B7 ޚ)};Zנ(w#mmb{ui("*;OeeEέ彎y{ߒ7u!"鍯owOo!c{_\mC`Њ5'zzKPfF1MN8ԝOgQ3q᮳L~|^YtM)FLL$">_eg=$bfDI߹D`]DDA/ZAg 2`fJ6ɡqqosh^ٱ\eZjFW#Ŗt[_1n;uoAҤ񶭯{gkttMJ#jm[SyNcO}Ǩsm {7z'821=+k}F,K>:1Յ[QS}!:Uz{ͽR11i볲֧XWox˿i}@׫inħ-H$uP^~b ZGIMDD}C}_=""kJS~Km~zܦh41DDsS:}gZ[j.\if>WLW~x+Iʮ_\ػzrNg 48h Y8{M6m;XQߺvMK=ְCz/o:sEm¾VeY H\3wevϬMۯL}Z{$ 8nEmџޮx|g̛wi5Lq̆I.nƼ⚩ϬCiآT2|RqxM>Wo?op*!Zb pGzbӫXۺoV4c_D(0:tfb\_ˉ8bA?,e;Q71%ܟnJ[jf؀F23e_cN'Rb^NLݖ@F,`ӗbLt[6kiz|8خJmecr(""uu=" wj[tۣ\C Dgjo9-QtPԭWԆ.kz-f  v9mD_ϧP0!])E`XzB0_{-_Gvս x%k;DGhU~*k~u}/Qd2$Sڭf9"e_JMp\'6t_fu#}5'S&<_w~$/gxGuxp]߭Ojێ7ugBǛX&ʓk3f>fN7֩L\(&2|abO[[l1h&jI˶QM Hc;uy""eTI$pb?'c+,(u_ƴԙ ^[AD_wYkR]'E:+4֤cG+i4QgH_4dsb/>"_ϟ.;E:+QBb?/K>VkEd-s^xγ{"|Q`,e1Ykwj%Hf}ZiJے:͚^tY' O"DDD8'"#}mg8["5 u?bהV'z*?V9SoOg-s4 =3Af<ӫWqRx(d1'{߸ti"@]z|"fEN29+!_E#iAmu,#1$aҖiq/[ )]'2q 'iLګ"W{C½^zkk"O5%kdNs1+#Qڭ% ϣfbL$j&b?[q=5WBDDbxIu^h3 JKnZK34ω*D} iMc !k1.L?kξsa IDAT7O-!K1~^moml y<S~4#8FguW&ٚlO}WvxkNWIƗ|Әyм~"f>Uqcװ>۹\aX`iMTlZ$|:/(JU=wh^]7L,<I\_ÜH&)һ8k~'On4׷c-1c`?m{= _;˃wϖ8\MC^~ע!?>>k>u{,[qXAus3%.łf&(ZS%'>&]'2F eF㞳-z}A|aռok/#nm(b ÜAUMp|J-JPj.&"+'63OGuNv8Ķwh5X}[;b5:ׯ>5jE, 2i?n8?8C[+{O u_U;2z%LoOu[ʒa'5%>)DDt""o.ް ##]my}5Oy>ṢCojN(ۺku=""m/#SxXZ7b$5꿰I,)`d{6fI_\{8+R"< ,0jTǺT.3 6Xsx'VyrO[IJiqDDc6N|g#6UT~"ZcG͏f$~dou;)("YHךqJ/n>,͗Vlͪ??nxѶhx K %aBBD,QݕM$hp[;͉ꇾ rӿ5Nŋ01o|/yƠOT5u<,8Bl7ذoӣz_na)iAuW&I]?NMk*<ް\hHr??YZ o:\("qM飼.)Q'"p_t9?#B4P%}sGmG=\:,("eOvcyml DG]_gEff^ӔuGԯ3p?m!ՠNE{nAtV-RXφs}{7ֺ9.bķg^?Ić /H^JvJfĄ Lpg%n;6)Ǯ\9!F@Ipdڬ_Y"TTm}!("eMQLX|v[uvNYß쏏هBj*wr#Y=c=a~vkzđn;cDg*A5ވ8#´xҘľSQ0Ψ*f: B$f{MpG&yg5Q24t%" z-70ы""g=P#&r3,> H)T\:iR; \;jx"wԩn&78xUTz$ܠo>|LDBC=5 ~Ŀ.  0'0ud )d?le+}疮>kNULϫ8qkӣAVmk;BBbF] on7O 4\ײWO*&;{"*ڲRBÅQt2Oꃇ ߔ(oXz &}H93u"K>@F` CИX!FƑ`0m#_}c㖌-?p7e 䗿xaLݽ{N: ^?WM܈3o32wKL@ <(32L@ <(32L@ <(32L@ <~1w"23eh]x|wWϑ7=CFx& P ge@x& P ge@x& P ge@x& P ge@x& P ge.Lle+D%  &D]a8#1]gj{GDAHxWC" >W&0g䘉V=MT{k@=$朑cDOP(0QY.'bF"e+lwxʰM;DD$Iǹ@&CڲUw8"b=U`ӽ܀<19+aDnE9lFEnQ7Ѽݍǥ^zF8ͽQ|„rᲞ;]Zƻ Y吽Cm>9I;ybu 7N4ZYvEޙ h@ֹaݪ~, UCD_q991tDX0&Q2D$ $"2k"#A>ɸ_∘Ԕؑrɜ єtOs@ mvy7U%V-򬼢R$Nbw$՟KW.C#""m˗l~Duݿ]Ҥ#WcY-h\8fշ:TT^M^w/JDm?d~RyVkybiH]֨KȚy n< g?\78DD *Y<6x D'DYwh(Є((0) Ŕ 1QH,(W.-a}4id(~EOr KmD$]4K7ƞ: 2L<6簸%d}NiN sARMXdb/7`~/=6 ' ^ĞND$FʥBC z]CυPSCFu_0"CD$!q͏X:%_{\y2vYR.-֫ ѭ4ih9R6o(wח$zܫ-Kʮu΍̤X}Z/n$8*xI .㝆c﫴NO+R7I;j?\]c٘xέ:(;Wp- ""m٢ q~;w6ZUQYۡu^^G47Q(+kg.:n)d y"z*}e1'P(8P )Ey{Se"$?BlS~#(v#IeJ0ܨ2̀fZrc,j{MSX%EݶTn5x (8ZR0l5_xA/h,N0=q,;JO,:A+"l]s6j?:C(O^h|fG,zj<WM"S*$k0lМ/}uHݼ'~n=;.V}]5-4by*4,qQBNPQlZ{;VH}3?7ycR#ˏKOrK _<ȎX(uث%?{]Zبk,@~`Hw7:ਜÿ_Zې}!̰Dêh9(*6Q!o+ hJ7>p8ӖgvM@  hUo`VM^9Jt6 D˲y y;)QJns  ֕ibPAfJ2t;o PK1…@8Ń;^TRy,ϐgM i; K5B}of.i}@ir |hȠ ;־Lvfan(X&%5=)J>Mc:>-uZ"Rd$]M9fmԆ!`YTRj\$hHP]n-3aDĩNڿs8BQ`Bvk(hniH$"MHM2dP˴.񧛥[iH&!7q~C;5enߥ6ZW5πQ:AmyH3 Ms 3d""sH[nYn)JF${U[$"V9PiLuy%5,8]}QVWK2@<3^X5D2fYJ;:FhufMF~.Tn׏h4:y<ߢ˗KU/QLqZ%eޅi9jknȢ.Ϋ} K/>;ҥMŮ -7.*nP`Dn/rHD% 6dبklUw8"FUb{t聦l^MN_9_vdswC"QT(u0Z{*u5NXO3ɎߐVn(PќN6|YkWvH_V$95򒧗 lI[wTrJC~Tveyurts#by/Wim@]v~m޷G-t9uy.,U-JQ`b:g.]4OAYTvW^S4]td9y目^mz`,ݗv+ySj[\xbf-|Kd\-Sr""ϲDyKKw. fUsɭ>>«CrTuJo? $N:i≈)6:Oxl*g"#&H#TGkik 2zʇ;Z: KbP&}s!oK*_ۘFoْa2"KQ2̴ܼ"C%E&qH۠mF+i?M?K x[ڶt8rD(ﲩ=9ua^Uŵ+L=sRqkKYmeN7dPۯv_<AD̲.2~x:&(Nk-+hKؑxaVEVIAFHpsbo }E%xȫDUAb)9SֶTDL s3bJd{ rehp冫"LRyK yއV[53Ζ" L]\&1AS̔r%:lIQ5ç.f>vȉr_>rlmC|ֶ1cϸw*PmVl?;*swΖD^$l onw~J",ms+Y\Qf$#etgx[ӘS0ɹō]؝<| O߷_&"UQ9~e`?~m,*(08GF]=PlciZ*D,;r*.JuaDDt4/'FDv£ū?JeDD1_ť-!"]<_(R^{M'GDGv/ǔm)A# NYQ87ˈH%X͑#x“`D$Xx%ROC>J)ݝJN }sV{eŗOPR!V5fHBsDD4/jѴEr"" VY|""ڢp7 %rkNe zˊEQ3"aN4+""&vzDM@"7ܢq$Q3E4_wuQI^@;Ubs-"Qr2<&nV""2M+~!" 4ő^#A@(rCp[mOrدDDncL? }/tyդSwny%f$C&3բ[7:6~v(0!_R(͕ 1POG4t%Y~Pmުq7% mܾu&` ^"LViR甶3pDDUDo1foH =?h4ㆉ"^HGK:Uaa'u?v_nmɆڛ) %/Wbֈ yF.]}$Gռ(r_sĉ$!7wxiKee.kM3lS[3/ov߉%~ 3=:2hKO6s?ؤ8['"^0gt4 ,Ŧ% 1"TZ+lCqzHDj4zx CcTÀ'RuU5 %h-PDD9Ε-S J"fĴXwGi*t)V%rf0M$׋zc͵un| .m:(l]JCDD/&'rD܍ʲ6""Ect5FP5;dHf**]#PMD[L lULpY ;/ƢBVꔎb0l[˾d+ EoIݑ.wd >6jW("IS.YؠEqE` Q|vTuH7; yNVdA&2lДo|ܭ6k֒)e߉VF62ԲDۓbiEk$X""RLc=ӷ+6ExzSr"Y2"{sJ-43EݬTzFm6M[U {_k ] qmKt=%m˟oP^+AD$_PꜼ!8vOeC˴ӒsWsr0Uf7oԔ*' )g[b _t +oe)DDls]I3]gi<=NсUMKYK-v*+MܼFuk=\DەB57.Y4T.A3 luy.es,(I=x>ȮںDA`~y^0 # :tk'@sk(syx;x\ue֛$$Ty(tյ=S奨DS 7Sۖ[*}oDt7*Qx?)’~J7k(l($lc=>vHU]q7rSxՑͣ yB:DD" FvMSD1w_\_E$.Z]{\fH(T. ZA;ӧZBFnM8q7#(Om)[p yhnNi7OD$I8X;k05](H/qc$ .|rd?!Ï}++xK)QD|RЄ#r4~$s+ '/ߪx3 ќWVJrd\||DD{^_1U\Dr*EZP,7x@{1:.8hS/9r~O<:˼Hlsc]جT#7L3ʜgP$dgVm:zɛc7PU^,XeNwTrӨ,0Gqڲ}ez" Lw؛ҭkedD=n $Os WbEҜ""9J><^6$%Rڷ(*42:syDegٽ|9K$SH[QM4M"""Fl~Q9\.Yuԫ8"fXO/^fvlX= [J/W(x(@|SdNpTnyylϸ/JCDD.X`%.|4Y_ZGQ47RIdcXkqG41p#Jc O_]1u]"<m}MWgd_?ҙ2ȇVh`{!\&Tϗ6>`a"ӥ"^|Ć9}7O$Fʥq*ÜNυdRO^JD.{eÜqpy\".3uE˜T. 4.Aahؾh" DZ$KO ze/J"4^(jaXoqU񟈞 B1*-|pcf D~;w65[]d.U_ױ?2*{y?"g4ean7b{U-Is*>^jY(g^>E*qֈga}#O/op_,ڪ(H=xn/|ju]E$͹iOT"ANK/bμ.\O{sÈ#YψId%2tiыGO IQL*Mo,-R3 m.ù^Vv5 _1HqRBW:>k/^Ӎ™7"~^'=+D$tr)}4?mZJ[vy]N~BɊ&]I1Qz㰵lm کFmqF7gfw*XfgXsqA`7]f} JPL;~" P4qϣ])pWǟL) 0Cx*b]D٢Q]"bndΙkxzP[j="p Mm#"Y?|tC> "&ȥxon@EF'G51M0zos6@ ykdZ@  Wyy2F솛ףPqGj7We5@PIkz4=(K~|F2R0}'1jhI /o_d^Gߖ*""<uuIUD%y)$2FcbXZiCvاe0ZU]~xҫM9+V-1O$V1vؑ°Edʔ)#=73gàP4h)ozsϰ7e[ܟPQѤ1#=`8@B (@P A$2He !@B (2??:zQfMP&4i#=3nZeQE|qNZh;/fiyRS>6.tF ձyEŁ؅d#(gHɤrm[hХ+W}{Wm p#( cHe*]dE3,-*1!( nGhA;O< ibƯZݟx.ĵpeFIQϺ> :i u8uC^y&7n^vK8Aצy^#( O1G-{sǻP{ثF0DFkޖ/Gmh~aCirK+Bܷ '#O1{m&wA IDATFMD귾RqOK{\GW:F4l\'"z~Ǧl]ۘa^dùeժ]^۸hiauH?bڸqš)Q XU}S닞*힒_[hmܸ|UEtJFD|=+VW9Oóh31R2"\:vudDgq#e A`t9V]WZFW[;yVӈ;*W{?Hk'{]_r#FFDĽsmՑJf뚼5V`ץuk߈He3q0Jhs69DeY|-;KW,7,*(\+Y4CfZ {ZvU:eF[8)) m=Rsۃ][s6Dk_^YάSDh<}{|FDD\JNwPz9GՓTR]a%"= cF᎚\}Spwt˼ļ+RKV NTF ˦ kzP]MUKWT4y"zlȾVS84iM%9w5"/x-Ii-.i[PT0ۨTS2"f}8vE].FЇ;ŕMvlpr.z_zGPM-MZ~EѾP(" W{qO7G Q5 Y?"Ժ}Һ&G8HK&@Fܲ?Ne2\q맇+̙z #Fi{l £c2{dq&LS""3pD~RSHf:XZbtT} 0Ji45{QV)^t=6O{Q;bHrCA1)'PUV8{).E3ݺi5աKwHt,ssalG_袗zpGi:r?E除0[ N;-5eVqrmtFVQ]֖q X_z2Ô%uK53DNt G}-IzGp>uNQݗ|Sݖf#H#"p龣4-CDd6rY9y^&-Y_[ ]uvw ޲aaI0*x6nkcRG uDQ~{{gYCeެH]lΡ:mo;CcQ+"k0cbIכWny1+w1:GP%Ԗŋbk<|m>ig uU[t9UrwOVUqvGs""1/_N87oRUl `rs/[=ִxTչdY?|~dahٍnj8TV6yC M{fBIa㢅zf mlqKRՁp;vHOaX}"2eʔə3g|tx-.wl+mp{\~eNYRju~޷ [S[hL -Z"b,UuږD3r4A]T1\u)W3/|N5we 5:**j+ !@B (@P !e'`Sٜ3ғ`hPQ A$2He !@B (@P A$2He !@B (@P A$2He !@B (@P A$2He !@B (@P A$2H)#=nYjOQ&h䑞J,j3u:%ί.rW}K")Fq|r>87e1c8ngd0ӇÈ 7>m,#=.-,wt 2mi۝kTEDfu1mW?v]DÚ7k7*uN?>NȘeUz>,6go5HT_}ڵk?~̘ի׾t/|;Fz:#t}3uB+:EP Yg)o:fAҌMֳV0ܥ9;~ouh zmm5:77S2cuyC/ኢv3 sn;`8rYfڤCCzā0f2z,NK!"dnUc$}k 2(]HaT *thsQ{J+Gj|<946Y ۟i3Pɽ]yc׼1`y ʈqj:sIIVDJxqiίz\&X*,\72j/P&huY}ЯSFOwȸIv/Wg],"2A>_] gGBRϺ>\I4mFtUr'8AM U}K=.3nGE-p&M5MȔL1rYܵ[Kz:9cUIS`ue}c >ﺬ ]sذ᙮^3IM%"tܫx_E{]H?MϹ}OA 72 `= ,޷^-\jشyY,>g]iI\D"T2wFD1O,Ub?CJU.;Uٕb<]W+yHsfM %T|ޖ/GP V!cMѲC7^1TǺPQfhť E츕*/NɘU+#S2")Z*?[JFDZnl;Zjɪ)QUuwoYr{x9bx-=|lR2Ơ #*T${kؙvG]XD޹j~S0ES붻u񴞌3zRN ָdr2 ""Ëμ}Uq[* tqoe_Yn҇TreO]yED4FYzCw)>08n .WbvjZYS A궦h-UCʼn]+ѵJDziA^>nu31Zu?x? Gøs >`C'h,[}y鿑_OQ<{l5y7IoOC #IYPTSj5hD|.[Y~P6FUoҨII[iA#W0ArYUOsU;xLY]uј7m|uGS[5-o|2$}+ш~ZݦՋ2-݂2'9fC"/{t ME>ܶhwpټh`ʄ{_zTcaDOO2?u eƈvWfwXG{nkzk}L9C']@׉nUxd>9{}"2s{b-o̜ rG_\5Gl[ 6"̭i.^a{Zv~~սU!g}>}'kuaoŰgecPc v,޷9cUmV5FKӚrYfq^Jdv~Q%D{GA9 ^`UEcY-n-x2bX{W>c $+Ee;^z6\j)EkX\wEIhnKLh5?Kl-jG>-6w4Y:6}t&]35jӻ:v^4g?πņНQf~vV-tpR9+{p%s)0ĪJKK;ǎ+--ya /]tt<= ]$]oʶZi"U5Q蜵Oh Y"ޯX|cNYj󁔌n00jU~]/9y}fQDD{Ø_]_7i d< {"Q& 3 mt[^rG )Aժ$H^έ)&t""~wEEFμy#cHy6̉\+Gtݳf{Sd 냽\ )rc_PES-or2Jּ%@vC-ßڈz{ f`3&Wkaǧsх^4 Y"J揽֩Z1R.n/-K0=ov?r3xN}|s-7̐jllQWWwSW#M=UTk/ ,S5\ R[*" o, ^ǖuEݮmuY]߂9ɣ?aɛq檵\2=wӫw9G6t;]y@}>\g.Bw#"ʲGB^DdjNK?͙ }K펊gݷlԬ5f(s3uu_Xc5w?-*_/m*29TʪM5e9FWI1'QJ (D&Lၪ0S'"~ d LȈsk;rg%L .0k/ h#[r %9˜&Y#Fi{l £3- -=) ntf>4̳EnL_=L^{M`vʲc /?lUǑkBDd̤>67O?ۮеIK}tnQ]NjrAWR43.1Rv:w>vw']xTIqظ18-}uҳNJ?jܳ3 2龤$_}?bxzuVsZY )Ƈmk)@uGx.D|;׶|3f~imNJؔ-I(̊P?݂_&ۊ|s2ю IDATnJuwu_MWʚʸ=e}RQuzgW&9Yտgң%{lg(/H֔o+I"}P>}vFþ|V.*u;;˓'u_u|U>\+oYQ)sʛ\6*?;r:SDD7_FM +>.MɁӦ&=q|r>I0=GnUD~h4SŒl0>$ȉn5wy.p/@co-"Nwt׫;a<^Qygs#(>k#jeȠLFxq/i<%{pZU2'xUW94UߟcѻXc*8~Q$]y@zP2s5<}Wf,)`Mh7[{LYiu":Ny΢˚fΜyرV <՗r{K[B5wܛ[Q~3;ߺ3S[CdzeoȈ<;n ug򏊮i'cFU]5*s_m7'V]2+Su3?e+JDd;^zZ7iu Jcgc^:.Htwrncv?SXcynMsa܅Cz,S{-{?sm\[0/h cMS7o !5o<J :V3iE"D (3i2OL"-"G{0#lniW^=l_Ũ\aLCNێИis£O-]~;ZV?k6Nml\r%Y ~oUEzSS+z#+Mkjl| h8d[޷Ee {ֻv6G݋L*qH6dԈ$ewto Pɚ|c=6UCozʁ,e<7~v-3inS_E.vNHɄ$־Z%7wo7n|ϾKT?J>>wx\0tWڛXz >?reism{s_+4n]ud\`֧:c9gFU剮sý̪gpr"S8!mRlj]2.)=:Ƽ|qge97oT_+\fԥ^Wcu[ і+0w9T/G bO Z,>VuO'}$߿= WtL.Ļ\@L\\CN˪)Kn+pg.2>2s̃)ޗ2etYYCx>NW6džʄzˊTE9%eC窷@^C_Mcʒv9pf;Vg"^ (bѥhD\ &A^qϫڗqjaucŻ^ '2bTǷZc,*Xx]l[:~9~"ਹjUvʚu5PoV"?v[\RY8'|\yd~o!) '¹IktV1cś;nw/҉#6s9VbqYgg#F u_0'K:UD:!"|}pvVA.9z2a,"3g|dž5%\xR_[,H8mq@ίv7DUdkҷ{$:.""Sc΋@t0R$9 n{i6Lkezn囅ƞg[}y>`Kփ,֭[:tС[hI*~o|CSlLȎ?̔W׏^0%#"Jʤn>zk}KCZșznҤ_vcRH~G[?>gѫ}zG~1^89Cc}RL9ODq)"lsT0g""Ҳd~t&+1&>ƣ哳-LӬO^S\芨~xz= r׋9nqY_g-ݕ b ;b,,@h[Ukde]ջQ.G}RW月ks]*U馂7ٴZ^#3 edWbY hYW2{ޢȱeެȧ0+;r<&|r0dk<6YQJec=wpܧݗ :)?&PNfW:P(c;Atzh̋mscŋQTo=vf>%""_:ۣתGk{tk4͎zd-_=Y!"|>V2ϾVQ#LqAooߠjKPu[e cVA^c' EDdrN0$jHLcǎ) ?\DL2ꋖVn\8GįzϺN}yY&W7T}"2QN 4W}O<D& z]fW}^}L=0e\mO<e}FTzr|c>C;<*jsIӦ괃{n gΜcN9+"ǿ˕ƒ+gV̾6㥒ٯۓzixĘmCV}S͏+[$=4K UWupɺ%) 1:^ 侒?W ;vTvmqU" J{k_\o[xUIƒ+uwl1+ {L{ݭ|#34oxYD%e,PVOUmv[v[ׅZ+_/2-۞+ӥݭ"6fU6(s7}Ke}-xylW:E?U>VW57|STI[DMes麾ATOˎҢmNUDYVٴ!>dQQk*_^c&_yQUi~ Gp4EiWuңg(Ƣ]oEuMEEXPZQ#"7U/+ZfulYW-+n^V0w=D0?/stp0F nBɊvQgǜhɊ.:&+zdenF5izO|է߬h|gK/tLY Yw{r_ cIJجyժY(""W/|vܤGz]QJ;~Qr|AJܔo+I"}Pg$Lwj傈R3UEDÚ\R2n7&pS \EhMuAt%3iI]fRnfJT- J*vw'݂1K_7]/6kp5͘@p;SgەW )s,+sS.l[֏-^W'V]2+SzKL\uOz!0gk@WEQr$M?tL/iv[R3rdYSO.vmZZsQY|egOwZhXO-$7ƿ&btS7OcQJ3佹˰uS]m"1ozaIܔ5Xntٛ޻^zO f뺂 MW_x*]4 _+5e״śvȫ]BOƸ17׫kkDTI.5YD$c՘ns^eyuIqzA%@7^lFҀZ/>c_ʾeA􍛤OJ^'ww4]ָ1.bnͲ8:Pm] ݫ1zl 6sc`;ң||8}@Dh>z hHlDOo#s`]D$kZ J0}@9b#alqkHOSDgd X]*"9&MΩlI@BvimuWDD$ו~{$'q')եUDDQ>A::ғ9ԅF 0k׮]zm̘ nWHR@iOPʤMAJKMt7.}kx2W}s针\ns .&#LCGmMl2stmGz7dE>s0cFzn9;Ǎ5-)))iqwN2%R*EQI#= 2He !@B (@P A$2He !@B (@P A$2He !@B (@P A$2He !@B (@P A$2{wԙ ce K6vLet8i.MV9Sd΀kf8-zfK,*]xSh'6:m]uC5C`C^CQ~!{?{?g;CDDDDDDDD#2DDDDDDDDDDDDDDDD4"0(CDDDDDDDDDDDDDDDD#2DDDDDDDDDDDDDDDD4"0(CDDDDDDDDDDDDDDDD#2DDDDDDDDDDDDDDDD4"0(CDDDDDDDDDDDDDDDD#Bx """"$IZ(d2"2 Z  Kooiii44LH:*5E&- .8z骓3I5#*v#""""""""e(X%22ѣd.Buu 롇C]NHU7{q[wgu32Z_e-ش|UjY^j߭^=""""""""bPih =`?tjvyůgP'kZ{T/Wh!""""`]bJj3匄))?X`CRݐ?fE吞G4p^icN u%DDDDDDD:  *îSqnQ ]gq\J`HzNɸ*_*wCPezzDd;Aʻ h )BTt p]WvzD""""""""""""T_˲PADDDDDDDDDD>(CDDDDDD4햚pA@r9.U`z:5Jo_}WcO>VvߩU֨B ʽԺVmی,Jum @PFZg[GhTq+W{-.o$RnwkEj|#׮ۻz]&-$X:ȓ׺v(Jj-O]O  [GS_Gr;zyhS! |{Ǡ$^nwm5ecǎ]t ٳ-[f0B]T4܈ɓC]7F:FbY`=nSbX?E0gFܟ<-v(:㒺nVj_fL x$?{Os 1%wqt'Nd/ͳv4(L(IU/ae'u*N5U s>5hHU8q4wþ΋9)@:d:j벦R.;Sl3%;vW³Fn{ɾ~YFi~ }vz ݳFQ7 W}C* 3f)L"; xõڶz2Ӯ] ~͎:Uȅ<;1 Omvw %oUnE ۓwMEe63vV%z!œZFAIoo}z5DDDDDDD41(CDDDD4/\p…/h4!5{T'kkw0W2s/irnˮܨ?dR_1;R Tmw^)}ӁִMFvV>J&IDATA0}jFn޼M'imJO}A )cT7۶}9PThtZ>L -\xQ.WV^vk+M^⍃;T)nNpoH{d7]SDr_qG l'9'vjv#%,#55 4W]e-4.I!v̶L0\%o r~X?ߐL {˷m~*QZiX\0ǀF(e)vfy̙#s^]؝yVdWiflo mr 6ǔ3!. G/.Ю}͌]L~q7mO8H:[K3Rv6ۧa-ز|'M`۷ q6 <`>-rP$/lmjZ**}^vvns0v'Β7֛=ƶ 8'=۲dY@k<{J3{1zsMʾB㼎`T]idu`QΑ톶`\g7l-v@ k^+¼bS5leVd̍'*@]L_JF=gc>&dov^C:ԃ(dtF0Z.oq .ݒJվE>ІN)ale_4IY9&=!j% ^1P%9byh MDDDDDDDt? uDDDDDt;vlQI.=%@Z 6[67JΘ:ⶼ5[aT3>\ٱnU,I7uJ7v&wfOHVOU-[Q z"ms8\) Wsi|TDŽ%Sg$@H-4vP[xU8qO,HIe!Fg?|8GPŶ?%/:^.hVb<_C;d(Ť ~Gzؽ0EZri"m;dbXNWvFmN[= JLzAw\3ǀF$eK. ` 5_4%ķ/o+<{7IK%emGe5@sͥ}%,Wϓ_nbi*7u>K ]GQs~wƿ] ~6sf1ޔwz~\KM=GeEۊyn}4H)9ii @:)b;QT 5+t31 ub"ei-Vk N9@%UT^JS#!!B+zb,\x,}c$!'!ޘ6N-r Ҡ""""""" 9?YiXBEB|i?ʈDn /Tp@eOq|[ r9?j,Rm/;VIi;2B"l.5U[? oMҺ n|xP#Z[ŴVWjr^hz;3[>tGMzB25m;5ۉTSgO7y BOzZGCE%`WegEݒB'4O.!M}Ds[vc--,\xb/w6 DQZ*`O5li؟G*+O6/K~Ga k" 25K"rƷ\wGOjz$Hڗ& S_.߿PIó=/A"cv}Vi~bЫӌQݟJigֈ@%]׺} S{>j2Қm(Ժu5Pk= #G US@Y`π*J\Rn ¤MXbV- sMʸBET]bVպ~ &I߶R˗56^S5  APf|NXb>\lhwv)1VsЋ7H?Zx p2d 0lٲ2˖-= {jrk<:_^]|={k Z&PO2|S'qc"_чfeDL K?zKM;ݚsҚ\浮D OQش|uiӜqs~n"'-XREdT"qi~p0Gq?4.fAIk?P$'v YmLA5*]ppǶJ7$sܜH3yIi1ŦjgYY"xR'Nk\պ-%oYFj^ERZQ1&Z3G4u삊.mo^ 5hg-""""""$ȬL)_1_f)))vүL˚uɲώ7+ H-~st!K7xI漈xƍSfO&V^j\\kd\B9 ȟZTz)nfz{}F&Č 2ٰX߁PIΘ3ta6Q~ȏu|8X,\',kty:EJ̘S4w4 !JR'k 7$醵 Y{.D,SEQNU[Scswan$yn@оFVt9G 1["""""""">2}d f4gΜyرK.={etJlƓ B O&k-3~;]@yђUAx`=zu.5y}|oi.<VrPg7Se4#> y.!z-q[!G_#fY?N7]4խ/n=ȑ4 Z~eLu)avü)=qn{i^63ki\JT4Pe^Q`h؊^+>Ô+M֛@9iuBFJ ɚ8+e.)?5)Sr0 x˃xzoBy3{].S&%+`G8O3+%gt&/uꎫ#UW֯; @H5vJe/ݝR@Oj`mAUwF^TG1S +yvWbqޛ~O%D杻K:oIꘁ^*Km^_DDDDDDDth+%.DDDDDDwlƓ-Y?&}I{{] cXt%4$"⦇ hy-esͿSS"_ˮkV/Wjkײ :rk xK/=U>[V㹜rqZZMe?K^1;7KggVm#i v>Ƕ6T_Y)"}+Ulv.I3}^Z Q 0c5]vMNtEIj\nFF X1eeT9zMRokL1?Lf^njI7u&qgrM ]Le_eQ./M *),;lfFgZLr$ qmv/4.ira]S]v3Pawע{`悮I.dx쀣$7$W<bZo&r\_0^ԨMЈ̪nLI~k_uڠ"K`{ZNr$Œ(mUAWa̫>iP~D͔_n~x~ҽ"""""""~oDƇA"""""3a:4b;~OtC6uN;e~0L=kL1JQ/?3zaoI w/I@1=Q-blזɣYM}  =}t!r>j󳦿Lr{z~0_{7`dhɁ ~ ao'/PÖd N>,s2<󻦢2pW['JdN} q;;U:;Ru*[WI >&؛u:G6y֌Qm´#'v R!Pj2^INNk%W%—y|Vy兠M۱u%Ϯ >ꡑ\m1M\i_~wyUlH3-_L܎v@fj, ԉ;bw r$jg<||];θ/Brcr /}wl+r)Ĥm;G Un]j}97 :uz"$N2KDD""҇/IB]O_WGoDƅ͆_>' u!CuIC]Ȁx%w"NQEr_}W?Dp˳+m懅[FI<+*OTU0*ZQ}6L`ZTG;~TөWr߮qڝGVdrBHI._iz\^r:5cԏjJŐCGՎ+[{>ow/B-r{jbT3I/iӦ͜ % sehDHoiIENDB`flask-openapi3-4.1.0/docs/images/openapi-swagger.png000066400000000000000000004134711475153525300223470ustar00rootroot00000000000000PNG  IHDR pl pHYsetEXtSoftwareSnipaste] IDATx}\Tu?` oK$i+qS(koj{W^U^?*,Z7vכlEWXCQPHff9?pfΙ|9s39/>|Y85XsǨȈ%FSOW^RT RT2 c.f] k#m*x_朣q7=nAaȡ^Nݰ!1v03e |Ź' j˹@?ݪ`!狽}̹r{V-^|pop5/L7\go,LsZw';wW'{0"!}_D0bN/&4yBI~ QFFO?5xHT`Dydel FHpq˚FcwDGnPQq1DD C ( DdP؝*crDzh8)8h4r,qե[ȎՁVߙ4_צ$MTL&sP Y+{p,r ~%qc9TXeFNXTDeY} 08G7eMltt XX!(uuNPƲ;"^xR{PVK/N!AVx@xc9"%cF<8L?H" - e n8w?oo[7 -%VZ1 S_2%~8d~gF4|zv;+s;W.qx'9^9zi/0LˆK|֯L0<<m#e%K l^sEFg&#.9I&i%zBKW0 E^ev}}]WW[ѩi>?kֿJR7wRh7gz~W~ӺYGlglioT7: t^Sv^SZMmW굷WqϋV[[+7n 5O9x_^e1xzmŵlͅ fEka8dOI5dCk {_at3g?ʿ٦doϯyvfjKuϯ}n#2dNu,ѣn-gߵJ7uԭ wh 3iGŌ5=F8v`*O&+lBs乵?qRX=%oϜگ;%{׭Y%de\+cyd*IQ}{a|w[U1@;o]W(r(a#d>|7|󫯾pٳ|b:.77777W8/h:?uԏTN;xW"o#tG^ Y EMF^~#{@\Oہ A[?7Qw*N^wJɸ?T(ڣuGzWqOc· tDža?S31éމ'v[NOOOOOOΒ iڦܲ2G)NVE2AdDt'ᶕ `'.}Xveҥ?DpN\чetT|$mɿfG=_og%PBVy2Ҧd=\a@(c=BAD`:=uwwQTT˒V222ccce+cNƖ3H{:J_1gAut/WeUIHOax)0<1hv @7+eJ]z;4H!?et!߆l Đ^hرDtGzzz>;v?vcǎYNloo߱c?}NGYdlh#~-*HF)BM/_Y!y\ % $WF:YdfWw }/2l22Iʟ+Zxa.H"Jw0~$C,;;\^8pe/ID۷o?p@QQQAA+6l裏Zn]}}}yy ̛^2999BVdyUs𜙮/3g%NA LŃ@@gԗ@g>0.%#2HG^/0i2`E}̟5b.3 0YukV Y"CJ3CF_GXh+_Y2Z6u/^uC7ǨogqOcwc+?vü>{5LHHxwx]vڵ˜e!9sџK S "Zdϟ?Vڵkyɿ/-W9wܪ&!rĉ"sE(sN%ZmQQ) ?駟^<ΜYfe|A"mJ' vn%(7J*hLypxHɀkR0 NO2R {-/8z`dw|"eV䐒!?eߣVX"/<<QhK.+--ݵk׆ ***^y!A;w 陊'!(sY"1cbZ_}ڵǏ[+ $.Q.'Ӷ\`}XM.1 !t-(^H'_WEEEI]z~ˆKD$FKq uoIY˺mZQHX$JcDpdd/[nYF]h4swMD=І """ R2EEE č7ZKӹY+K lYbQ6-F% ]]]eeeTłp7ϔpIM@W|$Z%6ƁF&'"\ )wJ0Yeb } )c/V0{l"jll2III-:}tAAɓ'aLf)..^hQAAYVk;U2l9'7–Z{(N0 )N[^^ND999:Y_o-ܭ0}2K2F\pJȥȼF6LC,<0o ='RWR2B,杒r(Q7^$x&)g kuww.R,Xpŋ ޙ3g~555&"2e2{zz@ĉi0ebޕV?uĉo;vʲ7*%Q^^+de4uTo]~p?2.j; W;40=YUZݔ EGGSwj4!+%IIDLkBnf(a (E`;vرc-'?~޽3gΜ3gy}"ڻw/YN)//7LDZVӳgϞ*ѪU,(fΜ]{7??ߺ*v,YxcǎYN?vXQQ=3^/ ٦dʀ⽸@3%KHH%=`ĥ bX?,Ď~YVHNQJF _^R31C~xGbxoc" rOL=۝«?,lቘ!?T31ì^wwUh̙s%g q>L_I # d9}EEEUUU6Oܵk׆ -[f^]wݕoLBBg}C=CJEEEqqqK+7߬ZСC^m藿w u [O`3mq`drY;@# yU_BqlVL 34="8D"N9r&=#I@qY `k2D爈|߳'zoCSSw߆F1ݳr(a}(??ڵk˗ =Cn,^z޽cǎ-..>{O} 7 G|Z9g%)ukVR괴?:첼Xݣ e֫Nbr޵N1brJVjk)===9s%@tqqq.S24د)׍AGc,#2o.W/13 l?%tkI?a&!b@V~/v>$9R24__͒ʟ+'Ky&Q?_j&FOii) qqqnf_\i@ނ ?ZMG:_ޢS˩Vttk!/+t(CDj&fI;LC o61Đ" _;I~on9رcV"Gޕկ`wNr[.3 )x6t¯!ƜH?xsS !GУ fds FrdsR# "ktIx<|ĐiXS`b$0zPRr5<Gd/(94_'320_x0x0̓j1Y H @p P<ۤ:,= ) Vس5!/%mlz_G- %mZE62Biu`Q,_!\n[a]"bOJ/pH7' S;w6K3I2|1)ïuP@T{+Jz|E!Շ]}F&GA!W2$(cq<<χVPyy2nf `?hYt;{+ci͞E'~uD3M ??7 gHIkOG\ƿ4B +0\vz 8(W F[B\f ]]pZpG"¥#.yDƐq'S2֤>R{@ d#Wj<p`X25/|}Rk**$$D!`4ا %x6p$Au`;b:(#]c1`6+WWl|W4ZHH&uCJy8ct!Q9(C2r<چ#2ӆ0(3hpelA>edA[OH"R* 0'`YX9XNRB#Ǐx 7nꮶ^ o2%+Nyݎpeo6{k]|GJd|ʧbMJS2 Ȗ3|eⳠ?(撬 (#e ƪwF0 P(J륪47f@TRWFۈ B32#<B\d A-@ 31? 2C!kde0VxS,2 (bAJ X`,S2qde@n8Vqde@ٍKnd󀋣9% 2 2G)'qB @~=1^ mJFxT*CBB Rė!@<˲FemG_BDdRew2)0)>GD k3 9(Ⳋ2}MD˗/"2gٵk֭[-##h4O>V<)Z%Kjjj>䓌 'mPwBŝb,;8q/ CUVVΚ5K,g:::ʐl2iiiZ,tɒ%IIIuuuRJVA{?\|9 /#uDrYTtuuYN4?TTgz',,< fR,=cbbL,7>q"O$`diPѣk׮t˗/7IJlKK˭[JebbbLL cf9tDsd$##cYYYDA'1NSSSWVV&UVF LsszĉW\yfxx/(/dNfӦMDd~Ne|饗JKK,X=Oʊ+x $;!6d~Ltw&^H8S(,~uP+<'U(}}}wqۖd&2""*&.2*ڇ[Gr'̞={T*'MDzlssݫyj˗////"tܹ˗ U=*df:)˗/_. tQ) /^?uTO.&MzWNoz˛wi&_ѬY~e#ƍ?߸qU -_<)))))IWF8qx!3z "JuRGG1'9 koom 1/&]ȮGY&q]zUO0!hD999O޼y-[[{G^p>#+V~$Q<ԩS7?)LNXgYMYv5ei,شnqI_[j]~zd8e/i3 ֭\b& *;tNY\Piin(]_+hZWڎo~[ /nc3sRn0S>7/}TexTZnUzֳY2eᑫ]${ #ew2c)Lz:9w܌3fΜ9qqqbW(cٝл@שwvv)ʞӧNs<ǐ iHh/ 9G;0`:&ȫGB )&L0n8"zkפ^{ӧOϟ? v݇ kIyf'Ş={6oLDk֬y뭷Q?^reĉ?O'FrʟgZAke \n!%CD/n0xƱi\V̴\Otwmm 3>UKׅږ~~ѝ+4yRO]]َiiw=ufkoqĎm;kn7dhXmډyTC\:SA?kbNME$%ʲ;a :!" 3@.Yv'#L Nexu;JIT^߯`G ]R[(,adi;2 ʬYf׮]$Qgj&L 7n\zU N+..&"O3R999C?ܣL@{nJD%%%nlRRRBD/ b7n_BD+Wt Loo﫯_ҥKDDDQcǎݸq`V}uYmhSSG`/=L]?UX&ޡkѣ; Ub>ЎsśN4\gc`ͭ'rƒֽetue-Mע}#g-^e鈾VEMׄ2[NQuk}tA}aKe>Si ?/;ѫ9ӗ[ 9ɠ9`D訬$|rceeeGGx #VzF)j <`z1>y y9sy!9A$t5k߿?66O>AJ#qqq&LP*:.2oNۼyt^\ϟ?zIC8)>XOc+V())l pkii)͝;ysW\qF}}b&M;wy;Ǟ]{ SwIQuolZge|]߽~Zߴ:ݯaTNA9#r /.m;]߹:U4wG_9q͸|a,Qme.ź#*Gwb9СэJ]4MzcfN^≨eo {Xzs'(uh76\@sH(3yd8-~ 5:el}J46bu m7\v֜k*L?DvB$g.ΠS'uKLVA a0QxjH>k[!'Q}Rx\CTk'5v^ׅ JKKmQǏ5jԼy󬺓?ӕB`046697VfϞMN<}tMMU?"66VxhanJJJ^zNG IFcpQ(*'yy^R1 #(c;/aY6 )2fڵfƌBBbիWɓCB H]Yؿ?;wn'EFFƹs> TWW=#b:tڗA |M;# 5y)XwRCD 3u?>i5ZlDtVe.$P]Se93m'v,kUgƁvhIFLKJ0eVcL3xs{YpgZ?wD3%!%sw5*>>>11QVJJJeɆqo4h2[nz5kH?~RRRRRRFFFllkWX8!b2j^pQ** aȨHBa2%l8ST&AIHh4B'"7.$, IDATihhHNNVRW-BPfRWD(1a!Awĉ)ꫯpYRXpU}JDf8ën(Uw2DMMv7.5]q \k "L:.9Hi5dqǥj5ukM7j{.{iNDQ cf uS{G {XڒL(u KץCUTQj6[:\՟f=’i~>M7j`2u4;L Qv|Td0ڛ}~<{I_} D* &2u4/]vׅ7bɦ֋%^(g/IpМB4P`2u7k42PHzF*ScmmASk "ٖɕ`sG_-jp@Qa B[?iɴemnm>mՇV;3xkMeSnYgyfݕD4VV?3,Pr%%%^}"$l~ӟߠ f~`n%o,k2al 4fب(^f/ ð, t'(a ] k3jiiV[VYBtX2yf@U)VKDXG} 7oѣ]Iȓ`t'}r&4ٱ:3'^&7i5\vʍ1[_qLdMdnXom.Y&:rL+QyV4h ROzlBȘ ^ODeF6wYDD]撗Ac oexBSYt5Qɓl6r['$q޳KCsEAUlzf^yQd:QFۦqNoζKD0o凌<{ɢr'Q]tSԔ) DKgTLD驷8%-gd(:e Dxyo,67$:%{ ZuU*k%CBd.;ŮX8d Sk׮UՕe)))6m*((HLLt]:|D#x=-[D̘1?ݿ5kq<)ǩI1G^y^İ&ӿLP=<TZy1|"l;$QСC999eee#v;S,+ T*'L @bMG-}l9StVQN4.] c7me)=jp|\:SA4~^g* DؐHР1ًf|P+3kMymY:^1kvQ퉯:(5wdpPiiC;rQZdݘ!Y袆;,#&fg'ar]l6fOܭS3<\MDp/Qjnݤ5oQB6|OBxׯLCC÷~",*%%;LII s?:@vvvz1_t(deei4'`!!s;+Wjsƪ0aސ|gI)*@D0ANk|^(Q^^>c F+t(d0ZNII qѸD$W'[?% \:?yZZZ[J]55j $f"8qEӑY>oNnr9RE6 ؂ Ż<<%Qg?^jrS u!Fa33]re"ԌtiS4˴)DD--C0} HDMDv~\N-?>я>W_}u2u<3ssNHɰ,V&M&0fMM#!(c5Y@ۺu[oe5Qվk:Ni7o}n:RrSד͎i1<:QƦ,V0.9SotMDDwu6kMD4 O_p=_liDDcF5H%|@zT҃Sϝ'ԅs]D0axaspkǎa뉆V0̾R=ZEn[o'7'Egx}_W.r=]gsVVnq{[mɶv"1nݮi47s2w^vg---pT駟їlRZZf͚R1͛=w֭~C}@h4r|T*c>͝0qB3g7u|m__RT(B9cY6$$dzTs3vq|=߳gO^^^MMMRRm%))ѣ+V}@|v2(CDqqqBV&77YS2'O3J_>}Zܠӧ)2BpܲN>SZZ:2wq?+WLfs ]rEX.CŹ?+oYVPqD+f "ͧMMDvz}]#{GDTtltM"ZwqQy_(J)ɸ٦VePٶ^۫bW݋)^y%teBnVšZZئb5x(A5 aN0rquϼ3C͓ʋXPF9q҆Iu"SGO8i3SyRDdԘpYnu*\ Y:} "#cn!.ja@?9}2tc_^9-o]Zw0w}anPLUߒȩA^wzT"ZP.sQD$*֨۽С"v<|Ndt76 ÌN5L'b}Q2"q,%XG=mVf˖-J-7)EeezK2eJvvE6mڴi&$U~_]=?(29+~)|W.(#Eej1"rWGDD|{"">>>AAAF$"kɨl6[pHE]}q7􋬏2"k׮E ӟCh`hii ;vl_o'f͚/~iii?~/ٷrgvq"ֿlԩfy޽se{qbxT&+ u.'sҲ&2$&'"RyyJJ)?<|־|k7cƌIJJZ~]qL,^XD^yM6%%%]^d?x￯iرc߾}"O^ #SLYn]RRr_ccDZQO:f566z@@oKsKCCRimr2MMM_Z^[g?YUUŋ/foرc:%#"~{HHȾ}z;oIIΝ;@ 0i$oO>[k>}Z(o2~|xdDlܿ{ ȧ䈓m,"$Ĺ4~OcE䯹푂,ӛjaj-F8}頵TD§{0"rݕ"vV2rRH&vZqQgk HĨ*#ED?S""uHTr@vio."ݻ{"*֨9e-U޲IƎR'':|R,Mơ,."Rd=(N4Y;5|(S9}ٝo-V|TTTTTݻw>|b q秥?\~FdD?ǧǻ' ˫U4!{yyt׷+\KPFD֭[tR!+}O>v예9rdQ/bo-ALd5d$"J^,,/T>.y)"INF${-_.G "E뜌ԟ>/rH'l<ا"/r2֍[EdX" ӯ$"8C"2=覂viMnNDվ`ܾyMD76Cuoa/v$9fZdGt&8rbhsR8FԺo׾C"bc՗W BXDt}ul_k*s,G7dD&O-C WZ2NS/"RTT=Nh`©@/~ϗN7]D߿رc{ >O?'?Ow|>/((8vXsswq???n78QPFD}Yŋ{PRsر ;vQ]yx≐7|Sto\ -.YJ l69|cJKKfp"7~gӎčwY!k/4 zѲ?3TG {=OFo,wv3P{V>=CT_g~z 'd3t>5{ۭ+M 6>Bnz#~S' ~ᭂʔ_R$QXXРDp v١A_~WG)+kձcǪDdر< K.}'/^lX禪nYtxqe]vm}ˇ jO?~gϾK"rm]veў8r@D{jO.=aNU|*"rܿ< ?}{#GL=>;qSU %"%%Wq?mL_.o/;egK2jvGW]X5<1l/=<wퟕ ~d׷h|`/^Q]Q+"A:? I\Ix$s_I_NltXprɨ|}uE_]ad-7r[Ѧ6MyDʃ};l=+w7󅝳5~IrЩS"k~H5yҰS"1;?V{}n6ۚEd_ί5f&/\pr[1>(~r)j6@ -kDlFoJ3o|xb3ÇjqQu|ZDBBB?/..P[544(AB2E:tY_?_???5@Rl-9:yqǵ*KiW.RQ2?///$$7Xxg)6eʔy)S/pЛ;wnDDDYYs5k֜>}:""bܹ>sED$n|1 zǣew|Kd8ѓ?%1?{O-"2nO|i{/ 5&pcHPMӇwxS&oHCl]ȝ7(3 |0EPxLM{0αXq\gk)r@7jiDŽ|Em6х_ȃqc4dߝur3ޠ~4ٚ}Fb/N{lCHi$"Sva1Sc-f-LS6/"Ͷfa>Ȩ?PbǗIMt# 3}vGvvqcEU__o߾9---GlJJF纁UUUW_}JHHطoߔ)SM``8oS=ԠGBٳ_U__?iҤ_nʸK/:t(b IDAT00𩧞t:CMYioI'l]_(?>`ztq@/g95TᎴq+XonUIԔJ0?zr[ly qRKGu|j„ կsc^^^^fѢEoӧ>D tY'8_ZZ*""\Kcǎ)SڵK=o~u]-]mmGK}}|l6xyy={veWuuu@@gb{IcCchh茸>>>ǩJ3U۷/!!_()??AP}(׹cǎ}dggДJljuVA`Ȑ!˗/ |RӧO%/r%~/ŋ})S,^X9[3oO>)"iii#G8'"_ԩS:A7qn 9Ru_~ftnsS__WUUuZhh?xr #@S]]=ydB/BBBjW"RZZ*"W_}u7WWW-]b^ϝ={'NtW5*$t._{e00;wܷoΝ;Փf7n/\gO2g:t￯WDDD1bԩ&M 8ڣo:Z~Y942Su6e.hddu7͎ &$\*++1cƸ ?~_w }ƌfGA)))IOO߱cѣG###իWmJF((#"%%%%%%} g6cZ[[{qeooc&zې!C{qeA_e`>;;۱bZ2mmm^^^&%(+eDϞ4yr^1A @wᢢÇHxxxTTĉzBe0(Ƕ}f54ee/r]@@N7ƛƌKJ (w ʴpQUUU9dʠG[Z[[ZZ#CӝЌ2w3>(( (wh2"JP x{{A }{h/kkjZZZ2t[[^r͵F]~.GEWL7N2JV J!(p㕕?477z{y5֦6|xxQQzAUPMƫ3!(3uugΜijl!AAA!}Ato_o swKEKKIR2 2w^0٥$ű e\p}\<|<"(`5 fvW8l6[[[]-'5ڟoi0赴466ب/6(gΜihhMjhh8srL9o_o4ZkihhhnnG"EHMPN@CPdԬrҢiH13ݿO@?AP@/S8fe\?!+]f\+ww ׳..p\Օq2.Ǭtt_8e@@Pʈ&%c鲨 d ) A6㪨 ef"7YWpaCg0u ..#x0~b\ \c T7 py;KZaaEE@P2$% . epI (KA\@P2$% tA $jDӬ A v2e008t?+CP@L72euNVkر}B'VqPwDuTp 0y͕nv7"AmMG|%mMn2O(de.G &!#ΫU֣݉7Wӷ7< HkEAxCUgO<{+K4(sW񮀑Ѿw mep/jo׻Tz_5u'|i2h\LuоLu7on덜2"Dd.j@DJU7nki \H萫]+!7\77zS(taet^W p\}lNh )AqHU ]vE A.y..оJ(FF~!ί{m))Kٰq}`0e&pwNΫ7">~2 0i2G\ه;@Ӆ BoeG7.:ɷt= <1}9>}`pe q~׻pbr. ysp_¥BfenM󚽢z? oj gz#HAAZyԩ2*0y~@_[GgL[{I7#[[z}?MGwƦmh 6!!!"RZZZZZ*"w/0 L]opƻCoRsG93ca팅^t[ V[&f, ڝ&\}nϫ^)^3555rQde_N/;U׻9S?ū'ro*5؍T7^OJJJJJ>+ 8}Q9{=%j8an2{tyUVjS2b:=+ DLɈHS6eU"҃G(O6iO}PsG?[ N'Ї E$)))99Y}QZZizhLHHٵ"dN59WG?9ǗlgEkɣ5'} #?222l1555fl6Xh4Z֞] `@ })'%^ZFqkύ4hTsrrd쐒GA/8`h؎B'}A*7[/7hqzX7fOgEDD͍Sjkkݗ111=|رRfΜ͝ cƐjuuN0ڝY""kjjVkaa+j븦`3 ò723=hrtAG71/x''G L7999v)q_(3<'tOf j|+V-֮Xl6zј>+"YYYݗZEDV\iZKKK CDDDJJf322nJv;vH̙3էRSScbbRRR\VJF}qc.4T uUUMCq6~in; ̰dN@3rbS/nff5UeJKK322^LMMՖqj̙3)Jf ZdE1Lv222L&ӫl&99977WL1 +WT ,XPSSc6fsvvv^^vML&5O֔愄TYYcTmhٲe<##h4fee)ݬVSnBHdddA⽔wfw/k޲t[~mK]-F}w>3o=_4.͹#zL q eet=y/jjj,XP[[IJJ2n*m%!!5b6-z +j[tm$E-K0ʁbqS/Gqzn[GMɈOMMͺuE\\JJ&++ \&L3喰nL0aas/P\ZAu{Uc^RTw i8zTQFDV3 'rrrfmPid2sfs~~r|7;]P]D5*(S]]f6Ct>8ґ*ڝz(E7ͪjkk׬Yf1mGE6lظ zˋ{᭭Etݨ 2[/ ^PECOi+17U !"}٩Qϟ*+xhYqucOhk82ڍ_^엣FNl[+=k曓RSSsdgggeeu#*---,,T#N/efK^^RR`08v_2jƮDB#3jF*o*p3sL}JFDYY2ijG#`p 0{aw"2w.zvpiAnVȯ홛R+5'|vҞ8JL?:DD[tmJvS&ΰGH 7=\P婆3e_\7zzI+;;;;;d2-_\|)))J&77W;l6+%bP+(VkmmmppL&؊&M9JՆ]r=ݭ; U7t'elp>M%=KS?"N ʜ*S͔?i/*}ٲxGΧl^``HLLLJJR˙l*:899Y99s6Fc4Cj^$77Wi?.$LYY];aر!!!JD+vdTVU_{_)Sʜ說bر5&evPDk2Qn>-3rbYmqBNdDVn+--ȰǔTVV<&cL&aRRrPVV9fQb4qwd2)O%;;[  TuvPmmʺu1&)!!Bׯ\R9.,,tц#""q)m [Ձ߷?EDϜ5gZ*224Td+іm+£g͙35s U杖GϹiΧ5۪:_jΏ}YnS.yyCc~ޝsGuohh-;|mK_54k׋{t pӭFL]a΃F]5\ i=ߋeq3dW"2"\Wuڲkkf]1WJB%33S%`0XB7l2RE);vlVVvh\rҘvٲen6|*Y?/Le]}xgkLsC C˟VyPk_u:]Qe~6Xp;noڝr0SH셺/&߻~ۗ'gnz1J'Ykӟ]vDf/zu<}=Vp~箶ӻ miKS_[[mk_#9NnV.O/c3v{.~h'6d \䇓´px8 4sV+q瞨pWΤ[3~p%CxG|Qx(ƤsF9c 5NtR}!3gzڬs.#SAd)3vqqQcoݳlw=T0 ̙ D٢l gwW˽||/N(3XX8qĦ~pԟQ-xW5v7һejjj-[땢/z>11Q[TFF^DL}K)L&AJ-FQۤ6777//ڮI;cQ%'';J%555%%j昘ճe˖)VJII)++3&IXbHjꨛh`_>}.8!n΄`)z knD2"u'~g  jNLe*`ްpY1' up7"5~)*-O&"ri΄@]Vap}vĜ5g1",>owX'L{sf=R.rnͻso9;^[>Zs?oMm+o1+OΝg(,]y^WsW&=4ౝgۭڔ4 o..f;y+(<矺oЊtjHrK^yi $/VM ㌤~.]ׇ|kde/+u (^\>\}D ԇdDejG!9sz-W'ھK L||R|E;bhƜ-[k6]56rdZ,XPXX<&)555!!A+VY'77711Q-b0uL&X,x= ?6)ӒWۊ[skay[}ՉJsoqHջ_]=a᩶ۧ_L\DD*޺5{gUlHɄOZ~ݭ_W~aI7US|biųZX^_sگfynYi.o*/ܶu?n5ڴ-z`%%0sor8?s򤚒1&3%uy[}?䷝՛Q|o{/jjr{KZ{.B:QNrYLjڿڤx͜ o?10uJsRqBDD✦dDzݏL;S> wLoԍQ.jL3)Ʀ&>f(#{u eD$`hثՇǾgUsj#&4y zIUZZZ[=LjЉZ1EwI $$$(j%j.IIIj&55U[ZSVUS[[] -KFFFjjj\\c'GfY2-NJJʂ hkQvy{Syۋn d.tsgopH[ʶ9 hM;%[67+锕 1\M5oǽڠ`R7c ^%Okڏw~x\@F,,jzPg禔}fEVsVn1JW#ammJ}\]ёZx)ɤ[,XNFIIIJŲl2fԊ;0x?zV9JNv *h^5D/3"fK{ϵ5n[ED$[PԆٛEÖ:YDsM7<>ڋ}fcm[(Gɋrλ=Pvo򘎿/.s@q#EM_}j|^׻k֞ z;W;in] >_=1|\n%Xgi{HmcvW}aи]\[JZBt=֭馒]:ں/.B9b4Ցʫ\RDzh9s:Z|gKu_FeZ-Rd2%%%`ݦd Y("o( q;uclS\&,ܾiǪ.G&HMZ$툈تʏ)..]qn Md^jG"2qG|Stݢ1]tB}MNTz`Є ]^5f{4WuUun+u8ݷψ 9BuڇM{ޫu_O u3M{5g64 (sA{i33jR*"F1$$$"""!!A8.}aرǎV`0TWWS:Lye>=#xXu9?wφ{]PRk!]6{Sު9S3Eh$U/t-.ZRf/[+Dg3=&%O:oj}ǛzMgňis.w/o9We"(c t#:eꪪKucȹN+e }CKk54vҸ罷G R}GwVVT{G{wIDrrrFY, )FbXc}vq W+VdddֺZ'//oƌJ]-yyyiNʲKƼ.S2*8ovSGNvZ&;]9mre܌kVasvZ%M;N]q4hS󦣽mZྛf:iId+< K6n]ȼ}oq{Hm۸TEo꬘J9o_k涛b+8|~uUqsa(;;Wv}D:_-Ac0T6qϛGFw^"nx=!FNlYj*۹N/"e_w*iZuJ%51J|Sf&MwR+nʜddddff( Kv{KMMVQ%''G X,|q^<|b6III+WԮ* Ԍ1)))Noj̙&IyjkkQr?N/ (z}vv6}\([tGilI)Z""2yqNGT%q/G""-9M"7lY2&WtNUe.y~ބw)7#ya>Y!ݖzNө{]b#N|o3=yh3sk.5[w륝W^sZgbȘo/𫧧ͨJQ^ 26㶰N'K*""hԸH}"q{"l@7$뻚U>{ϲ A $)$JʃVmm]ljBkv]]su%nڒv MhBUj$J  $Gruq{;{o'?8:{CP&Pgdz݆Dvf\Ls&^W۫oGWZ[h}ŦNi#9vtjMNݷy֤h/h%???zi˗/f\bݳۮ$:ϲe˖/_M̘1K-[n&|W1'Q;:v֝JYYYʢs:"\INNl,X=RVV֑N:?颱߸9GB// VFox'_B_کB2er/ʸ{[ yOh3w$d3OS;"הTVօ*+MW5NσNhvʺ*Ee N[3>}nj)>߁QPFNWPWYo~oˆ{n6g3ffL7SgWB-2}ldܢK:B6{~}"}jLvCp(ߐS^IÚdT= ;:u_~/k{ׇC8`PϤV.|qt i*ӫOk[lۿc;z+D;%;:8͚5+QVV%ciiiEEEE:՗*M$1<_w?5yޑwo!#"7KW>4;gͯr3+>{Ws~>S{MՌ B(?qF%m]rآkmV|om~{-˯lvU9+]xܜ!l5f1ʿ> nѥp{-pLU;!4W LtwyGE~KS_ݏ3oMv9[X_IGɦ2;'wD^^7M'w|ꡊqw}do9L۲؊K!P:kƌ'q9;Էo|17ܳ`s7i8Zj6a۾Ε~%"X̹FL_F{W>|{sk~q{Opr؂3n6| #ӑ{5C~Y h ,^ṯa=aU[j!0{~roߘ8GC֘]  jg~*~@ȩI-vl !ݹOw>Rs?,.ʯnz/?LԦ'?g߮mVyTڪ/)݄QFuu MƧ{}޾]]EG/N&8rrrfΜB(**n+999???`Sqq_BX|yٻZ IDATt.ᔖVPǯ8f~s\l;s?ͳUl(y(:ǹ:|3iƽ!_h5!\82㲉#:ڰ| cNL k+߼x2q̀g=z$3Eeeek&77z5BH$+,,\h^2tKB7~..|reBG^VUOWstbѮʤE^JMMMMMP\\\]]lٲYlٲe"HVVVVUU痖8?E. ;{8_gV ʄ?Zw@uܷ 9oվd}WWq*--7oީSPPO $n]]ͮju RG뺺uv|7T^s1jسss5p>wl|fށonryGUoI{g]h]Q^ٹmKL(~S/ޓnglSغ%;RxB!--mԨQ®-xKLHmt{_+qݣҙDfgggggv.]:}BQQ̙3cdzbRSS̜VPP.--mrB$ˋ?RZZx✜n"c,X{S5yҨ̜9ɀ'2Qʏ{Ƨ'~E9diXi kxbSvs=HpʕGdff&'' KYYYLlaaarrrFFFZZZiiiaeee!Yf-\0))iѳf̘1}$MB0!ةȜӧ7OĆE5'---55, D"̓2QфMJJ̙3Ν9{Gy&2Q€GM}Dž:$=JW)[u|mC.k!yϊO; Ztp>efϞ=k֬h%0k֬62-\`A$i-(xeb#O<=|򌌌ۘ!hP&VL-Z|H$Z'vrѢ!!㻧ox蟾nucw;ʒ]}ywddd466RRR"HMMMcbB۷oo."\r̙@Xi䪪X?FfffVVVlҥK .Z|yaa 333Ν|񴴴ŋGfʚ5kV^^^YYYZZZM^^^^^^ ,_|K/^\XXtdee-]tѪrrrbgΝZߗE$''ϝ;wҥo]&''G0&7Yretwʕiiiiii*55a-ZpҥK.]}y=lҥgnr6999 eb?$''_!y8w9t|5;Fg̹,///;;;փ$uCf&KΚ5kٲesW\Q]]D.\x뭷yhd奥_nȟ%6ΝtG/)(((--]paaaܹs.]lٲOiNNNX+s6DUWW-5kV$={v4(S ,9sfuuuFFF|hP&555:[OO$i5jTtLjjj4ͳx֦mC^^^t򔔔좢淋 V !̘1#+)hTfΜr_5;0]a/zBuU ]]9@P6gΜ{o/H,xH$]reKAAArrrrrrJJJjjj4`Q]]DɌn1k֬G\믿>sŋϞ=bJ&mH*++kٲe/;wyn0*//oƌ-eJKKfΜmZ1ݠL|WJ$---Mvverss/s̙={vh2Ų#Ht#Eo pNrӧhTTiii^^ٳ;Hk6?oG{h@ז@VPwsmWWq9Weee? ;wn4PVV6cƌ3gVUU} _?wʔϜ93z/3gNQQQVVV~~~xw1 <#Hd̙_}͝;wʕ-zGj ~-^OII-ĬYJKK̙[h999m4BXpaF/555-jm|AAAMMM٢2&NB?eb c+MEݞ1cF󲣻-.?ٳ͛'(紷VKN5lB߮..S@ɋG'''wu qƍ7.--'~=zS?뭷V^Bʚ7oެY Cm޼9{{fffłіhXf'oVZZڸq "HZZÇ ;u޽{gggWWW7x|#j3w ٳ/**>]uu /Oė?0z{^vvvcg%)))EEE[__;XVVV]]]TT+>)))&&=ؼԲX +`ŋ;,*VСCʞx= VTv'e&8;àvu!mG+Sն?VVZN՗ByyĈ!QFޱt[oH$Db̜& ch[Jj٫^=/pfU隷,f7vu-Gqqqaڵghɓ'K/񞒖6{ٳg/Xk8TUuu&(z*((+--ZLB(**j~ӆꂂ8?ul !eH2$A AP (@B !eH2$A AP (@B !zlݺk :t,EG AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A AP (@B !eH2$A GWp9Ұu[Y]+WV3LrY+l-)}jm H[>ާυg$iozkkiWW^׋7]qiǿ籒hI焒؄Յ9z˫־ʚ6\4b{޽~pqڨn TSan9c`B] ?}nIM2pt̴jS%;O/A#/޻gW麵[L^xP)nz F 3G]uUK\Q†mp?A +v7LzEF 7Wk*UU?l;|t衷]:dbcpdCš7v%#'zIC{[W$&8q0tб_qeƂҞ̕>3W_1:Ai:C;e;)ߵw־'wl7r1;7 p_YQ+n!ļL|xt?Ak/zeڳm^rwn΋z>h  a׎^NXcp!c?z7={>[d'}"(gVcc㛛W .yyzڴy랽Neޯ_={?M,~cSC6Lx%6?%GO6'}Z:uAoBBYJ8upt5=y+ؑ̆Kf&9xI'p.ikџߟɣRæ֮JϘY'V~z9!cݧ=Vjv/ YU5M;[nM>]~xwe}t"2C JJJ v1Y2G4w؏GRo )cGBF[$_뛤dBfF>\iꍍ[iٳ/c+ok ŤFw$1c qe ܼ>)c~nJm GB=NHU\c=I2pf;‹/`ݻt`횵YgΨF_;iIۺW14dMm+W C-׍~ }pWPO7|wv^L[Tf[󍡍7yì˛d)<BÓ#{u6a҄< IDAT_RJO~wѺy # Zxk6ظ㿊=2K_3f>STȪ+B!}tóܒ1u|ɘPJԪ14n|^S8tW;v׽baChUho~뻃NiTT8V$_=q%~jg!y/usߨ]\W^:xU{^},B'~6sW ԫ; t{yA/qvlw&_qrGݿݼ*O: Jk>8U|Kyqw^b W4-,xߍlSmϒ[B}OlW:Twd[i֣W}qToS^;_I'kg?ډervys_yTVV?ٵ}Puw+ '9ii{v=hm5q=eOT?U'n䠑t=[?v*?S2!]祅ҧ__뎟KI~񻇷kQMW:!%;[k=2p[a%9XÇzN=g)UNꓸj皗>z߷~G_Xn?i0~O{{_\63r`\ǖםpdJ}Qu[Z=7S7ܩ]ϴ8j}#,&_<٠\rwm\$(g֒ғXGb^] G9UN}Z&I7P\ /7w/Ik@nIڿ`n &xic;f]!|k&6?YMH0xw>M7¶ iL[?g/^YŌ/ 膵7Om$(g8f^*SW5h9iO>\Q;tn=5cO\u sZpKߞq{ [wu2I=ӧm]:an:7`]ӣF _|pϜIg>蟔nu|އ|uڳ-kI=t^Ḿ?]iҲ?ܰaC"WLѣ@eek7ݻSg&N z?IJJ¬Lm6Ӝ~III Ig$ѤaSvX]&}=l{ĉY*~w HoyM0xмO|6~vwI>b@[2FmؾyIU;s lRvkgjʪu7xiYʌIO?s?ӻwC+ _]GwP5m-)= eǻgGOppj=ٽ5Ota9R'׮8ء4;{=xUxŦs=b=;dS'˗5iXWw{!wjzMӿN_z=k$p~Z{1ۧV(~9|OuwŁK}|gڞ;:Htw)Ւv_T rh|ۜh4ꎷl}e|bO@ʸL{izWW`U449m¤K~tզg?=[T]+y&ӴZs ~{TTݺu,}#mc-%oo޲ i\sOEo9rV |6o m`u1⻸~N(ӽGXKqu:*VT/j@Z-82$yF7n`݊U'ǽ)o MbOi.U,[siwoM ny9>C)$(ߛرÇ5jKG^Y Ti=ztgn}6زڳPRax2 LO<}qwnk=q^k3ܴ5OMlVxㄩ?givwYۿ36&V%B-'xF8tߛ5qk.sqcx-HPNGrձcZK 8`%))[B(xyʪ6MVU]oeNQ#*[RO^6xc; ۜj՞qN% Q힭mfnZ|Ek}5Z^h l,g3%52a%Æ tKƎNNԹ_z';_]?8gϞ#G ;f6lGQƫddLt:TwY(3ftjRRYis-.I?]鐚܋~;VT1ѭ[K^oqIhh8f.}b G[4=xYY׎13[۱swúu;>}GfBYQ8a\O }`RAcǎftI HIn;(sm 8z1a o8oƩCNzkOɒBk֕N{iwFNzuznuȸi{oY»+o=sLB|[_/!64x;_MM5O\uu.#x){ZO?gv{t3n͛*~u'^螱S|sW?pPy]cI]8׉vk;;6XBEC=OvxXx F|)#mś{lٿdKMo:)28TZy'ܣ[VN S#^zȌ/{I(mDR;vئ%իק>9붿i:ǫk:uaE_<'&?Qg9.sǖ>MCgoCϮc2&\:bɗ ӫ,ߝ:jikذ!yIxPiƲ݇Nvݓ%υBXq3n>];c>1dzL9fͣ[Ca'HN3W7@,l2wda  .w,i@>Z&jo> U(YUR2!kjJNw7}Tma经<nu;Ho-_q⒇Z>av 8GuƟV#)cǎ=K_|sIJС⯿0_TJ&^}}_ynKLcc|gM;vl?:tŜWOa.I/oud)-۹i6L .x'k:qc{OźNg}G e_Œ o.yW/b%Y=/?ζ1|qyɠO VOW|RF}8XEy純J\ڵn[+*[f3X7-kg3/N 1(S{|d!wƂ7m.ewW>IGdWm[º=?Gԏ}jSkotCK /rovշIh}fӾk' G2ݺ5 Z+~ BM޵[K#s߫~\cyrե(iv^pnJ6m.Y?l7cцg_ZoxXS W}SAUVV=oʟ_SJv?~ݤ'ޙhh8'7U^zww]ڱFKeU|.EҺ7dVɪo7YРgxe\h[[O2ǟBW<#cҐgC 49.z)L4f!C۳u%;[?t甌Ii}kJ\uKnԌqG wl֖dLev|aϒ[RR5xk^3>yŃ&!+QY}3ZϿW,]g ^ >DRBC}K*Z?/wQgy&349 C!A9PvŶjnVZbO5 @„$d!g&'uy]gg线"mSm7ljx\9@sYo(̼3\*b+(hlzG/ =r濞}->~j$R2awm֣ wt~=%\F4=le^;^Q0xsrOqR[[/L}&횤 OK?n(ErЇ[i4T6TF^tmջ4Ku>;0~f@Zc!&;=#*#/E?=7҉nawKBl^?|h(T|ɶ[DSL؇vW/GsS |~ï*>d 9y/o1~׿Mpg)K /D9bg57;+;FD L7s Z{E̛ۖ;̑>Fj$&Z~苙ûu4>=xA`^>;n<ӗܝ[?>p0t9(caȜ luǟ6Fݕsf,nPV6)ڱaptI9vݵyOvFPNoz+S2"2ӿpߝFPU?v1S'~sAtVkʭg?[%7u-4 ncc"VmMiVfٔǛ͝<MII^7=$ HG(wJ!hN_Iek7XPh>#~' fyW|S,_zmϕY˗^;uQL͕24]YXi+HO>ͯ-]65ч;9}{z|sѵWu`4;2on_wj߮(Wt1@:nYO ۵{ߧhD/wjY|4^9h<`?Q?኱c4KǎE|aN6~#Z[w"-_/@{-n7^7*ѥn!ΒڼN|N'ԉn%Ă;z)|4[fIJ8y<ko(W{t7;l߱[,=szvlnvjrEn1lIwݾ~Q{Va +ǯvKg]`%'=f|8 |>ۆ}{ρ/=hD0l<='IêFp̜m{݊.`4 &.̂]vؤ|]"gaaw3}'&D'f5V=zHTY0%t""fsgXi44j|>;xzwֻXeD-I'h޳͂N:N}Uz{H(94 gϚ]{OjLzuu u's1Ks6%+#o^b[h22֬Xr7{J&,<}ʹM7Mޘe9돖,̛&o\3m) vVP&=zEQ|^k)"rx}(qHU(EQ-Yq&OP8%Bg\.wM-G׾+gg}3) ]sD]S 勯hphԘLGIנ;^``Dd 9c?o ՃoM,LJ0Z)fL[OEyHM˿w͘6i+̼s*VN}yRK0&&Y9ͪ(̼spAK 20[c؎Ө9M/ʹ9O}'"bO\SfZ:\.bCCc$EQ[x^?CfM5 >wgg㩦!rEQ.C ٩Q}R؄/{̟7`@jŇn$&OG֟t46;[O\n?;vqtFCb&۲sL"`/H[Q"pSjvb|:f-5>?\46+RS̑p9{$ZY "eF} IDAT<^oe=TS&$'EIXqQK/Qz͖bm em+g1YYuu 5k#lO]n7-I!mwګQ`NHU`KAٻ`FȲ% n$ZC\813ڍ\s813ڍKY{a22ӴkA57GШ}WN؁Npdxff%Zu0'$ej45 tٳ>_Q+r4 3ݡU4 &N;."ܷ??x d2iԁh> ]8eED<9`jߒh1 8"f}N;[lݦQn׿<-[?ҞycMM٣y`YO'Z,-V_ F^&UsU&2n-nۭ]3nmͤ&'%%kԸ\mNkՋHFz4ď4n AdӮyI}NSsˮ=klݦ;^?erp-qcn]}Ӳ% .Qe"p;Gs.Ӳt:T3qqz^4jxkrt2;?ฐ;vt>ٽe22lY3M)]T8 )i0cxe[8v{<Q=¬D<+OƎ^s?Ou߶}ׅw]+o^6Czjãx2 TU]zqԊs0j^ק5dEzӉ>R37 `Pb ܡNaW͛tv߽͑z 0Em݉}^}TUp#(l<]3vlVBB|+)Iinv٩5Qh4LWL&SFzm@mk2$Nwm+&i7P(4mmmf_'\b+(39.O8kR N2 w;^?Nœm>_b1''ddD444jolkЮ2lwݝ3iTU6V\.s޶ӣ[A'TxaLNX^J|i-Su.{@͈Č4ksir2cp457h Wt͝r2f~SMCk@;?ClezFY vޯF(S&sj/^}x](; koص{vfsBY愞swv?fDd]mӆ8QFDz4qvّ_vS vQ B}#JU-[? /py\rPӦN-jd[Ǎ>hhRtt~It:]4͈ȑc5ǵk̚cd;~Jx}JJRʹIv͔IҬQ.!=ŵ}r^ko5>7{lm횔E^e4~{{ǖi?oOYYC";b+(zOe|h4hWW_ؤ]6m$[_I㳯sQ{ܼOmx{5&YӧM}=12kt̡Whw@Dd9CE+W,^רQUW~hgnCufsKp;O5hkصgˮĦ:zIDl_BezWdfY䤒E67;ߺݝw Be}{;&uN7Y UU?>R9|7/]3vѬ(`jkk_8<\) grȼ39ʒeT\PFU_o@ gA(z>FDrrΝwA-ڈ'}m7|VϿKddaSL=kv3l߱[#E v'nnvjvݵWefk Hzu7LeGj~M}âd=kƎN).sf%&Zkt4ebJeDDU՟7鞨BCUO|?GLɈidAYTu_Ts߶>K>s݂Ӧi|i-7D%#=m(dx,Wssʡ@LF͞_ڷYz>$SZDŽk[rY^+wV{)K|?L4Dijj\u93o37TUb;EHgk`㩦(Ǵ(rݷeF|t:عk2UU7ӦN<o#͙5c.Y,s+~]~Ŏ7,61"'tO48jN5D2 xZY^Dm݉S2&ܜqeqq}vĂ(UUx*̉GY| ,"Ҏ׏~N5E2ǎ?pH&+3}CkbsB#Gk փ2sK̟̂7/ܭ(#Ey wϟ7;̌o|jFD_rОm3DQt|9|OҮ6u-+c{7qbvMGkC X[A4E{QfAt:ʛsך(r]kV޼L&M{쟾2Ч⢩_/1@ LXP(4\[w|X#bٕ%3 з ÊC^le,6Jᔉ?PzBg0Ed2=o;?xģjْЛ|_bELU՟7{?}T9]n1}p8h!sS]sHXle]\=뿑>}W.}'ľOӏ(],&ke΍bڃk/GL ?{-˼^s/TSw B[ne 2ܥ yy~3>ǻqp ʈH\|b1Gb~+k[ z&俿&'䤯='&zIMMo|?_'M0EoXᚕ:ft:[nY<-b呪ňNj޹koIJy%3mtPRRY3"U|􉳵m6r7 NwӍ7wozgk (NNNZbٚ˭Gd2 kV./3æVU5ESҹs#%NW4ps?xϯ-^7{ǍY%28IǿҮܲuیS֬qpx>q¡]Mvg@ϛMeAںZxͼˌvܽg?~rY6[9EW]99#7+VU۳j:=Gi 'Mȟ0~fѴɓ& ÓO l8ط}v*x)"XiԌ3M6mrjJ6bJgg޽{֟5k0AeV.*e &@L (@P1 bA2 e &@L (@P1 bA2 e &@L (@P1 bA2 e &@L (@P1 bA2 ׁ_8yo"w_}Cc#xHLTUTqw6_T_Prs:8LuYE{:[bb˅ |bZgg޽{֟5k0Q1"n^A_bGC\^o1*WPD`7F.OHD ~Wڃ~Heou0{u^+Ve ;xF={U$w%[/޾΍o\3rU%{lƖG_EYw[Vi٫mn.Vk< b+LX;?ukzAǙmYnژaFe[p'Teyq6Ū-\Y޾ Ua՘/͋[dE\m/7;ۛ|W>E^ykU5X|[\i>^_^UZկJe"u އtZ2VWY'{Pr2JvyCs5^_x_^)ɍ+T7z6Wˆvޠ!XDGlQ p!\eqo1)_]n]5exPYy[nF*""Ryʠ|uUzoy5]U^5] նߡ:EhA#]+>OyoÎ'%locGOת7}~l*I^|N}k[JFDi)ԉ}޳gY eL75̔U"d(-LI\TWe- EDcAѭܣҚHK)q zɍ+ tO(Zu}\`ϏiuRt9""軠WI>0?OΟtc2LMiaԏ^:C"b2ؖ t 3.wP)ZG"u""76;6 ""j]uXӛ*!WDe'3tu~ĽjDk5&!"}~V_gE=b@"bQ:Fѥ;""cu7>Y {C`xm{J c,&ժHu}>a+:xRAm*)0=$ժ8;B""h@K=( e@KD$+ "Oޖ|uB7>Ź*է(XN>mHPVB_{/vנk骐4v]LnbqV}ݞ-h묨mnz暿cS[i/eMϽTi-w%X'""F(ɥ3HuK9Dts48čvCg)]}|[ RtT+:UDQ֭{ݔNf'1->kVy g,x1TDaRuSѭy;Ts߯R A^mL[{hKԭfzFD#9^F~&b̊_]Ub[DSGc`[D(q4 ^veD)RitTaijI[Luklv+\l9βV}O:jyMl^YTp]`Ky]u5زϔh[)7S1V>٬dD$E)ԋۣ+dzhI[ŷh[wjn=3z.F:7=  :lhTvZ܄WoI :uuc1o8J\G_2LxŒ$Vn%7`1,d}~?~cˣ좬-4vݭfڃNi0& KNٽ{?k,L&ʜe0Y _ zc9zhI2dH &Ki Ey xCN%'WYb|I $-)6..QM&塮*4}hwp̹:Tc{HDM1[F#qy^۞:qrz'bL134M29Q$rE]I%n `T|>h|~Ѥ>)"wg͚%L iK )p!@L (@P1 bA2 e &@L (@P1 bA2 e &@L (@P1 bA2 e &@L (@P1 bA2!w IDAT e &@L (@P1 bA2 e &@L (@P1 bA2 e &@L (7 J[OOR^W!_Đe.;0. zpŪ޳~k@DJ˺!eڼ곋p,2~/U-CLFҦvqzߧUqYL9#+:bIཌ77ErM}~RtgTs*(c4(b|AanK DH?$"b{gWPDo4>Wx`1EDGN!T"ݩ>cobH15:J^'tnk:6,s(^'"pq jtx7x\]WWE\xg9N$E1%gֽUn E,Lko3$=i Hx|Bƻް,I7t  ~_>gWӊmB|Ns{ss'q~KA=y6h|b߿:bLLe"S}mRgWdxfm|4ug),Jzfa\{mngyUaOg;#-On..u=e$L{dv[sve Yhq=o}Cok-Ҭ]m/=V谤=r$-h?zAƷY[dqn~mu)kyETO {gN)-L:ɻ_Cetlp?Z-"#f-J:o ޶>DJ>9Xc"=2[D'jE$ jZ~ӭ^q߾$I8Ę:zIoֲ6ID`2V/N-VZ>Dl:[.s߿ݶ့>wJXL:[fb("~|zA-vzz| ED+'Q#M|1+&ʈbYس5~[Rm?>kymA)OyC,-Jt%<^WuJs:ot4Y'oLO:`{{_C6ߣo=KJQ0oͱ_h%h1.zume"-)Q<^KhZuv\MQ(Gw>1)>\fxgT ;s(6GgעO~dNsWA=+ӤdU x醐w,X|EY}sӻpw_<,RM"M7HaA' A#_j/0?}6OmkQ7$>Z7XOrѲgݹ^"okfN[""u,!?#"PGLB9kr.f)eP&QM:c=wosZ_O>n(?gU1̭֞#XD: UD,㍅},eD/1'^ 3,4ˢy}όc!5iSoNG&~;rj3ߘk_xZŤYyuEqD S#ĿvH~wJFDmZm+ӏ궳R9""#"i(1$&#"vV.!f @.259vu-*:kiꌺ}~*"{nH/-薒PPV,kJM""9Wǖެ!:}5`K1Ύ\HűQ;*EDrbW&8t͛q]rf  ʸ^8E,٦;f'?Y'"*ƪ @]HaLE}?W? zӎ+TWe4 8s~Jn)AW7#$" ?e)f}n:^p];+UC\Qy,q1VC$̈́5 U._ xwn,(ZDؼ$"MվpW.ݶqG'",qH8z6mDD/?i;4)IN؛z>TjsBAM~ES-8Uj8=6{u+[L2_kqmkljMwqǟ}JkK%;R;Vj q^#5-`hK_s-^Za tA(RC LRcH.6N$M앭vXi8Vdzg޿;²Vr$t&p#V/ˌ6l^wy!lq!? eDƺ>B!^۴)BroIeY`$Î葁ʎb}_>i(nv gG"P8Zx>W[X;r?Bܺ=AK2Ûv !0?%$+3scB>| oMJg PMt)io|Ĥ}JFyHރGoT;ュ˛>$sw^̄я嫻O]0ž 0_/ojÄ0|b#p1BhǷ5~CruW>zY*=xa^zռӯ.DL}CHm;B¼2"dɽkg* 05ݽ~\'{jpSنDf?V5wiCõi~ F B9?vյ{Q˻R-B(?lڜȖW+ϗ޾tdCζBȮiCAP*}c_}X!o"tI(s>M/mSzúW2L9w>D&X%L'ji?#Lf|ӂ!۔BJ&;?;|`Ef'\t1bB:q!׭iHCBMR *L&?u8 vrpK +$Iei]k)X}cmyc˵Mg]v͢OZ%F*dƫ?rmڎO}yòXh (.`8?G_.a}Bܳ?hYڰ1s|X8>o_8d5v6{ 7Z,|+"xSfe{#h94Wg/S&W4.! 8X M澈s.>"X8q"puׅKl)t%2T^rۯh|\`.Sjj> 7!ƥx!05Zz x eP(eP(={ L3 Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q QHFk:Db.˕>F%t2V̤I0ixJru' B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  Br\!!H{ĥǜ6g1i H);qxRI%K2k-֮%D\.keP 1ܷp jr酡#/ y5.[_d2SJ(J"S*칯=1}}?[+d2heR`U+{;*w/|T*U%ˌ2ʏryGvťΦ,ߎaJ#O |w _`UWD"Q.+3(pL'=Mm /_pJ"L IDAT| Mm՝YT e.edUe-L2T7<\f*e.qKRoauPUe- TfQL {\1 _bY%32Q; SJ(p3A.SG(@2DA(@2DA(@KC~Ȟ= !Lʦ|®zE(V{Og@ξ@_a`_ۯlZ\1Xz Ndj{9qrh}L>e&1 0l=-3k޺nl!jX`׼-3z·>[12egPdb&fS]jZ| ; 11= vn<95cyK=SQ, CӘd==;|qh%`) OlV{|~B!zƅ2Bf޽_B%k;﹬n)z0|;7.\Uզ]!0$=8Mw)|`7?ogea|x|~ۖn;Oo|x϶R!tͿz~-K/t2pq2,}ٰ$9[r`+&Q[zMVjƗ>m8vLF(LRܹDu>٣_86X<CRE7{o~pcǪ-X{oϦ&b0z4:_;+_z7ϝqIa0rdl#5G !l6=( r!47Zxb?Byʖ+խL.Bq(|sii̍N*sr0_9J6eRgzE񆾱ŚBokys{CHK&^{w|̾U켣+;͟ݶx!$^S[mtG[vGT{SzSէ. !}_Hr.{һz{ {37dVH{03[NV7Z_њ ֹ9ɶzb|)L!FvGzO?wp gT2Gn.iV2opOiL!_O3E0S{\t㭛̽Cm!lh%}nhkؽ5ue[eRW7-l!c|[, U }7ܻGKƫ77 ؁W2!wwkmƛdBKVKc '6Q0 e?] 8BcC]՗gUnZZtt5l&>4dnX_FB!չ![s>-.k}=G]ظu`ۉ+>eΘT3;rqχKݏǗ+j6>/K9XF3@7sI,rNۙgtm ǪopwXr9+.?c߅9{ǶZ^Υ5n4ōf. 3@k?^iO^\uAJ]ѰxS7K=5?٫8"#KMp&ޡ!5[ٖ\aL~n5Ή:2מ=XpC0K e-'?ƿ}e7d&82rDsǷ{O}i{ZC[h3CC5CJ^17LxPus7m\xY6K_u<f uVxaۃ5mXt=-g5{?prc'nu41IeB(r`+s?jtB] 02ْՏO|-?^e.C!4?3L5]! n<6DoXdRA rC)o.\6wů檞-Ǫ\ښ惃!pg?ήg͇B(:\85յq叾PB1P,?yh7wG?ɽ۶W7S䲎00?`Bkww;#̣Pqm0,q(@]xfKMi,Ke:Vm[J=ɧ?ѭdC4&xe/#χ^/aPؾؾ ND#X۵Syi{߽qawzfk&B#KB!t3eg6lbՙ:c]yuâ k !\mxz]8|t٦Bks}sra}/Nڸ[W9eW4[d%??\ g?9wSsk]5Zje┳4Ͼvm_/=Ե?wes:}jyv /‹[|5ϤyX fD\*rT*ő_M72BG K^7uh![Wt2|'!+he'/Y#?=\![;̝YëM=gη}t*J&7\3@? !0Rk'\<5d/ ؔmijel^8l!_Nݘ-,tknߐY2L^֐׾9q%ʹ^~ L]2,p5/\Wzdhʹr[nLnJL&fLum׿zY)Yt Q Q Q Q @,R߈ :2~I >_X,䪯[ǑU_/)SA(pK$D"pM|ߤ2Pw{_ݬ|IY&P Dy+/} E4#{~v mJ̔J{LW5.кVv_ kj\l:)eFK\wd2L&u{D鮟7WZ#U~n2T~aC˚z eB*d2SKJ%S.ST\֛nͮؒRI%K2k.5aQ*2Vޣd e.qD\.WZꞫWf+;BPbLej(c:)%U~t edT*JJF.ӣT.TDT'T2ꗱJf ebQ]ҥS2T2Z~jjBTj+S)Wi/:Bc硎|P R~b`:eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(eP(?<<<22R*BrCH$Bd2Nϙ3qNӕe #H$T*UWA:N't:-`$ƤRyb:NW@U"H&###t*J&! )HTP2MeC+&+&]<LdA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2DA(@2=> Ds,imUh*Ե[Z+RQ++WT)(q+ VUnjBDA !Sgyg\@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2ARBf[bkʗoܩ6( AP7Mh䓉ӧO?|8ebiO=[^۴WI4x5W_tVZk,|_R4T#}Aڴr_Μ[ts|C)o^r%=}Kj^馧g_}~5gDX_hEsGunlߛrMUb+fM{c_3h?R>-~eҏdBx„NɄV|%B3 1&:hĨ^8z Hy6+#%siAҩ7]7zT̾j\ l)+Sl]5if7}}w_s־Zˬy<~vU=?//# [iY͂Gy#i(oԝ2s8jVWWէ:t*,.ofU6VBF4''BʷWlB>QƕV~!^ Z۴rٻb!?]{iEs?=5kV,[][!]{/=&ښ[hPeedo2͉W>7Vdꭟ%s&L\vVenUͺ5oZ9kO7cysU6=G=k}!2']|VFJ渗?b'}3>7Kԫ~~gW6s0%{ou~T-ɮOw`DE!V>}msmnEk*܂= ,ȏtϯixsze= w2 coθj청ž{&Zl-hMf_ޜc=A xRANnvCLg^֚!{Y7d1h'K&M7[$bUO}nz蹊Uz`~e!zƤ7][ !ȼI.{|}L!oBW.]{ߝz y=<ERԙc!DϸkꞔL! cC7fϦ%eO|w1KoU=4}Z\ޞ}BAJ&[4ֻo.*x+1Ň42В}e,Y|90R2)EPyxQq&+y^fUuKɺ;i]_|ם /xc㥥sf/MVq$ф{[?%jA]g&7Ή{nܛxuSoZ$0ՙCaK}0cѹ!ŕIYx%2pCr&T^{q|#BxŃĆtBXhL oΝx혉6:'!P|g$֓Q0a=Ba/' +qzHɤAfqEkg$olٸդd{IiV.=(ف_-k8eNqe cnvQyW%v ]*l~'ˬ|d¸7mWc!P|֐fFFDeM[حZ׮C! 4TPxR!EԔ-!/kC]z%QWJ^a2Wpά~pCDBvI߸b T!g iz JLdcoθejE,/]ߵR2@gVFJ& 4'{[ÓxjÞ7eoW$;錯߿YB{v lސH̜ԫ;USSP12K-2R2IP9xCQ߫j0j))a׷4z=b^dKUXlQ-zdC {aG4[4)ܓV_X˛//vČ'0 H`Ǝvee/GdR2Yy_D9^hsd˥Z萵7|:r] 9-~j%)U2쬢oSB=#!ޭLW !P(2ltn!,}瓵TY7"?p pBX;wfB5:GR{f$u"Ç!ՏhںUkC3'F^sB;&>.+fedRL3[ܙgrR %ϴ:iF6Ul9?A4!y7sɂ V)rd1t߷w@b]?fI?WV6pj7 7M|)B\2dţ&+ !*O-}Īʦ~U5_C=G} wzA !>(u_{r~qEy٢] ѤvB7\kϩJ.uD^!V>g)xMt!E7)ms]w&>;&5r8r.e˖-[LJ&5euGX!6BED{y׻8&'-;c`jޫXd9=3􍆊&j+€v+l-p[oT=g-5=5쩩J6;7\4W_5f֩M6ׇ?ryӓ:D]v嫮z"[$ݿKva/oml޴%Y).wu@O {w߶{ތT̽銹!ݿKC4 62VIiw7 p9(Ӝ74嗒9 JXx=Xd9zFiq%Ó]})_$˃O+ޟtK_dS=ϵb~?I,K4x>ĘMnLj1eܨh!7JdDa=ZL, م߼\<[$bT-[S1B^C9kKCKCF|ߔ9S.;'Q0i?wp7Bso2-'9m}GC}O̟# -kґ$l|MHr˰% ^š8Ɂ~yN ޞysDj#hޜq-+G3aWn7n=)לLÏ'Oi Ofΰ(uɌ&$)5&?|婇gQfG+6BUܣK&Fj7~x-ߧ '[j޸5܂= r~]sNݲskR֭[Coa|Q%tܵuM_)9;5^pd={&<ݑ6Ye[;&fNKw(([l^sǯ oBݻw!dݏ[WΚ:i}=Q[l ~6mZQFa^wzv'yy܌I&h7/|a+v vEE81%' {%XϕN UGK͉DkLABkᄍ Do4f`N3G{a{(1ei~S|y3(#KP90Q%|%Q0|ot@Y%7<0qxN=W^ߎҜ}>ǍKCճxziSGL Gז~Iv;wۇy3kOߟ8KYcgԜ<6}Pt>6MɄB(۬O8P2-q2>'Q'DFv ?̔Y:0DK4{|n?v+lSs~4nԩy-{r:g~xِozL!V>USZ,k5K\^x۹_% дvϚՕ+]vA}?ӫgv 7|5NUܿwaqQ~ںuk᭷:L2#1z (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH m]Aymںu[pQplwt %KMܹK8ںcqRLAzǥᘴDRB\5LdLu 3a>&Jɤ˴ұٝi*ǯ).uLeJرcǎ2qaJ&===黏ʴ 4LHak1$b22-82qJn7~Kҭk?۹k8,2һENء OtHڝi^XPfv2q i;6m~a;_=Kw=ISLhNf?[Bw.9=ov2ۺLb>6ܘf5)1IJ&c)(W;DPgU z5.j߹k82ҋw.jܾ[~YvYDV&i뼲WV/ KR2@)jg^_vؑT] reB2RB8s w>sI[*iY:f2 Y[׆NvuDpbֵMS23{9f2 ܹs]Bvں.NW:cA_j!̴c.^"8Qs@E[Wt:V#&O ISӱ@9^P@f[pP>ݴ}玶޻]s߹k1)}Ȫ߮ܶ `m߹c +mx}kz]=+)1zIJrۆGV%Pfކr)mm(o*cy}.ŗ Y馶.ŗ پsG[Z@ EeH 2AR )AP (@J %>wSTۺ`>_eFں`@ּ8sQ8kͫg umVV.8Ff[RNV~$7vGިi߮k6no|ioliYE#:Hv9!>Z}[V.ټ|ɧ[mie׿3$;B{|tY]UU[G1A#"{nb?~9^͜uӿ!+gO! }iKU5웻 f4Z? y? }Τ>+c̚g.^qy#o}f?pF-u)p3z8 .6c̥:쳳d]1%?wIIv9j]l7;d.kpi5~UW twvXB/ܳOÔFlۏ.s,e_q}:}qڕ?M=iV3Կ+*B= B!lCŖF=bF?wJљ7LO Hoř\vay{^j ّH^v$7+}ͯ^/[pQfVBckeCv4?sϨHfzv;U=bS)gwBRsMQ{7~22323LIhTj$یvYxǔ5/\//NwP_ϘkC;ol.{3GձY}~PM~~۲bӿaݖx]{ 8Ԉ>5%{|`4Zt[Gvp\:NSe5mBh׷OÐƧ0+!-}> IDATCVd;K "'&.m|ӆ'~iEt?ɌZW͹zp̏313??Ʃ ?jr3Op֍'~9?qG'_sY%˟3aGvצ6aO}t_/]/0yBJT&qvb}b5or~?ّ&)zk8})딘7:5]gO)A;ҏKv2﫝Lᵟ̨e٫SBLyjѦFV?=g7i<[\v2v2!xo oأY7$bɗM~>cy<𪬓]wib˧>y'K!d]p%'g78=[+!<ߴoMk]wrw"[)11 Z&(>gK{zKtSJ !X'-nLKoWcI;h€K︿pJyZ,wgnܦ%rJA!]ˊ_|%BnSo]C Z[_?c]1ͧ;hF|[v^?bkk%97 oݟ_v2gf&KމK׮_Z7υBK!䊞'B75SZ~zׂUaqM9e;,XDa{yIzA2=+;ڱݺu/W&mj]&=X/z?ּ8sQ ! }DݒozH4z2фv͚B-tn}B{h}^b֚$'@Jim߯[y/pGV4gW;ڍ .+ҖUÜU5:֮}4Hvdum+[qBg;[:.Էy}[Nfjkh$(k;EYɎ3]҆wO͉۞8{;asҚsǧn'=/NTyqx];D.g>4{O~٣ 6>iĖE-xL[hcv$cF)Bƛ/U4x9/Nݠtmؾuu=)iiG;wdv[#;~2 sɄqW.t !ɓH&rS{B;_A!?^Օ+i)CB!ū[6!(2edkѴKi V2=y 'yjleOT '_q_7==!kk:'Էs̚z/+93BUɒ2ՕB|~K?7$Bek\ѪeBg7=ݙE]1oO@'F/2vϣO=b"=.{ň>o!D"3?ٱ~=Ovތg}PϵlB_>c> /mHT};UЭR?)"݃Nȓsak~g ּ6oy!<]Wd :oZYZ lɣF~Cvpw̻oQ㗧Wx:R5qLhSzV/̎}:鐿wJ&l+[i'~RzV ~&}aD)'kd^p~7kAU]u멷C\pzPJo-7'iCO2{?K=KۜwC]ƙd"g\SBa՜iyg?o/2Gmy}Ú:wG+~_VmdoVt{S5(h'3a'ܝA_nKGB!?qׇHw1q~]#Sse[Ba?xoMNi{FMuė>OYqG<3֩KGK۲ic25$b-]4w??`irigv4Yk.l[43C˲\=?ĝkX~z}FT)yqxt_~n?]K^~Zu,} #:gvNYzC_Y} w%sG_X_|cv(\ly!=3fz d_>_6E'_#8:%F#HFFFFFFzzz!-1u[ou?t96dF#ĖW/*nϱh4#}F$:##Cۼlּ`fzhfHZo 㟠t=-۵Ko.ܹsǎ;v3Bh43 !ܹs{z2uᡫhu8"e5iiiita$pR )AP (@J %eH 2ARL;1er;suj8\|@Asu .\Kwn*8w>sI[W3Lggwm]p,l w8rކ7X;wuE̴:W/`DP&%)+%F/IKKk^92 iiiTݤ) $Դ;ں"NdTuEGc,(;x-rBaէں.NtW:3A_LOOءG+ڮ.N #==}^nS2_* !Prۆ H[mü !/f&ɴ 4l~bWrO !<귲2@JYm#~BJ)'F:7L$쇴޽GYL&``*Ro#n-R,֫Ԯ.[uWh[El="V? TK d9Dd~ !u򚹟繟?>w&Id2d29`ܶkGwDD; Lu7l/a& O&:4Hm]^d2Q]]dv۹s笭~m]!٣?>`xnnnrlhoA۷GÆ Q&j5f}N;o>\eTeۮF(w ]N;^5dZ΂2Y2$ԠdGS 3i^behT k*S{[2e#22@S0&I$(B;LL&ɑ:YzBJY2(215)q# nIJy5(v\fK8&cDdZejejt2Y5K !Q9m] 2t 2t 2t 2t 2t 2t 2t 2t 2t 2t 2t 2t 2t 2t m]dں.Hu O JMXBb}Pfj1D"ivɾߐ:؝I$2^25BDjR2 es3m]]ed/devJ&'''ߜ2hA)n-Б4-4enJjR6t[%w㇉*!J&בAwnd2L&j2mZ᫝e굓Fdv9wMuu]U&+y/;o[jl̤LYP&ٹsVdD?>ndN[W ppTUmߗ%^Xxi]ަ=xSޕ)LSP^;lPfؘ5&XݯK@̉~~OIz=st_d22 +2k;.IM̄#uykvemgHPY3lCl&+CC&(S{ k2F'X]'Vmmej7d2?LDDQ7 tRbm]᨝ej*l'2{m\afLDjhQ XQfW4e`u k/KߖnRWɜ(dYe)2ǬsJ&ںWUunMnM,3^]ۺ [/It<[^O/pt kR2R k,Z?(2Y2RtXhn 8h,.z?(!(@'!(@jټim"[n=z:_~cں4 @6m_شiSm۶m۶mk망yVϞڪ<m`^Lm6mmPVhK/N?'NCSKmڴjs})/_tN3k]mݺ67to55u;nORT7>[jU[^֒E3oW\Kn{i~ sw?a_,V{O5Oyjj>Ù hM}:'L?7f{y#7=97_Pxg&>Ԭ{>|+odew ka0'(-HGީ3h@+9v,y汒饿zbCİ8G{y}N#׵u5[L;avIPnccWKo)]o?_1O]x*?{~ 0np󧕮ZCG o%Krm}n)j/XG﹦8ldD~P@mhB8018H^TOۣr{X|~m޽c]?w%OYUgdS zŌfuQ3%bォŵdݘwq/xoYxu!qϫx_6gC&B挮Xnf>NyWFDzY/\녝R̄ή};o&.j#lv2EkP箝>Ϡϻ)ݒ}?z|)_wzpzkWܥ*~wv9\!ѻupgmEe%LDboIv2~9@^m\āosqiDD^G^<~~(ouJ6W3gyn{hGPpL⺆)U+7yg {/=*TWY"[U˫35Kͻ̖U6bڞ"Nf3}O{m'sS6@Zz])#ߍ؞g*~':zU[V/x#7)e+"F)mڴ׿Ŷmy?5=gJo2I'FQ8JU՛oo}pɇ5#|UާO9zDK;+ 6+x{m;j_p^їj7U˞*{u]zvk>A[t |nR6/ӧ=WϹ ^%CYP۫K^ޟL=k]ҋ/|3=Prʴ'&FjK/nIuKI%r"+3Jx "ɥIY0n%udɜD_97^JG䍹NTeǒg[7F(['{!ӵ\1{tDS2-IW^i{^ϿP+?up*%yD2%љ͗ӥ[S=2:9H4smgnc/wjYF;셯oQ8ijML*՜\|s:"?׀ǎ!}jRUGdiyE:"UPد1uba+B oz:C#&M"0egOtVVld`/oWTH|tD*NWFD7LfϜ[+#z;ӗ^5'lkEEJl]2oAED9o4C'{;.&\O޵zŌ5θٗŭIf{Syލ~d:+&O[Kxÿ^9]k0?ko<|ͭlH FC4)79$e2oR]:NJɮI]dtnN\+&|UӟOO9=ٷ|u\)WGRwj IDATLgVĨk?9si3nڗ%̥8nU%GDߏj6(SY[&{IySۯ__^_ԩٜ6gߞPк2.ny|Y |j?>u=7{U#R_|uw{(""YUzw*#_|nxqA^(5۞mmgx̅[R?6|ED;so NŊWmړJ+#?hȘQC"*JO|<럻SӑߣpgXa.銥o=WVR}Uv p@e=M]*7W>{d~lwsىQ+cWcΤd"ĉݕ>IIb1Sku)){,IGqSbX~Dz =cnʂYޣ^|mߝ2zs}-{zs}}so;O#=T/}aeԺiLzE~y/Bf1&*>_YՙK7,w>{ElmuuϹڳ$#^{ͽ]n.#}}k.y;훗ҿ tղ>p_yiϭNGS:;(}ɳV.q“.L{xL+5Ĉ{p-"bo{qizO:~8~ƞf0fO̴?i{Ƭo|yGD!cjTU^XRfgR)oUFj[o2m8ֻۓ{b2W|&6wG0HA5;~dW;KnLDT.ڡQ^ o^<둕>];N`%x "+Z*͜VeDkR\!W\?OD34S.,+fN"oĤiIj'3d6?;:wosAt個r(<{̨ҟ ]*9cMDĩ_wo|wu~w?NdUGc*g;w~_鞇r왋NaW]?"V>1eI^\cԵwzc3W#R3lT8|·H5wOUeg>!"N1weJ[>+:*V̘:ʈrx%(2*sLӪʬPEcNiO;_$oə?SFD݃U+\֠g+X:Mo[^?y7Mi]l;[NӞs}&x-1;7n[ZwF{le;vDK]ʞz ?~z!Q[[^kvTDĩ_;^dߏȏ7KL셼q6lM4pE#"jNz(l!qɋ/5ns""F]:#7}7^xo^%o۪@KeZL^]7ESH?}괲dFiC'3w] Y*emEFY݊% <{~tXDDlw(b#Ӟ+3&%+NfEg5hhR#~BAgg.l+L{Q4 bGK%og% ,kw,y摕{ցXT02Y4"{lˏXQhO4n Uw`*F*5M<$H͖#N(n!,2-j,{v53V#ԫ]1BJ80rۺNn%:oou5-CRɾh7N?JEIđkB:u [=Q /ckle`߆};""߈eol="*TZCm(`UUWW>"VykPʯ5*/U#>pۂ-鈡˔Z 5eچ-rj~F>83oXo3{Pk+_7:k88:u\`}T-T aݡt-VRpˣʵӦz˂O:0qߔyrK6H4iA>&N@(H?l?F,+}=""Rf=n7_IpNq%fTd;<غv٘[HK/X]&^s@u9z\~D(c'4˨蜛?qD*bs7M?'(ys_߾H^?=1i`U)j'sl% COϋHm,)yMI:""o~J*""GU'jx4;HqDD}v2Ml,sH|WtKͮSgK7,"殮jF#*oZ3,d*_tD{UgIED#,n{-n/+#5O}#"|fE#xK#"u)#ؔ)S^=۞+PLK&7n?= MLLw>޿usى5;3qA#v2+׮?T""Ɯ~RM1GD_PVFDhɻ!oDĊܰ{ǚſXQ|EC4[`!:#[5ct8ddVrزbiID9V<[]񻥯J [-^<]mTUVp^YS.>_Dl{wZXŊ_=u'xm]VJ}%{J*_z+""oZ(wTDzS]\Vy*_74ӷf9-5uZ!O~["FL85l͓"oͼ[2mD/oxȏ|oݘF*zEQ.+}˶93̂'3>5VrcoDD;V'x⁅.sl߯&<7uֆu<܈վE~""z_I$4%uO_Y3.M̸gv2/>` ;gyD䍸Dk ?qԂybG'UxxƷ9R_v +W/}O6qb^ZPNt7>YR_ݯ7VsNsmባuk~ÿ_ӨT:cƔ ?>G*"6o,ߜwMW炛nӔ-/}=@رyʈ#|obqr&̗wqäG G[ Y}.\~kmwڑ=h@磣a ;2s++[>=":^'Z9um'3ұ{q·fvUeY"Uxm7׊DDTzk""bǺfN~sd2Q]]]UUUUUUYYN䘈O GLzˎmfz'uran&~=O󝜞1NjLm<`⋅=xdo+ͯ~+u3֟q]&zŌ)u_yE+ق }Eg7Z7xcǜ7UKW#RQ3zXpRwm'>|9="VzWvҝ 9kO٧ _P. qoq{ԒرmG~{^~~K+*6,Kw?a~([SRc@ab}}>>OAˏDdTъUeoW/ p?~?_|Φd^*K&d2'''"DKWo˗/?H6,e+wӻEn-^Aժ?GDD"-UP$Woӎ-͞ѵ[׆~&i]zvk_]3(~/߾x9m:Kf|_]ٿ/ɝ[=T p=_d40{֏l"J]:+p ʴ(=HGݵ:UvaK=#'7wdG&sWUQb@'t9Uus?ڒqŽ?f=3M|xl*^zg*"5OwC~wv82L&Sٹ3d^"h/!Ci^@Z _^vyʼnv:|-y-e:+W|76oڴ|tRU+("esD"k):&4jb[J&>R+p|tmk׬y۶}L=u;c߿[jA]u;' 6Nrں8eeeeeee:~P&?beqAn.2#KaY\:~P~PY,Z?(N`d&XUIn[p(מ\š%+ڭdNu|^2^AKWt^b%hAD"%+2YD¢ԐhQ d5H$Um\@['=6.0΂25ѧ^Gf"b6 tRD#3bm]ej_v9srru/@' N 3''^DF\vQ{{D&QպN k1lB;fLA999vK޷2"f#+t*[^ωV-];%#+Өܶ.`/$L&Sd2drl-v9}2E2vhX-]d>ѫjl-dn22 LVLDMk.5coM&^jQ; 4=}c>Z-?LTeڮD)^Gfu9cd2 h'Ӽvɪ6ˣ;jkuuuf6K얳K^N-4e55):1ٰDMb&A4enV&ve2LNN бR2jAɾIɈIMv&vKR2kAؽ25#m]A0#"8(U;.S{3hvɪYrd@>ftL iPSSSSSSSSSSSSSSmL&%tDKh:HPF>Tj325@TI$٤LkLdLu бL"i4"2@Ui(i_2(S/%S]]]]]}!+t`S2999iF NT7vkyLn٘DM\FV/(uS2U;/%LU[pp$n}zQO&d2{&+ӦYP^;lDfΝGan3m]J|nv+yfq}͑4iT; Dv2;w얜%."}.V[KŪ<65vʔ݌IJQ)PRL6(s-̧nںF,'7t}G%^#l+~_feAqrںS/+zKR2@}PS7FDn3߮f*bwĂzYP&v.:bEe/i L%$7,LQmW@['5LHn2Ye2Ķuڸ,R0 "ՋUuYvڳV*""' h+DjO&ˠL>%MѢhJ >mU?M[[Kzg[¾ɍbEm] tLīwĖUm]zglYز*-|/jvSl$%mYx.hO:APfOd:-bOۺAĺEm]ZebkI[WAcqVAm]Ze@PNBPNAPNAPNAPNAPNAPNAPN! ڿkdWVQ+o^m]PЭGveT/ IDATp(-zokt[qꎯš?3cVF})vɯoKNm] xc>vl+9v,k׿O^#]2=w+%xnH<ںAh{{5NZck^}7jF܅w;@U_08a4ښwވN*hQ׾GnM7=9WjаuYܶ.;Gت&])}CH}Ԟ/llc>8zq;6;+?ݮ]l dӼ?TY]uԫLc~/‰#"7qޔ[Z]>UU]Sg+Ls/ֵWֽ{ Y9yvM:'AvĜwrs'y]lAf2,ZɃwTKCsLM$ ʴxdžxocM'vy>_noy>w6sJO>nzίx~}b^W^|ˆxIWu>K#&j3oSdr {V/VwS96iɮٲd>?vtKdZqE~u^q*5̱\ncwQe"hsΆɮdH;z؊;d:n's${ƍڸ:AK{%"";=r&N0~cǺD_^CSd纗|[e|o?խs;l;s/d0#(sx4ešw}DXgww,^{d̾g|-sEAnIyoc/ⰼP[`S7/Nu%p`uF!^r{u핟,H'u{eR#$ j֙5'.ɼ_} lJ{¶$"vl_7+ͦ 9%#vI∼35/d""8KݼMn~Htiޛث_rJdoyoѭGveDd;yG?Ŋzw\⢦Tm_'}|+^_xѹ=W*8o-w3tʕq~fY`ScᯇLl~zuʈEOg. =|+\pǫT*/(l{?_ʈǜWcO%]s"oADKDtJ2d*Q'S:4Y8+w=T:+zVmݏqE/p?^_ˣl_t3{7?ѱ}k+Jz̒TiΘpJMf>uw~s߀`A͗;p꼻?T^_&RfW;+'ߣt#>֠*KM}?s 7S|_zV<`حӮEN<"+el}s=Z^ }iEU[WSR|j+=?2VS}9nPftv2c[j'ғW3uҕWCqDlmsy?SkQotՙ""c7t^;?UMofIe:{'RY| ^^.קȣE%-6ɶdқK5cּA_BŹ 8S""UtěpJDl\>W5uUgKaG+YtiEɈT|4I u’i::AV w'\βl|V$4*ǜ{չ;?~_gx3T(:jw^z•&DK6:CeD/^jw/E0羟ol޽ v}׊SaͶ{ʶ9sl+lw箸{çvCWIW/_;-<3#"ҿ/J[uǦlލs|W>sə-\[WW˯ƴnC+Z8vNP3pIKK_Zʳ9#>[|h^`Y̹ҊdIĨqۦ%DDě]Q|Y u̧JF]+"by=XUFk]R/j JzS2oks^MTˮv2Lϲi7vC+( =HDD{KhJMyd.'TAcGhOy? Grd*rS 2U/M0ɌuL?L_o'|Yd;g#* dDDl+4QZ5hKVI~֜[cNfĂUk+L'OQ1n}݈PTFު]e_گǮ^R*bGuiStwB=KƕW嘊Y(mwjTm'3ᴒiGM?hG ɂOl75Uі#I]mڷssG_w="br~e{'2Nfy;""6n󎨮iMmټeMqNW>c{ta@TG؍67_o^dYM_%zNhND&kQo^uj׍wC:4ޞAs>rA_tZ/S_zLdJQ=*#vMĞ%//>[~z[{d"bM&$z& M7yly/zcUd"zl??xdY&sϱf+v@7tx彵x,"" Kz'GO""C3^hH^}G?vҮ2p':/k^K(gIgOQ2̏D=#s,f?鱵1>m[LN([ö1)Hv };/>lҧFMFD{ڈ䑣KZ[8fȗ.WD^ncq2'S/~jxkTn]jƷ 7 eDy>#Ҭ VMޭ}s۲dVSri㟖Qx#Ǥ<ԽU~[?̿= "bgiuw]1oO?jnpqt2"6ϝ/On'KnDDǴ~T ==""6jӲ wuT7kEghܯ SKQxWn'iOyu'N|8MW&U_VMDĦ_֯bUjeϞ?tO90ƣ_\bSoN7SW*2{tDDloO7tdF}ё;3Х#~/_|n]DD]mŢGuӫWNmW;ovNԑH=GXZ/淵Oe3&Mu7[ROt[yy}YN{ì3o_?IEQǜ}'{pp9bWϝӯ߷"ykį~1WyYG${uoy G|ߙǎثڈ}@NHR$ Q___WWWWWW[[J̈h8wvL% GߐMIA7[ lDذ*7g{C쨼5aXdʍP&u@ʥP=ؑGKϟKϾ+$lo`%hK e HMѮnʤoj"D~DD,o [D"[PAY(Ә>i9("bê,o [6Zd֞ۄ2M_v@6ÉZ$2rM(Ө4ԈGՊl `Z+MOMlG7 eKDDbѭZ TH,5"6='M+LFT*=tBCCCCCC}}}}}}]]͛7ow5 #"Jo46z l`רV%g8f)tLMMnJ e"n͛7^~w';js>t"~JOt2Bj+}*_$<경M]#sP]ћD,UɄP-2M[Kl5v0y4&2B v5V"hhhHΦT^^^c%[{Ic%d]%tP&25ihhS{L ٮnDV&K>{iӘȄJ=5i.ӸBLGtP&i.t (%:ۇ2i8 cv^4e{;e BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e @TSTFXlK̟7k+?HGG~:(ad_Z4,lƄ2;dÒxV|sڭga?Ks̹kj[QSjşwPf[j~YЍ e+rcop@loU*v e7fxwJ]Bnn~Kzyj??pmwd[A7Nx_>0ʱX[;}5+[8wUms% ۄ2ҥ2ͩ9xMe[鬫&Zamq|xIIɃ>+ >\+wՇ~NX.iӦuɧNzGDUUՇ~ص''+ =UM.]Z^琣j! 嫗V? x@qɡz&"PWqce7z孪~uHߞ9({71dȐ{7\wuڽ%g{C Ӓ]B>xe̻yfOFD$>;ues~=~F8<8K7w_.{5T^% TZKsEn۹^[:g=C*÷oSlϲi]5g)no2y܁K""ʤӖoeZT2K4'~4Wjz?f=Zp;+|h?f;`o5*齃ڎ?66Oݷ'^~ ͓nQ򲽁=ڗ۩d]ؔ] TJf̼?~_=_աJftK[=%\}oɒyf<]AgKZpʔۿq̀-/'F|̴ko}~u~gqA[⒭oWn dQIf+̔D~g=~=QWS'UٳoL(dYճ׬_ ~ӕV`o\҉̃>kw>}sHUU^{-R%T=?/{˫Ox g~;;񐦈tL;ld-N-+Ky^><""_x lEWiuC/mwK=5/>0s@\7u&/jQT}+oY0i/Nq^ x3Z`oԴy׾/lQ䔜ҚJIwld~1s[]x݄q4.lu-\ kȅfOf%Eǟ[Vꃮ "Mne27h֋V벍.n+A.ɴ<Ӷ o-*=]i+AKK+# X'"럿LLA?G(9OgQ2?""̻ {J&7v(cߌXJm(/,Ҏ͉)rX-W߯ |%+[-6*VQMgj~;sjq\ dW?|fUSwC^]`IiTWY: :tgfƥKtC˴tUCw.]S P֫g?.1EJG/P2 ozbQuGOUVf(WjRoj"i{~?_9+:vꮳju$P&uL멥xZͼv;>&y1&[/W?:'JkeV<9k_l:T/{ʝ3sa#U~ϴ9cY E߸ۯ Qr=N8ﬞsJ+>au?q9:ؠ;˃2J7Զ|?/[:Wܽd]m:LX+?4Y_:G7\;'2|tҙ_k. iOMjʜ%ܲlnd{YKΚʜ %HYrϏܳs2̝ aj̚Y" 7=w&ru]MCG.jۿ򝽋ET_ap=ɣ97.v΀z3׾0sqwM{(Y}:uÆt=FMc2WгgCO<7j`$3IǷ?$yY~ޝBrԄa8i=V|'O<=4Y|N:#yeӷ%dtT*=qR^eo5""4|Q%IvpTuşK)-]Z^mǠQ4|ȁE]yj+J.y&""rgG^MU~cWǠQǎ=|.FVF=:f7\E(@Pƣ BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e BrP  'e BrPP .3&[ә(@NHRlVSSb 9A(@N(XCCC%loKB} Sc LuP`$t)!n4-cҿ7P{}Lld\k(Ĵ`Xɴf=W eZT2_2^i%߼w2BL}&MGMm$svtP&W2u[1dmU>JԉdT~"߷kOdu{D*:!='&=61ټy}^(/v՞= nLMMnJ(b͛cy7GIC>;a`φl`ר$]xM:TØW%t2dT[[_o~pq{y+6$c޺8~P0=T&// =et+8TݍT2@ܫ#w7&MElM,h2KK4d4WIC"b隢n4̊wQpbEUAJF1B eҚjhhXQ""tS'}hUd{_{nʤ5Ժn%@:kTdyC{ژHRS2(v+Bkku ;*?/l쀆hɉPf]M<Ǽ loUWeei{d{C@^R}6$cN t?5xvuB%W*ېxv; tʼFKrsCZ tg{2n.q{(! Ge BrP  'e BrP  'e]s9R32Ўԋ?VlBؾ?c.9sDTUقYoeAeֶA_I]  }ž' 6DmM땱xm|@X>k^ǧztn#tJX]t'ED9.^òfZ*[ɚoʈ㯾=/Gg{3e@88UrpDD ?]SG&NܯV+1Ӊ:Ͻp؛ԑg{OUϮ<:kY7?qFrm/f{+Y^[":4^YQѧ>; "2{;"۷'v ;z`NJS6($_lEg{E?4/Gw*:l9#m>Ӛ8 dmRM7cy2Qfp@QѿMuk?._ZaCqO N}򓅃/~T__a}mXW^lGA>׵x_ruGz9O=cUwF(G4G[憟/q孏Vx'3o#jׯ^]|AEme[!C/J7G)U]t͇ߢE>=t@/@Wd?Wp;|~M&"??pjw}zu_4Y'K?|jȄs~0~?VUG->Sθdř~?kdѰ~3רmeu+# WV"u^nQ9{WݵҘ+66yoͻWn{WѧO/C^TrTW>w=peqp\ua/)On;`_W;_s{FmG۰j3CTdј3*#ֿ^PfEOډ}CZ3gҷ"1+Gmu[gߑ>QD%43gymrIh`'l'sv>XI7M_55etߺsyqoONnQuT|` V\}ץcSKLS|`S&_VEuc?C+kŧ\ߏٿv[ξGSevcLtzi$Ϲzw$5xOED8 -}YC9%\kx6":AzZŒk6mE&Z>TF#?S>U|ci?7_bmߙ⦈Ň AʲʲMՋ3~>'OŖ%曫*˞ڥNɥG5Ϙںٴny6p7(UY7Vnn@ewlbjٷpgwTw)-kcs /YsIDsïɖI'G\q+ky1kܦ5sna֪T9gm"wiv=\Ԫ&wͧ lrS?n鈤qW9S\9ŭ2m\li4h7' "Ξ:eˆ-`Ywȓ/f8bIwZɡ OofkN箝wǃLrT2F]_^>ĸkNmlуثriU.٤TC >[7-}3^Ϩ+3U|O3tꖛTpiW͛\?c+CylۃoRθ"wMM<*k#_qgKGS䤼?._m̥&;7mㄉ'{V?w؋l'S'_zF,x'DD2)hY];OD͙R]ٴثofOe*}?^96*}hvi{w"xOh9U[17;ha"ɶj vfL,G/oF`se8bСGFDĆ/~U/|yeqWJsvޚ7SQ832]";dh2"ea՛cF)5&]#> V,H>4SXC8&""c=ɵ,7[?0kIS/f@k^}˿l\OAE7JyzUľF{?v';eCeؠօDDāD,X1oD{e'1$c (~seUhۺl~*"??'qׯ: {3S]1""8kx/-< h#7Tnl^&{ETl{][S1tp[yEmM!*o)[Qt¤;'n[rS}h|Ft0 'ɶUC~Ĉ綾ܚj8q#%_ Uo}B'z]Pǖq2ל=CdU1dDaXikj<ݝԥZ-վ;g/~IL~zc,8{Ho1xGC:l}]XTizˏ8's4j3CTX׌q*Bl[]# n}Oba ϽUWr߭ou0t)({@ L{?|W):|VH_[]?q?[* B)_>"CR+d ;+ٵGmmk""UTőxmjCDDrM)Ue}3^bkޔly :ƩqOU-?6j^C^u+^=ٯo}I}>pq?@'u5_8k?' {'K"G'ӎԋ?:ZWR7^"5e% J}H|/U8lpc7"p""^ѯɀ#""\i{V\bOEDZZVሮlU1o0""9 IDAT,^Lʥ+""N9"0dTDDjɟWgFD B5uⰶB樫^wb,<&B<?Lҥm]-6OJi)wr>?2oD~X)wﴔ.'>Tk!شS514jڅBᝥ7nӫϿB#.6iS.B~jK6X.F]|I:`ї_;)=Plov]o1 6~9(j:\W]cţyEd9Eߘڷ~2 ?Bֲ;bxys~o eymOS|;!m !}38y޽ɎMo<2kwjs]NXF^|վ7^vgފgw^b꿴.By޼Bo,-+.-Nj]W n+7!Vx7˪k =oeMԯ+j:J옳=SǷU׆9luw2ی4uւJˊK˶ֆacwid[fjg;>+[!=s箙 䵾Yc+C}gBi! 1JWϜč'-{_lkm?qM8?=3ϝ9U/\]V]Bz3OJoiێʣL aCj}3KtR]zƍ LO !o[oYt/5rHNzffsܚ>lཝ֮՛79 >v}uEeܡÎCCMnSFէqꊝ>{;B! HJkK}п~C_o*z*CԾ/w6";Vο{KBo~we:O7܃NJ7}mͮښxݖgIe雑֧g,6U3i*._/ !WpxnRSa׮B)i"Ge?q^eƻ/J=ѻ+^}֨)֗;!=w.!9lz̾QD˶uny`~ГO]P0nXVuYћoK!;uεӓ=#"_zzNzZmzw'u믝:,3i@G2;Gz zB7`QXIÕP8 iGO IPH B"A(@$eʤG.?.y?4А8T@PCʜ1aXp,Y? !\>^+ j|L}z>; 6 )oN)ݖRRCހO "4:c "P&%%%#yL?*@35>PGMIIIK !$,DZʞ"zX(Ӝ>5cPFCtpbPFC"szL(5Bx}pRSS[%2rVzL(ӬIMM=↔U~W r6V!%06w{cMa9aLAG=uhiii=t]۷w]z^(Bohhkk׮_oʦXV;1iԸKOieФy]FlK_[O}Ԥ5${JC#-% h8~றۏPۜ$%ӳ* H2L+%M'bLj{Lt=VJJJCCCϙA6W2M;XI\HU2ݦ2e+rT л%2^P&$2Hc+ Л4G0MLs"T2SC&2G=!ԶtFe%2G@)y=>i[N}%L3+5@w B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH }=@x۶?yKe;vL,P~}W${$63t6l.+OjcF${.}nKoXQ?UTV2hSRR=!P]}˖|XysYΝ'zLZ2}y|̌dMU׼;wuegEnǡm;.d1mK8=@e-!dqTސQw=;80h 9Éƈ١̎;C=@r4{׳CF))) 9B'  2DPH B"A(@$e  2DPH B"A(@$e  2DPHO2wEJ<Q!i+^S?OE}({I#:2b]yGMtLx_=Բx^[)7}q~7A`=Sm_mR-еR=aӒw%,6l8taÆ knBB'Է<=aɻ3";bȑ… /\9rVRe+^yŁQڔ᱖K__ԏ+q2UVV6&,3f̨̙3gΜBEEEee9D,¦6L!57w]scĈ ,h 70cƌ3f4V2! 1>Cʊ2meͽE\îxnnc'cxiVa]}>iiy}>gZiǏ/ixM]{^韕 !? 'OlqyTg9iߍmwYS͒m޵sW}Bߣ Ǝw/ !EkWPu}>}*]PP/f!Dqt͘1y/ 4ނA(V%%5e"^ꩇ[)xBX⢅㯙u+lZ~O%ڪ?.dy^QGs[[.j!jZ~֧o>zqοft<[w]Oa!Լ.j1UX|qFNU\Q%X⢅qͺv$^>Jik>*!z1#Ͽi9]ʨdz4[/pJ3Uu_y[d:d9-+FE?{|f+E+&o-kkjzךn۲i/yG^YXTJW=JS+^%(B!c57O*oL':|ڷisr'LyqzknyYYYS[&%%zyIb&wݕL!5ImI3N=qk}F޴j}BB?g:- !IJ&q S~h>*MxtB͟4}ƙLue>OWTTO*A(dФksƩy,a/}eM7-hzLڵ-^bG:iK)gҤ[PQy?KWTq2_I򭢢nqg&% nyx¤tU0 ?A˖ 7zd\AKle^)S쥕2e믿R%z)A,wo+SۜPuMEELƤϜ暽}B&žύ7#;ׯ(叫V>;M˫Bh/9Vc983zL;Xn'Qd ܰ[*kִ_n-GdK뚢~uqޘL9{{.p$,]tNS>Bb9xۤ˪~#K +[,z59+,BG=b艥>}Dvb(֪ۺk۷״2ֶZ8kEg3fc-,-|~a cCǝeSFu}2*^Ke}=9)v/ΎhֱiܛW2rcezmdwB|Ӛg͛/l.=T2e*;髳nuQG73Ǵ=rmXvwzT3٦*c]6#2JZʛ<-Y/֋P# GsX0pHLA%[ e)oļEKߵ蛑IҦ;CnC6HF/s̜{~![gWy5g d t&S>҄`fUcόR=zm[4elڧ}rAw$;ﯾߖC'kı9 /|s!['o(Ivƍnٴ\dZHOvm\;:9۶W`RbBBe{9뮧^.jOXVX;vbJ[FUђb1g-*i]Q!W{( J׿f͏~VѕUEU=q]7tFb+H%+zIeZxegg w)IDAT>*/VTִ.Q2&6߱ E%{V)|??椓F~,Pʹ3`Ƃs}d̵ϔϜtQ!VXk2}"/=QNsƿ8퓹hYVMW?{:3N8axހXהEk8o/@$魄2++++ Wmh݌/:1ӾrޚlK|k׼x9r=GvYpCpN1k ۝xZxԌ~552MK.\to1]lgqFaĈɞCK;y\Fˣy=҂"Ξ5n['pѹ;x/c_\ց~rjgN;@/7bL/& aeߺA'Ͽf]>&/,o;ofv?"jbϞ}gvOݷKٳf_24p5cwxYǎhRcy/{fh}R[[ܫ+WN8k>>^w6pd^%^SQIQiU#mo78{?Z˷F{t>^,w2腺H۷w]2=[/(('  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  ${.Ýeް)'{O'YQH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  ${^.'{KMMMKKbiiiɞt;vdOtx<q[/۷d ۓ="9(;vصkWi׮];vHPYK!񺺺dO>eJh&$8 e h&$8 e\}}}GBßPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DPH B"A(@$e  2DBd@D<'M4]t受q/iFB>^Sa`Pe˖'TC~ٲ2pXyYO)i=yЌnDs/9Ao+{/2sC+=M7{!xk=GRwgO !P鍢+wxr>w^>82 vP_oK{Ny򐟛>8lKfp&hqIE}L;8m{ OWn|7הT4>Ľ>&H,#omk%m!s@qN8<ówzXƀ;@eeW\qw3&iC@^eNC5%˟;ۼ_'vA^޾5 f`eb,r4yp[/~GOk{EhT'|o֦|=']y7'uJw^aay睷xb =z~,aU^>9I5럞3/gVA^ށ?>1?' 3J{lٝ7~U%+ھBSm'kުi;YNxy1ɩdB?paaaH(f5 t +_cwqk˟]'~_nԗ/{Kک=Zۺfw̺2ԬybAL&n𚷟;:rvH==Li7V֜2ųm+|-!G2u2a֬Y%%%?OBS+c]Fq&WH*)F٤$U;b((Դw(Awz^4p^K5yY,XWjt@ ݙd6YM7ϫ?ݏx)SnJΕ5AALvo-/^?u.?ޏvfB',Btp|oeo^0_PHAVPTt=~302%0v|p-[~(Ngө}[nx{O,˧O Z}MYHq.zdIRhՑ+rUʍ+ }0\77'#PhǎO~fFP飩xġf?x4ߨ~OkRnhsRnMn0*_oiZyڸۀ8}ܡ]5uK!)p!'\W^deKCD6FW>|%7?2wSJ"!{VW3r+j)CLFr7n*%&׍'a\oeqHvDWC{=!z%fWYְ3vinn~۳}DP`x676C(Mc>8K gNGBp2T'\Vɋ5!.PV&&\F5XX;&9ܾהىOz$vS.cWP꫰ZFdehoo=+3MJF0z9;M?shGVgY.4ϔ6Ӓq,ڀ!٣ &bl(~%LL12l+C_'-L9|XP.lki(\:[/|mfӳ5L@GԟB܌E2eپvX,f !(StmtKeȡl9CsRnJOn dWc`r0_ 49O3^<=ɻΗYh4[$9-a [<$XdIww7y3M&#gۑӍ%㣂w1]OLu4h8+I2=m/)eSL-O\ [gV3Q->鿔~[m"T^2cJF)XczZox^b4:32ZL(l<4{yYgڴ/h':NSS,Ә˫*Map<"J}A8J_Fo,׬y<#%X &brO!5.&!UjOp[=&ҏ'Pzt'74m+9* LV{?|ffU= XRjLJ4Ns/),2%#]n;7s>ODզ,˳z1o{e2j'kvg-eߎ{]IcɁpY\_<)WPwd FdX`'W`^HewMm;=GZֻ$"Cg3]fYLB\oxl^ٲBQizgweny7'/^YH9R2B‰2Hv)++,8d"컥M$)kƆ gp6ב':~g/g$qWmK:!Ms/;rhS!VKÝw27<Y#8yFJPH#{xvBkݳE״TrhBiir;N)#fYnYtk[/(Zw\EODώG'.OȼQUu˖-ׯ_'%9*))&(ăNT2@HRS%IY66T /y#O1k, 6 (E̡nIENDB`flask-openapi3-4.1.0/docs/images/openapi.png000066400000000000000000002432561475153525300207140ustar00rootroot00000000000000PNG  IHDR pl pHYsetEXtSoftwareSnipaste] IDATx{TTw.@ /I:@mMV$Mz5dvw-Y'"dL;^~NŬN 9#㥏L(RD4RE(n\WwSdxt;am6nh4QQQ ~d-bvfh4ZVѨjZRX$,+%t:N7E< vMJf!+޴w+qW/՚6_qqqr3hN'GsqdNnZѤd Dthjە_r8Ue%F3f Ƙ1c4fS2A, B2r3" QF feV>?]"׮5;+%=WYj!T@zKvգE x|V > ʈ۝Ag|h@`2.yPƧ4{Z kVIKĴ/jөR/+8' &Aw]5tӦ|YsM˪i cuY >uMs%Zt枒q9c|f_ JU0$8&ϠL(;%0u5?5B.(cfWxL _ N=ݎp4ՈȱWz2s:idm?/v-Aࡵbx HHHJmc!mҥZ2psGv@2$<]|8Vn[p?LrJaKkXZZZZZZ222233V^^ì+%7幄\P?{82jd@R1,897Z_S_ā,@1L---w}w#ĨnG}0uuu'OILL}e,XpfeS2 ,"ED='ViCd@&QZN3z\ߺgì'__aV ԘL_|q͚5_u_5k^|E48l xf _`T.)//BL鞶?;pU']4'G̜׳Qi8(-@#Ê*ڵkk֬ټy2JJd29j,ɓ'AJD&<<<--ѣmmmSaP(Y}eP(SG~27q/ d!gnӒJ9VqӲEM Vw}:rH䯯+%3rW_} 0D=z۔\teO zJFB隵1pJF,V\0;MDf?cT{K=M98S]V7XeY>TMQ{g'k.\3.X:>ܴ9ᇟ(*#ͣ۠de5ʐ&Y`ABBBaT9 ZG1&Dʁ^t?,"?{,'{mjᔖ}ZGf?TPP0/|/ΎY>|n/VY'.R<Wheu{kEƟnyOfc Jz&Y 0RZ/ȽݭU&"bj<9g*!-S5E$\ZNLNGj8`]aQ &R^^$"Lg_1Z%,nrr.CSXI<Ϥ}8WXQa='H믿^fMgggddZf͛C Aa=%`Q` zV&2u lw [syeV {h^a'~cT{8ЪJ=."R"9E,Om,TN/HIIɘL#G"/L&2.###% Y`A(deA9OQt-^_ߋEjlŧ͚,Rcjn=kC "5yb9Sq* e)#3f `ydW_9r믃]# HHHvgJFD,Xde,KyH2Wk^;|mi~{Z7t`HCɈHrC"{g?-RϸZL^VvR'ko*R^[]*vz;%l'+ 333% 8)E(defj{8u2-IX^#g?-ҺQ ]7kυ?~=xٿy]?_L?챧^_o+c^ur{ 7f}g9`PK(dA-(F2~k^᫣qi9Y^+/dSD+)q˻nKm徽T&,*S#[^8+{wҔ<`ݟ>x)EFܷwK[/S2 2pړ>"OOR2feT---#)"t:Nam6b_g=W~Lo#2$&^hmmq_|ń-[DEE?#222222<<\j4ZRT*ZJugyD?!fϞ=>`0tqX,4 = Tڙ\LlFЦF+kN+Gý ct׺k+ߚӆ%#jdڲeKKKK+_)lGmmmJZZZvvT\J;2lkk?s>쑆1]7vԜ6^Z2dt|L׬;|/+Ȩڌiq: aGcUdnϔ `M>=%m ,]k6|5rvS] W^y%%m ŠLƴypolP,CQ&QV>qOKv` (;A@Pw2#tq:!j :pKPƻ&V;UzAպ>=vf\|tqp8/ǵkqWP=TWJn1bp TddfS>ebVn=u@Dd79 W #))ʕ+mZRT*+R ;rTU"26fYV՚zEDD(YFdDtj4өf?NDbZ:7)AbZ#""&LM8ᄈZZv82.;ʝS]+)Kj⒓b@455)ej1@\c t|J2KFVkڰ `RYߙL&,2` q@\RPFi of8m pG1= 2BA#Cb`xpa\{<@!c2CJ!+ 0P1>2d{J=.CV@!(`@xdb})e Y! 3 %C X\%`\)2BA_J&@\Fwp\22wJFyhjF!tvaZvKDdUWWWk0|NDDDDDD> ׯ_~]DT7+H::qөVGV[NDDDXXXggPPMk}J+" *Z%-$|ܷϼh4V\CDDDT*׭"e#! N&X`y5)C ON7&`jpJ b1 3LPTd"7nYmVjjFhZC*Nzw1NNbh"UjڶۜG`#FD"]5AP@Q] v2v{WdZ,:;fX\/lW$ZtحaQQ#FF`? FP@? (=#`hfho7h5jX,a6_2Zf2u|=*vdTt"( ) Vhlsm6V PWw]br~ ,B3w naf2V|?tpubvarKWW%tw4SwFUiuj|hjiF vec*#<<ˣb㣣GZ)D`4⋚ÇFIIIIII̜;wn~~Q"RVV6^x7g}W_ v9!ܹsWo7eeeM2eĈ.``]tIDƏBBWqqqII^OII֯_zD$9995x`2;cf(qdvw}wY[D$""bTLtt(ZmZ#5E|g'w`hǏPWW'"&ص ?7T1<쳿occc]["##E$_})zkKfffiism<9A+ի%%%ǎvYG=U "G  999D$666&&&X%ZP<̖-[]΀vfl/C;…vcfsabccSSSOt:vZvuͦjZmq1tPFƌ'NPWWg65MjjN v94iҥKフ W"q%%%<̯ke ʸR2111JP ( NP>w{?ĉnja:;;/\p .(2`1 6mr}vf A 6ܹ3??駟noon"$2/kkkng ج֖ NW7j՗[/je%0%p8Əa\܅4V ʸdʂ-#Da1-:|,YD>Cqq{ddezE555)))~afff nP_W/=ixfĉW&+{VUU͘1CDY`PTY LzzzcccYY^dIIIAAAJJJmmm O?gϞ%K`Ν.]ؤը;::7n}jZtk׮EDD˭/cJuY9:n|5fw$( w)D2_rh\dɖ-[Hɸ.]4Gn˘AL+2}FA1ј[SSZL2MMMzWWĉ/_?EFF^:D~Mv2k׮l*:A^xDYƒD=,]t˖-Se\dؐpj*cZG$N%tr8jnyp!^}RZ}1c̚=K=RR1"jTȨ|V莏?A{E));whcccv{SSOK%ظdɒ^}Ȧ8qbɒ%Jd|WR2exSƖ-YDYi3(^2]]]sOo./=wvv\Qש^Yf#] r1cF\\\\\WFxzw};UwcIC† Ddɒ%))))))J_ep8^bXĩcnd"j֞5 JJƕlw8W\뺰0$Nn%s` 2w^b ˮMŋʧ M(77ׯ/** *ʗ )g"cǎKR}7kť{v~;yi sε9>J?شOO4H]0M|jֺ-N <\p9lNiVIp>|Dm9 IDATk?xyUaênK^056oqd+fV4e9|ѫMo|ixLvԻkvY.';{;6)#D:(MbN8чkjjfΜߥ"e( uFzW{{ ph4k׮uhb|tM^#Usu49;3JD3=@J NNFBS DaG([YSn4X,K)i4ER<ڥV:;\jt?PVRmvc[?=EeVX?A c=z Dի/^ v9h4frss#o(3="cǎv!"/Ʈ^˗h+={1#FP6}իWZ7}VF&7 6TQO2tc/RrRr:2|2AD~p.Pa7-~Ƅ|||3ﯜ)6**[S+Í37ژ >ꝆM}?4J+ߴ7{j:u?6$jWoPț1ww{9V#Uf9ӓsDNK7 r# *?v˪*Wx;Qcc=J={463!եVfwNg[U+fhsM=mgWn~~AѸbŊݻw|ᇤdz%66v„ h4̛oi4ׯ_>]..sνCySٳ'&&ѱKر#&&FYi[KJJDd֬Y[555]pիΝ 0,99y֬YiG1?S0P]_[0+pZξiݾ}ZYUfKUo+\wBVoS^Ŧ/_rEZXNmUV},Ϻٸ&q}< RvSi/}>ó)?[}/EWۍ2ݯn$RWY$enʧ~'"J>?}Ddʼ=x`0+w:(\MeJJJ/^[(SSk&N'r0NSRvsYVXqTNZk#L},Mh4`W4th6SSS5M5LbRrK.ݽ{wMMMiiill7+ 9ǎ#'N9eʔ#;vر-jn.v /Iԉ42krߛW'*ˎvmKʗgwt ̵......)))22rQן?^׻\򢨨hMfuMR(aNP.8ŕ >UTak-Aʐ/:.55l6744 FcFFFo Kŷ,++[l"Nis==:#FK=s'Np¹s粲JnGwzŊf>ΦOEfz_X]񩈬\<4Y 3Rv[^.19"s MD&u;TϘ+ld9d?wYzp/GWDf>0|\SSӿ%ݻwkS(7m}YnSRRv /W-XjZ-%VS]|Sj*J(}4"*>n +W{MJVŋJVfҤIaaC0w_ w'N"33ĉ{~Aj~пΞ={޽7~MM춝̾Mx Ћȼt;cs핟VTVos}U5OD/ ޹|hͺ'tW|YD&McgIQd2\$2{9VgTD<0{#b2DDQI3<@x5]ȴ'6/.mg|^qlm\<6:|\r\&"Zؤ̟"?+>ֶJ 7'/"֎nV>Egu+G[RG/' qJJ殻?~NKKCZZȺ:|%7ߔ LqqqW*))YbwdPR2sMIIIIǏڥKabYu:]WWϽJEь9ʕ+Zm/bʱ#Fj/ap8Zj%(A bS3E6vSHOfI_^7}N-⢻(s;g0n\I.^vg>Re2og?fHfj>GO>W0W̮SカKoN5m6CӉި:\ge0:+M'߬sOe{O.]ݴYDӊ&6CӉ77u7}Tom%;ΝpKoa@әۜJ L>]D7 ۋڽ{ձ555ʵ;wpSQF;v Zfg%""cƌnhh=ft*K53FQ*Uddf!ۿ,Y{occcIII{{%K|| ZPfѢE|Hyhhhv@l/^h \G?gFp]zUDƌѺ}.\#Fa޶]|R\6Er tlnollq~7$9>11UD/ؚ:'If]ߧ8ڔ2>*Z󑈈huQc'ϙV]6hߵFGG̽:NT͘ukĦԻ:m_IVv[/*y͆?o;Ab8W<Ɠ;ޮzssNh}fW\ c_6hסI޳_)߾0>ozy{I}.]6iJk]o|t\RR=,hrnsa)6.!!**}_T*n5jرcv26*m=AOٳGy=7֭x7? |S,Yy2JP"2>~A&"Gi7 0Vj IXum7mbm*lgmi?mh񳧥==PXyKn.7 |tEuO>:zD^h7i1gJ6=ndD$l KҒj|M^#Bi|d1W}~5"q?_VN N{dt"gf[=ak %ٽ{+]Πs8EDp8:.4W%`өdb6MGSTZyqq#o4o>|޽{sss333KKKإn+K/i4 &aߪŏn?!37{gqj$YKøֶKN4] gTD<TEdʣDn3*\e>w#+%Oj327:k9uKL{pȬ9?9]e>6rxbMϙ*b2gYpIctL]NFL}7uuu.]RJKK뮻"#}!*&&=&&*طÇ %+Ӈ2999z>!J%C*tb뮻 t:+ 1^٨R]ԻR-%P""C&(zqۇ_:dffedd<nlVR2:.--m ՏդI7./GIOO/..vi->>^n.ԏDdĉ;mhW8/'RLnS:^I/Pp^}45I VT X'OەN;@F[TE3o ""S2 L>YDL2YDҥ[0m%F>.4VD˞{]ꪫキ+_/ۺ{9`q+u֮nڵk׎;fl///߱cǮ]z2Pq}ءŽLNNNqŨQ$D`*JTp8 FTftlBVNlNp8Lt:U*Q%K{/g%&&ƻS_:ͬLnn^)-- b=C:.55ˤ%YSS/JPc!7ظaѣھ]}SDB/.ݰp7*[Sݷ1W*6/G'HLMnAD.1oii^_O)蜜;]/YSEDōՐW{9DdʼIDƎDFsH?(]}_3׬y9]]"}Sґ]o~tKyZV~i0477o߾}ʕq\ti׮]߀SN]t'/XD ~^[\\g=aqeee9x6pZb0F38Z=s3gFV+NaaaӦO2e3sKJJJmmۗ.]}>/Xh ܠ R2#Fr)Q~8>|U&NA%ZVVFi\澮۝`̘1"r…VPU\2z~?Mwaaaׯ ʈyQ8s;9mȑ#U*n****{^Jt[lֆ [TT|Y0;(bccO8Oٳ'//W_ K3Ɩ;a„`s}7?쳢~V ;軣|y_uر?q?N{Qe~󶘏o7k݉E9!Hz<6Ii=^)" slxyqW"9+/l_2i$:'t:.7H}""N6͘c5#]Nl2Y@N<ҦDuƹA2 =>/mM{|mY_.' WǏ/((صkץK裏S2+V;vo4 %%%Q ?{gڇc>z(..CHFFΝ; mb hQ/QFeeel6lXT Іi6וN37m NFV[Vv(ePYjΝK,1+V{GFŋC:%#"<QO/hL""߯^_s^zU(2Asu'd}HɈ~ " szNh౴O|K#"Vm6ŋȗ+WVTV8TQYQyGxf Wetmɏ> lٲEibŊ>\xED&L0nܸ`s֯_geۤe˖۟ IIA;v)'t1 pUwE$'5w5TOsRk7KT.wKں(A.Tr#``*E Mװoy*q&PM7ū>68MKŦh7-N xx\>u5GN}C.uzX.UuӮf݌?c$,+?/I|7ޫb;/ "Iyɓ^ Dt3x]n_0M'b>:rO} ei|ᆭ\GZ|_O]v\ctлkqƊ4}ޑK7vRtv Ö} 2蹚IIIvdz8 DEGwuuzlt<~SWۉ׷[,h4FD,K{{{}]NpӧO_~]7tvvv/&:(mE\MsZ IDATe<ăŋFL0!66P"66g)..^boIE3<3<.|=z?g;W1b7n :#RKO.' WADƏͮ+VhʟLIz--̺H­R$n])ug>ٸ "")3CǷ:'IZ3on{ 4}Rd\ʗVg*Lfh,;Av36٦Ě9"b3mZrV?qȟӊf%M_ʜMݷaIɵ @Ixy] I&gMGYz)##c{qqqzz̙3Q WIrq8Zf߾+PNcQNSVGDDX,WvF ntjX?x0(T]]]ݏ ^ޞYZZ: n744f%%e4ssskjj}7yyyz>##ll"##E{߮>\AKwʏEggoۮ)S Woֹs"##poN<\)Իkveoցwu!ATXXXUUU^^rʸm۶}7*d֬Y뮻Zjmf0T*ZVJDO?gq ={lذQD,Ys,i8h5,"*nkOGDD(7={1X%ؙ93###m6FQnFDD8R: (Bgff*}ez}^^^ JJ&,gC?s{I F !W~Z.T5sV^mϫȩV[i}^Vefd%EQGG֭;sӦM֭{衇iZ 5#]fBᩧ?^avq-*#o}*dVwix??;ٳk׮(F4pcc{OOϤU2q0-}zw,[׷mv|k_yLEDs^mOk lws___h9J&LJ5ݻwb1Nڟf29T2ҏ5k|իW+p|~ɒ%3/m۶_~ƍl۶nX~}_ꫯhݺuW[~}E6l]f z뭵=n/‘ /|[j;.z뭓]޳Q?cݴZepa2߲e'?Z"񖷼eŊQ-^hy w?xگ~8QO~2+իkkn_^Z*WZZZ(J$J=o߾ږLk ڽ*J6 6-J_ G#5qO1̰xp-ZSG>oo_f5kִY&m۶m߾}ӦMw}wioo?w;mݶqM6ݻw񞞞 64| wdr.T[W[W͛7o޼y{… B-ٱcǁvq>3;^uU%?zMCN(zoFQq嗯]v,Y||]l28z)9[,kӟƍOt۶m۸qcxܣ;G>EQOO7<fL&zx|\.Z+}'\reE~{?~Qm۶O_Wo~\s57tӴ^Hf1CT*& /pwo޼y[s9W_}ugg$L/|wLUS]> /?6'?K/{B۳gO2L"޽{.]zo?hook(vE$o_q{moooɼz{M)R]>IZ͛1;_ 0=|+_{z{g\hѢ:s"Lz|?p@m  9s~駞zUdkp2]"/Ο?ˮ]/x;1zM5C(mv-lڴi===^x>x+(P&m۶m۶m͚5?߿Wwܙd*JϜL&K҉'x/\\3s̄2@}LPQ8pwSgqWe-@MZM$O={Z(Se(J%L.9uڅ01G(ԇP`۲e֭[lFQݽdɒŋ/Y0gw7Q(kej\.?Q%3ue8\(SV;::IK/Ze=]Trf"Lf,$JRɞE9 [ZZ>7GC(P&J"P&LFQ$a>~r__\.J VL&Jn%'2r2@} 2шuejP 4PfK{ S_T>c>ۛfKRlb3(JtzppV8ԥT*/ e32BмK#+ <[/ @cǖt3JmX,JL&L&SojZ.+P\4\XNj2@C W2íLr\.k1pR3cCQ= !lxQC[2:wH۸\pȩ&PckewFM q8ܺ2`* qh+|G eT ed+dF2.*Tsh2f @##-*cn; X}O<t4n10ue0*|{0yw_3!0}ciĊ2@<|$Kt{hdd A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A AH="L3t:JgRt*J&Sd2L&DH= WVjRV*J\.˥riT* V*(l63|&K 0m%(J%X.CŁI^%H&[2s\H`KҩB:_hZ WV`R%:t=AH$|%/= SBq}V+q@24\6/465FDܳ0T|e2hB(_hini,!  X`f*44td` \ahKe?+Pg|SKl. LcŁ}/){fTSSK30Cҙm흩L8.tԚNgJ0Ce斎ٝ'k :dmjuhp Y 2t&1-J$'囲PR)= ӛP6t& dtR) iL(k5md DМJJ(H3; ,l.kV*q#essNdq@Rt<44X.iF(ZfN&q@dSskihT{ G:kvWS@EQ\.2LPjjn5/)|R. BLH}ܸg-4=ӃP -֒`5/_r60l0+)Hfuves`p$tcd:fK3qO&H:fKqOK6 Ȅ2V\>)`|qO%`lMmMmqOGG ` LcNShdH(DDS1I$A(h-|!)斎`*t}vSjkJgE(Ͷ3A"&U27ZԒ7=SPZZg=ԓ6FBSK6{ l.o4 exEsKG#@y`P(|%=_&,*@ eini{hOse|6W{ hl e ͭqH(@"lj!3\SSk"P t({ hDL(|SB%S\0rB2{ $Z>0y<N(\)`xP h] ('W6O$`@@d6{ bPpes) 8B&WƯO!+=L6O!*LR鸧ɖJTS @ҙl#@<< K(t:O*=BBI XB@RGxxP PT* %T2=BI XB@%~$ PB@%G@< JDGxxP T *O A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A AH=0!j[OҶSɶVj:rtdp(. IDAT>JQ@T.&JŨ4|9ѷ=ٷ#w{o{rD_I(ST2u.+w.w^jgUǿKi-uO$'{L>zTeC&RJrkKl=ϜU*[S^E/xм9SNmZodC(g\:u.[]>嚡jf2L{B O)/qpSh \Z\ZVϾTK55=1hmHo<+qrUNl(LPz JוLS82Po\RSiɕk/)H2p}*qOշrSa e฼}jSL\;+%cwwd^%QO=A(,v^{Y{ - Ġmv#Lob\VNq:AƶoO# @2c⊷d1wE9SP L^Ҫ * YapyWe(tW^k7[+qOQ$"8Z" L E Vn? LȂsJ6 =tjs4FL(K7d lB;O+=yZYl@qSSo,6价fv>-K|$;u]qX?ķ }(v>=q`Em]9' ]<Ԉ+.ja!q,q0iV_j덭ٽ5Oy9wb*-q{Ti`lGx J=g?yׇ:+#۽5Otb_:XxAi,W|UZ%3r)Grhe eN|si|wo{kfmm=gIkePl'TKB(c빰4ouy⛅pr<ͦprυƕ{_YUOy$w?sВsjͫs3y𧻷fޗ[_qo}ʡ훼.R/ycat{_:uܑuG2Q=Huz˾Tݯ޽}fzic%3˘ɜ&ӂUWGGX 5Ak[03뿜 4,X]hvr}f)LPssӹQ {D@e`3K'V~[w)\kM 'V&s%_ַ5.8)cwȣ~'&g6&PFkЖ@w@xV9fD#N;$27ŋ_3f3R6fgu\q oyӏx4Bm2]jN\58fQ}s~MZDCd29S JچEG8zb{m5wƷx}KKK#Nt4΂9+Ӡ}w/dɊ2*ser YMW7/9c }=SΝ{{Wϙ۹rKO]`w`Al+]<4WFpo\.7/_㽽׾9iss oګpkm& 4we){2ܕq^7vww:ӟn?Ï<67{ux9ΪTLɸ)d.'{hpZ <Zꨎ{ss^&|+ٟf"Hw}탿_ 2pм e1|b.z ey箛;sJWK0} e࠶J#/2B'-* # }ziM(ȶV󳪍;O=8>GxЃ;6Ylk}t:HHZ- rr1^05t9b?L>G58ں5m[OR :9guƼs(*W*?S[W׷{qQt_(eu$W72ٽ53豙xWv?i\(ްP&,Z.7}#=skoxuk85͔=7GjlPBxE2[7ǾYQfu hOo/,)ɜu_`p ̿׏<]n>ĸ=5%5o3/]]~]vEϚՑTj~ /߿֏~q_~= yD"1{W\z;>}n+F>[lLW׼.|~7٥|)K|5/5K.` fhh&ʌe47^b;o'tͻK;t(Jp¼ k>@~k9JOCٱZ=.?/yi˗W7|ѾG5k֪U'rӖϿjwSFV{477}?~={*r쥗\׷筛퇯?߿w,Bl+^Wgg}/q~a7p̰D"pwveR*+H{37 ׯ_\.wuM:+־7\yoƕL&;?'`7kPw^Wuev>M?Oͻ^y7\_oj✓J4fv>-K.X]_w֝vY#r_gÏkA@PA B df~'aL0a/k^L>\WQEEy~?'8.;ڏC)3@xawMm<.//Dn^R5E짯fggOv̝͝o9ND"tox {W{h)++k$9c'?)gq}gu>ΜS^*M|)={Y:m]q#:VΝ~>/™eF6eVVVO8S:I Sg1%SvB[/)v;s&eL- ~P6Sm RZP?[>Y_S{!>|C cvYw)))y{e}MMͽMN .-02O9)堢ҒI't4gϞKuϞ_ZSSwC%C&(k-3;ŏ‘w$Aw>s[b׿wǏ.z pѫx?󩏦 'OsN6H~B9x̙;w~NX,((+~湍7shnnS6oO~s/$o(..yqrN<r AKn{pӗpS?ܔ=7C^߲SJ.M!/uo~ W^߾MMͷ|̋S?lЛ>>S4k֮KQPP0a)\2iѣF&!9//k/`ÒWV|E[[|=7|/~yKWrW |g8{gLtعs-_mK!s&wJجY_zʔL?/nhhS&O˓CDbKn)S2WӟgE#;.LcR6|X(Zv׾N)Kn@7e`mjҕ"7US^=H4Jяdwub.Og*.xɲyU\>yC1hmOw¤v Sկܹc~GJ8NoΈc+8㴔u5'4.y%H̛ǃ֮[fa.肢A+{ɬtg@8һw\sň+;ww<3wO>ֱRs<7;;;++޿h-jm=o:Ⱦ)2<@m}]3*>|u^=-[YWWR^6|yӻ 0'ZZcN:y]]{'彑aڷ~)wnܸN[zM=WLy9g%GvBPM?iG{RJ^0f}쉧Ӽ=wݺO3f9g<[*,xt.=={5i眙Db{   gM[t_r.#)㦷XVTkRi7jrC)n._t/&{羸Ľnᆴdee57ٔ^[{@s)]]NFD%E,U1[3O2dpJ]NɄB9su ySYYu};5k999O9)M//pWgYzMۣ7mޒf1cFqlJ4]tEoO\}ins䇭UqH$yWvvvQQZ ս; 84.Ӈo 7eTU+\AT?T\un3}.n޺,?ܔ="VunkA60WnfyP*u/&of˲U[SFpw,KO(/zQ)DʁM~+} fhWw][TTt7&s߼e:V :w9mʤ(KBKSWR?SZZ2i℃tjm'zW/v\IN4qˆc+GY^60?ʊN]oMب+D?ɝ0]8^_zoOljj= 8N]sbΘ:yRZ)UnwF;rGPkk˦&Qã&}:|UOJ5)vB&q[t*]dN^gm7Cַo~v~k䉟G9̂Nrrr.]j~˻Sa5S&O*4hУ{sqImQ-[ 6nܼG_wɝ/ujH$ҟy5WxbUqQQVVC鴲c7n$47߉eÇwJM߉b55F/~~jǯ6?Ud?©d\}V ߽ۛV/~֯ZZĜv}e..zyqCCc Og 뗯XŋodU4ii\ZO?ۿ<%} Beee3fT7M^(ؿ(kniݟ1 @22~S58~E-)"Y[zQK?dBh >}D 25EqsL^6 IDAT0(HdOXz߾퓛xA:`a7b\|eÇI$H/xy$t%O°#E"[ $(oǿ&O8kSdʬ:!jWunӝA=jjjw~p#NOM=%k֮KC3dx8f1Pi}˖xॗm}rӁ1) ~}]?j;g15eD&Hnٲm鲕>M7i3>W/Guf_65)Jݺce*ip>ѯo~~~ׯO{sJO=mң=yK `S:zlٶॗ/_z͔0&7M8iGpxT}:d‘Ȓ%|vΝ}餰[_fn N9uy-n@f%:455:acre+ ѱ2jlǟg'be+Vv|--%G9s9sBɓO:th}W\Uٗ_zӓ$+Wtي^!yea^0ɳ.Ydٞ=u.zڤמrraaASwIv3=Ph-{d' 54U%;;{1Ǥsa'Ç M_mkk% gqZIqQZ'67$w-p7d@T3EPfH?8ɴkjjerh͚CJKK&M fΘ>zȎm۶ܘd_xw'y)'uQJٍ&IŞ{~N򝻑_PPnN+c΅=|E"U0'Y\R/ؔRP"8: ` عS~}߼x,S,JGei:e] ꔳW5v̘9s7ZFa9gr'C;vVVVtIӔGN7^mN OՉv Ν?ޞ pA&\ڏSʫ=o ~}{➺CМ7oٚv97 jnij RͰaC6y㓳/۶ 2dH]a}tiS:lʆ7w9!v`(qU3gLOD`7ԮΉ!REB,(kzeaÒW[ V^meذ>3ydKSΦhР0K"XzMSSsWЏ'+;;hHqqѹڮ8讀Vg;-ʸ]tAǵ[tyʱW]yiwx׎u\/X!SÆ +ӿ0 WLjnԛxߵڙz;}^[\\tݵWujښu\  L8lЎկݸqsW'lNim zmM7ﮭmniI^)).;n.iw>v҉U=nkq+V%;pw>s.Wќ9s[_5swvF׫pIvWhN)w77𒒒g`q>N7m#wsx7fpx׺|I|`O^M>_]{=8 e_ʦdDMua>?𬬬Q<~wx.Oxy܈cCP<F{u/-|6lWt=?O'GO` rnґ5id [܏No`}vlݶF(OJ$/),{ThPSoXbU7nܸy}Ǐh4fO`ԩS:z؄G]~Ʉ '?=ΝBиW_u/yۨeeeB}l6=?g?:y1''.>y =Գci2h``ζ/-yo髉ʊʊ3ND"K㟧yٲuuuFn͚3ϛфC]ޅ^^|х.YYYU'_?u~*dt}x_{grgFSRRrE^rх]]ǟx۷8w ~u='~g>յ;wlN\lݿ-aC׳ zՕ^u奡P(HDmyy1pd@`ض0MWP|w)[&b~ƛt/_굵^oXdy:744m/MMͿoƤwpJ]w?W߫'?U[kom/4Go8gNhm;o>ܵfw7{J$X Al܆}W沪[Ylwu/Ž)akvwygVz-?LR"ظiw/|uұfhsh5sb%K{ٴ3wɦ[FУ{;w7mN3Dx)K$z//}G߾Ll[4~[pe+{ B{8 ^cԿ4-5՝+21wi 澸NګLxlee~~WIss˚У?!xÇuUפy֎rr{ƦV_Ͻrk_^00D"{4N)zi7~W]C骫J4]zͯ~}BPSS{SO?{?6ajH$>蓿o-l'𞪪,mx}?y=OIL@5.% h#n-}~_\u\~P;jrϏ- O>?ꏬL'o9M>q#FT<}q_}=uo7^Jʊ+/g ɉD;v\zSO?N^O8P(TWwW^y:ͮ_k5>?[ҫUK>댊C BhfÆM<£=3N߮XٷѓO=C/ӧNy܈e_i,]Լa 3YoI11 28-2rRd 7uLoNO[oYzT NfQaHɄB/xԄ`ұsϿßq>dlJ7JJJ:W_&̓KK: ;C;~# Vx^eʫ)2PhEOPZζqՏ5ɘG%?D۷N[\|Y/x~=Ȳ3] PfZӷp9eE}\9f;r3G B  O)(8OҖmD`.U?]Oʫ}:o.j\y_ow9fHJڹ/L8eh4f;eKʫ}ʼe'D'8O=k_L;3&*HYsc^7,^N>Aκ8QP#"+SZm |؇Gę3_vŹLZre;Om~!}QPᮚKn[Z#J~q| 7US^ǁ/䚻tz%om…}_|ar;P(n]=cƌ~YYY>/}᳝470g^ooe>89m9}| 7O:z~aΜ>mpa ЌI~XP/UEGMy#P( Vrx8o;|5\RYY/ g0¬xpis#=21S\\tu׼OWթ{M(ڵ?5 tRvUv2Gïҫ4ݻo_}}dzefee|m"xiwZ?O~yWXÆs̐N}k:444oUAY<Z9+d#YqANAAAeeE9G]s׾|'fggϞG7AH˖y+gH 9"eo!o?a;1H^G?kWۡSThR:xXK8|5bRdD"Ӆav3G2]o֞=u%ŕs?˶.[nʪ8fH$l{o}۶:ht d@L@5.%{뿷ʤ+ڜWվBEW]qOСCRnb k׮CofR(4q5W_~ڔIǏY\\24H$^YGEm߲>%2%(sN&zֿf#Ƣ_}(u@eeS0WkXN;̊&8 @0 ̛1K'\tG OQaHAP 3]yp+f `+9R2 (2p(jQ:RݺgZ*C5Yb jzLW JbL1¡$U@ 2wui^}e6ewuie{Jv.twCK+XAT:xXK8lx*44||,Ӆd̶O(U.%2%(/-O bVf3(U @0 LJn(<-XY OͻSJ8 @@ ]KsV ?%_t-.ڜ݅K_BzMP eJPo޶ "1xkG;_͙-/fC!(L2%(ӯ"Y cVʟA-ٙ.  @@ ]SrlT*_G~ xSeIP e-KIJ&IJ!Ephş GMG8z&(L2%(s4;yWቢ#)lRZβ?,]Ad @0MRD^y'\4Z1%rzkiNyt!7epTކK[ege/ʭ~KP bLC e*t >KP !X2d %(Px,ktpbmx,U23]n>LP L?Y IDAT Ho A&(\Hk"tp$2&(h֖L>pksK@ ZkkSK@ Z<3cLW@& ]kKcKG2AJ$2]DYP e.777d _ Dh'(@( 4F#LW}/ 4f Akjܛ 47F­R$Ҭ ƆL}GexC?nmt 2~O(t% {2] E"G}{bmLW"(@gM{í-]qo` } `HH۝"eH-ٷׯ8"ۻ-t D2TT*w|~ A&ntHu_]M`;{vbmzە*eN-w_:pػgW-*eA$RW3U@wjwF-NP4fnWkKcSTT8E#XpPq 쫫iijteHW4de8풒 }2B[4wIڝ&. 2N[[$n-(ZXvG9Ӆpb漼L@D­u;ڢLGPC[rrr 2] T_W#gH2pks<+,, eeevľ2]G0Aޔh4ڔZ8j[[6gl2Yx!O Z>HS&ex2h1;;'//?ӵphin۳S# }&4E#᜜L,nݷao"t-=|k @ 6[_r8D#ƽ-͍.PLQpPIqA.#@$X""@Q~Ҙ_84x憖H5ӥpQ$++{PQI᠒B fBpkKkKcKsc"t- [vNNaaqAaQAᠬLaHí-֦x,rA2)?00/0/ 'ǀ`S,H|%2WdR$oLsrssrrrrrsrrsBY̖ @wD(x<bX[-mFqce(X$ dd< (@ 2 (@ 2 (@ 2 (@ 2 (@ 2 (@ 2 (@ 2 (@ 2 (@ 2 ?{w_gY{tc/("(eUDqeF_\Fq}eqDQQQw@@MYDDe)]vs~_IO4m9BAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP eEBAQPP Ad*Hx<cx4bH4U*H\.Wrypppppp`p40P(˃AHDQ`RT:Letɥ3hԡ3_T*ޞbyN3LhSs[h42B: : SB_oOoOWOwWR: (K2|M.WFStww;DS`e|6 ;StfDBPLJgSlص[]gGY:+4ҙ0vuvv% O$[j ;[D6WHJJtfEGP4YKd2V*RoY e]d-_DAg`ƉFә\*-0)[rƦYd2 dD2-K0)Zjjm$dF3|<: ӕ "H66dA \t:ﭔAg`Q`Yd* QB: :S /)4Ɠ6R/B* :T:c4R/#V2D"H&[HAL3Y(EPt(BQH*IAJgSL) l&0Q r~w D>;?]6WDA fsC0EdP @t&t L6 )Z&:Lo|!(jL.0y @9w Pr2JeQ?"h,< Ri) t @x%z@)W2:L6oa(RX<O&[Y80P߇+ӹ}/]*I3)t D6uwmD"T:Φ3T:'x{6_3ʥ}at)&jO2})HQR_عq*6̚xaeR~13A?5ʄSoOװ^`+E!Bޕ)vǓ)EBQfwezQAӖ̬yG+'&3kcx2.*qRi_fxe9eG>bw[,vOh0q(3Mh2=ݝÎRq,jk=Ulmm-=H$288f 7=8^ya|]Jk~7Su (3P82ryT:[4a̞5YO>G :F\v-85Y[o׭Y6hÈ[2Lҕ찃4n7;w }}}Ukbç& $S#ͷcW  ggݟkG;T0 LL$OJ#D)<o^?t0Lu)ˎ:F[+͝_F"ko DeG㎾λwQ'3O_~RdRGGǣ=ӟ[nE <㔗w̼sr/r$)˛;;Wx|nI8(!520;ʔ3BM~&+HTxa1~ٛp756VU7FuuG/;rQG_pMz͗c**) +3ʄ-]I˳avz,\0?N ፋ-Wz߾iht{|?VӟkbC8{;vu9gnUWZhΦs:CGiTf쮟L&Eӕ 0tMd"Ⱦ{UL{~wyġǏ^vdսK&9봏~ʬ\P)Q[[ 8n/{şG1VdG/;sG:WS_Nv<SQ2N$SH$yTypoUfLaǧڎ2K]T䟋ryġ{-\P5^T:;^XʕeyΞՖmI,;ǯ_x /K'-Z閷K/hrOR)k֮D"l)=fB.xߕW\?\zВ{5[:;;7G"X4T[S@ e`iG}c>sc ÎNe/Zpх) ܼ[|>۪LRyU_7xS/{^{VC}X,v)~W~fE=˒J7w{%_}K[nƷ۾rک'_|yKط^wW/Z]}KR^۴1w=zySury%DW>Yf ]̊gš#hiɋ-sDJz;u/]ysgBv嗽qo_U}_"͝_k/پgfگ]_wG>+,G_"e`j'jvera{+)`oU>phtzk|kO]_y|,g8r?G%;׿5tpvɻ vʕU 沙a'_zE:RTw#е߼߾WL&O]-鮋/:[}Yn'?WD"7xӅW; L^%1h=WL3aNj]'ah4:w3O?j"e3N_^WͩRO~}nƣsg'muo={֥\L&38%mk~㦟r,+Ӌ L-}XML"+VtmL._S{-w:hƲo-FW>cdB [;Ztvv}{?T~c =*Hsl?OY` llso4%Z6mX;AOn_j3Luö.:7{'?*ϯU_~ۮaeGosԓO>~2Ƶ#lPLIuݢX{㶟|rБg}`VEZJ@Y.ܟ{cetetmC˰: VwwC_>S<[ݱrC:@R+W>z6|>hႱ?aUڶN{~ V5 ~S5%6;'{7#G/۽ɇәܰ쬁R0 x|7J}S(ST֭_oƛxˁKomi:cԺUbdjnnY]ZpA>:iG.,wϽoR*JΛ7j{/:uϽŠt(ST_owzW&5{QT&'FR+[t4:'sGq>bq=ܣfHR^N~cV(lSI&Mc}\dl&]5ѱjdysSБ wfUҩsgj-^0fkΧ^P e1R]ֹBݰ:7o޸~rbůo׮vW6;\|yTI3PWsB.t`ҕ5oHte^XĄ74@U.~t3N[>(ZtE}SsV>_ṇ ugj:200uQ<  L]L.W ˃}݁LL"jn7Վuz'hq7sx֑X,'s7r۰ V*ی$gV|'48?O8>ܥ۷S;g./T;ghV[.L 203=_T[jEOs9veZ%Sa/6ml_;^mK.uws_8L^,JKmd*USSםp#J?#m?y J%nilh奷^jUFZvyE`Z -L-dz}iɌ}-]Q&lʌkH$ilSS8Jұn4u=\vC9K.vuБB>`q\}F"?Zn ?׬]W* ijj<|eHTZD~R4.+Ӆ L-uMxqKeL~Wm(:֭)tu뾿n#T3N]zВ's6m:ɤw8N^v}U 7s'5tЃ8e-CG{ӟ=?wwˊt(SK: :B$~6ٕig}Ӭ;7q\PwyͷV.Ι3opO=b衇,?K8.^y޿oG×fnΪM_1sT:jՋTMwWvyE`Pe?X3LNWu΂V=k^xf74}O=Mh4zܱG]p9O<7o;{b#M?Ǽ5k~*G/7TeG~̲#b>Y,v?zK"0(Բy#D:;vbwe.o@ͪ.?\܏~ӞޡBs~V I$wήxтx_o~ymmDkџpo_fБd2y֙/es֬YwovoMGƪ/8ϝm$`)Sܸa&.@wgU;7ue52gճO})T*CkW5s~s]U4+v;WoGqI[[[7/o4qo~䱛omz+Oxos9Çf(J?>crǝw{6)|wE|;c .tښUll_zw{sǝwv[/dqG>BaCzCY˷{SꩧWbK_?UE .x)'t]y׽[+ˎ:xI'd2Yg{[5[<_+mV| ^w}~#E =fّGvFџL)2L}S[c˜XlJh_ksǮ Gq^<䐃./嚡?.X祗\熎G9g{v$T*@*U] D{{Ƿ7w}zGkKh4ذ|X՟Uwyg^#]H$R(^vˎs|`Jx mʌ2aKWfچ=ܶ[2}}ݪJ9<~˾T3N]zВq_7Fy`4%S Yہ`C" DB EA`AX) @n,2 _cE&Q8" 8Iӧꪷr1ݝlwLw|"v e`^VfvQz 4SQ^ڽjwjZV?|~wm^ z?GY|tpޓ|ʜRx~W^䤕 xQU$ٻ)>Y}?{kR<*kvo|c-`7,N>{ (|iKen-h{XQBX;V{k景̲foI`/V[/ݿ 9 e ;+4GW2xEdz#]ܚ@\'h}kRoY1|wS`e ,::9ܾzj8>ۿt@-W IDATzqlmtvtGOOƣc,b>?t/!4`2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !e  @TB!P ,SO4a e|G@TA2AezK(TQa e*y C , @P|zK(wDK(|6M=p%,^V,R @\$X4@dBf<X4@dB 2 @\i^Ue8UUN]Q 0 @h|z, 8 @h|z, 8 @hy~z, 8 @heQ ALqYWP |^RA ppE(ƣt^i:#MB62ƣ$^JYU=H="|vtkEVb>K"N|z>^G݀ UUUGS` exlzx+FVlzH(NF'GWq~p pt^5R`y e8ݢ^g++XjBNSg| 8xpR` e8t2߻zft2Ne'lxf,xz+ Ro`gӲ(A!{+X Bk6\+8ؿ2K#w\ ex2t:;^Hbw{R`exbE1Qɚ[g:ngCX=BFUq5[N-2:9߻]e!$ Ooʢvz=:p{R` exVeYGUYu:=Uუ{eYp1f|<6YN+b<?B2\ *||2N5¦AUpu%l&_mڝsX1dx0 S ^zWV6Xhwz(Kd,(%|9NS\QZ Z6x8 L2,Z#˺At{z#I>@B~ @Jߘ6Yn6[Ye,h4z^N4UUժ,,ˢ(|>Ϧe1, ˢ,dgٴ,VVmwvdO+^Y֨ey(d6ͧ|:>W,Z#˺At{\}YznoPժI>p{f P7znoR|<UU@B.] Zz ˧Fɱ2pل2\nom gN'ã|2qzCv7 P 2 @\-x ej;'9"hdYL-˚Fziejک'@A2A5 @XB2C , @PYL=p%*˲ @XB C , @PB# * @X>USO4a eQ eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBP2   !eA(@BBg.IڤRJKYmY"({~랈 *"eJ mYetdKr#Ҭ&iK}Ky (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2AR )AP (쬱9Ϗ؜d=KKv풹O>1OƯy.ܒ[d@%vycs6} LJzdC%YuXmuEK \ QB7mPח vz!y HG ; _ "d'O '6%X@(t03E{B(>27"ya vLP1Y 'E/ǝJ ƅ?)R9tE@_ }2)?S&#%fڨПNe+~wBd@.rC8)8kPk~v /\s|${6HN '(UFƥ~ztdz##LcC_?Ó)G ;yR2mΚ^'$R@ }z!ytY"G=`xy"t9COMR@gNc|pvx F2v'PpaI ^šE<`x$;a}\y<%(#HWԕmxMM->6 PfN} .TUA'jkj1YX#(CsvAA"1u3 P@AIvp” g%pR Ahp$y r҃zL BAI~Jp괤 EcC%Ia؜$ <5\BApT0Ғ]ܬ`Ƅ``RIhƄ 3xsKP5Au}bse涠2ظ5h r 8vH3%t.w/AVY{LeC۵{ys[V/m*}vޜѡ1Yd [.MS;/ ' 6UkᑓR#'o~9*xw} E)v2%Ian/qe -&AAFR19pP<( 䣡30;=أpeS-A.%>>)p88j}B ~sO⮕;^oI(<)cs젼>un =& A߹(tC'ٳh$d Y#$e`LP讯Ŏ٧9GTʴW20L s$[3+x}NF7N+vdfwxwaR2*9&D"1+׹J xaLm}K)Wv^$;;75yE.!L. ׻‘p$>ؘMGzN 7wاP"Q m" Z9f9H,ܝx5BBPqQS&=࿚Nx{A˯^kMm:<'_D"ź~򭭭iinM8jk^|z_d@%H7?=UP_YH2pTZ4گ}z#iX uW=siSz GqS&wmB},寮G7?ooowӄiS> 0k֌&\ZZ>3aHY^aRϠLLǸFmEeKw0zz)Hw`ڐWYgWӫ3]sju˾|ɧ{uɝg}/燿B`W'(6+ԏL(굩̈ ʔ uO>7O霕{|_;yƩI u 0on®Yee{z)2"7߲V_}&++s*)Ք;?—zͺN?ht9gsɪ  Yi w2+9(qA*FDG6x~RJvEi.RȪ5 x^ңќQU}5*rFJψ0 xחZ3F\P//߼0''333 hll|tʺrG=-TUWG!zͺ˥%pЃz¥˖Q7XN79 Z_~eup(8A>ϬlKV&=#kh:OkGZílVqѝRZZr=O5 Q766}#K[sfAA~HUU7ߺ~;=<`3O?u)-D"QWW굷q-7߶>#>HwyK-]^En+**<)--YtΙ4 9995 )G[={2Ǝ-thkkkee˯׏;A>ko&mYʜQ齶9I6(Akfsne.Addt`n|S9x/X {ٳž0w~طKۧ׬{͚yyg?;9>p3gL{ =|xWw /lP&Lh(jQsfSz]oIƑ{1; nVʁ׵L/ G\zg锓>rBɓ&^ϜǷ7֯+N-..~;dv }?133Yp뮿O:+_l~BPvvVNNvv?a#NLǫ[*+z\ E{ђ,02F8ܼ{_yJɴ Ç| ۜs_giq_NL'|HFFƬ=gj)?->}Jɴ+**'/#鏿pe20V4yYUww0UgvS4;x<mĺg=F9"wчggoӪY*Wlټu֭l3x ƍ+Gqعgq_nǏw=1Cc^~Ǘo˭O#C/+++/**88yn,6u7={VlYX:^>h3NϯKGq{-555UVVLv`.+SUtEZ[Ԝ42Y1OyvI$=eI<_;)++9?ُ>'pl2sld466>Gݟ:FtهzPFt[23{ʓOZ~<_0kv-d oz䜜|]S2DWWx_oNKMrgp1 :gD9sӦoY{۔Lkk/[{q5nliT`-yt0ojlwSUUOw0et;˭Ē%xtҧ>)Py֛rkwH$/\s]הL>'>+H${Ӧ~ܱGE{9 _+:d x?)߼Kc~}?1[[[;dff..v`uJTWߞu]+A^[EO>GxYӦN饼\|aהLYY7qOG8g 9qHxZkKfٚasxh(3wMwv2Uտxuu#rss٫5 +/\L{jjj;Bw! ~hֵn뒖)fO9&g7k}3_cs9s[v}.C w7,>]Z\I߼ y}L9AiffvN}uuiaj'FO=㥥cfcϙ{L8!'jno?}9b'=U+|ŗKJƴD"=Ovee?U׬̛oY2yӡQ9~],,7IOO0a\י{TI#M؍7ߺû~vo -8p׿u9>lee AH>{zP?{ee+ڻ_UUB_YuN˛sZím̾;onTTV뮻>@WWzOƌ"sKsSLj ós.' ҺtK{C^oSyժgxjzͺ~cǚqJ>=]}{JKtijjM $xs$A%υvOʪX;rs]TP7%.uwßu^Ś;t݆ o>uIi~+x*?nܑlu?77uȘn'Nd^xk~w} $ǟ *j۶LǑNYH%byH+U$}Q<kS٭KB}CCǑ㻹h g93cJ6{׿g{56uJי{T){dc="^o G gv-IgϞۄE:rrOSSӋ/W^[]U>6a3&O-.Һy&D"Ku_bc7 Gǖ?qӳ:lݲu'p"0e iPpAiWL$--3']Kp]1Ħam'577sQffF%G'̼}|?zG1c=O[ɘEE|͛6w/)5>f̘A)iHw555to}+[N\v]]]}ǠLvVN&M-}vM6 /P` @45_!O ~S?YNYܜXScD(8&{v7 o-v2OzK?qdٳ8c:Sb777/]|`gG~:u@\ [v**a0vliQcc-5I]\١R08Qڎ 'L|MUaav6l|s`'Ca> jٿ` WUۿ)(Bc:hs0,~Tam'ӭ_xW~>tx|Nii>iSwҒܹ]xޔ)4%v9RyK-hZv$68QO=LwB -s/ѣGwyFs (I5 Cy[_2ܜGWvtk~v/_{딕c/]Vu딓>c:u4I<ot.=9C:$k'`"(I*J[VH}UuLFvvzFӒhK㨚+qo~{?]P(>qs:+_liiIVW׬^no]r7/g>/o2f'-(8_NV=.$mS!3˃mw0U':hVf*Z[Z:.iU֘9C3X~Tmɿt_)'vLdDre˟}ޗO:e:8 Zl?45NW%xkkɓ'p~$ݝG[w (#ޘXiolwSw0BQO J*iCxld]ev׽Ǐ{vsΜ#ͷ-S~cd=++3HKKmےUO_zSL8W>otl)3f`={VNvvǑXy JKK+.*}0e`Dhh >DELHS&D8^WRKY]Gtϒ ~{uϿbǑP(t=^VM<v%|kO,((}Π?~\NNNǑ斍oebkL:IOO8XQQCteƎ#&YGl|[֖-[:~ ƏP" MCxD/}e"iiEʴFFW b -GZ-D]]mU]]q077Nm;}:%jkk^ŵlsv 0e 22"%ҫ?k x@.+SNV&sV9.ZT r!rے;YX<Q{Lgk帙3gfLff̙j"HǑ6=3.cH]?pN}ذw%׬4=[Nl֭k5k65mv]\|Ђp"0JF&N}⒴αl^߯ϙ{tijjZٮ3_KUU%%t:Ѓn~ e`=1 >6LO ͦ-a+A8+*L߾3JcAN?J) };ᄃ`QQsߗu~FFF$G(ԧg/'xSw f͚Kn.TSSēvA7w_{N ]?K/k_oUq$#=MүN=ѣ;V+|W; s>_'CDP\vNcF.l})zGya,>Y#5M APΨ܎6Ԗ .mӋOy~?=‹͛sweNcKvxONNsY̳>F@޿;_c˯d(''שx=%-=ⅇJjmU;ܧ9FW AA2u#5 W\T_SA%ֆďd/[u'k}bWMɴmɝc(?~/Wt¿_.)>~glٲJKK/|N6 X(w߹?w~_<#ι^pN lģ;^ʛ0aܩ'f3xcǕdgwj!ӮλҗL׬m]Nx±ͱ>ܗ7+# IDAT P(_1vy{N8__2q%rkY =Lm}e+i A4++P8SPВ X1q[:6R۶K7&pkulh^p޳gM0}$ rs>㦿>b/jǠLyyǏ8nmCz =y%cF†P( B'M|q߃Wv;穧V-}tO?{ 8hfhS^_#A4M:c'dpf$UXPUYoMFD*w]CUyi;C3IYmߦE_~$κ囇D&~Tw^]Erss>+X;w{w\.\p~ʯbh4)~QUU[|\aMMc+8䷳ Hd֬f̧vG?)IOyηŷì|O=s^{^ RN_NϯkV& *8Ï>`p 5:rκ`K>?/)6ȨKy,/h;Wc&qΪuT.W,w&qݭ璉L^{+4_|vǑ{ͷ,iuH' eddtJ^VY}om*;?\lN>tZ[[}_|KAPWWׯCSSM_߾/ϯkkkv"0t5}V>[vN pAQ~ueMsO%z2#Gh+?w0AK-6 Iכ$/]O_A8>أV|nϯN$_|vNΎSM۶UW'+lii淾N9oz=ZZ"غmOܺo?oXWWg6a|zd7~_>#=}>KsFNq G'9v C(*(̯klhͱ[ʊͯ7.Wfe`'YRjʔLۖyA ;Ȏo;~_?33g鮟vMʕO׿ĜJ{/6uJOɌx‹I9zK-Oѻk` z t=ǎyvĔLp8\PӍ9݊Cdl!#ڲ2+<}esH eBE[X8tGdegF3uMJ 7dd{d7Ni,3MOIAueN I!(}:3f AH8 /+_kk`LyL$R2z@ʢ[UR > .4mf6 ommccϷii-xz8GÉP" bMM귂 kʂ˃K_0=[i2eON>kpdE3ьh!۩۔j["-n_]ΛtҊע9+MK읩Y8.`{<(D3 G BCqӎ8W۲2wquJ`L{% H$S40T~Vvf$-2/c=*+KЛѥ^41Ul' E3ң9AcM斖֖斁DҢihzz43G{;N`x @"Of܆p83+3z֖֖DW˔#IvLG/2{"Ƞ3eeKA4dbGz@7֯_rQ] -CQpnR̍AFe-P `e{u=ssoP(=D% z? $!@ZodWD?ϔ eG_]ȶdW1{hɮ o*{1U 64޻dS!!(iiNxx  dWCEPv`ۖ-mHvC.n5U-. sۻBD"XǍk^Kv!0"ٹɮ$/Jv g ݖYxe >.$HQ2a]C}my.dvgR @jHQ2m[bB.e0&7Lu n2)-.'bM/kek_Kv!0LtHQ:-屗>+7+;Zk~vd:&A%(j[W,Y\Z+j}Sc<م$ @jHQ2;%ܓU9Юq Ss,|]@ &A%(3XXװک3rF5Lomh׼RBOP 5 (AAT]IJP(mJv8<[bVOZ] (eRǃ{Uun~ژ.g;O- TBFA*]HqMKv Z'ewZ{$߫→|KyS qڰ:%2)JPfM}I%I$T?͛Dd'(eR8%{K*ƞz[Ddz#(eR045{9y*J7ml|){q7xWHM2)JP&YNȜ:#gٓfLm[b߰k*5b@P 5 Ggݦ66n 7?mtIh蘱%c]4'nnZ\ִmslKySXS|k] $MmuKmu˺]p (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2AR )AP (@J %eH 2;m伢0Qr` oףHQdC.҈im~R{'wb} PB"e  PB"e  PB"e  PB"e  PB"e  PB"eRsP#J(j^xF@(G P 4M'@ @, @3BR92i,=pb eBgrb eBPz(%5xF@*G P w'@ @, @iq(^84^@B\}זH&wM ڜ?dB\]I!q?dB\]Tzy:o&6u z>pBhmS'48|2Ѧq BM=c$Hԧ58yei1ϥW6u%H'H7SU=^?VU=Tz eXg ocB(b躦k+Gں+(O(bXOGq7 bԧkKwmSJ"e|:/ρ?2|UWmJԵM]y _ ey_z$ &mS{ތ:MUzD(<\ze||,="Wơ?>y;>=C_zE(:mS^^ӡ .Pp|L\y>>}.=K$7 }tt<]\" : > zڦ y*%[8^oc\4 2CG8Wpф2_>^߲k+tB~_S|ulS\C \qݽ+=~i>?^uG};VqT eC }oI.ˇ%P3 ]6jUz qy6U!\ mfSz qy?m!\ >=i,='e]SקjޔQW/ex145zlJumtt~>Tz o_-xamSMuwm9\kϧC]J Z,7kwo7wp>M-GF~>5fweE\yuM)q(+Y.Ww۝X,mS7N<@ m^vnwrUzjۦj4E(@I77f]} mǡھk}% $%u/OLWf{lכzY׫zZVbX.N[y^44M88C? w1\ bƮ.=x| B"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PBLqzIDAT"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  PB"e  P X5 |ZIENDB`flask-openapi3-4.1.0/docs/index.md000066400000000000000000000000231475153525300167160ustar00rootroot00000000000000 --8<-- "README.md"flask-openapi3-4.1.0/docs/index.zh.md000066400000000000000000000141471475153525300173520ustar00rootroot00000000000000

为你的 Flask 项目生成 REST API 和 OpenAPI 文档。

test pypi pypistats pypi versions

**Flask OpenAPI3** 是一个基于 **Flask** 的 web API 框架,使用 **Pydantic** 验证数据,自动生成交互文档。 主要特点有: - **编码简单:** 使用简单易于学习 - **标准的文档规范:** 基于[开放 API 规范](https://spec.openapis.org/oas/v3.1.0) - **交互式 OpenAPI 文档:** [Swagger](https://github.com/swagger-api/swagger-ui), [Redoc](https://github.com/Redocly/redoc), [RapiDoc](https://github.com/rapi-doc/RapiDoc), [RapiPdf](https://mrin9.github.io/RapiPdf/), [Scalar](https://github.com/scalar/scalar), [Elements](https://github.com/stoplightio/elements) - **数据验证:** 基于 [Pydantic](https://github.com/pydantic/pydantic) 的快速数据验证 ## 依赖 Python 3.9+ flask-openapi3 依赖以下库: - [Flask](https://github.com/pallets/flask):用于WEB服务 - [Pydantic](https://github.com/pydantic/pydantic):用于数据验证 ## 安装 ```bash pip install -U flask-openapi3 ``` 或者 ```bash conda install -c conda-forge flask-openapi3 ```
可选依赖项 - [python-email-validator](https://github.com/JoshData/python-email-validator) 支持邮箱验证; - [python-dotenv](https://github.com/theskumar/python-dotenv#readme) 在运行 `flask` 命令时启用对[ dotenv 环境变量](https://flask.palletsprojects.com/en/latest/cli/#dotenv) 的支持; - [pyyaml](https://github.com/yaml/pyyaml) 用于输出 `yaml`格式的 OpenAPI 文档; - [asgiref](https://github.com/django/asgiref) 允许在定义视图函数时使用 `async def` 和 `await`; - [flask-openapi3-plugins](https://github.com/luolingchun/flask-openapi3-plugins) 为 Flask-OpenAPI3 提供 OpenAPI UI. 和 flask-openapi3 一起安装这些依赖: ```bash pip install flask-openapi3[yaml] # 或者 pip install flask-openapi3[async] # 或者 pip install flask-openapi3[dotenv] # 或者 pip install flask-openapi3[email] # 或者安装全部 pip install flask-openapi3[yaml,async,dotenv,email] # 或者手动安装 pip install pyyaml asgiref python-dotenv email-validator # OpenAPI UI 插件 pip install -U flask-openapi3[swagger,redoc,rapidoc,rapipdf,scalar,elements] ```
## 一个简单的示例 这里有一个简单的示例,更多示例请查看[示例](https://luolingchun.github.io/flask-openapi3/latest/zh/Example/)。 ```python from pydantic import BaseModel from flask_openapi3 import Info, Tag from flask_openapi3 import OpenAPI info = Info(title="book API", version="1.0.0") app = OpenAPI(__name__, info=info) book_tag = Tag(name="book", description="Some Book") class BookQuery(BaseModel): age: int author: str @app.get("/book", summary="get books", tags=[book_tag]) def get_book(query: BookQuery): """ to get all books """ return { "code": 0, "message": "ok", "data": [ {"bid": 1, "age": query.age, "author": query.author}, {"bid": 2, "age": query.age, "author": query.author} ] } if __name__ == "__main__": app.run(debug=True) ```
基于类的 API 视图示例 ```python from typing import Optional from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI, Tag, Info, APIView info = Info(title='book API', version='1.0.0') app = OpenAPI(__name__, info=info) api_view = APIView(url_prefix="/api/v1", view_tags=[Tag(name="book")]) class BookPath(BaseModel): id: int = Field(..., description="book ID") class BookQuery(BaseModel): age: Optional[int] = Field(None, description='Age') class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') @api_view.route("/book") class BookListAPIView: a = 1 @api_view.doc(summary="get book list") def get(self, query: BookQuery): print(self.a) return query.model_dump_json() @api_view.doc(summary="create book") def post(self, body: BookBody): """description for a created book""" return body.model_dump_json() @api_view.route("/book/") class BookAPIView: @api_view.doc(summary="get book") def get(self, path: BookPath): print(path) return "get" @api_view.doc(summary="update book") def put(self, path: BookPath): print(path) return "put" @api_view.doc(summary="delete book", deprecated=True) def delete(self, path: BookPath): print(path) return "delete" app.register_api_view(api_view) if __name__ == "__main__": app.run(debug=True) ```
## API 文档 运行[简单示例](https://github.com/luolingchun/flask-openapi3/blob/master/examples/simple_demo.py),然后访问 http://127.0.0.1:5000/openapi。 > OpenAPI UI插件是需要手动安装的可选依赖项。 > > `pip install -U flask-openapi3[swagger,redoc,rapidoc,rapipdf,scalar,elements]` > > 更多可选[UI模板](https://luolingchun.github.io/flask-openapi3/latest/Usage/UI_Templates/) ![openapi](./images/openapi-all.png) flask-openapi3-4.1.0/docs/js/000077500000000000000000000000001475153525300157065ustar00rootroot00000000000000flask-openapi3-4.1.0/docs/js/db.js000066400000000000000000000004571475153525300166370ustar00rootroot00000000000000(function(c,l,a,r,i,t,y){ c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); })(window, document, "clarity", "script", "gjkqgxq59d");flask-openapi3-4.1.0/examples/000077500000000000000000000000001475153525300161605ustar00rootroot00000000000000flask-openapi3-4.1.0/examples/api_blueprint_demo.py000066400000000000000000000032601475153525300223740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/6/6 14:05 from typing import Optional from pydantic import BaseModel, Field from flask_openapi3 import APIBlueprint, OpenAPI from flask_openapi3 import Tag, Info info = Info(title='book API', version='1.0.0') jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } security_schemes = {"jwt": jwt} app = OpenAPI(__name__, info=info, security_schemes=security_schemes) tag = Tag(name='book', description="Some Book") security = [{"jwt": []}] class Unauthorized(BaseModel): code: int = Field(-1, description="Status Code") message: str = Field("Unauthorized!", description="Exception Information") api = APIBlueprint( '/book', __name__, url_prefix='/api', abp_tags=[tag], abp_security=security, abp_responses={"401": Unauthorized}, # disable openapi UI doc_ui=True ) class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') class Path(BaseModel): bid: int = Field(..., description='book id') @api.get('/book', doc_ui=False) def get_book(): return {"code": 0, "message": "ok"} @api.post('/book', responses={200: {"content": {"text/csv": {"schema": {"type": "string"}}}}}) def create_book(body: BookBody): assert body.age == 3 return {"code": 0, "message": "ok"} @api.put('/book/', operation_id='update') def update_book(path: Path, body: BookBody): assert path.bid == 1 assert body.age == 3 return {"code": 0, "message": "ok"} # register api app.register_api(api) if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/examples/api_view_demo.py000066400000000000000000000034161475153525300213450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/10/18 9:00 from typing import Optional from pydantic import BaseModel, Field from flask_openapi3 import APIView from flask_openapi3 import OpenAPI, Tag, Info jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } security_schemes = {"jwt": jwt} info = Info(title='book API', version='1.0.0') app = OpenAPI(__name__, info=info, security_schemes=security_schemes) security = [{"jwt": []}] api_view = APIView(url_prefix="/api/v1", view_tags=[Tag(name="book")], view_security=security) class BookPath(BaseModel): id: int = Field(..., description="book ID") class BookQuery(BaseModel): age: Optional[int] = Field(None, description='Age') class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') @api_view.route("/book") class BookListAPIView: a = 1 @api_view.doc(summary="get book list") def get(self, query: BookQuery): print(self.a) return query.model_dump_json() @api_view.doc(summary="create book") def post(self, body: BookBody): """description for a created book""" return body.model_dump_json() @api_view.route("/book/") class BookAPIView: @api_view.doc(summary="get book") def get(self, path: BookPath): print(path) return "get" @api_view.doc(summary="update book") def put(self, path: BookPath): print(path) return "put" @api_view.doc(summary="delete book", deprecated=True) def delete(self, path: BookPath): print(path) return "delete" app.register_api_view(api_view) if __name__ == "__main__": print(app.url_map) app.run(debug=True) flask-openapi3-4.1.0/examples/async_demo.py000066400000000000000000000022451475153525300206560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/11/30 14:55 from typing import Optional from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI, APIView app = OpenAPI(__name__) api_view = APIView(url_prefix="/api/v1") class Query(BaseModel): q: str class BookQuery(BaseModel): age: Optional[int] = Field(None, description='Age') class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') @app.get('/open/api') async def get_openapi(query: Query): print(query) return 'GET, OpenAPI!' @app.post('/open/api') async def post_openapi(body: Query): print(body) return 'POST, OpenAPI!' @api_view.route("/book") class BookListAPIView: @api_view.doc(summary="get book list") async def get(self, query: BookQuery): return query.model_dump_json() @api_view.doc(summary="create book") async def post(self, body: BookBody): """description for a created book""" return body.model_dump_json() app.register_api_view(api_view) if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/examples/enum_demo.py000066400000000000000000000011101475153525300204730ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/8/18 17:19 from enum import Enum from pydantic import BaseModel, Field from flask_openapi3 import Info from flask_openapi3 import OpenAPI app = OpenAPI( __name__, info=Info(title='Enum demo', version='1.0.0') ) class Language(str, Enum): cn = 'Chinese' en = 'English' class LanguagePath(BaseModel): language: Language = Field(..., description='Language') @app.get('/') def get_enum(path: LanguagePath): print(path) return {} if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/examples/header_demo.py000066400000000000000000000014051475153525300207660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/12/26 15:07 from pydantic import BaseModel, Field from flask_openapi3 import Info, Tag from flask_openapi3 import OpenAPI info = Info(title='header API', version='1.0.0') app = OpenAPI(__name__, info=info) book_tag = Tag(name='book', description='Some Book') class Headers(BaseModel): hello: str = Field("what's up", max_length=12, description='sds') # required # hello: str = Field(..., max_length=12, description='sds') x_hello: str = Field(..., max_length=12, description='Header with alias to support dash', alias="x-hello") @app.get('/book', tags=[book_tag]) def get_book(header: Headers): print(header) return header.hello if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/examples/image_demo.py000066400000000000000000000006331475153525300206220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/6/26 17:05 from flask_openapi3 import OpenAPI from flask import make_response app = OpenAPI(__name__) @app.get('/image') def get_image(): with open("../docs/images/openapi.png", "rb") as f: content = f.read() r = make_response(content) r.mimetype = 'image/png' return r if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/examples/init_oauth_demo.py000066400000000000000000000022741475153525300217060ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/6/21 11:23 from flask_openapi3 import Info from flask_openapi3 import OpenAPI info = Info(title="oauth API", version="1.0.0") # https://spec.openapis.org/oas/v3.1.0#implicit-oauth2-sample oauth2 = { "type": "oauth2", "flows": { "implicit": { "authorizationUrl": "https://accounts.google1.com/o/oauth2/v2/auth", "tokenUrl": "https://www.googleapis1.com/oauth2/v4/token", "scopes": { "write:pets": "modify pets in your account", "read:pets": "read your pets" } } } } security_schemes = {"oauth2": oauth2} app = OpenAPI(__name__, info=info, security_schemes=security_schemes) # https://github.com/swagger-api/swagger-ui/blob/0ce792c9d0965a8b6b5d75f5e1341ff6936a4cb0/docs/usage/oauth2.md oauth_config = { "clientId": "xxx", "clientSecret": "xxx" } app.config["OAUTH_CONFIG"] = oauth_config # https://spec.openapis.org/oas/v3.1.0#oauth2-security-requirement security = [ {"oauth2": ["write:pets", "read:pets"]} ] @app.get("/", security=security) def oauth(): return "oauth" if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/examples/just_flask.py000066400000000000000000000003561475153525300207030ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/5/11 13:37 from flask_openapi3 import OpenAPI app = OpenAPI(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run() flask-openapi3-4.1.0/examples/nested_apiblueprint_demo.py000066400000000000000000000011171475153525300235760ustar00rootroot00000000000000from flask_openapi3 import OpenAPI, APIBlueprint app = OpenAPI(__name__) api = APIBlueprint('book', __name__, url_prefix='/api/book') api_english = APIBlueprint('english', __name__) api_chinese = APIBlueprint('chinese', __name__) @api_english.post('/english') def create_english_book(): return {"message": "english"} @api_chinese.post('/chinese') def create_chinese_book(): return {"message": "chinese"} # register nested api api.register_api(api_english) api.register_api(api_chinese) # register api app.register_api(api) if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/examples/openapi_extensions.py000066400000000000000000000011671475153525300224510ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/5/31 14:24 from flask_openapi3 import OpenAPI app = OpenAPI(__name__, openapi_extensions={ "x-google-endpoints": [ { "name": "my-cool-api.endpoints.my-project-id.cloud.goog", "allowCors": True } ] }) openapi_extensions = { "x-google-backend": { "address": "https://-.a.run.app", "protocol": "h2" }, "x-aperture-labs-portal": "blue" } @app.get("/", openapi_extensions=openapi_extensions) def hello(): return "ok" if __name__ == "__main__": app.run(debug=True) flask-openapi3-4.1.0/examples/openapi_extra.py000066400000000000000000000037511475153525300213760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/6/1 15:04 from pydantic import BaseModel from flask_openapi3 import OpenAPI, FileStorage app = OpenAPI(__name__) class UploadFilesForm(BaseModel): file: FileStorage str_list: list[str] model_config = dict( openapi_extra={ # "example": {"a": 123}, "examples": { "Example 01": { "summary": "An example", "value": { "file": "Example-01.jpg", "str_list": ["a", "b", "c"] } }, "Example 02": { "summary": "Another example", "value": { "str_list": ["1", "2", "3"] } } } } ) class BookBody(BaseModel): age: int author: str model_config = dict( openapi_extra={ "description": "This is post RequestBody", "example": {"age": 12, "author": "author1"}, "examples": { "example1": { "summary": "example summary1", "description": "example description1", "value": { "age": 24, "author": "author2" } }, "example2": { "summary": "example summary2", "description": "example description2", "value": { "age": 48, "author": "author3" } } }} ) @app.post('/upload/files') def upload_files(form: UploadFilesForm): print(form.file) print(form.str_list) return {"code": 0, "message": "ok"} @app.post('/book', ) def create_book(body: BookBody): print(body) return {"code": 0, "message": "ok"} if __name__ == "__main__": app.run(debug=True) flask-openapi3-4.1.0/examples/orjson_demo.py000066400000000000000000000017311475153525300210520ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/12/9 17:50 import orjson from flask.json.provider import JSONProvider from flask_openapi3 import Info from flask_openapi3 import OpenAPI class OrJSONProvider(JSONProvider): # https://github.com/ijl/orjson#option option = orjson.OPT_INDENT_2 def dumps(self, obj, **kwargs): return orjson.dumps(obj, option=self.option).decode() def loads(self, s, **kwargs): return orjson.loads(s) info = Info(title='book API', version='1.0.0') app = OpenAPI(__name__, info=info) # use orjson orjson_provider = OrJSONProvider(app) app.json = orjson_provider @app.get('/book') def get_book(): return { "code": 0, "message": "ok", "data": [ {"bid": 1, "age": 18, "author": "tom"}, {"bid": 2, "age": 19, "author": "alice"}, {"bid": 3, "age": 20, "author": "中文测试"} ] } if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/examples/pydantic_custom_root_types.py000066400000000000000000000025561475153525300242360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/2/27 15:26 from typing import Any from pydantic import BaseModel, RootModel from flask_openapi3 import OpenAPI, Tag app = OpenAPI(__name__) class Sellout(BaseModel): a: str b: int class SelloutList(RootModel): root: list[Sellout] class SelloutDict(RootModel): root: dict[str, Sellout] class SelloutDict2(RootModel): root: dict[Any, Any] class SelloutDict3(BaseModel): model_config = { "extra": "allow" } @app.post('/api/v1/sellouts', tags=[Tag(name='Sellout', description='Loren.')], responses={200: SelloutList} ) def post_sellout(body: SelloutList): print(body) return body.model_dump() @app.post('/api/v2/sellouts', tags=[Tag(name='Sellout', description='Loren.')], responses={200: SelloutDict} ) def post_sellout2(body: SelloutDict): print(body) return body.model_dump() @app.post('/api/v3/sellouts', tags=[Tag(name='Sellout', description='Loren.')] ) def post_sellout3(body: SelloutDict2): print(body) return body.model_dump() @app.post('/api/v4/sellouts', tags=[Tag(name='Sellout', description='Loren.')] ) def post_sellout4(body: SelloutDict3): print(body) return body.model_dump() if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/examples/raw_request_demo.py000066400000000000000000000006661475153525300221070ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/9/12 17:19 from flask_openapi3 import OpenAPI from flask_openapi3 import RawModel app = OpenAPI(__name__) class BookRaw(RawModel): mimetypes = ["text/csv", "application/json"] @app.post("/book") def get_book(raw: BookRaw): # raw equals to flask.request print(raw.data) print(raw.mimetype) return "ok" if __name__ == "__main__": app.run(debug=True) flask-openapi3-4.1.0/examples/response_demo.py000066400000000000000000000036361475153525300214040ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/6/22 9:32 import json from http import HTTPStatus from flask import make_response from pydantic import BaseModel, Field from flask_openapi3 import Info from flask_openapi3 import OpenAPI, APIBlueprint app = OpenAPI(__name__, info=Info(title="Hello API", version="1.0.0")) bp = APIBlueprint("Hello BP", __name__) class HelloPath(BaseModel): name: str = Field(..., description="The name") class Message(BaseModel): message: str = Field(..., description="The message") model_config = dict( openapi_extra={ # "example": {"message": "aaa"}, "examples": { "example1": { "summary": "example1 summary", "value": { "message": "bbb" } }, "example2": { "summary": "example2 summary", "value": { "message": "ccc" } } } } ) @bp.get("/hello/", responses={HTTPStatus.OK: Message, "201": {"content": {"text/csv": {"schema": {"type": "string"}}}}}) def hello(path: HelloPath): message = {"message": f"""Hello {path.name}!"""} response = make_response(json.dumps(message), HTTPStatus.OK) # response = make_response("sss", HTTPStatus.OK) response.mimetype = "application/json" return response @bp.get("/hello_no_response/", responses={204: None}) def hello_no_response(path: HelloPath): message = {"message": f"""Hello {path.name}!"""} # This message will never be returned because the http code (NO_CONTENT) doesn't return anything response = make_response(message, HTTPStatus.NO_CONTENT) response.mimetype = "application/json" return response app.register_api(bp) if __name__ == "__main__": app.run(debug=True) flask-openapi3-4.1.0/examples/rest_demo.py000066400000000000000000000076731475153525300205300ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/4/28 11:24 from http import HTTPStatus from typing import Optional from pydantic import BaseModel, Field from flask_openapi3 import ExternalDocumentation from flask_openapi3 import Info, Tag, Server from flask_openapi3 import OpenAPI info = Info(title='book API', version='1.0.0') # Basic Authentication Sample basic = { "type": "http", "scheme": "basic" } # JWT Bearer Sample jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } # API Key Sample api_key = { "type": "apiKey", "name": "api_key", "in": "header" } # Implicit OAuth2 Sample oauth2 = { "type": "oauth2", "flows": { "implicit": { "authorizationUrl": "https://example.com/api/oauth/dialog", "scopes": { "write:pets": "modify pets in your account", "read:pets": "read your pets" } } } } security_schemes = {"jwt": jwt, "api_key": api_key, "oauth2": oauth2, "basic": basic} class NotFoundResponse(BaseModel): code: int = Field(-1, description="Status Code") message: str = Field("Resource not found!", description="Exception Information") app = OpenAPI(__name__, info=info, security_schemes=security_schemes, responses={404: NotFoundResponse}) book_tag = Tag(name='book', description='Some Book') security = [ {"jwt": []}, {"oauth2": ["write:pets", "read:pets"]}, {"basic": []} ] class BookPath(BaseModel): bid: int = Field(..., description='book id', json_schema_extra={"deprecated": True, "example": 100}) class BookQuery(BaseModel): age: Optional[int] = Field(None, description='Age') s_list: list[str] = Field(None, alias='s_list[]', description='some array') class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') class BookBodyWithID(BaseModel): bid: int = Field(..., description='book id') age: Optional[int] = Field(None, ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') class BookResponse(BaseModel): code: int = Field(0, description="Status Code") message: str = Field("ok", description="Exception Information") data: Optional[BookBodyWithID] @app.get( '/book/', tags=[book_tag], summary='new summary', description='new description', operation_id="get_book_id", external_docs=ExternalDocumentation( url="https://www.openapis.org/", description="Something great got better, get excited!"), responses={200: BookResponse}, security=security, servers=[Server(url="https://www.openapis.org/", description="openapi")] ) def get_book(path: BookPath): """Get a book to Get some book by id, like: http://localhost:5000/book/3 """ if path.bid == 4: return NotFoundResponse().model_dump(), 404 return {"code": 0, "message": "ok", "data": {"bid": path.bid, "age": 3, "author": 'no'}} # set doc_ui False disable openapi UI @app.get('/book', doc_ui=True, deprecated=True) def get_books(query: BookQuery): """get books to get all books """ print(query) return { "code": 0, "message": "ok", "data": [ {"bid": 1, "age": query.age, "author": 'a1'}, {"bid": 2, "age": query.age, "author": 'a2'} ] } @app.post('/book', tags=[book_tag], responses={200: BookResponse}) def create_book(body: BookBody): print(body) return {"code": 0, "message": "ok"}, HTTPStatus.OK @app.put('/book/', tags=[book_tag]) def update_book(path: BookPath, body: BookBody): print(path) print(body) return {"code": 0, "message": "ok"} @app.delete('/book/', tags=[book_tag], doc_ui=False) def delete_book(path: BookPath): print(path) return {"code": 0, "message": "ok"} if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/examples/servers_demo.py000066400000000000000000000015611475153525300212320ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/1/8 14:25 from pydantic import BaseModel from flask_openapi3 import Info, Tag from flask_openapi3 import OpenAPI, Server info = Info(title="book API", version="1.0.0") servers = [ Server(url="http://127.0.0.1:5000"), Server(url="https://127.0.0.1:5000"), ] app = OpenAPI(__name__, info=info, servers=servers) book_tag = Tag(name="book", description="Some Book") class BookQuery(BaseModel): age: int author: str @app.get("/book", tags=[book_tag]) def get_book(query: BookQuery): """get books to get all books """ return { "code": 0, "message": "ok", "data": [ {"bid": 1, "age": query.age, "author": query.author}, {"bid": 2, "age": query.age, "author": query.author} ] } if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/examples/simple_demo.py000066400000000000000000000014011475153525300210230ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/5/10 17:01 from pydantic import BaseModel from flask_openapi3 import Info, Tag from flask_openapi3 import OpenAPI info = Info(title="book API", version="1.0.0") app = OpenAPI(__name__, info=info) book_tag = Tag(name="book", description="Some Book") class BookQuery(BaseModel): age: int author: str @app.get("/book", summary="get books", tags=[book_tag]) def get_book(query: BookQuery): """ get all books """ return { "code": 0, "message": "ok", "data": [ {"bid": 1, "age": query.age, "author": query.author}, {"bid": 2, "age": query.age, "author": query.author} ] } if __name__ == "__main__": app.run(debug=True) flask-openapi3-4.1.0/examples/upload_file_demo.py000066400000000000000000000015071475153525300220240ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/5/11 14:03 from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI, FileStorage app = OpenAPI(__name__) class UploadFileForm(BaseModel): file: FileStorage file_type: str = Field(None, description="File Type") class UploadFilesForm(BaseModel): files: list[FileStorage] str_list: list[str] int_list: list[int] @app.post('/upload/file') def upload_file(form: UploadFileForm): print(form.file.filename) print(form.file_type) form.file.save('test.jpg') return {"code": 0, "message": "ok"} @app.post('/upload/files') def upload_files(form: UploadFilesForm): print(form.files) print(form.str_list) print(form.int_list) return {"code": 0, "message": "ok"} if __name__ == '__main__': app.run(debug=True) flask-openapi3-4.1.0/flask_openapi3/000077500000000000000000000000001475153525300172405ustar00rootroot00000000000000flask-openapi3-4.1.0/flask_openapi3/__init__.py000066400000000000000000000022711475153525300213530ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/4/30 10:14 from .__version__ import __version__ from .blueprint import APIBlueprint from .models import APISpec from .models import Components from .models import Contact from .models import Discriminator from .models import Encoding from .models import Example from .models import ExternalDocumentation from .models import FileStorage from .models import Header from .models import Info from .models import License from .models import Link from .models import MediaType from .models import OAuthConfig from .models import OAuthFlow from .models import OAuthFlows from .models import Operation from .models import Parameter from .models import ParameterInType from .models import PathItem from .models import RawModel from .models import Reference from .models import RequestBody from .models import Response from .models import Schema from .models import SecurityScheme from .models import Server from .models import ServerVariable from .models import StyleValues from .models import Tag from .models import UnprocessableEntity from .models import ValidationErrorModel from .models import XML from .openapi import OpenAPI from .view import APIView flask-openapi3-4.1.0/flask_openapi3/__version__.py000066400000000000000000000001341475153525300220710ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/4/30 9:20 __version__ = "4.1.0" flask-openapi3-4.1.0/flask_openapi3/blueprint.py000066400000000000000000000171651475153525300216300ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/4/1 16:54 from typing import Optional, Any, Callable from flask import Blueprint from .models import ExternalDocumentation from .models import Server from .models import Tag from .scaffold import APIScaffold from .types import ParametersTuple from .types import ResponseDict from .utils import HTTPMethod from .utils import convert_responses_key_to_string from .utils import get_operation from .utils import get_operation_id_for_path from .utils import get_responses from .utils import parse_and_store_tags from .utils import parse_method from .utils import parse_parameters from .utils import parse_rule class APIBlueprint(APIScaffold, Blueprint): def __init__( self, name: str, import_name: str, *, abp_tags: Optional[list[Tag]] = None, abp_security: Optional[list[dict[str, list[str]]]] = None, abp_responses: Optional[ResponseDict] = None, doc_ui: bool = True, operation_id_callback: Callable = get_operation_id_for_path, **kwargs: Any ) -> None: """ Based on Flask Blueprint Args: name: The name of the blueprint. It Will be prepared to each endpoint name. import_name: The name of the blueprint package, usually ``__name__``. This helps locate the ``root_path`` for the blueprint. abp_tags: APIBlueprint tags for every API. abp_security: APIBlueprint security for every API. abp_responses: API responses should be either a subclass of BaseModel, a dictionary, or None. doc_ui: Enable OpenAPI document UI (Swagger UI, Redoc, and Rapidoc). Defaults to True. operation_id_callback: Callback function for custom operation_id generation. Receives name (str), path (str) and method (str) parameters. Defaults to `get_operation_id_for_path` from utils **kwargs: Flask Blueprint kwargs """ super(APIBlueprint, self).__init__(name, import_name, **kwargs) # Initialize instance variables self.paths: dict = dict() self.components_schemas: dict = dict() self.tags: list[Tag] = [] self.tag_names: list[str] = [] # Set values from arguments or default values self.abp_tags = abp_tags or [] self.abp_security = abp_security or [] # Convert key to string self.abp_responses = convert_responses_key_to_string(abp_responses or {}) self.doc_ui = doc_ui # Set the operation ID callback function self.operation_id_callback: Callable = operation_id_callback def register_api(self, api: "APIBlueprint") -> None: """Register a nested APIBlueprint""" # Check if the APIBlueprint is being registered on itself if api is self: raise ValueError("Cannot register a api blueprint on itself") # Merge tags from the nested APIBlueprint for tag in api.tags: if tag.name not in self.tag_names: self.tags.append(tag) # Merge paths from the nested APIBlueprint for path_url, path_item in api.paths.items(): # Parse rule: merge url_prefix and format rule from /pet/ to /pet/{petId} uri = parse_rule(path_url, url_prefix=self.url_prefix) self.paths[uri] = path_item # Merge component schemas from the nested APIBlueprint self.components_schemas.update(api.components_schemas) # Register the nested APIBlueprint as a blueprint self.register_blueprint(api) def _add_url_rule( self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options, ) -> None: self.add_url_rule(rule, endpoint, view_func, provide_automatic_options, **options) def _collect_openapi_info( self, rule: str, func: Callable, *, tags: Optional[list[Tag]] = None, summary: Optional[str] = None, description: Optional[str] = None, external_docs: Optional[ExternalDocumentation] = None, operation_id: Optional[str] = None, responses: Optional[ResponseDict] = None, deprecated: Optional[bool] = None, security: Optional[list[dict[str, list[Any]]]] = None, servers: Optional[list[Server]] = None, openapi_extensions: Optional[dict[str, Any]] = None, doc_ui: bool = True, method: str = HTTPMethod.GET ) -> ParametersTuple: """ Collects OpenAPI specification information for Flask routes and view functions. Args: rule: Flask route func: Flask view_func tags: Adds metadata to a single tag. summary: A short summary of what the operation does. description: A verbose explanation of the operation behavior. external_docs: Additional external documentation for this operation. operation_id: Unique string used to identify the operation. responses: API responses should be either a subclass of BaseModel, a dictionary, or None. deprecated: Declares this operation to be deprecated. security: A declaration of which security mechanisms can be used for this operation. servers: An alternative server array to service this operation. openapi_extensions: Allows extensions to the OpenAPI Schema. doc_ui: Declares this operation to be shown. Default to True. """ if self.doc_ui is True and doc_ui is True: # Convert key to string new_responses = convert_responses_key_to_string(responses or {}) # Global response: combine API responses combine_responses = {**self.abp_responses, **new_responses} # Create operation operation = get_operation( func, summary=summary, description=description, openapi_extensions=openapi_extensions ) # Set external docs if external_docs: operation.externalDocs = external_docs # Unique string used to identify the operation. operation.operationId = operation_id or self.operation_id_callback( name=self.name, path=rule, method=method ) # Only set `deprecated` if True, otherwise leave it as None if deprecated is not None: operation.deprecated = deprecated # Add security _security = (security or []) + self.abp_security or None if _security: operation.security = _security # Add servers if servers: operation.servers = servers # Store tags tags = (tags or []) + self.abp_tags parse_and_store_tags(tags, self.tags, self.tag_names, operation) # Parse response get_responses(combine_responses, self.components_schemas, operation) # Parse rule: merge url_prefix and format rule from /pet/ to /pet/{petId} uri = parse_rule(rule, url_prefix=self.url_prefix) # Parse method parse_method(uri, method, self.paths, operation) # Parse parameters return parse_parameters(func, components_schemas=self.components_schemas, operation=operation) else: return parse_parameters(func, doc_ui=False) flask-openapi3-4.1.0/flask_openapi3/commands.py000066400000000000000000000026651475153525300214240ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/4/16 14:21 import json from flask import current_app from flask.cli import click from flask.cli import with_appcontext @click.command(name="openapi") @click.option("--output", "-o", type=click.Path(), help="The output file path.") @click.option("--format", "-f", "_format", type=click.Choice(["json", "yaml"]), help="The output file format.") @click.option("--indent", "-i", type=int, help="The indentation for JSON dumps.") @with_appcontext def openapi_command(output, _format, indent): """Export the OpenAPI Specification to console or a file""" # Check if the current app has an api_doc attribute if hasattr(current_app, "api_doc"): obj = current_app.api_doc # Generate the OpenAPI Specification based on the specified format if _format == "yaml": try: import yaml # type: ignore except ImportError: # pragma: no cover raise ImportError("pyyaml must be installed.") openapi = yaml.safe_dump(obj, allow_unicode=True) else: openapi = json.dumps(obj, indent=indent, ensure_ascii=False) # Save the OpenAPI Specification to a file if the output path is provided if output: with open(output, "w", encoding="utf8") as f: f.write(openapi) click.echo(f"Saved to {output}.") else: click.echo(openapi) flask-openapi3-4.1.0/flask_openapi3/models/000077500000000000000000000000001475153525300205235ustar00rootroot00000000000000flask-openapi3-4.1.0/flask_openapi3/models/__init__.py000066400000000000000000000056231475153525300226420ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/4/28 10:58 """ OpenAPI v3.1.0 schema types, created according to the specification: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md The type orders are according to the contents of the specification: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#table-of-contents """ from typing import Optional, Union from flask import Request from pydantic import BaseModel from .callback import Callback from .components import Components from .contact import Contact from .discriminator import Discriminator from .encoding import Encoding from .example import Example from .external_documentation import ExternalDocumentation from .file import FileStorage from .header import Header from .info import Info from .license import License from .link import Link from .media_type import MediaType from .oauth_flow import OAuthFlow from .oauth_flows import OAuthFlows from .operation import Operation from .parameter import Parameter from .parameter_in_type import ParameterInType from .path_item import PathItem from .paths import Paths from .reference import Reference from .request_body import RequestBody from .response import Response from .responses import Responses from .schema import Schema from .security_requirement import SecurityRequirement from .security_scheme import SecurityScheme from .server import Server from .server_variable import ServerVariable from .style_values import StyleValues from .tag import Tag from .validation_error import UnprocessableEntity from .validation_error import ValidationErrorModel from .xml import XML OPENAPI3_REF_PREFIX = "#/components/schemas" OPENAPI3_REF_TEMPLATE = OPENAPI3_REF_PREFIX + "/{model}" class APISpec(BaseModel): """https://spec.openapis.org/oas/v3.1.0#openapi-object""" openapi: str info: Info servers: Optional[list[Server]] = None paths: Paths components: Optional[Components] = None security: Optional[list[SecurityRequirement]] = None tags: Optional[list[Tag]] = None externalDocs: Optional[ExternalDocumentation] = None webhooks: Optional[dict[str, Union[PathItem, Reference]]] = None model_config = { "extra": "allow" } class OAuthConfig(BaseModel): """ https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md#oauth-20-configuration """ clientId: Optional[str] = None clientSecret: Optional[str] = None realm: Optional[str] = None appName: Optional[str] = None scopeSeparator: Optional[str] = None scopes: Optional[str] = None additionalQueryStringParams: Optional[dict[str, str]] = None useBasicAuthenticationWithAccessCodeGrant: Optional[bool] = False usePkceWithAuthorizationCodeGrant: Optional[bool] = False class RawModel(Request): mimetypes: list[str] = ["application/json"] Encoding.model_rebuild() Operation.model_rebuild() PathItem.model_rebuild() flask-openapi3-4.1.0/flask_openapi3/models/callback.py000066400000000000000000000011741475153525300226340ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:35 from typing import TYPE_CHECKING if TYPE_CHECKING: # pragma: no cover from .path_item import PathItem else: PathItem = "PathItem" """ A map of possible out-of band callbacks related to the parent operation. Each value in the map is a [Path Item Object](#pathItemObject) that describes a set of requests that may be initiated by the API provider and the expected responses. The key value used to identify the path item object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation. """ Callback = dict[str, PathItem] flask-openapi3-4.1.0/flask_openapi3/models/components.py000066400000000000000000000025331475153525300232650ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:36 from typing import Optional, Union, Any from pydantic import BaseModel, Field from .callback import Callback from .example import Example from .header import Header from .link import Link from .parameter import Parameter from .path_item import PathItem from .reference import Reference from .request_body import RequestBody from .response import Response from .schema import Schema from .security_scheme import SecurityScheme class Components(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#components-object """ schemas: Optional[dict[str, Union[Reference, Schema]]] = Field(None) responses: Optional[dict[str, Union[Response, Reference]]] = None parameters: Optional[dict[str, Union[Parameter, Reference]]] = None examples: Optional[dict[str, Union[Example, Reference]]] = None requestBodies: Optional[dict[str, Union[RequestBody, Reference]]] = None headers: Optional[dict[str, Union[Header, Reference]]] = None securitySchemes: Optional[dict[str, Union[SecurityScheme, dict[str, Any]]]] = None links: Optional[dict[str, Union[Link, Reference]]] = None callbacks: Optional[dict[str, Union[Callback, Reference]]] = None pathItems: Optional[dict[str, Union[PathItem, Reference]]] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/contact.py000066400000000000000000000005671475153525300225400ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:37 from typing import Optional from pydantic import BaseModel class Contact(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#contact-object """ name: Optional[str] = None url: Optional[str] = None email: Optional[str] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/data_type.py000066400000000000000000000005241475153525300230500ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:38 from enum import Enum class DataType(str, Enum): """ https://spec.openapis.org/oas/v3.1.0#data-types """ STRING = "string" NUMBER = "number" INTEGER = "integer" BOOLEAN = "boolean" ARRAY = "array" OBJECT = "object" NUll = "null" flask-openapi3-4.1.0/flask_openapi3/models/discriminator.py000066400000000000000000000005511475153525300237450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:41 from typing import Optional from pydantic import BaseModel class Discriminator(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#discriminator-object """ propertyName: str mapping: Optional[dict[str, str]] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/encoding.py000066400000000000000000000012011475153525300226550ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:41 from typing import TYPE_CHECKING, Optional, Union from pydantic import BaseModel from .reference import Reference if TYPE_CHECKING: # pragma: no cover from .header import Header else: Header = "Header" class Encoding(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#encoding-object """ contentType: Optional[str] = None headers: Optional[dict[str, Union[Header, Reference]]] = None style: Optional[str] = None explode: Optional[bool] = None allowReserved: bool = False model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/example.py000066400000000000000000000006571475153525300225400ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:42 from typing import Any, Optional from pydantic import BaseModel class Example(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#example-object """ summary: Optional[str] = None description: Optional[str] = None value: Optional[Any] = None externalValue: Optional[str] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/external_documentation.py000066400000000000000000000005521475153525300256520ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:43 from typing import Optional from pydantic import BaseModel class ExternalDocumentation(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#external-documentation-object """ description: Optional[str] = None url: str model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/file.py000066400000000000000000000015521475153525300220170ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/5/11 16:17 from typing import Any from pydantic.json_schema import JsonSchemaValue from pydantic_core import core_schema from werkzeug.datastructures import FileStorage as _FileStorage class FileStorage(_FileStorage): """ An uploaded file included as part of the request data. """ @classmethod def __get_pydantic_json_schema__(cls, *_args: Any, **_kwargs: Any) -> JsonSchemaValue: field_schema = {"format": "binary", "type": "string"} return field_schema @classmethod def __get_pydantic_core_schema__(cls, *_args: Any, **_kwargs: Any) -> core_schema.CoreSchema: return core_schema.with_info_plain_validator_function(cls.validate) @classmethod def validate(cls, value: _FileStorage, *_args: Any, **_kwargs: Any) -> _FileStorage: return value flask-openapi3-4.1.0/flask_openapi3/models/header.py000066400000000000000000000006651475153525300223340ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:44 from typing import Optional from .parameter import Parameter from .parameter_in_type import ParameterInType class Header(Parameter): """ https://spec.openapis.org/oas/v3.1.0#header-object """ name: Optional[str] = None # type:ignore param_in: Optional[ParameterInType] = None # type:ignore model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/info.py000066400000000000000000000010561475153525300220320ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/4/28 10:58 from typing import Optional from pydantic import BaseModel from .contact import Contact from .license import License class Info(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#info-object """ title: str summary: Optional[str] = None description: Optional[str] = None termsOfService: Optional[str] = None contact: Optional[Contact] = None license: Optional[License] = None version: str model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/license.py000066400000000000000000000005531475153525300225220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:45 from typing import Optional from pydantic import BaseModel class License(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#license-object """ name: str identifier: Optional[str] = None url: Optional[str] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/link.py000066400000000000000000000010421475153525300220270ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:45 from typing import Any, Optional from pydantic import BaseModel from .server import Server class Link(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#link-object """ operationRef: Optional[str] = None operationId: Optional[str] = None parameters: Optional[dict[str, Any]] = None requestBody: Optional[Any] = None description: Optional[str] = None server: Optional[Server] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/media_type.py000066400000000000000000000012441475153525300232160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:46 from typing import Any, Optional, Union from pydantic import BaseModel, Field from .encoding import Encoding from .example import Example from .reference import Reference from .schema import Schema class MediaType(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#media-type-object """ media_type_schema: Optional[Union[Reference, Schema]] = Field(default=None, alias="schema") example: Optional[Any] = None examples: Optional[dict[str, Union[Example, Reference]]] = None encoding: Optional[dict[str, Encoding]] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/oauth_flow.py000066400000000000000000000006551475153525300232520ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:47 from typing import Optional from pydantic import BaseModel class OAuthFlow(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#oauth-flow-object """ authorizationUrl: Optional[str] = None tokenUrl: Optional[str] = None refreshUrl: Optional[str] = None scopes: dict[str, str] model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/oauth_flows.py000066400000000000000000000007721475153525300234350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:47 from typing import Optional from pydantic import BaseModel from .oauth_flow import OAuthFlow class OAuthFlows(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#oauth-flows-object """ implicit: Optional[OAuthFlow] = None password: Optional[OAuthFlow] = None clientCredentials: Optional[OAuthFlow] = None authorizationCode: Optional[OAuthFlow] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/operation.py000066400000000000000000000022101475153525300230700ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:48 from typing import Optional, Union from pydantic import BaseModel from .callback import Callback from .external_documentation import ExternalDocumentation from .parameter import Parameter from .reference import Reference from .request_body import RequestBody from .response import Response from .security_requirement import SecurityRequirement from .server import Server class Operation(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#operation-object """ tags: Optional[list[str]] = None summary: Optional[str] = None description: Optional[str] = None externalDocs: Optional[ExternalDocumentation] = None operationId: Optional[str] = None parameters: Optional[list[Parameter]] = None requestBody: Optional[Union[RequestBody, Reference]] = None responses: Optional[dict[str, Response]] = None callbacks: Optional[dict[str, Callback]] = None deprecated: Optional[bool] = False security: Optional[list[SecurityRequirement]] = None servers: Optional[list[Server]] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/parameter.py000066400000000000000000000020271475153525300230560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:49 from typing import Any, Optional, Union from pydantic import BaseModel, Field from .example import Example from .media_type import MediaType from .parameter_in_type import ParameterInType from .reference import Reference from .schema import Schema class Parameter(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#parameter-object """ name: str param_in: ParameterInType = Field(alias="in") description: Optional[str] = None required: Optional[bool] = None deprecated: Optional[bool] = None allowEmptyValue: Optional[bool] = None style: Optional[str] = None explode: Optional[bool] = None allowReserved: Optional[bool] = None param_schema: Optional[Union[Reference, Schema]] = Field(default=None, alias="schema") example: Optional[Any] = None examples: Optional[dict[str, Union[Example, Reference]]] = None content: Optional[dict[str, MediaType]] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/parameter_in_type.py000066400000000000000000000004241475153525300246040ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:49 from enum import Enum class ParameterInType(str, Enum): """The place Parameters can be put when calling an Endpoint""" QUERY = "query" PATH = "path" HEADER = "header" COOKIE = "cookie" flask-openapi3-4.1.0/flask_openapi3/models/path_item.py000066400000000000000000000020271475153525300230500ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:50 import typing from typing import Optional, Union from pydantic import BaseModel, Field from .parameter import Parameter from .reference import Reference from .server import Server if typing.TYPE_CHECKING: # pragma: no cover from .operation import Operation class PathItem(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#path-item-object """ ref: Optional[str] = Field(default=None, alias="$ref") summary: Optional[str] = None description: Optional[str] = None get: Optional["Operation"] = None put: Optional["Operation"] = None post: Optional["Operation"] = None delete: Optional["Operation"] = None options: Optional["Operation"] = None head: Optional["Operation"] = None patch: Optional["Operation"] = None trace: Optional["Operation"] = None servers: Optional[list[Server]] = None parameters: Optional[list[Union[Parameter, Reference]]] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/paths.py000066400000000000000000000002731475153525300222160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:52 from .path_item import PathItem """ https://spec.openapis.org/oas/v3.1.0#paths-object """ Paths = dict[str, PathItem] flask-openapi3-4.1.0/flask_openapi3/models/reference.py000066400000000000000000000004601475153525300230330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:53 from pydantic import BaseModel, Field class Reference(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#reference-object """ ref: str = Field(..., alias="$ref") model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/request_body.py000066400000000000000000000006621475153525300236060ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:53 from typing import Optional from pydantic import BaseModel from .media_type import MediaType class RequestBody(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#request-body-object """ description: Optional[str] = None content: dict[str, MediaType] required: Optional[bool] = True model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/response.py000066400000000000000000000011411475153525300227300ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:54 from typing import Optional, Union from pydantic import BaseModel from .header import Header from .link import Link from .media_type import MediaType from .reference import Reference class Response(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#response-object """ description: str headers: Optional[dict[str, Union[Header, Reference]]] = None content: Optional[dict[str, MediaType]] = None links: Optional[dict[str, Union[Link, Reference]]] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/responses.py000066400000000000000000000004171475153525300231200ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:55 from typing import Union from .reference import Reference from .response import Response """ https://spec.openapis.org/oas/v3.1.0#responses-object """ Responses = dict[str, Union[Response, Reference]] flask-openapi3-4.1.0/flask_openapi3/models/schema.py000066400000000000000000000044461475153525300223450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:55 from typing import Any, Optional, Union from pydantic import BaseModel, Field from .data_type import DataType from .discriminator import Discriminator from .external_documentation import ExternalDocumentation from .reference import Reference from .xml import XML class Schema(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#schema-object """ ref: Optional[str] = Field(alias="$ref", default=None) title: Optional[str] = None multipleOf: Optional[float] = Field(default=None, gt=0.0) maximum: Optional[Union[int, float]] = None exclusiveMaximum: Optional[float] = None minimum: Optional[float] = None exclusiveMinimum: Optional[float] = None maxLength: Optional[int] = Field(default=None, ge=0) minLength: Optional[int] = Field(default=None, ge=0) pattern: Optional[str] = None maxItems: Optional[int] = Field(default=None, ge=0) minItems: Optional[int] = Field(default=None, ge=0) uniqueItems: Optional[bool] = None maxProperties: Optional[int] = Field(default=None, ge=0) minProperties: Optional[int] = Field(default=None, ge=0) required: Optional[list[str]] = Field(default=None) enum: Union[None, list[Any]] = Field(default=None) type: Optional[DataType] = Field(default=None) allOf: Optional[list[Union[Reference, "Schema"]]] = None oneOf: Optional[list[Union[Reference, "Schema"]]] = None anyOf: Optional[list[Union[Reference, "Schema"]]] = None schema_not: Optional[Union[Reference, "Schema"]] = Field(default=None, alias="not") items: Optional[Union[Reference, "Schema"]] = None properties: Optional[dict[str, Union[Reference, "Schema"]]] = None prefixItems: Optional[list[Union[Reference, "Schema"]]] = None additionalProperties: Optional[Union[bool, Reference, "Schema"]] = None description: Optional[str] = None schema_format: Optional[str] = Field(default=None, alias="format") default: Optional[Any] = None nullable: Optional[bool] = None discriminator: Optional[Discriminator] = None readOnly: Optional[bool] = None writeOnly: Optional[bool] = None xml: Optional[XML] = None externalDocs: Optional[ExternalDocumentation] = None example: Optional[Any] = None deprecated: Optional[bool] = None flask-openapi3-4.1.0/flask_openapi3/models/security_requirement.py000066400000000000000000000002701475153525300253630ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:56 """ https://spec.openapis.org/oas/v3.1.0#security-requirement-object """ SecurityRequirement = dict[str, list[str]] flask-openapi3-4.1.0/flask_openapi3/models/security_scheme.py000066400000000000000000000013251475153525300242710ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:56 from typing import Optional from pydantic import BaseModel, Field from .oauth_flows import OAuthFlows from .security_scheme_in_type import SecuritySchemeInType class SecurityScheme(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#security-scheme-object """ type: str description: Optional[str] = None name: Optional[str] = None security_scheme_in: Optional[SecuritySchemeInType] = Field(default=None, alias="in") scheme: Optional[str] = None bearerFormat: Optional[str] = None flows: Optional[OAuthFlows] = None openIdConnectUrl: Optional[str] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/security_scheme_in_type.py000066400000000000000000000004101475153525300260120ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/4/7 11:13 from enum import Enum class SecuritySchemeInType(str, Enum): """The place Parameters can be put when calling an Endpoint""" QUERY = "query" HEADER = "header" COOKIE = "cookie" flask-openapi3-4.1.0/flask_openapi3/models/server.py000066400000000000000000000006641475153525300224110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/4/28 11:26 from typing import Optional from pydantic import BaseModel from .server_variable import ServerVariable class Server(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#server-object """ url: str description: Optional[str] = None variables: Optional[dict[str, ServerVariable]] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/server_variable.py000066400000000000000000000006411475153525300242510ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:57 from typing import Optional from pydantic import BaseModel, Field class ServerVariable(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#server-variable-object """ enum: Optional[list[str]] = Field(None, min_length=1) default: str description: Optional[str] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/style_values.py000066400000000000000000000004651475153525300236210ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/4/6 16:29 from enum import Enum class StyleValues(str, Enum): matrix = "matrix" label = "label" form = "form" simple = "simple" spaceDelimited = "spaceDelimited" pipeDelimited = "pipeDelimited" deepObject = "deepObject" flask-openapi3-4.1.0/flask_openapi3/models/tag.py000066400000000000000000000006321475153525300216510ustar00rootroot00000000000000from typing import Optional from pydantic import BaseModel from .external_documentation import ExternalDocumentation class Tag(BaseModel): """ https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#tag-object """ name: str description: Optional[str] = None externalDocs: Optional[ExternalDocumentation] = None model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/models/validation_error.py000066400000000000000000000015571475153525300244500ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/5/10 14:51 from typing import Any, Optional from pydantic import BaseModel, Field class ValidationErrorModel(BaseModel): # More information: https://docs.pydantic.dev/latest/usage/models/#error-handling loc: Optional[list[str]] = Field(None, title="Location", description="the error's location as a list. ") msg: Optional[str] = Field(None, title="Message", description="a computer-readable identifier of the error type.") type_: Optional[str] = Field(None, title="Error Type", description="a human readable explanation of the error.") ctx: Optional[dict[str, Any]] = Field( None, title="Error context", description="an optional object which contains values required to render the error message." ) # backward compatibility UnprocessableEntity = ValidationErrorModel flask-openapi3-4.1.0/flask_openapi3/models/xml.py000066400000000000000000000006541475153525300217020ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/4 9:58 from typing import Optional from pydantic import BaseModel class XML(BaseModel): """ https://spec.openapis.org/oas/v3.1.0#xml-object """ name: Optional[str] = None namespace: Optional[str] = None prefix: Optional[str] = None attribute: bool = False wrapped: bool = False model_config = { "extra": "allow" } flask-openapi3-4.1.0/flask_openapi3/openapi.py000066400000000000000000000435071475153525300212560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/4/30 14:25 import os import re import sys from importlib import import_module from typing import Optional, Union, Any, Type, Callable from flask import Flask, Blueprint, render_template_string from pydantic import BaseModel if sys.version_info >= (3, 10): from importlib.metadata import entry_points else: # pragma: no cover from importlib_metadata import entry_points # type: ignore from .blueprint import APIBlueprint from .commands import openapi_command from .models import APISpec from .models import Components from .models import ExternalDocumentation from .models import Info from .models import OPENAPI3_REF_PREFIX from .models import Schema from .models import Server from .models import Tag from .models import ValidationErrorModel from .scaffold import APIScaffold from .templates import openapi_html_string from .types import ParametersTuple from .types import ResponseDict from .types import SecuritySchemesDict from .utils import HTTPMethod from .utils import HTTP_STATUS from .utils import convert_responses_key_to_string from .utils import get_model_schema from .utils import get_operation from .utils import get_operation_id_for_path from .utils import get_responses from .utils import make_validation_error_response from .utils import parse_and_store_tags from .utils import parse_method from .utils import parse_parameters from .view import APIView class OpenAPI(APIScaffold, Flask): def __init__( self, import_name: str, *, info: Optional[Info] = None, security_schemes: Optional[SecuritySchemesDict] = None, responses: Optional[ResponseDict] = None, servers: Optional[list[Server]] = None, external_docs: Optional[ExternalDocumentation] = None, operation_id_callback: Callable = get_operation_id_for_path, openapi_extensions: Optional[dict[str, Any]] = None, validation_error_status: Union[str, int] = 422, validation_error_model: Type[BaseModel] = ValidationErrorModel, validation_error_callback: Callable = make_validation_error_response, doc_ui: bool = True, doc_prefix: str = "/openapi", doc_url: str = "/openapi.json", **kwargs: Any ) -> None: """ OpenAPI class that provides REST API functionality along with Swagger UI and Redoc. Args: import_name: The import name for the Flask application. info: Information about the API (title, version, etc.). See https://spec.openapis.org/oas/v3.1.0#info-object. security_schemes: Security schemes for the API. See https://spec.openapis.org/oas/v3.1.0#security-scheme-object. responses: API responses should be either a subclass of BaseModel, a dictionary, or None. servers: An array of Server objects providing connectivity information to a target server. external_docs: External documentation for the API. See: https://spec.openapis.org/oas/v3.1.0#external-documentation-object. operation_id_callback: Callback function for custom operation ID generation. Receives name (str), path (str), and method (str) parameters. Defaults to `get_operation_id_for_path` from utils. openapi_extensions: Extensions to the OpenAPI Schema. See https://spec.openapis.org/oas/v3.1.0#specification-extensions. validation_error_status: HTTP Status of the response given when a validation error is detected by pydantic. Defaults to 422. validation_error_model: Validation error response model for OpenAPI Specification. validation_error_callback: Validation error response callback, the return format corresponds to the validation_error_model. doc_ui: Enable OpenAPI document UI (Swagger UI and Redoc). Defaults to True. doc_prefix: URL prefix used for OpenAPI document and UI. Defaults to "/openapi". doc_url: URL for accessing the OpenAPI specification document in JSON format. Defaults to "/openapi.json". **kwargs: Additional kwargs to be passed to Flask. """ super(OpenAPI, self).__init__(import_name, **kwargs) # Set OpenAPI version and API information self.openapi_version = "3.1.0" self.info = info or Info(title="OpenAPI", version="1.0.0") # Set security schemes, responses, paths and components self.security_schemes = security_schemes # Convert key to string self.responses = convert_responses_key_to_string(responses or {}) # Initialize instance variables self.paths: dict = dict() self.components_schemas: dict = dict() self.components = Components() # Initialize lists for tags and tag names self.tags: list[Tag] = [] self.tag_names: list[str] = [] # Set URL prefixes and endpoints self.doc_prefix = doc_prefix self.doc_url = doc_url # Set servers and external documentation self.severs = servers self.external_docs = external_docs # Set the operation ID callback function self.operation_id_callback: Callable = operation_id_callback # Set OpenAPI extensions self.openapi_extensions = openapi_extensions or {} # Set HTTP Response of validation errors within OpenAPI self.validation_error_status = str(validation_error_status) self.validation_error_model = validation_error_model self.validation_error_callback = validation_error_callback # Initialize the OpenAPI documentation UI if doc_ui: self._init_doc() # Add the OpenAPI command self.cli.add_command(openapi_command) # type: ignore # Initialize specification JSON self.spec_json: dict = {} self.spec = APISpec( openapi=self.openapi_version, info=self.info, paths=self.paths ) def _init_doc(self) -> None: """ Provide Swagger UI, Redoc, and Rapidoc """ _here = os.path.dirname(__file__) template_folder = os.path.join(_here, "templates") static_folder = os.path.join(template_folder, "static") # Create the blueprint for OpenAPI documentation blueprint = Blueprint( "openapi", __name__, url_prefix=self.doc_prefix, template_folder=template_folder, static_folder=static_folder ) # Add the API documentation URL rule blueprint.add_url_rule( rule=self.doc_url, endpoint="doc_url", view_func=lambda: self.api_doc ) ui_templates = [] # Iterate over all entry points in the "flask_openapi3.plugins" group for entry_point in entry_points(group="flask_openapi3.plugins"): try: module_path = entry_point.value module_name, class_name = module_path.rsplit(".", 1) module = import_module(module_name) plugin_class = getattr(module, class_name) plugin_register = plugin_class.register plugin_name = plugin_class.name plugin_display_name = plugin_class.display_name bp = plugin_register(doc_url=self.doc_url.lstrip("/")) self.register_blueprint(bp, url_prefix=self.doc_prefix) ui_templates.append({"name": plugin_name, "display_name": plugin_display_name}) except (ModuleNotFoundError, AttributeError): # pragma: no cover import traceback print(f"Warning: plugin '{entry_point.value}' registration failed.") traceback.print_exc() # Add URL rule for the home page blueprint.add_url_rule( rule="/", endpoint="openapi", view_func=lambda: render_template_string( self.config.get("OPENAPI_HTML_STRING") or openapi_html_string, ui_templates=ui_templates ) ) # Register the blueprint with the Flask application self.register_blueprint(blueprint) @property def api_doc(self) -> dict: """ Generate the OpenAPI specification JSON. Returns: The OpenAPI specification JSON as a dictionary. """ if self.spec_json: return self.spec_json self.generate_spec_json() return self.spec_json def generate_spec_json(self): self.spec.openapi = self.openapi_version self.spec.info = self.info self.spec.paths = self.paths if self.severs: self.spec.servers = self.severs if self.external_docs: self.spec.externalDocs = self.external_docs # Set tags if self.tags: self.spec.tags = self.tags # Add ValidationErrorModel to components schemas schema = get_model_schema(self.validation_error_model) self.components_schemas[self.validation_error_model.__name__] = Schema(**schema) # Parse definitions definitions = schema.get("$defs", {}) for name, value in definitions.items(): self.components_schemas[name] = Schema(**value) # Set components self.components.schemas = self.components_schemas self.components.securitySchemes = self.security_schemes self.spec.components = self.components # Convert spec to JSON self.spec_json = self.spec.model_dump(mode="json", by_alias=True, exclude_unset=True, warnings=False) # Update with OpenAPI extensions self.spec_json.update(**self.openapi_extensions) # Handle validation error response for rule, path_item in self.spec_json["paths"].items(): for http_method, operation in path_item.items(): if operation.get("parameters") is None and operation.get("requestBody") is None: continue if not operation.get("responses"): operation["responses"] = {} if operation["responses"].get(self.validation_error_status): continue operation["responses"][self.validation_error_status] = { "description": HTTP_STATUS[self.validation_error_status], "content": { "application/json": { "schema": { "type": "array", "items": {"$ref": f"{OPENAPI3_REF_PREFIX}/{self.validation_error_model.__name__}"} } } } } def register_api(self, api: APIBlueprint, **options: Any) -> None: """ Register an APIBlueprint. Args: api: The APIBlueprint instance to register. options: Additional keyword arguments are passed to :class:`~flask.blueprints.BlueprintSetupState`. They can be accessed in :meth:`~flask.Blueprint.record` callbacks. url_prefix, Blueprint routes will be prefixed with this. subdomain, Blueprint routes will match on this subdomain. url_defaults, Blueprint routes will use these default values for view arguments. """ for tag in api.tags: if tag.name not in self.tag_names: # Append tag to the list of tags self.tags.append(tag) # Append tag name to the list of tag names self.tag_names.append(tag.name) # Update paths with the APIBlueprint's paths url_prefix = options.get("url_prefix") if url_prefix and api.url_prefix and url_prefix != api.url_prefix: api.paths = {url_prefix + k.removeprefix(api.url_prefix): v for k, v in api.paths.items()} elif url_prefix and not api.url_prefix: api.paths = {url_prefix.rstrip("/") + "/" + k.lstrip("/"): v for k, v in api.paths.items()} self.paths.update(**api.paths) # Update component schemas with the APIBlueprint's component schemas self.components_schemas.update(**api.components_schemas) # Register the APIBlueprint with the current instance self.register_blueprint(api, **options) def register_api_view( self, api_view: APIView, url_prefix: Optional[str] = None, view_kwargs: Optional[dict[Any, Any]] = None ) -> None: """ Register APIView Args: api_view: The APIView instance to register. url_prefix: A path to prepend to all the APIView's urls view_kwargs: Additional keyword arguments to pass to the API views. """ if view_kwargs is None: view_kwargs = {} # Iterate through tags of the APIView for tag in api_view.tags: if tag.name not in self.tag_names: # Append tag to the list of tags self.tags.append(tag) # Append tag name to the list of tag names self.tag_names.append(tag.name) # Update paths with the APIView's paths if url_prefix and api_view.url_prefix and url_prefix != api_view.url_prefix: api_view.paths = {url_prefix + k.removeprefix(api_view.url_prefix): v for k, v in api_view.paths.items()} elif url_prefix and not api_view.url_prefix: api_view.paths = {url_prefix.rstrip("/") + "/" + k.lstrip("/"): v for k, v in api_view.paths.items()} self.paths.update(**api_view.paths) # Update component schemas with the APIView's component schemas self.components_schemas.update(**api_view.components_schemas) # Register the APIView with the current instance api_view.register(self, url_prefix=url_prefix, view_kwargs=view_kwargs) def _add_url_rule( self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options, ) -> None: self.add_url_rule(rule, endpoint, view_func, provide_automatic_options, **options) def _collect_openapi_info( self, rule: str, func: Callable, *, tags: Optional[list[Tag]] = None, summary: Optional[str] = None, description: Optional[str] = None, external_docs: Optional[ExternalDocumentation] = None, operation_id: Optional[str] = None, responses: Optional[ResponseDict] = None, deprecated: Optional[bool] = None, security: Optional[list[dict[str, list[Any]]]] = None, servers: Optional[list[Server]] = None, openapi_extensions: Optional[dict[str, Any]] = None, doc_ui: bool = True, method: str = HTTPMethod.GET ) -> ParametersTuple: """ Collects OpenAPI specification information for Flask routes and view functions. Args: rule: Flask route. func: Flask view_func. tags: Adds metadata to a single tag. summary: A short summary of what the operation does. description: A verbose explanation of the operation behavior. external_docs: Additional external documentation for this operation. operation_id: Unique string used to identify the operation. responses: API responses should be either a subclass of BaseModel, a dictionary, or None. deprecated: Declares this operation to be deprecated. security: A declaration of which security mechanisms can be used for this operation. servers: An alternative server array to service this operation. openapi_extensions: Allows extensions to the OpenAPI Schema. doc_ui: Declares this operation to be shown. Default to True. method: HTTP method for the operation. Defaults to GET. """ if doc_ui is True: # Convert key to string new_responses = convert_responses_key_to_string(responses or {}) # Global response: combine API responses combine_responses = {**self.responses, **new_responses} # Create operation operation = get_operation( func, summary=summary, description=description, openapi_extensions=openapi_extensions ) # Set external docs if external_docs: operation.externalDocs = external_docs # Unique string used to identify the operation. operation.operationId = operation_id or self.operation_id_callback( name=func.__name__, path=rule, method=method ) # Only set `deprecated` if True, otherwise leave it as None if deprecated is not None: operation.deprecated = deprecated # Add security if security: operation.security = security # Add servers if servers: operation.servers = servers # Store tags parse_and_store_tags(tags or [], self.tags, self.tag_names, operation) # Parse response get_responses(combine_responses, self.components_schemas, operation) # Convert a route parameter format from /pet/ to /pet/{petId} uri = re.sub(r"<([^<:]+:)?", "{", rule).replace(">", "}") # Parse method parse_method(uri, method, self.paths, operation) # Parse parameters return parse_parameters(func, components_schemas=self.components_schemas, operation=operation) else: return parse_parameters(func, doc_ui=False) flask-openapi3-4.1.0/flask_openapi3/plugins.py000066400000000000000000000004611475153525300212740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2024/4/23 14:31 class BasePlugin: name = None display_name = None @classmethod def register(cls, doc_url: str) -> None: """ Register the plugin. Args: doc_url (str): The API doc url. """ flask-openapi3-4.1.0/flask_openapi3/py.typed000066400000000000000000000000001475153525300207250ustar00rootroot00000000000000flask-openapi3-4.1.0/flask_openapi3/request.py000066400000000000000000000175441475153525300213150ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/4/1 16:54 import json from json import JSONDecodeError from typing import Any, Type, Optional from flask import request, current_app, abort from pydantic import ValidationError, BaseModel from pydantic.fields import FieldInfo from werkzeug.datastructures.structures import MultiDict def _get_list_value(model: Type[BaseModel], args: MultiDict, model_field_key: str, model_field_value: FieldInfo): if model_field_value.alias and model.model_config.get("populate_by_name"): key = model_field_value.alias value = args.getlist(model_field_value.alias) or args.getlist(model_field_key) elif model_field_value.alias: key = model_field_value.alias value = args.getlist(model_field_value.alias) else: key = model_field_key value = args.getlist(model_field_key) return key, value def _get_value(model: Type[BaseModel], args: MultiDict, model_field_key: str, model_field_value: FieldInfo): if model_field_value.alias and model.model_config.get("populate_by_name"): key = model_field_value.alias value = args.get(model_field_value.alias) or args.get(model_field_key) elif model_field_value.alias: key = model_field_value.alias value = args.get(model_field_value.alias) else: key = model_field_key value = args.get(model_field_key) return key, value def _validate_header(header: Type[BaseModel], func_kwargs: dict): request_headers = dict(request.headers) header_dict = {} model_properties = header.model_json_schema().get("properties", {}) for model_field_key, model_field_value in header.model_fields.items(): key_title = model_field_key.replace("_", "-").title() model_field_schema = model_properties.get(model_field_value.alias or model_field_key) if model_field_value.alias and header.model_config.get("populate_by_name"): key = model_field_value.alias key_alias_title = model_field_value.alias.replace("_", "-").title() value = request_headers.get(key_alias_title) or request_headers.get(key_title) elif model_field_value.alias: key = model_field_value.alias key_alias_title = model_field_value.alias.replace("_", "-").title() value = request_headers.get(key_alias_title) else: key = model_field_key value = request_headers[key_title] if value is not None: header_dict[key] = value if model_field_schema.get("type") == "null": header_dict[key] = value # type:ignore # extra keys for key, value in request_headers.items(): if key not in header_dict.keys(): header_dict[key] = value func_kwargs["header"] = header.model_validate(obj=header_dict) def _validate_cookie(cookie: Type[BaseModel], func_kwargs: dict): request_cookies = dict(request.cookies) func_kwargs["cookie"] = cookie.model_validate(obj=request_cookies) def _validate_path(path: Type[BaseModel], path_kwargs: dict, func_kwargs: dict): func_kwargs["path"] = path.model_validate(obj=path_kwargs) def _validate_query(query: Type[BaseModel], func_kwargs: dict): request_args = request.args query_dict = {} model_properties = query.model_json_schema().get("properties", {}) for model_field_key, model_field_value in query.model_fields.items(): model_field_schema = model_properties.get(model_field_value.alias or model_field_key) if model_field_schema.get("type") == "array": key, value = _get_list_value(query, request_args, model_field_key, model_field_value) else: key, value = _get_value(query, request_args, model_field_key, model_field_value) if value is not None and value != []: query_dict[key] = value if model_field_schema.get("type") == "null": query_dict[key] = value # extra keys for key, value in request_args.items(): if key not in query_dict.keys(): query_dict[key] = value func_kwargs["query"] = query.model_validate(obj=query_dict) def _validate_form(form: Type[BaseModel], func_kwargs: dict): request_form = request.form request_files = request.files form_dict = {} model_properties = form.model_json_schema().get("properties", {}) for model_field_key, model_field_value in form.model_fields.items(): model_field_schema = model_properties.get(model_field_value.alias or model_field_key) if model_field_schema.get("type") == "array": if model_field_schema.get("items") == {"format": "binary", "type": "string"}: # list[FileStorage] key, value = _get_list_value(form, request_files, model_field_key, model_field_value) else: value = [] key, value_list = _get_list_value(form, request_form, model_field_key, model_field_value) for _value in value_list: try: value.append(json.loads(_value)) except (JSONDecodeError, TypeError): value.append(_value) elif model_field_schema.get("type") == "string" and model_field_schema.get("format") == "binary": # FileStorage key, value = _get_value(form, request_files, model_field_key, model_field_value) else: key, _value = _get_value(form, request_form, model_field_key, model_field_value) try: value = json.loads(_value) except (JSONDecodeError, TypeError): value = _value if value is not None and value != []: form_dict[key] = value if model_field_schema.get("type") == "null": form_dict[key] = value # extra keys for key, value in {**dict(request_form), **dict(request_files)}.items(): if key not in form_dict.keys(): form_dict[key] = value func_kwargs["form"] = form.model_validate(obj=form_dict) def _validate_body(body: Type[BaseModel], func_kwargs: dict): obj = request.get_json(silent=True) if isinstance(obj, str): body_model = body.model_validate_json(json_data=obj) else: body_model = body.model_validate(obj=obj) func_kwargs["body"] = body_model def _validate_request( header: Optional[Type[BaseModel]] = None, cookie: Optional[Type[BaseModel]] = None, path: Optional[Type[BaseModel]] = None, query: Optional[Type[BaseModel]] = None, form: Optional[Type[BaseModel]] = None, body: Optional[Type[BaseModel]] = None, raw: Optional[Type[BaseModel]] = None, path_kwargs: Optional[dict[Any, Any]] = None ) -> dict: """ Validate requests and responses. Args: header: Header model. cookie: Cookie model. path: Path model. query: Query model. form: Form model. body: Body model. path_kwargs: Path parameters. Returns: dict: Request kwargs. Raises: ValidationError: If validation fails. """ # Dictionary to store func kwargs func_kwargs: dict = {} try: # Validate header, cookie, path, and query parameters if header: _validate_header(header, func_kwargs) if cookie: _validate_cookie(cookie, func_kwargs) if path: _validate_path(path, path_kwargs or {}, func_kwargs) if query: _validate_query(query, func_kwargs) if form: _validate_form(form, func_kwargs) if body: _validate_body(body, func_kwargs) if raw: func_kwargs["raw"] = request except ValidationError as e: # Create a response with validation error details validation_error_callback = getattr(current_app, "validation_error_callback") abort(validation_error_callback(e)) return func_kwargs flask-openapi3-4.1.0/flask_openapi3/scaffold.py000066400000000000000000000426621475153525300214050ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/8/30 9:40 import inspect from functools import wraps from typing import Callable, Optional, Any from flask.wrappers import Response as FlaskResponse from .models import ExternalDocumentation from .models import Server from .models import Tag from .request import _validate_request from .types import ParametersTuple from .types import ResponseDict from .utils import HTTPMethod class APIScaffold: def _collect_openapi_info( self, rule: str, func: Callable, *, tags: Optional[list[Tag]] = None, summary: Optional[str] = None, description: Optional[str] = None, external_docs: Optional[ExternalDocumentation] = None, operation_id: Optional[str] = None, responses: Optional[ResponseDict] = None, deprecated: Optional[bool] = None, security: Optional[list[dict[str, list[Any]]]] = None, servers: Optional[list[Server]] = None, openapi_extensions: Optional[dict[str, Any]] = None, doc_ui: bool = True, method: str = HTTPMethod.GET ) -> ParametersTuple: raise NotImplementedError # pragma: no cover def register_api(self, api) -> None: raise NotImplementedError # pragma: no cover def _add_url_rule( self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options, ) -> None: raise NotImplementedError # pragma: no cover @staticmethod def create_view_func( func, header, cookie, path, query, form, body, raw, view_class=None, view_kwargs=None ): is_coroutine_function = inspect.iscoroutinefunction(func) if is_coroutine_function: @wraps(func) async def view_func(**kwargs) -> FlaskResponse: func_kwargs = _validate_request( header=header, cookie=cookie, path=path, query=query, form=form, body=body, raw=raw, path_kwargs=kwargs ) # handle async request if view_class: signature = inspect.signature(view_class.__init__) parameters = signature.parameters if parameters.get("view_kwargs"): view_object = view_class(view_kwargs=view_kwargs) else: view_object = view_class() response = await func(view_object, **func_kwargs) else: response = await func(**func_kwargs) return response else: @wraps(func) def view_func(**kwargs) -> FlaskResponse: func_kwargs = _validate_request( header=header, cookie=cookie, path=path, query=query, form=form, body=body, raw=raw, path_kwargs=kwargs ) # handle request if view_class: signature = inspect.signature(view_class.__init__) parameters = signature.parameters if parameters.get("view_kwargs"): view_object = view_class(view_kwargs=view_kwargs) else: view_object = view_class() response = func(view_object, **func_kwargs) else: response = func(**func_kwargs) return response if not hasattr(func, "view"): func.view = view_func return func.view def get( self, rule: str, *, tags: Optional[list[Tag]] = None, summary: Optional[str] = None, description: Optional[str] = None, external_docs: Optional[ExternalDocumentation] = None, operation_id: Optional[str] = None, responses: Optional[ResponseDict] = None, deprecated: Optional[bool] = None, security: Optional[list[dict[str, list[Any]]]] = None, servers: Optional[list[Server]] = None, openapi_extensions: Optional[dict[str, Any]] = None, doc_ui: bool = True, **options: Any ) -> Callable: """ Decorator for defining a REST API endpoint with the HTTP GET method. More information goto https://spec.openapis.org/oas/v3.1.0#operation-object Args: rule: The URL rule string. tags: Adds metadata to a single tag. summary: A short summary of what the operation does. description: A verbose explanation of the operation behavior. external_docs: Additional external documentation for this operation. operation_id: Unique string used to identify the operation. responses: API responses should be either a subclass of BaseModel, a dictionary, or None. deprecated: Declares this operation to be deprecated. security: A declaration of which security mechanisms can be used for this operation. servers: An alternative server array to service this operation. openapi_extensions: Allows extensions to the OpenAPI Schema. doc_ui: Declares this operation to be shown. Default to True. """ def decorator(func) -> Callable: header, cookie, path, query, form, body, raw = \ self._collect_openapi_info( rule, func, tags=tags, summary=summary, description=description, external_docs=external_docs, operation_id=operation_id, responses=responses, deprecated=deprecated, security=security, servers=servers, openapi_extensions=openapi_extensions, doc_ui=doc_ui, method=HTTPMethod.GET ) view_func = self.create_view_func(func, header, cookie, path, query, form, body, raw) options.update({"methods": [HTTPMethod.GET]}) self._add_url_rule(rule, view_func=view_func, **options) return func return decorator def post( self, rule: str, *, tags: Optional[list[Tag]] = None, summary: Optional[str] = None, description: Optional[str] = None, external_docs: Optional[ExternalDocumentation] = None, operation_id: Optional[str] = None, responses: Optional[ResponseDict] = None, deprecated: Optional[bool] = None, security: Optional[list[dict[str, list[Any]]]] = None, servers: Optional[list[Server]] = None, openapi_extensions: Optional[dict[str, Any]] = None, doc_ui: bool = True, **options: Any ) -> Callable: """ Decorator for defining a REST API endpoint with the HTTP POST method. More information goto https://spec.openapis.org/oas/v3.1.0#operation-object Args: rule: The URL rule string. tags: Adds metadata to a single tag. summary: A short summary of what the operation does. description: A verbose explanation of the operation behavior. external_docs: Additional external documentation for this operation. operation_id: Unique string used to identify the operation. responses: API responses should be either a subclass of BaseModel, a dictionary, or None. deprecated: Declares this operation to be deprecated. security: A declaration of which security mechanisms can be used for this operation. servers: An alternative server array to service this operation. openapi_extensions: Allows extensions to the OpenAPI Schema. doc_ui: Declares this operation to be shown. Default to True. """ def decorator(func) -> Callable: header, cookie, path, query, form, body, raw = \ self._collect_openapi_info( rule, func, tags=tags, summary=summary, description=description, external_docs=external_docs, operation_id=operation_id, responses=responses, deprecated=deprecated, security=security, servers=servers, openapi_extensions=openapi_extensions, doc_ui=doc_ui, method=HTTPMethod.POST ) view_func = self.create_view_func(func, header, cookie, path, query, form, body, raw) options.update({"methods": [HTTPMethod.POST]}) self._add_url_rule(rule, view_func=view_func, **options) return func return decorator def put( self, rule: str, *, tags: Optional[list[Tag]] = None, summary: Optional[str] = None, description: Optional[str] = None, external_docs: Optional[ExternalDocumentation] = None, operation_id: Optional[str] = None, responses: Optional[ResponseDict] = None, deprecated: Optional[bool] = None, security: Optional[list[dict[str, list[Any]]]] = None, servers: Optional[list[Server]] = None, openapi_extensions: Optional[dict[str, Any]] = None, doc_ui: bool = True, **options: Any ) -> Callable: """ Decorator for defining a REST API endpoint with the HTTP PUT method. More information goto https://spec.openapis.org/oas/v3.1.0#operation-object Args: rule: The URL rule string. tags: Adds metadata to a single tag. summary: A short summary of what the operation does. description: A verbose explanation of the operation behavior. external_docs: Additional external documentation for this operation. operation_id: Unique string used to identify the operation. responses: API responses should be either a subclass of BaseModel, a dictionary, or None. deprecated: Declares this operation to be deprecated. security: A declaration of which security mechanisms can be used for this operation. servers: An alternative server array to service this operation. openapi_extensions: Allows extensions to the OpenAPI Schema. doc_ui: Declares this operation to be shown. Default to True. """ def decorator(func) -> Callable: header, cookie, path, query, form, body, raw = \ self._collect_openapi_info( rule, func, tags=tags, summary=summary, description=description, external_docs=external_docs, operation_id=operation_id, responses=responses, deprecated=deprecated, security=security, servers=servers, openapi_extensions=openapi_extensions, doc_ui=doc_ui, method=HTTPMethod.PUT ) view_func = self.create_view_func(func, header, cookie, path, query, form, body, raw) options.update({"methods": [HTTPMethod.PUT]}) self._add_url_rule(rule, view_func=view_func, **options) return func return decorator def delete( self, rule: str, *, tags: Optional[list[Tag]] = None, summary: Optional[str] = None, description: Optional[str] = None, external_docs: Optional[ExternalDocumentation] = None, operation_id: Optional[str] = None, responses: Optional[ResponseDict] = None, deprecated: Optional[bool] = None, security: Optional[list[dict[str, list[Any]]]] = None, servers: Optional[list[Server]] = None, openapi_extensions: Optional[dict[str, Any]] = None, doc_ui: bool = True, **options: Any ) -> Callable: """ Decorator for defining a REST API endpoint with the HTTP DELETE method. More information goto https://spec.openapis.org/oas/v3.1.0#operation-object Args: rule: The URL rule string. tags: Adds metadata to a single tag. summary: A short summary of what the operation does. description: A verbose explanation of the operation behavior. external_docs: Additional external documentation for this operation. operation_id: Unique string used to identify the operation. responses: API responses should be either a subclass of BaseModel, a dictionary, or None. deprecated: Declares this operation to be deprecated. security: A declaration of which security mechanisms can be used for this operation. servers: An alternative server array to service this operation. openapi_extensions: Allows extensions to the OpenAPI Schema. doc_ui: Declares this operation to be shown. Default to True. """ def decorator(func) -> Callable: header, cookie, path, query, form, body, raw = \ self._collect_openapi_info( rule, func, tags=tags, summary=summary, description=description, external_docs=external_docs, operation_id=operation_id, responses=responses, deprecated=deprecated, security=security, servers=servers, openapi_extensions=openapi_extensions, doc_ui=doc_ui, method=HTTPMethod.DELETE ) view_func = self.create_view_func(func, header, cookie, path, query, form, body, raw) options.update({"methods": [HTTPMethod.DELETE]}) self._add_url_rule(rule, view_func=view_func, **options) return func return decorator def patch( self, rule: str, *, tags: Optional[list[Tag]] = None, summary: Optional[str] = None, description: Optional[str] = None, external_docs: Optional[ExternalDocumentation] = None, operation_id: Optional[str] = None, responses: Optional[ResponseDict] = None, deprecated: Optional[bool] = None, security: Optional[list[dict[str, list[Any]]]] = None, servers: Optional[list[Server]] = None, openapi_extensions: Optional[dict[str, Any]] = None, doc_ui: bool = True, **options: Any ) -> Callable: """ Decorator for defining a REST API endpoint with the HTTP PATCH method. More information goto https://spec.openapis.org/oas/v3.1.0#operation-object Args: rule: The URL rule string. tags: Adds metadata to a single tag. summary: A short summary of what the operation does. description: A verbose explanation of the operation behavior. external_docs: Additional external documentation for this operation. operation_id: Unique string used to identify the operation. responses: API responses should be either a subclass of BaseModel, a dictionary, or None. deprecated: Declares this operation to be deprecated. security: A declaration of which security mechanisms can be used for this operation. servers: An alternative server array to service this operation. openapi_extensions: Allows extensions to the OpenAPI Schema. doc_ui: Declares this operation to be shown. Default to True. """ def decorator(func) -> Callable: header, cookie, path, query, form, body, raw = \ self._collect_openapi_info( rule, func, tags=tags, summary=summary, description=description, external_docs=external_docs, operation_id=operation_id, responses=responses, deprecated=deprecated, security=security, servers=servers, openapi_extensions=openapi_extensions, doc_ui=doc_ui, method=HTTPMethod.PATCH ) view_func = self.create_view_func(func, header, cookie, path, query, form, body, raw) options.update({"methods": [HTTPMethod.PATCH]}) self._add_url_rule(rule, view_func=view_func, **options) return func return decorator flask-openapi3-4.1.0/flask_openapi3/templates.py000066400000000000000000000067531475153525300216230ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/2/16 9:46 openapi_html_string = """ APIdoc
{% for ui in ui_templates %} {% else %}

Please install at least one optional UI:

$ pip install -U flask-openapi3[swagger,redoc,rapidoc,rapipdf,scalar,elements]

More optional ui templates goto the document about UI_Templates.

{% endfor %}
""" flask-openapi3-4.1.0/flask_openapi3/types.py000066400000000000000000000013501475153525300207550ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/9 15:25 from http import HTTPStatus from typing import Union, Type, Any, Optional from pydantic import BaseModel from .models import RawModel from .models import SecurityScheme _ResponseDictValue = Union[Type[BaseModel], dict[Any, Any], None] ResponseDict = dict[Union[str, int, HTTPStatus], _ResponseDictValue] ResponseStrKeyDict = dict[str, _ResponseDictValue] SecuritySchemesDict = dict[str, Union[SecurityScheme, dict[str, Any]]] ParametersTuple = tuple[ Optional[Type[BaseModel]], Optional[Type[BaseModel]], Optional[Type[BaseModel]], Optional[Type[BaseModel]], Optional[Type[BaseModel]], Optional[Type[BaseModel]], Optional[Type[RawModel]] ] flask-openapi3-4.1.0/flask_openapi3/utils.py000066400000000000000000000543121475153525300207570ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/5/1 21:34 import inspect import re import sys from enum import Enum from http import HTTPStatus from typing import get_type_hints, Type, Callable, Optional, Any, DefaultDict from flask import make_response, current_app from flask.wrappers import Response as FlaskResponse from pydantic import BaseModel, ValidationError from pydantic.json_schema import JsonSchemaMode from .models import Encoding from .models import MediaType from .models import OPENAPI3_REF_PREFIX from .models import OPENAPI3_REF_TEMPLATE from .models import Operation from .models import Parameter from .models import ParameterInType from .models import PathItem from .models import RawModel from .models import RequestBody from .models import Response from .models import Schema from .models import Tag from .models.data_type import DataType from .types import ParametersTuple from .types import ResponseDict from .types import ResponseStrKeyDict HTTP_STATUS = {str(status.value): status.phrase for status in HTTPStatus} if sys.version_info < (3, 11): # pragma: no cover class HTTPMethod(str, Enum): GET = "GET" POST = "POST" PUT = "PUT" DELETE = "DELETE" PATCH = "PATCH" HEAD = "HEAD" OPTIONS = "OPTIONS" TRACE = "TRACE" CONNECT = "CONNECT" else: from http import HTTPMethod def get_operation( func: Callable, *, summary: Optional[str] = None, description: Optional[str] = None, openapi_extensions: Optional[dict[str, Any]] = None, ) -> Operation: """ Return an Operation object with the specified summary and description. Args: func: The function or method for which the operation is being defined. summary: A short summary of what the operation does. description: A verbose explanation of the operation behavior. openapi_extensions: Additional extensions to the OpenAPI Schema. Returns: An Operation object representing the operation. """ # Get the docstring of the function doc = inspect.getdoc(func) or "" doc = doc.strip() lines = doc.split("\n") doc_summary = lines[0] # Determine the summary and description based on provided arguments or docstring if summary is None: doc_description = lines[0] if len(lines) == 0 else "
".join(lines[1:]) else: doc_description = "
".join(lines) summary = summary or doc_summary description = description or doc_description # Create the operation dictionary with summary and description operation_dict = {} if summary: operation_dict["summary"] = summary # type: ignore if description: operation_dict["description"] = description # type: ignore # Add any additional openapi_extensions to the operation dictionary operation_dict.update(openapi_extensions or {}) # Create and return the Operation object operation = Operation(**operation_dict) return operation def get_operation_id_for_path(*, name: str, path: str, method: str) -> str: """ Generate a unique operation ID based on the name, path, and method. Args: name: The name or identifier for the operation. path: The URL path for the operation. method: The HTTP method for the operation. Returns: A unique operation ID generated based on the provided name, path, and method. """ return re.sub(r"\W", "_", name + path) + "_" + method.lower() def get_model_schema(model: Type[BaseModel], mode: JsonSchemaMode = "validation") -> dict: """Converts a Pydantic model to an OpenAPI schema.""" assert inspect.isclass(model) and issubclass(model, BaseModel), \ f"{model} is invalid `pydantic.BaseModel`" model_config = model.model_config by_alias = bool(model_config.get("by_alias", True)) return model.model_json_schema(by_alias=by_alias, ref_template=OPENAPI3_REF_TEMPLATE, mode=mode) def parse_header(header: Type[BaseModel]) -> tuple[list[Parameter], dict]: """Parses a header model and returns a list of parameters and component schemas.""" schema = get_model_schema(header) parameters = [] components_schemas: dict = dict() properties = schema.get("properties", {}) for name, value in properties.items(): data = { "name": name, "in": ParameterInType.HEADER, "required": name in schema.get("required", []), "schema": Schema(**value) } # Parse extra values if "description" in value.keys(): data["description"] = value.get("description") if "deprecated" in value.keys(): data["deprecated"] = value.get("deprecated") if "example" in value.keys(): data["example"] = value.get("example") if "examples" in value.keys(): data["examples"] = value.get("examples") parameters.append(Parameter(**data)) # Parse definitions definitions = schema.get("$defs", {}) for name, value in definitions.items(): components_schemas[name] = Schema(**value) return parameters, components_schemas def parse_cookie(cookie: Type[BaseModel]) -> tuple[list[Parameter], dict]: """Parses a cookie model and returns a list of parameters and component schemas.""" schema = get_model_schema(cookie) parameters = [] components_schemas: dict = dict() properties = schema.get("properties", {}) for name, value in properties.items(): data = { "name": name, "in": ParameterInType.COOKIE, "required": name in schema.get("required", []), "schema": Schema(**value) } # Parse extra values if "description" in value.keys(): data["description"] = value.get("description") if "deprecated" in value.keys(): data["deprecated"] = value.get("deprecated") if "example" in value.keys(): data["example"] = value.get("example") if "examples" in value.keys(): data["examples"] = value.get("examples") parameters.append(Parameter(**data)) # Parse definitions definitions = schema.get("$defs", {}) for name, value in definitions.items(): components_schemas[name] = Schema(**value) return parameters, components_schemas def parse_path(path: Type[BaseModel]) -> tuple[list[Parameter], dict]: """Parses a path model and returns a list of parameters and component schemas.""" schema = get_model_schema(path) parameters = [] components_schemas: dict = dict() properties = schema.get("properties", {}) for name, value in properties.items(): data = { "name": name, "in": ParameterInType.PATH, "required": True, "schema": Schema(**value) } # Parse extra values if "description" in value.keys(): data["description"] = value.get("description") if "deprecated" in value.keys(): data["deprecated"] = value.get("deprecated") if "example" in value.keys(): data["example"] = value.get("example") if "examples" in value.keys(): data["examples"] = value.get("examples") parameters.append(Parameter(**data)) # Parse definitions definitions = schema.get("$defs", {}) for name, value in definitions.items(): components_schemas[name] = Schema(**value) return parameters, components_schemas def parse_query(query: Type[BaseModel]) -> tuple[list[Parameter], dict]: """Parses a query model and returns a list of parameters and component schemas.""" schema = get_model_schema(query) parameters = [] components_schemas: dict = dict() properties = schema.get("properties", {}) for name, value in properties.items(): data = { "name": name, "in": ParameterInType.QUERY, "required": name in schema.get("required", []), "schema": Schema(**value) } # Parse extra values if "description" in value.keys(): data["description"] = value.get("description") if "deprecated" in value.keys(): data["deprecated"] = value.get("deprecated") if "example" in value.keys(): data["example"] = value.get("example") if "examples" in value.keys(): data["examples"] = value.get("examples") parameters.append(Parameter(**data)) # Parse definitions definitions = schema.get("$defs", {}) for name, value in definitions.items(): components_schemas[name] = Schema(**value) return parameters, components_schemas def parse_form( form: Type[BaseModel], ) -> tuple[dict[str, MediaType], dict]: """Parses a form model and returns a list of parameters and component schemas.""" schema = get_model_schema(form) components_schemas = dict() properties = schema.get("properties", {}) assert properties, f"{form.__name__}'s properties cannot be empty." original_title = schema.get("title") or form.__name__ title = normalize_name(original_title) components_schemas[title] = Schema(**schema) encoding = {} for k, v in properties.items(): if v.get("type") == "array": encoding[k] = Encoding(style="form", explode=True) content = { "multipart/form-data": MediaType( schema=Schema(**{"$ref": f"{OPENAPI3_REF_PREFIX}/{title}"}), ) } if encoding: content["multipart/form-data"].encoding = encoding # Parse definitions definitions = schema.get("$defs", {}) for name, value in definitions.items(): components_schemas[name] = Schema(**value) return content, components_schemas def parse_body( body: Type[BaseModel], ) -> tuple[dict[str, MediaType], dict]: """Parses a body model and returns a list of parameters and component schemas.""" schema = get_model_schema(body) components_schemas = dict() original_title = schema.get("title") or body.__name__ title = normalize_name(original_title) components_schemas[title] = Schema(**schema) content = { "application/json": MediaType( schema=Schema(**{"$ref": f"{OPENAPI3_REF_PREFIX}/{title}"}) ) } # Parse definitions definitions = schema.get("$defs", {}) for name, value in definitions.items(): components_schemas[name] = Schema(**value) return content, components_schemas def get_responses( responses: ResponseStrKeyDict, components_schemas: dict, operation: Operation ) -> None: _responses = {} _schemas = {} for key, response in responses.items(): if response is None: # If the response is None, it means HTTP status code "204" (No Content) _responses[key] = Response(description=HTTP_STATUS.get(key, "")) elif isinstance(response, dict): response["description"] = response.get("description", HTTP_STATUS.get(key, "")) _responses[key] = Response(**response) else: # OpenAPI 3 support ^[a-zA-Z0-9\.\-_]+$ so we should normalize __name__ schema = get_model_schema(response, mode="serialization") original_title = schema.get("title") or response.__name__ name = normalize_name(original_title) _responses[key] = Response( description=HTTP_STATUS.get(key, ""), content={ "application/json": MediaType( schema=Schema(**{"$ref": f"{OPENAPI3_REF_PREFIX}/{name}"}) )}) model_config: DefaultDict[str, Any] = response.model_config # type: ignore openapi_extra = model_config.get("openapi_extra", {}) if openapi_extra: openapi_extra_keys = openapi_extra.keys() # Add additional information from model_config to the response if "description" in openapi_extra_keys: _responses[key].description = openapi_extra.get("description") if "headers" in openapi_extra_keys: _responses[key].headers = openapi_extra.get("headers") if "links" in openapi_extra_keys: _responses[key].links = openapi_extra.get("links") _content = _responses[key].content if "example" in openapi_extra_keys: _content["application/json"].example = openapi_extra.get("example") # type: ignore if "examples" in openapi_extra_keys: _content["application/json"].examples = openapi_extra.get("examples") # type: ignore if "encoding" in openapi_extra_keys: _content["application/json"].encoding = openapi_extra.get("encoding") # type: ignore _content.update(openapi_extra.get("content", {})) # type: ignore _schemas[name] = Schema(**schema) definitions = schema.get("$defs") if definitions: # Add schema definitions to _schemas for name, value in definitions.items(): _schemas[normalize_name(name)] = Schema(**value) components_schemas.update(**_schemas) operation.responses = _responses def parse_and_store_tags( new_tags: list[Tag], old_tags: list[Tag], old_tag_names: list[str], operation: Operation ) -> None: """ Parses new tags, stores them in an old_tags list if they are not already present, and updates the tags attribute of the operation object. Args: new_tags: A list of new Tag objects to be parsed and stored. old_tags: The list of existing Tag objects. old_tag_names: The list that names of existing tags. operation: The operation object whose tag attribute needs to be updated. Returns: None """ # Iterate over each tag in new_tags for tag in new_tags: if tag.name not in old_tag_names: old_tag_names.append(tag.name) old_tags.append(tag) # Set the tags attribute of the operation object to a list of unique tag names from new_tags # If the resulting list is empty, set it to ["default"] operation.tags = list(set([tag.name for tag in new_tags])) or ["default"] def parse_parameters( func: Callable, *, components_schemas: Optional[dict] = None, operation: Optional[Operation] = None, doc_ui: bool = True, ) -> ParametersTuple: """ Parses the parameters of a given function and returns the types for header, cookie, path, query, form, and body parameters. Also populates the Operation object with the parsed parameters. Args: func: The function to parse the parameters from. components_schemas: Dictionary to store the parsed components schemas (default: None). operation: Operation object to populate with parsed parameters (default: None). doc_ui: Flag indicating whether to return types for documentation UI (default: True). Returns: tuple[Type[BaseModel], Type[BaseModel], Type[BaseModel], Type[BaseModel], Type[BaseModel], Type[BaseModel]]: The types for header, cookie, path, query, form, and body parameters respectively. """ # If components_schemas is None, initialize it as an empty dictionary if components_schemas is None: components_schemas = dict() # If operation is None, initialize it as an Operation object if operation is None: operation = Operation() # Get the type hints from the function annotations = get_type_hints(func) # Get the types for header, cookie, path, query, form, and body parameters header: Optional[Type[BaseModel]] = annotations.get("header") cookie: Optional[Type[BaseModel]] = annotations.get("cookie") path: Optional[Type[BaseModel]] = annotations.get("path") query: Optional[Type[BaseModel]] = annotations.get("query") form: Optional[Type[BaseModel]] = annotations.get("form") body: Optional[Type[BaseModel]] = annotations.get("body") raw: Optional[Type[RawModel]] = annotations.get("raw") # If doc_ui is False, return the types without further processing if doc_ui is False: return header, cookie, path, query, form, body, raw parameters = [] if header: _parameters, _components_schemas = parse_header(header) parameters.extend(_parameters) components_schemas.update(**_components_schemas) if cookie: _parameters, _components_schemas = parse_cookie(cookie) parameters.extend(_parameters) components_schemas.update(**_components_schemas) if path: _parameters, _components_schemas = parse_path(path) parameters.extend(_parameters) components_schemas.update(**_components_schemas) if query: _parameters, _components_schemas = parse_query(query) parameters.extend(_parameters) components_schemas.update(**_components_schemas) if form: _content, _components_schemas = parse_form(form) components_schemas.update(**_components_schemas) request_body = RequestBody(content=_content, required=True) model_config: DefaultDict[str, Any] = form.model_config # type: ignore openapi_extra = model_config.get("openapi_extra", {}) if openapi_extra: openapi_extra_keys = openapi_extra.keys() if "description" in openapi_extra_keys: request_body.description = openapi_extra.get("description") if "example" in openapi_extra_keys: request_body.content["multipart/form-data"].example = openapi_extra.get("example") if "examples" in openapi_extra_keys: request_body.content["multipart/form-data"].examples = openapi_extra.get("examples") if "encoding" in openapi_extra_keys: request_body.content["multipart/form-data"].encoding = openapi_extra.get("encoding") operation.requestBody = request_body if body: _content, _components_schemas = parse_body(body) components_schemas.update(**_components_schemas) request_body = RequestBody(content=_content, required=True) model_config: DefaultDict[str, Any] = body.model_config # type: ignore openapi_extra = model_config.get("openapi_extra", {}) if openapi_extra: openapi_extra_keys = openapi_extra.keys() if "description" in openapi_extra_keys: request_body.description = openapi_extra.get("description") request_body.required = openapi_extra.get("required", True) if "example" in openapi_extra_keys: request_body.content["application/json"].example = openapi_extra.get("example") if "examples" in openapi_extra_keys: request_body.content["application/json"].examples = openapi_extra.get("examples") if "encoding" in openapi_extra_keys: request_body.content["application/json"].encoding = openapi_extra.get("encoding") operation.requestBody = request_body if raw: _content = {} for mimetype in raw.mimetypes: if mimetype.startswith("application/json"): _content[mimetype] = MediaType( schema=Schema(type=DataType.OBJECT) ) else: _content[mimetype] = MediaType( schema=Schema(type=DataType.STRING) ) request_body = RequestBody(content=_content) operation.requestBody = request_body if parameters: # Set the parsed parameters in the operation object operation.parameters = parameters return header, cookie, path, query, form, body, raw def parse_method(uri: str, method: str, paths: dict, operation: Operation) -> None: """ Parses the HTTP method and updates the corresponding PathItem object in the paths' dictionary. Args: uri: The URI of the API endpoint. method: The HTTP method for the API endpoint. paths: A dictionary containing the API paths and their corresponding PathItem objects. operation: The Operation object to assign to the PathItem. Returns: None """ # Check the HTTP method and update the PathItem object in the path dictionary if method == HTTPMethod.GET: if not paths.get(uri): paths[uri] = PathItem(get=operation) else: paths[uri].get = operation elif method == HTTPMethod.POST: if not paths.get(uri): paths[uri] = PathItem(post=operation) else: paths[uri].post = operation elif method == HTTPMethod.PUT: if not paths.get(uri): paths[uri] = PathItem(put=operation) else: paths[uri].put = operation elif method == HTTPMethod.PATCH: if not paths.get(uri): paths[uri] = PathItem(patch=operation) else: paths[uri].patch = operation elif method == HTTPMethod.DELETE: if not paths.get(uri): paths[uri] = PathItem(delete=operation) else: paths[uri].delete = operation def make_validation_error_response(e: ValidationError) -> FlaskResponse: """ Create a Flask response for a validation error. Args: e: The ValidationError object containing the details of the error. Returns: FlaskResponse: A Flask Response object with the JSON representation of the error. """ response = make_response(e.json()) response.headers["Content-Type"] = "application/json" response.status_code = getattr(current_app, "validation_error_status", 422) return response def parse_rule(rule: str, url_prefix=None) -> str: trail_slash = rule.endswith("/") # Merge url_prefix and uri uri = url_prefix.rstrip("/") + "/" + rule.lstrip("/") if url_prefix else rule if not trail_slash: uri = uri.rstrip("/") # Convert a route parameter format from /pet/ to /pet/{petId} uri = re.sub(r"<([^<:]+:)?", "{", uri).replace(">", "}") return uri def convert_responses_key_to_string(responses: ResponseDict) -> ResponseStrKeyDict: """Convert key to string""" return {str(key.value if isinstance(key, HTTPStatus) else key): value for key, value in responses.items()} def normalize_name(name: str) -> str: return re.sub(r"[^\w.\-]", "_", name) flask-openapi3-4.1.0/flask_openapi3/view.py000066400000000000000000000211361475153525300205670ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/10/14 16:09 import typing from typing import Optional, Any, Callable from .models import ExternalDocumentation from .models import Server from .models import Tag from .types import ResponseDict from .utils import HTTPMethod from .utils import convert_responses_key_to_string from .utils import get_operation from .utils import get_operation_id_for_path from .utils import get_responses from .utils import parse_and_store_tags from .utils import parse_method from .utils import parse_parameters from .utils import parse_rule if typing.TYPE_CHECKING: # pragma: no cover from .openapi import OpenAPI class APIView: def __init__( self, url_prefix: Optional[str] = None, view_tags: Optional[list[Tag]] = None, view_security: Optional[list[dict[str, list[str]]]] = None, view_responses: Optional[ResponseDict] = None, doc_ui: bool = True, operation_id_callback: Callable = get_operation_id_for_path, ): """ Create a class-based view Args: url_prefix: A path to prepend to all the APIView's urls view_tags: APIView tags for every API. view_security: APIView security for every API. view_responses: API responses should be either a subclass of BaseModel, a dictionary, or None. doc_ui: Enable OpenAPI document UI (Swagger UI and Redoc). Defaults to True. operation_id_callback: Callback function for custom operation_id generation. Receives name (str), path (str) and method (str) parameters. Defaults to `get_operation_id_for_path` from utils """ self.url_prefix = url_prefix self.view_tags = view_tags or [] self.view_security = view_security or [] # Convert key to string self.view_responses = convert_responses_key_to_string(view_responses or {}) self.doc_ui = doc_ui self.operation_id_callback: Callable = operation_id_callback self.views: dict = dict() self.paths: dict = dict() self.components_schemas: dict = dict() self.tags: list[Tag] = [] self.tag_names: list[str] = [] def route(self, rule: str): """Decorator for view class""" def wrapper(cls): if self.views.get(rule): # pragma: no cover raise ValueError(f"malformed url rule: {rule!r}") methods = [] # Parse rule: merge url_prefix and format rule from /pet/ to /pet/{petId} uri = parse_rule(rule, url_prefix=self.url_prefix) for method in HTTPMethod: cls_method = getattr(cls, method.lower(), None) if not cls_method: continue methods.append(method) if self.doc_ui is False: continue if not getattr(cls_method, "operation", None): continue # Parse method parse_method(uri, method, self.paths, cls_method.operation) # Update operation_id if not cls_method.operation.operationId: cls_method.operation.operationId = self.operation_id_callback( name=cls_method.__qualname__, path=rule, method=method ) # Convert route parameters from {param} to _rule = uri.replace("{", "<").replace("}", ">") self.views[_rule] = (cls, methods) return cls return wrapper def doc( self, *, tags: Optional[list[Tag]] = None, summary: Optional[str] = None, description: Optional[str] = None, external_docs: Optional[ExternalDocumentation] = None, operation_id: Optional[str] = None, responses: Optional[ResponseDict] = None, deprecated: Optional[bool] = None, security: Optional[list[dict[str, list[Any]]]] = None, servers: Optional[list[Server]] = None, openapi_extensions: Optional[dict[str, Any]] = None, doc_ui: bool = True ) -> Callable: """ Decorator for view method. More information goto https://spec.openapis.org/oas/v3.1.0#operation-object Args: tags: Adds metadata to a single tag. summary: A short summary of what the operation does. description: A verbose explanation of the operation behavior. external_docs: Additional external documentation for this operation. operation_id: Unique string used to identify the operation. responses: API responses should be either a subclass of BaseModel, a dictionary, or None. deprecated: Declares this operation to be deprecated. security: A declaration of which security mechanisms can be used for this operation. servers: An alternative server array to service this operation. openapi_extensions: Allows extensions to the OpenAPI Schema. doc_ui: Declares this operation to be shown. Default to True. """ new_responses = convert_responses_key_to_string(responses or {}) security = security or [] tags = tags + self.view_tags if tags else self.view_tags def decorator(func): if self.doc_ui is False or doc_ui is False: return func # Global response combines API responses combine_responses = {**self.view_responses, **new_responses} # Create operation operation = get_operation( func, summary=summary, description=description, openapi_extensions=openapi_extensions ) # Set external docs if external_docs: operation.externalDocs = external_docs # Unique string used to identify the operation. if operation_id: operation.operationId = operation_id # Only set `deprecated` if True, otherwise leave it as None if deprecated is not None: operation.deprecated = deprecated # Add security _security = (security or []) + self.view_security or None if _security: operation.security = _security # Add servers if servers: operation.servers = servers # Store tags parse_and_store_tags(tags, self.tags, self.tag_names, operation) # Parse parameters parse_parameters( func, components_schemas=self.components_schemas, operation=operation ) # Parse response get_responses(combine_responses, self.components_schemas, operation) func.operation = operation return func return decorator def register( self, app: "OpenAPI", url_prefix: Optional[str] = None, view_kwargs: Optional[dict[Any, Any]] = None ) -> None: """ Register the API views with the given OpenAPI app. Args: app: An instance of the OpenAPI app. url_prefix: A path to prepend to all the APIView's urls view_kwargs: Additional keyword arguments to pass to the API views. """ for rule, (cls, methods) in self.views.items(): for method in methods: func = getattr(cls, method.lower()) header, cookie, path, query, form, body, raw = parse_parameters(func, doc_ui=False) view_func = app.create_view_func( func, header, cookie, path, query, form, body, raw, view_class=cls, view_kwargs=view_kwargs ) if url_prefix and self.url_prefix and url_prefix != self.url_prefix: rule = url_prefix + rule.removeprefix(self.url_prefix) elif url_prefix and not self.url_prefix: rule = url_prefix.rstrip("/") + "/" + rule.lstrip("/") options = { "endpoint": cls.__name__ + "." + method.lower(), "methods": [method.upper()] } app.add_url_rule(rule, view_func=view_func, **options) flask-openapi3-4.1.0/mkdocs.yml000066400000000000000000000055601475153525300163530ustar00rootroot00000000000000site_name: flask-openapi3 site_url: https://luolingchun.github.io/flask-openapi3/ site_description: Generate REST API and OpenAPI documentation for your Flask project. site_author: llc repo_url: https://github.com/luolingchun/flask-openapi3 repo_name: 'GitHub' markdown_extensions: - admonition - toc: permalink: ⚓︎ - md_in_html - pymdownx.magiclink - pymdownx.superfences - pymdownx.snippets - pymdownx.highlight: linenums: true theme: name: material logo: images/logo.svg favicon: images/logo-blue.svg palette: - media: "(prefers-color-scheme: light)" scheme: default toggle: icon: material/weather-sunny - media: "(prefers-color-scheme: dark)" scheme: slate toggle: icon: material/weather-night features: - navigation.tracking - navigation.footer - navigation.top # - navigation.expand - search.suggest - search.highlight - search.share - content.code.copy - content.action.edit - content.action.view - toc.follow plugins: - search - mkdocstrings: handlers: python: options: show_root_heading: true show_root_full_path: false - glightbox - i18n: docs_structure: suffix fallback_to_default: true reconfigure_material: true reconfigure_search: true languages: - build: true default: true locale: en name: English - build: true default: false locale: zh name: 简体中文 nav_translations: API Reference: API 参考 Changelog: 更新日志 Contributing: 贡献 Example: 示例 Home: 简介 LICENSE: 许可 Model Config: 模型配置 Quickstart: 快速开始 Request: 请求 Response: 响应 Route Operation: 路由操作 Specification: 规范 UI Templates: 自定义模板 Configuration: 配置 Usage: 教程 extra: version: provider: mike extra_javascript: - js/db.js extra_css: - css/img.css nav: - Home: index.md - Quickstart: Quickstart.md - Usage: - Specification: Usage/Specification.md - Request: Usage/Request.md - Response: Usage/Response.md - Route Operation: Usage/Route_Operation.md - Model Config: Usage/Model_Config.md - Configuration: Usage/Configuration.md - UI Templates: Usage/UI_Templates.md - JSON: Usage/JSON.md - Example: Example.md - API Reference: - APIScaffold: Reference/Scaffold.md - OpenAPI: Reference/OpenAPI.md - APIBlueprint: Reference/APIBlueprint.md - APIView: Reference/APIView.md - Models: Reference/Models.md - LICENSE: LICENSE.md - Changelog: Changelog.md - Contributing: Contributing.md flask-openapi3-4.1.0/pyproject.toml000066400000000000000000000043771475153525300172710ustar00rootroot00000000000000[project] name = "flask-openapi3" description = "Generate REST API and OpenAPI documentation for your Flask project." readme = "README.md" license = { text = "MIT" } maintainers = [{ name = "llc", email = "luolingchun@outlook.com" }] classifiers = [ # "Development Status :: 1 - Planning", # "Development Status :: 2 - Pre-Alpha", # "Development Status :: 3 - Alpha", # "Development Status :: 4 - Beta", "Development Status :: 5 - Production/Stable", # "Development Status :: 6 - Mature", # "Development Status :: 7 - Inactive", "Environment :: Web Environment", "Framework :: Flask", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", ] requires-python = ">=3.9" dependencies = ["Flask>=2.0", "pydantic>=2.4"] dynamic = ["version"] [project.urls] Homepage = "https://github.com/luolingchun/flask-openapi3" Documentation = "https://luolingchun.github.io/flask-openapi3" [project.optional-dependencies] yaml = ["pyyaml"] async = ["asgiref >= 3.2"] dotenv = ["python-dotenv"] email = ["email-validator"] # ui templates swagger = ["flask-openapi3-swagger"] redoc = ["flask-openapi3-redoc"] rapidoc = ["flask-openapi3-rapidoc"] rapipdf = ["flask-openapi3-rapipdf"] scalar = ["flask-openapi3-scalar"] elements = ["flask-openapi3-elements"] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.version] path = "flask_openapi3/__version__.py" [tool.hatch.build.targets.sdist] include = [ "/flask_openapi3", "/examples", "/requirements", "/tests", "/CHANGELOG.md", "/CONTRIBUTING.md", "/LICENSE.rst" ] [tool.ruff] line-length = 120 [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401"] [tool.mypy] files = "src" plugins = ["pydantic.mypy"] [[tool.mypy.overrides]] module = ["pydantic_core.*", "devtools.*"] follow_imports = "skip" ignore_missing_imports = true flask-openapi3-4.1.0/requirements/000077500000000000000000000000001475153525300170655ustar00rootroot00000000000000flask-openapi3-4.1.0/requirements/build.txt000066400000000000000000000000051475153525300207200ustar00rootroot00000000000000buildflask-openapi3-4.1.0/requirements/doc.txt000066400000000000000000000001311475153525300203660ustar00rootroot00000000000000mkdocs-static-i18n>=1.0 mkdocstrings[python] mkdocs-material mkdocs-glightbox pyyaml mikeflask-openapi3-4.1.0/requirements/mypy.txt000066400000000000000000000000041475153525300206160ustar00rootroot00000000000000mypyflask-openapi3-4.1.0/requirements/ruff.txt000066400000000000000000000000041475153525300205620ustar00rootroot00000000000000ruffflask-openapi3-4.1.0/requirements/test.txt000066400000000000000000000000251475153525300206020ustar00rootroot00000000000000pytest asgiref pyyamlflask-openapi3-4.1.0/tests/000077500000000000000000000000001475153525300155045ustar00rootroot00000000000000flask-openapi3-4.1.0/tests/test_api_blueprint.py000066400000000000000000000060011475153525300217470ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/5/17 15:25 from typing import Optional import pytest from pydantic import BaseModel, Field from flask_openapi3 import APIBlueprint, OpenAPI from flask_openapi3 import Tag, Info info = Info(title='book API', version='1.0.0') jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } security_schemes = {"jwt": jwt} app = OpenAPI(__name__, info=info, security_schemes=security_schemes) app.config["TESTING"] = True tag = Tag(name='book', description="Book") security = [{"jwt": []}] class Unauthorized(BaseModel): code: int = Field(-1, description="Status Code") message: str = Field("Unauthorized!", description="Exception Information") api = APIBlueprint( '/book', __name__, url_prefix='/api', abp_tags=[tag], abp_security=security, abp_responses={"401": Unauthorized} ) try: api.register_api(api) except ValueError as e: assert str(e) == "Cannot register a api blueprint on itself" @pytest.fixture def client(): client = app.test_client() return client class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') class BookPath(BaseModel): bid: int = Field(..., description='book id') @api.post('/book', doc_ui=False) def create_book(body: BookBody): assert body.age == 3 return {"code": 0, "message": "ok"} @api.put('/book/', operation_id='update') def update_book(path: BookPath, body: BookBody): assert path.bid == 1 assert body.age == 3 return {"code": 0, "message": "ok"} @api.patch('/book/') def update_book1(path: BookPath, body: BookBody): assert path.bid == 1 assert body.age == 3 return {"code": 0, "message": "ok"} @api.patch('/v2/book/') def update_book1_v2(path: BookPath, body: BookBody): assert path.bid == 1 assert body.age == 3 return {"code": 0, "message": "ok"} @api.delete('/book/') def delete_book(path: BookPath): assert path.bid == 1 return {"code": 0, "message": "ok"} # register api app.register_api(api) def test_openapi(client): resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json == app.api_doc assert resp.json["paths"]["/api/book/{bid}"]["put"]["operationId"] == "update" assert resp.json["paths"]["/api/book/{bid}"]["delete"]["operationId"] == "_book_book__int_bid__delete" def test_post(client): resp = client.post("/api/book", json={"age": 3}) assert resp.status_code == 200 def test_put(client): resp = client.put("/api/book/1", json={"age": 3}) assert resp.status_code == 200 def test_patch(client): resp = client.patch("/api/book/1", json={"age": 3}) assert resp.status_code == 200 resp = client.patch("/api/v2/book/1", json={"age": 3}) assert resp.status_code == 200 def test_delete(client): resp = client.delete("/api/book/1") assert resp.status_code == 200 flask-openapi3-4.1.0/tests/test_api_view.py000066400000000000000000000062571475153525300207320ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/11/4 14:41 from typing import Optional import pytest from pydantic import BaseModel, Field from flask_openapi3 import APIView from flask_openapi3 import OpenAPI, Tag, Info info = Info(title='book API', version='1.0.0') jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } security_schemes = {"jwt": jwt} app = OpenAPI(__name__, info=info, security_schemes=security_schemes) app.config["TESTING"] = True security = [{"jwt": []}] api_view = APIView(url_prefix="/api/v1/", view_tags=[Tag(name="book")], view_security=security) api_view2 = APIView(doc_ui=False) class BookPath(BaseModel): id: int = Field(..., description="book ID") name: str class BookQuery(BaseModel): age: Optional[int] = Field(None, description='Age') class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') @api_view.route("/book") class BookListAPIView: a = 1 @api_view.doc( summary="get book list", responses={ 204: None }, doc_ui=False ) def get(self, query: BookQuery): print(self.a) return query.model_dump_json() @api_view.doc(summary="create book") def post(self, body: BookBody): """description for a created book""" return body.model_dump_json() @api_view.route("/book/") class BookAPIView: @api_view.doc(summary="get book") def get(self, path: BookPath): print(path) return "get" @api_view.doc(summary="update book", operation_id="update") def put(self, path: BookPath): print(path) return "put" @api_view.doc(summary="delete book", deprecated=True) def delete(self, path: BookPath): print(path) return "delete" @api_view2.route("//book2/") class BookAPIView2: @api_view2.doc(summary="get book") def get(self, path: BookPath): return path.model_dump() app.register_api_view(api_view) app.register_api_view(api_view2) @pytest.fixture def client(): client = app.test_client() return client def test_openapi(client): resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json == app.api_doc assert resp.json["paths"]["/api/v1/{name}/book/{id}"]["put"]["operationId"] == "update" assert resp.json["paths"]["/api/v1/{name}/book/{id}"]["delete"][ "operationId"] == "BookAPIView_delete_book__id__delete" def test_get_list(client): resp = client.get("/api/v1/name1/book") assert resp.status_code == 200 def test_post(client): resp = client.post("/api/v1/name1/book", json={"age": 3}) assert resp.status_code == 200 def test_put(client): resp = client.put("/api/v1/name1/book/1", json={"age": 3}) assert resp.status_code == 200 def test_get(client): resp = client.get("/api/v1/name1/book/1") assert resp.status_code == 200 resp = client.get("/name2/book2/1") assert resp.status_code == 200 def test_delete(client): resp = client.delete("/api/v1/name1/book/1") assert resp.status_code == 200 flask-openapi3-4.1.0/tests/test_api_view_args.py000066400000000000000000000024511475153525300217360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/11/4 14:41 import pytest from pydantic import BaseModel, Field from flask_openapi3 import APIView from flask_openapi3 import OpenAPI app = OpenAPI(__name__) app.config["TESTING"] = True api_view = APIView(url_prefix="/api/v1") class BookPath(BaseModel): id: int = Field(..., description="book ID") @api_view.route("/book") class BookListAPIView: def __init__(self, view_kwargs=None): self.a = view_kwargs.get("a") @api_view.doc(summary="get book list") def get(self): return {"a": self.a} @api_view.route("/book/") class BookAPIView: def __init__(self, view_kwargs=None): self.b = view_kwargs.get("b") @api_view.doc(summary="get book list") async def get(self, path: BookPath): print(path) return {"b": self.b} app.register_api_view( api_view, view_kwargs={ "a": 1, "b": 2 } ) @pytest.fixture def client(): client = app.test_client() return client def test_get_list_view_kwargs(client): resp = client.get("/api/v1/book") assert resp.status_code == 200 assert resp.json["a"] == 1 def test_get_view_kwargs(client): resp = client.get("/api/v1/book/1") assert resp.status_code == 200 assert resp.json["b"] == 2 flask-openapi3-4.1.0/tests/test_async.py000066400000000000000000000036221475153525300202350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/12/5 10:27 from typing import Optional import pytest from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI, APIView app = OpenAPI(__name__) app.config["TESTING"] = True api_view = APIView(url_prefix="/api/v1") class Query(BaseModel): q: str class BookQuery(BaseModel): age: Optional[int] = Field(None, description="Age") class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description="Age") author: str = Field(None, min_length=2, max_length=4, description="Author") @app.get("/open/api") async def get_openapi(query: Query): print(query) return "GET, OpenAPI!" @app.post("/open/api") async def post_openapi(body: Query): print(body) return "POST, OpenAPI!" @api_view.route("/book") class BookListAPIView: @api_view.doc(summary="get book list") async def get(self, query: BookQuery): return query.model_dump_json() @api_view.doc(summary="create book") async def post(self, body: BookBody): """description for a created book""" return body.model_dump_json() app.register_api_view(api_view) @pytest.fixture def client(): client = app.test_client() return client def test_openapi(client): resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json == app.api_doc def test_get_openapi(client): resp = client.get("/open/api?q=1") assert resp.status_code == 200 assert resp.text == "GET, OpenAPI!" def test_post_openapi(client): resp = client.post("/open/api", json={"q": "string"}) assert resp.status_code == 200 assert resp.text == "POST, OpenAPI!" def test_get_list(client): resp = client.get("/api/v1/book") assert resp.status_code == 200 def test_post(client): resp = client.post("/api/v1/book", json={"age": 2}) assert resp.status_code == 200 flask-openapi3-4.1.0/tests/test_command.py000066400000000000000000000012251475153525300205330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2024/7/3 9:44 import os import pytest from flask_openapi3 import OpenAPI from flask_openapi3.commands import openapi_command @pytest.fixture def app(): app = OpenAPI(__name__) return app @pytest.fixture def cli_runner(app): return app.test_cli_runner() def test_openapi_command(app, cli_runner): result = cli_runner.invoke(openapi_command, ("--output", "test.json")) os.remove("test.json") assert result.exit_code == 0 assert "Saved to test.json." in result.output result = cli_runner.invoke(openapi_command, ("--format", "yaml")) assert result.exit_code == 0 flask-openapi3-4.1.0/tests/test_default_query.py000066400000000000000000000013331475153525300217660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/12/1 9:26 import pytest from pydantic import BaseModel, Field from flask_openapi3 import Info, OpenAPI info = Info(title='book API', version='1.0.0') app = OpenAPI(__name__, info=info) app.config["TESTING"] = True class BookQuery(BaseModel): page: int = Field(1, description='current page') page_size: int = Field(15, description='size of per page') @pytest.fixture def client(): client = app.test_client() return client @app.get('/book') def get_book(query: BookQuery): print(query) return {"code": 0, "message": "ok"} def test_get(client): resp = client.get("/book?page=2") print(resp.json) assert resp.status_code == 200 flask-openapi3-4.1.0/tests/test_empty_body.py000066400000000000000000000013231475153525300212670ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/12/1 9:39 import pytest from pydantic import BaseModel from flask_openapi3 import Info, OpenAPI info = Info(title='book API', version='1.0.0') app = OpenAPI(__name__, info=info) app.config["TESTING"] = True class CreateBookBody(BaseModel): pass model_config = { "extra": "allow", } @pytest.fixture def client(): client = app.test_client() return client @app.post('/book') def create_book(body: CreateBookBody): print(body.model_dump()) return {"code": 0, "message": "ok"} def test_post(client): resp = client.post("/book", json={'aaa': 111, 'bbb': 222}) print(resp.json) assert resp.status_code == 200 flask-openapi3-4.1.0/tests/test_enum.py000066400000000000000000000016411475153525300200630ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/2/5 14:54 from enum import Enum import pytest from pydantic import BaseModel, Field from flask_openapi3 import Info from flask_openapi3 import OpenAPI app = OpenAPI( __name__, info=Info(title="Enum demo", version="1.0.0") ) app.config["TESTING"] = True class Language(str, Enum): cn = 'Chinese' en = 'English' class LanguagePath(BaseModel): language: Language = Field(..., description='Language') @app.get('/') def get_enum(path: LanguagePath): print(path) return {} @pytest.fixture def client(): client = app.test_client() return client def test_openapi(client): resp = client.get("/openapi/openapi.json") _json = resp.json assert resp.status_code == 200 assert _json["components"]["schemas"].get("Language") is not None resp = client.get("/English") assert resp.status_code == 200 flask-openapi3-4.1.0/tests/test_form.py000066400000000000000000000072111475153525300200610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/8/6 13:47 from enum import Enum from typing import Union, Any import pytest from pydantic import BaseModel from flask_openapi3 import FileStorage, OpenAPI app = OpenAPI(__name__) app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client class MetadataParameter(BaseModel): tag: str class MetadataParameter2(BaseModel): tag2: str class FileType(int, Enum): a = 1 b = 2 class FormParameters(BaseModel): file: FileStorage file_list: list[FileStorage] file_type: FileType number: float number_list: list[float] boolean: bool boolean_list: list[bool] digit: int digit_list: list[int] string: str string_list: list[str] obj: dict[Any, Any] parameter: MetadataParameter parameter_dict: dict[str, MetadataParameter] parameter_list: list[MetadataParameter] parameter_list_union: list[Union[bool, float, str, int, FileType, MetadataParameter]] parameter_union: Union[MetadataParameter, MetadataParameter2] union_all: Union[str, int, float, bool, FileType, MetadataParameter] none: None = None default_value: str = "default_value" class FormParameter(BaseModel): obj: dict[Any, Any] model_config = dict( openapi_extra={ "encoding": { "historyMetadata": { "contentType": "application/xml; charset=utf-8" }, "profileImage": { "contentType": "image/png, image/jpeg", "headers": { "X-Rate-Limit-Limit": { "description": "The number of allowed requests in the current period", "schema": { "type": "integer" } } } } } } ) @app.post("/example") def complex_form_example(form: FormParameters): print(form.model_dump()) return "ok" @app.post("/example2") def invalid_json_in_form_example(form: FormParameter): print(form.model_dump()) return "ok" def test_openapi(client): from io import BytesIO data = { "boolean": "true", "boolean_list": [True, False], "digit": "1", "digit_list": ["2", "3"], "file": (BytesIO(b"post-data"), "filename"), "file_list": [(BytesIO(b"post-data"), "filename"), (BytesIO(b"post-data"), "filename")], "file_type": "1", "none": "null", "number": "1.2", "number_list": ["3.4", "5.6"], "obj": '{"a": 2}', "parameter": '{"tag": "string"}', "parameter_dict": '{"additionalProp1": {"tag": "string"}, "additionalProp2": {"tag": "string"},' '"additionalProp3": {"tag": "string"}}', "parameter_list": ['{"tag": "string"}', '{"tag": "string"}'], "parameter_list_union": ["ok", '{"tag": "string"}', "7.8"], "parameter_union": '{"tag2": "string"}', "union_all": "true", "string": "a", "string_list": ["a", "b", "c"] } resp = client.post("/example", data=data, content_type="multipart/form-data") print(resp.text) assert resp.status_code == 200 def test_invalid_json_in_form_example(client): data = { "obj": "{a: 2}" } resp = client.post("/example2", data=data, content_type="multipart/form-data") assert resp.status_code == 422 data = { "obj": '{"a": 2}' } resp = client.post("/example2", data=data, content_type="multipart/form-data") assert resp.status_code == 200 flask-openapi3-4.1.0/tests/test_list_with_default_value.py000066400000000000000000000013571475153525300240310ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2024/9/29 10:36 import pytest from pydantic import BaseModel from flask_openapi3 import OpenAPI app = OpenAPI(__name__) app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client class BookQuery(BaseModel): age: list[int] = [1, 2] class BookForm(BaseModel): age: list[float] = [3, 4] @app.get("/query") def api_query(query: BookQuery): assert query.age == [1, 2] return {"code": 0, "message": "ok"} @app.post("/form") def api_form(form: BookForm): assert form.age == [3, 4] return {"code": 0, "message": "ok"} def test_query(client): client.get("/query") def test_form(client): client.post("/form") flask-openapi3-4.1.0/tests/test_model_config.py000066400000000000000000000103741475153525300215470ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/6/30 10:12 import pytest from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI, FileStorage app = OpenAPI(__name__) app.config["TESTING"] = True class UploadFilesForm(BaseModel): file: FileStorage str_list: list[str] model_config = dict( openapi_extra={ # "example": {"a": 123}, "examples": { "Example 01": { "summary": "An example", "value": { "file": "Example-01.jpg", "str_list": ["a", "b", "c"] } }, "Example 02": { "summary": "Another example", "value": { "str_list": ["1", "2", "3"] } } } } ) class BookBody(BaseModel): age: int author: str model_config = dict( openapi_extra={ "description": "This is post RequestBody", "example": {"age": 12, "author": "author1"}, "examples": { "example1": { "summary": "example summary1", "description": "example description1", "value": { "age": 24, "author": "author2" } }, "example2": { "summary": "example summary2", "description": "example description2", "value": { "age": 48, "author": "author3" } } }} ) class MessageResponse(BaseModel): message: str = Field(..., description="The message") metadata: dict[str, str] = Field(alias="metadata_") model_config = dict( by_alias=False, openapi_extra={ # "example": {"message": "aaa"}, "examples": { "example1": { "summary": "example1 summary", "value": { "message": "bbb" } }, "example2": { "summary": "example2 summary", "value": { "message": "ccc" } } } } ) @app.post("/form") def api_form(form: UploadFilesForm): print(form) # pragma: no cover @app.post("/body", responses={"200": MessageResponse}) def api_error_json(body: BookBody): print(body) # pragma: no cover @pytest.fixture def client(): client = app.test_client() return client def test_openapi(client): resp = client.get("/openapi/openapi.json") _json = resp.json assert resp.status_code == 200 assert _json["paths"]["/form"]["post"]["requestBody"]["content"]["multipart/form-data"]["examples"] == \ { "Example 01": { "summary": "An example", "value": { "file": "Example-01.jpg", "str_list": ["a", "b", "c"] } }, "Example 02": { "summary": "Another example", "value": { "str_list": ["1", "2", "3"] } } } assert _json["paths"]["/body"]["post"]["requestBody"]["description"] == "This is post RequestBody" assert _json["paths"]["/body"]["post"]["requestBody"]["content"]["application/json"]["example"] == \ {"age": 12, "author": "author1"} assert _json["paths"]["/body"]["post"]["responses"]["200"]["content"]["application/json"]["examples"] == \ { "example1": { "summary": "example1 summary", "value": { "message": "bbb" } }, "example2": { "summary": "example2 summary", "value": { "message": "ccc" } } } assert _json["components"]["schemas"]["MessageResponse"]["properties"].get("metadata") is not None flask-openapi3-4.1.0/tests/test_model_extra.py000066400000000000000000000031411475153525300214170ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2024/11/20 14:45 from typing import Optional import pytest from pydantic import BaseModel, Field, ConfigDict from flask_openapi3 import OpenAPI app = OpenAPI(__name__) app.config["TESTING"] = True class BookQuery(BaseModel): age: Optional[int] = Field(None, description="Age") model_config = ConfigDict(extra="allow") class BookForm(BaseModel): string: str model_config = ConfigDict(extra="forbid") class BookHeader(BaseModel): api_key: str = Field(..., description="API Key") model_config = ConfigDict(extra="forbid") @pytest.fixture def client(): client = app.test_client() return client @app.get("/book") def get_books(query: BookQuery): """get books to get all books """ assert query.age == 3 assert query.author == "joy" return {"code": 0, "message": "ok"} @app.post("/form") def api_form(form: BookForm): print(form) return {"code": 0, "message": "ok"} def test_query(client): resp = client.get("/book?age=3&author=joy") assert resp.status_code == 200 @app.get("/header") def get_book(header: BookHeader): return header.model_dump(by_alias=True) def test_form(client): data = { "string": "a", "string_list": ["a", "b", "c"] } r = client.post("/form", data=data, content_type="multipart/form-data") assert r.status_code == 422 def test_header(client): headers = {"Hello1": "111", "hello2": "222", "api_key": "333", "api_type": "A", "x-hello": "444"} resp = client.get("/header", headers=headers) assert resp.status_code == 422 flask-openapi3-4.1.0/tests/test_multi_decorator.py000066400000000000000000000013171475153525300223130ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/9/7 9:46 import pytest from flask import request from flask_openapi3 import OpenAPI app = OpenAPI(__name__) @app.get("/t") @app.get("/tt") def get_t(): return request.url @app.post("/t") @app.post("/tt") def post_t(): return request.url @pytest.fixture def client(): client = app.test_client() return client def test_get_t(client): r = client.get("/t") assert r.text == "http://localhost/t" r = client.get("/tt") assert r.text == "http://localhost/tt" def test_post_t(client): r = client.post("/t") assert r.text == "http://localhost/t" r = client.post("/tt") assert r.text == "http://localhost/tt" flask-openapi3-4.1.0/tests/test_nested_apiblueprint.py000066400000000000000000000025631475153525300231630ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/4/2 9:09 import pytest from pydantic import BaseModel from flask_openapi3 import APIBlueprint, OpenAPI, Tag app = OpenAPI(__name__) api = APIBlueprint('book', __name__, url_prefix='/api/book/') api_english = APIBlueprint('english', __name__) api_chinese = APIBlueprint('chinese', __name__) class BookPath(BaseModel): name: str @api_english.post('/english') def create_english_book(path: BookPath): return {"message": "english", "name": path.name} @api_chinese.post('/chinese', tags=[Tag(name="chinese")]) def create_chinese_book(path: BookPath): return {"message": "chinese", "name": path.name} # register nested api api.register_api(api_english) api.register_api(api_chinese) # register api app.register_api(api) @pytest.fixture def client(): client = app.test_client() return client def test_openapi(client): resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json == app.api_doc def test_post_english(client): resp = client.post("/api/book/name1/english") assert resp.status_code == 200 assert resp.json == {"message": "english", "name": "name1"} def test_post_chinese(client): resp = client.post("/api/book/name2/chinese") assert resp.status_code == 200 assert resp.json == {"message": "chinese", "name": "name2"} flask-openapi3-4.1.0/tests/test_number_constraints.py000066400000000000000000000017151475153525300230400ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2024/4/19 20:53 import pytest from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI app = OpenAPI(__name__) app.config["TESTING"] = True class MyModel(BaseModel): num_1: int = Field(..., ge=1, le=10) num_2: int = Field(..., gt=1, lt=10) @app.post('/book') def create_book(body: MyModel): print(body) # pragma: no cover @pytest.fixture def client(): client = app.test_client() return client def test_openapi(client): resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json == app.api_doc model_props = resp.json['components']['schemas']['MyModel']['properties'] num_1_props = model_props['num_1'] num_2_props = model_props['num_2'] assert num_1_props['minimum'] == 1 assert num_1_props['maximum'] == 10 assert num_2_props['exclusiveMinimum'] == 1 assert num_2_props['exclusiveMaximum'] == 10 flask-openapi3-4.1.0/tests/test_openapi.py000066400000000000000000000441641475153525300205610ustar00rootroot00000000000000from __future__ import annotations from typing import Generic, TypeVar, Optional, Literal from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI, Schema T = TypeVar("T", bound=BaseModel) def test_responses_are_replicated_in_open_api(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True class BaseResponse(BaseModel): """Base description""" test: int model_config = dict( openapi_extra={ "description": "Custom description", "headers": { "location": { "description": "URL of the new resource", "schema": {"type": "string"} } }, "content": { "text/plain": { "schema": {"type": "string"} } }, "links": { "dummy": { "description": "dummy link" } } } ) @test_app.get("/test", responses={"201": BaseResponse}) def endpoint_test(): return b"", 201 # pragma: no cover with test_app.test_client() as client: resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json["paths"]["/test"]["get"]["responses"]["201"] == { "description": "Custom description", "headers": { "location": { "description": "URL of the new resource", "schema": {"type": "string"} } }, "content": { # This content is coming from responses "application/json": { "schema": {"$ref": "#/components/schemas/BaseResponse"} }, # While this one comes from responses "text/plain": { "schema": {"type": "string"} } }, "links": { "dummy": { "description": "dummy link" } } } def test_none_responses_are_replicated_in_open_api(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True @test_app.get( "/test", responses={ "204": { "description": "Custom description", "headers": { "x-my-special-header": { "description": "Custom header", "schema": {"type": "string"} } }, "content": { "text/plain": { "schema": {"type": "string"} } }, "links": { "dummy": { "description": "dummy link" } } } } ) def endpoint_test(): return b"", 204 # pragma: no cover with test_app.test_client() as client: resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json["paths"]["/test"]["get"]["responses"]["204"] == { "description": "Custom description", "headers": { "x-my-special-header": { "description": "Custom header", "schema": {"type": "string"} } }, "content": { "text/plain": { "schema": {"type": "string"} } }, "links": { "dummy": { "description": "dummy link" } } } def test_responses_are_replicated_in_open_api2(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True @test_app.get( "/test", responses={ "201": { "description": "Custom description", "headers": { "location": { "description": "URL of the new resource", "schema": {"type": "string"} } }, "content": { "text/plain": { "schema": {"type": "string"} } }, "links": { "dummy": { "description": "dummy link" } } } } ) def endpoint_test(): return b"", 201 # pragma: no cover with test_app.test_client() as client: resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json["paths"]["/test"]["get"]["responses"]["201"] == { "description": "Custom description", "headers": { "location": { "description": "URL of the new resource", "schema": {"type": "string"} } }, "content": { "text/plain": { "schema": {"type": "string"} } }, "links": { "dummy": { "description": "dummy link" } } } def test_responses_without_content_are_replicated_in_open_api(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True @test_app.get( "/test", responses={ "201": { "description": "Custom description", "headers": { "location": { "description": "URL of the new resource", "schema": {"type": "string"} } }, "links": { "dummy": { "description": "dummy link" } } } } ) def endpoint_test(): return b"", 201 # pragma: no cover with test_app.test_client() as client: resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json["paths"]["/test"]["get"]["responses"]["201"] == { "description": "Custom description", "headers": { "location": { "description": "URL of the new resource", "schema": {"type": "string"} } }, "links": { "dummy": { "description": "dummy link" } } } class BaseRequest(BaseModel): """Base description""" test_int: int test_str: str class BaseRequestGeneric(BaseModel, Generic[T]): detail: T model_config = dict( openapi_extra={ "examples": { "Example 01": { "summary": "An example", "value": { "test_int": -1, "test_str": "negative", } }, "Example 02": { "externalValue": "https://example.org/examples/second-example.xml" }, "Example 03": { "$ref": "#/components/examples/third-example" } } } ) def test_body_examples_are_replicated_in_open_api(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True @test_app.post("/test") def endpoint_test(body: BaseRequestGeneric[BaseRequest]): return body.model_dump(), 200 with test_app.test_client() as client: client.post("/test", json={"detail": {"test_int": 1, "test_str": "s"}}) resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json["paths"]["/test"]["post"]["requestBody"] == { "content": { "application/json": { "examples": { "Example 01": {"summary": "An example", "value": {"test_int": -1, "test_str": "negative"}}, "Example 02": {"externalValue": "https://example.org/examples/second-example.xml"}, "Example 03": {"$ref": "#/components/examples/third-example"} }, "schema": {"$ref": "#/components/schemas/BaseRequestGeneric_BaseRequest_"} } }, "required": True } assert resp.json["components"]["schemas"]["BaseRequestGeneric_BaseRequest_"] == { "properties": {"detail": {"$ref": "#/components/schemas/BaseRequest"}}, "required": ["detail"], "title": "BaseRequestGeneric[BaseRequest]", "type": "object", } def test_form_examples(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True model_config = dict( openapi_extra={ "examples": { "Example 01": { "summary": "An example", "value": { "test_int": -1, "test_str": "negative", } } } } ) BaseRequestGeneric[BaseRequest].model_config = model_config @test_app.post("/test") def endpoint_test(form: BaseRequestGeneric[BaseRequest]): return form.model_dump(), 200 # pragma: no cover with test_app.test_client() as client: resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json["paths"]["/test"]["post"]["requestBody"] == { "content": { "multipart/form-data": { "schema": {"$ref": "#/components/schemas/BaseRequestGeneric_BaseRequest_"}, "examples": { "Example 01": {"summary": "An example", "value": {"test_int": -1, "test_str": "negative"}} } } }, "required": True } assert resp.json["components"]["schemas"]["BaseRequestGeneric_BaseRequest_"] == { "properties": {"detail": {"$ref": "#/components/schemas/BaseRequest"}}, "required": ["detail"], "title": "BaseRequestGeneric[BaseRequest]", "type": "object", } class BaseRequestBody(BaseModel): base: BaseRequest def test_body_with_complex_object(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True @test_app.post("/test") def endpoint_test(body: BaseRequestBody): return body.model_dump(), 200 # pragma: no cover with test_app.test_client() as client: resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert {"properties", "required", "title", "type"} == set( resp.json["components"]["schemas"]["BaseRequestBody"].keys()) class Detail(BaseModel): num: int class GenericResponse(BaseModel, Generic[T]): detail: T class ListGenericResponse(BaseModel, Generic[T]): items: list[GenericResponse[T]] def test_responses_with_generics(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True @test_app.get("/test", responses={"201": ListGenericResponse[Detail]}) def endpoint_test(): return b"", 201 # pragma: no cover with test_app.test_client() as client: resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json["paths"]["/test"]["get"]["responses"]["201"] == { "description": "Created", "content": { "application/json": { "schema": {"$ref": "#/components/schemas/ListGenericResponse_Detail_"} }, }, } schemas = resp.json["components"]["schemas"] detail = schemas["ListGenericResponse_Detail_"] assert detail["title"] == "ListGenericResponse[Detail]" assert detail["properties"]["items"]["items"]["$ref"] == "#/components/schemas/GenericResponse_Detail_" assert schemas["GenericResponse_Detail_"]["title"] == "GenericResponse[Detail]" class PathParam(BaseModel): type_name: str = Field(..., description="id for path", max_length=300, json_schema_extra={"deprecated": False, "example": "42"}) def test_path_parameter_object(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True @test_app.post("/test") def endpoint_test(path: PathParam): return path.model_dump(), 200 # pragma: no cover with test_app.test_client() as client: resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json["paths"]["/test"]["post"]["parameters"][0] == { "deprecated": False, "description": "id for path", "example": "42", "in": "path", "name": "type_name", "required": True, "schema": { "deprecated": False, "description": "id for path", "maxLength": 300, "example": "42", "title": "Type Name", "type": "string", }, } class QueryParam(BaseModel): count: int = Field(..., description="count of param", le=1000.0, json_schema_extra={"deprecated": True, "example": 100}) def test_query_parameter_object(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True @test_app.post("/test") def endpoint_test(query: QueryParam): return query.model_dump(), 200 # pragma: no cover with test_app.test_client() as client: resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json["paths"]["/test"]["post"]["parameters"][0] == { "deprecated": True, "description": "count of param", "example": 100, "in": "query", "name": "count", "required": True, "schema": { "deprecated": True, "description": "count of param", "maximum": 1000.0, "example": 100, "title": "Count", "type": "integer", }, } class HeaderParam(BaseModel): app_name: str = Field(None, description="app name") class CookieParam(BaseModel): app_name: str = Field(None, description="app name", json_schema_extra={"example": "aaa"}) def test_header_parameter_object(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True @test_app.post("/test") def endpoint_test(header: HeaderParam, cookie: CookieParam): print(header, cookie) # pragma: no cover with test_app.test_client() as client: resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json["paths"]["/test"]["post"]["parameters"][0] == { "description": "app name", "in": "header", "name": "app_name", "required": False, "schema": {"description": "app name", "title": "App Name", "type": "string", "default": None} } assert resp.json["paths"]["/test"]["post"]["parameters"][1] == { "description": "app name", "in": "cookie", "example": "aaa", "name": "app_name", "required": False, "schema": {"description": "app name", "example": "aaa", "title": "App Name", "type": "string", "default": None} } class Model(BaseModel): one: Optional[int] = Field(default=None) two: Optional[int] = Field(default=2) def test_default_none(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True @test_app.post("/test") def endpoint_test(body: Model): print([]) # pragma: no cover works = Model.model_json_schema()["properties"] assert works["one"]["default"] is None assert works["two"]["default"] == 2 breaks = test_app.api_doc["components"]["schemas"]["Model"]["properties"] assert breaks["two"]["default"] == 2 assert breaks["one"]["default"] is None def test_parameters_none(request): """Parameters key shouldn't exist.""" test_app = OpenAPI(request.node.name) @test_app.post("/test") def endpoint_test(): print([]) # pragma: no cover data = test_app.api_doc["paths"]["/test"]["post"] assert "parameters" not in data def test_deprecated_none(request): """Parameters key shouldn't exist.""" test_app = OpenAPI(request.node.name) @test_app.post("/test") def endpoint_test(): print([]) # pragma: no cover data = test_app.api_doc["paths"]["/test"]["post"] assert "deprecated" not in data class TupleModel(BaseModel): my_tuple: tuple[Literal["a", "b"], Literal["c", "d"]] def test_prefix_items(request): test_app = OpenAPI(request.node.name) test_app.config["TESTING"] = True @test_app.post("/test") def endpoint_test(body: TupleModel): print([]) # pragma: no cover schema = test_app.api_doc["paths"]["/test"]["post"]["requestBody"]["content"]["application/json"]["schema"] assert schema == {'$ref': '#/components/schemas/TupleModel'} components = test_app.api_doc["components"]["schemas"] assert components["TupleModel"] == {'properties': {'my_tuple': {'maxItems': 2, 'minItems': 2, 'prefixItems': [{'enum': ['a', 'b'], 'type': 'string'}, {'enum': ['c', 'd'], 'type': 'string'}], 'title': 'My Tuple', 'type': 'array'}}, 'required': ['my_tuple'], 'title': 'TupleModel', 'type': 'object'} def test_schema_bigint(request): max_nr = 9223372036854775807 obj = Schema(maximum=max_nr) assert obj.model_dump()["maximum"] == max_nr flask-openapi3-4.1.0/tests/test_openapi_extensions.py000066400000000000000000000031021475153525300230230ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/5/31 14:32 import pytest from flask_openapi3 import OpenAPI, APIBlueprint, APIView app = OpenAPI(__name__, openapi_extensions={ "x-google-endpoints": [ { "name": "my-cool-api.endpoints.my-project-id.cloud.goog", "allowCors": True } ] }) openapi_extensions = { "x-google-backend": { "address": "https://-.a.run.app", "protocol": "h2" } } app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client @app.get("/", openapi_extensions=openapi_extensions) def hello(): return "ok" # pragma: no cover # APIBlueprint api = APIBlueprint("book", __name__, url_prefix="/api") @api.get('/book', openapi_extensions=openapi_extensions) def get_book(): return {"code": 0, "message": "ok"} # pragma: no cover app.register_api(api) # APIView api_view = APIView() @api_view.route("/view/book") class BookListAPIView: @api_view.doc(openapi_extensions=openapi_extensions) def post(self): return "ok" # pragma: no cover app.register_api_view(api_view) def test_openapi(client): resp = client.get("/openapi/openapi.json") _json: dict = resp.json assert resp.status_code == 200 assert _json.get("x-google-endpoints") is not None assert _json["paths"]["/"]["get"]["x-google-backend"] is not None assert _json["paths"]["/api/book"]["get"]["x-google-backend"] is not None assert _json["paths"]["/view/book"]["post"]["x-google-backend"] is not None flask-openapi3-4.1.0/tests/test_options_in_viewfunc.py000066400000000000000000000026501475153525300232070ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/5/15 14:19 import pytest from flask_openapi3 import APIBlueprint, OpenAPI app = OpenAPI(__name__) app.config["TESTING"] = True def get_operation_id_for_path_callback(*, name: str, path: str, method: str) -> str: print(name, path, method) return name api = APIBlueprint( '/book', __name__, url_prefix='/api', operation_id_callback=get_operation_id_for_path_callback, ) @pytest.fixture def client(): client = app.test_client() return client @app.get('/book', endpoint='endpoint_get_book') def get_book(): return 'app_book' @api.post('/book', endpoint='endpoint_post_book') def create_book(): return 'api_book' # register api app.register_api(api) def test_openapi(client): resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json == app.api_doc assert resp.json["paths"]["/book"]["get"]["operationId"] == "get_book_book_get" # Default operation_id generator assert resp.json["paths"]["/api/book"]["post"]["operationId"] == "/book" # Custom callback operation_id def test_get(client): resp = client.get("/book") assert resp.text == 'app_book' assert 'endpoint_get_book' in app.view_functions.keys() def test_post(client): resp = client.post("/api/book") assert resp.text == 'api_book' assert '/book.endpoint_post_book' in app.view_functions.keys() flask-openapi3-4.1.0/tests/test_populate_by_name.py000066400000000000000000000054541475153525300224500ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2024/8/31 15:35 from typing import Sequence import pytest from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI app = OpenAPI(__name__) app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client class BookQuery(BaseModel): age: int author: str = Field(..., alias="author_name") model_config = {"populate_by_name": True} @app.get("/book", summary="get books") def get_book(query: BookQuery): """ get all books """ print(query) return "ok" class QueryModel(BaseModel): aliased_field: str = Field(alias="aliasedField") aliased_list_field: list[str] = Field(alias="aliasedListField") @app.get("/query-alias-test") def query_alias_test(query: QueryModel): assert query.aliased_field == "test" return "ok" class HeaderModel(BaseModel): Hello1: str = Field(..., alias="Hello2") model_config = {"populate_by_name": True} @app.get("/header") def get_book_header(header: HeaderModel): return header.model_dump(by_alias=True) class TupleModel(BaseModel): values: tuple[int, int] sequence: Sequence[int] = Field(alias="Sequence") model_config = {"populate_by_name": True} @app.get("/tuple-test") def tuple_test(query: TupleModel): assert query.values == (2, 2) return b"", 200 class AliasModel(BaseModel): aliased_field: str = Field(alias="aliasedField") @app.post("/form-alias-test") def alias_test(form: AliasModel): assert form.aliased_field == "test" return b"", 200 def test_header(client): headers = {"Hello2": "111"} resp = client.get("/header", headers=headers) print(resp.json) assert resp.status_code == 200 assert resp.json == headers def test_tuple_query(client): resp = client.get( "/tuple-test", query_string={"values": [2, 2], "sequence": [1, 2, 3]}, ) assert resp.status_code == 200 def test_form_alias(client): resp = client.post( "/form-alias-test", data={"aliasedField": "test"}, ) assert resp.status_code == 200 resp = client.post( "/form-alias-test", data={"aliased_field": "test"}, ) assert resp.status_code == 422 def test_query_alias(client): resp = client.get( "/query-alias-test", query_string={"aliasedField": "test", "aliasedListField": ["test"]}, ) assert resp.status_code == 200 resp = client.get( "/query-alias-test", data={"aliased_field": "test", "aliased_list_field": ["test"]}, ) assert resp.status_code == 422 def test_query_populate_by_name(client): resp = client.get("/book?age=1&author=aa") assert resp.status_code == 200 resp = client.get("/book?age=1&author_name=aa") assert resp.status_code == 200 flask-openapi3-4.1.0/tests/test_pydantic_calculated_fields.py000066400000000000000000000016361475153525300244450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2024/1/28 16:38 from functools import cached_property import pytest from pydantic import BaseModel, Field, computed_field from flask_openapi3 import OpenAPI app = OpenAPI(__name__) app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client class User(BaseModel): firstName: str = Field(title="First Name") lastName: str @computed_field(title="Display Name") @cached_property def display_name(self) -> str: return f"{self.firstName} {self.lastName}" # pragma: no cover @app.get("/user", responses={200: User}) def get_book(): return "ok" # pragma: no cover def test_openapi(client): resp = client.get("/openapi/openapi.json") import pprint pprint.pprint(resp.json) assert resp.json["components"]["schemas"]["User"]["properties"].get("display_name") is not None flask-openapi3-4.1.0/tests/test_pydantic_custom_root_types.py000066400000000000000000000041061475153525300246120ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/2/27 16:53 from typing import Any import pytest from pydantic import BaseModel, RootModel from flask_openapi3 import OpenAPI, Tag app = OpenAPI(__name__) app.config["TESTING"] = True class Sellout(BaseModel): a: str b: int class SelloutList(RootModel): root: list[Sellout] class SelloutDict(RootModel): root: dict[str, Sellout] class SelloutDict2(RootModel): root: dict[Any, Any] class SelloutDict3(BaseModel): model_config = { "extra": "allow", } @pytest.fixture def client(): client = app.test_client() return client @app.post('/api/v1/sellouts', tags=[Tag(name='Sellout', description='Loren.')], responses={'200': SelloutList} ) def post_sellout(body: SelloutList): print(body) return body.model_dump_json() @app.post('/api/v2/sellouts', tags=[Tag(name='Sellout', description='Loren.')], responses={'200': SelloutDict} ) def post_sellout2(body: SelloutDict): print(body) return body.model_dump_json() @app.post('/api/v3/sellouts', tags=[Tag(name='Sellout', description='Loren.')] ) def post_sellout3(body: SelloutDict2): print(body) return body.model_dump_json() @app.post('/api/v4/sellouts', tags=[Tag(name='Sellout', description='Loren.')] ) def post_sellout4(body: SelloutDict3): print(body) return body.model_dump_json() def test_v1_sellouts(client): resp = client.post("/api/v1/sellouts", json=[{"a": "string", "b": 0}]) assert resp.status_code == 200 def test_v2_sellouts(client): resp = client.post("/api/v2/sellouts", json={"additionalProp1": {"a": "string", "b": 0}}) assert resp.status_code == 200 def test_v3_sellouts(client): resp = client.post("/api/v3/sellouts", json={"a": 11, "b": 23, "c": {"cc": 33, "ccc": 333}}) assert resp.status_code == 200 def test_v4_sellouts(client): resp = client.post("/api/v4/sellouts", json={"a": 11, "b": 23, "c": {"cc": 33, "ccc": 333}}) assert resp.status_code == 200 flask-openapi3-4.1.0/tests/test_request.py000066400000000000000000000062351475153525300206130ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/9/2 15:35 from enum import Enum from functools import wraps from typing import Optional import pytest from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI, FileStorage, RawModel app = OpenAPI(__name__) app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client class TypeEnum(str, Enum): A = "A" B = "B" class BookForm(BaseModel): file: FileStorage files: list[FileStorage] string: str string_list: list[str] class BookQuery(BaseModel): age: list[int] book_type: Optional[TypeEnum] = None class BookBody(BaseModel): age: int class BookCookie(BaseModel): token: Optional[str] = None token_type: Optional[TypeEnum] = None class BookHeader(BaseModel): Hello1: str = Field("what's up", max_length=12, description="sds") # required hello2: str = Field(..., max_length=12, description="sds") api_key: str = Field(..., description="API Key") api_type: Optional[TypeEnum] = None x_hello: str = Field(..., max_length=12, description='Header with alias to support dash', alias="x-hello") def decorator(func): @wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @app.get("/query") @decorator def api_query(query: BookQuery): print(query) return {"code": 0, "message": "ok"} @app.post("/form") def api_form(form: BookForm): print(form) return {"code": 0, "message": "ok"} @app.post("/body") def api_error_json(body: BookBody): print(body) # pragma: no cover @app.get("/header") def get_book(header: BookHeader): return header.model_dump(by_alias=True) @app.post("/cookie") def api_cookie(cookie: BookCookie): print(cookie) return {"code": 0, "message": "ok"} class BookRaw(RawModel): mimetypes = ["text/csv", "application/json"] @app.post("/raw") def api_raw(raw: BookRaw): # raw equals to flask.request assert raw.data == b"raw" assert raw.mimetype == "text/plain" return "ok" def test_query(client): r = client.get("/query?age=1") print(r.json) assert r.status_code == 200 def test_form(client): from io import BytesIO data = { "file": (BytesIO(b"post-data"), "filename"), "files": [(BytesIO(b"post-data"), "filename"), (BytesIO(b"post-data"), "filename")], "string": "a", "string_list": ["a", "b", "c"] } r = client.post("/form", data=data, content_type="multipart/form-data") assert r.status_code == 200 def test_error_json(client): r = client.post("/body", json="{age: 1}") assert r.status_code == 422 def test_cookie(client): r = client.post("/cookie") print(r.json) assert r.status_code == 200 def test_header(client): headers = {"Hello1": "111", "hello2": "222", "api_key": "333", "api_type": "A", "x-hello": "444"} resp = client.get("/header", headers=headers) print(resp.json) assert resp.status_code == 200 assert resp.json == headers def test_raw(client): resp = client.post("/raw", data="raw", headers={"Content-Type": "text/plain"}) assert resp.status_code == 200 flask-openapi3-4.1.0/tests/test_response.py000066400000000000000000000027501475153525300207570ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/9 11:23 from http import HTTPStatus import pytest from pydantic import BaseModel, Field from flask_openapi3 import OpenAPI, APIBlueprint app = OpenAPI(__name__) app.config["TESTING"] = True api = APIBlueprint("/api", __name__, url_prefix="/api") class BookResponse(BaseModel): code: int = Field(0, description="Status Code") message: str = Field("ok", description="Exception Information") class BookPath(BaseModel): bid: int = Field(..., description="book id") @pytest.fixture def client(): client = app.test_client() return client @app.get( "/book/", responses={ HTTPStatus.OK: BookResponse, "201": BookResponse, 202: {"content": {"text/html": {"schema": {"type": "string"}}}}, 204: None, "422": {"description": "validation error"} } ) def get_book(path: BookPath): print(path) # pragma: no cover @api.get("/book", responses={ HTTPStatus.OK: BookResponse, "201": BookResponse, 204: None }) def get_api_book(): return {"code": 0, "message": "ok"} # pragma: no cover app.register_api(api) def test_openapi(client): resp = client.get("/openapi/openapi.json") _json = resp.json assert resp.status_code == 200 assert _json["paths"]["/book/{bid}"]["get"]["responses"].keys() - ["200", "201", "202", "204"] == {"422"} assert _json["paths"]["/api/book"]["get"]["responses"].keys() - ["200", "201", "202", "204"] == set() flask-openapi3-4.1.0/tests/test_restapi.py000066400000000000000000000143361475153525300205730ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/5/6 16:38 from __future__ import annotations import json from http import HTTPStatus from typing import Optional import pytest from flask import Response from pydantic import BaseModel, RootModel, Field from flask_openapi3 import ExternalDocumentation from flask_openapi3 import Info, Tag from flask_openapi3 import OpenAPI info = Info(title='book API', version='1.0.0') jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } security_schemes = {"jwt": jwt} class NotFoundResponse(BaseModel): code: int = Field(-1, description="Status Code") message: str = Field("Resource not found!", description="Exception Information") def get_operation_id_for_path_callback(*, name: str, path: str, method: str) -> str: print(name, path, method) return name app = OpenAPI( __name__, info=info, security_schemes=security_schemes, responses={"404": NotFoundResponse}, operation_id_callback=get_operation_id_for_path_callback, ) app.config["TESTING"] = True security = [{"jwt": []}] book_tag = Tag(name='book', description='Book') class BookQuery(BaseModel): age: Optional[int] = Field(None, description='Age') class BookBody(BaseModel): age: Optional[int] = Field(..., ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') class BookPath(BaseModel): bid: int = Field(..., description='book id') class BookBodyWithID(BaseModel): bid: int = Field(..., description='book id') age: Optional[int] = Field(None, ge=2, le=4, description='Age') author: str = Field(None, min_length=2, max_length=4, description='Author') class BaseResponse(BaseModel): code: int = Field(0, description="Status Code") message: str = Field("ok", description="Exception Information") class BookListResponseV1(BaseResponse): data: list[BookBodyWithID] = Field(..., description="All the books") class BookListResponseV2(BaseModel): books: list[BookBodyWithID] = Field(...) class BookListResponseV3(RootModel): root: list[BookBodyWithID] class BookResponse(BaseModel): code: int = Field(0, description="Status Code") message: str = Field("ok", description="Exception Information") data: BookBodyWithID @pytest.fixture def client(): client = app.test_client() return client @app.get( '/book/', tags=[book_tag], operation_id="get_book_id", external_docs=ExternalDocumentation( url="https://www.openapis.org/", description="Something great got better, get excited!"), responses={"200": BookResponse}, security=security ) def get_book(path: BookPath): """Get a book to get some book by id, like: http://localhost:5000/book/3 """ if path.bid == 4: return NotFoundResponse().model_dump(), 404 @app.get('/book', tags=[book_tag], responses={"200": BookListResponseV1}) def get_books(query: BookBody): """get books to get all books """ assert query.age == 3 assert query.author == 'joy' return { "code": 0, "message": "ok", "data": [ {"bid": 1, "age": query.age, "author": "b1"}, {"bid": 2, "age": query.age, "author": "b2"} ] } @app.get('/book_v2', tags=[book_tag], responses={"200": BookListResponseV2}) def get_books_v2(query: BookBody): """get books to get all books (v2) """ assert query.age == 3 assert query.author == 'joy' return { "books": [ {"bid": 1, "age": query.age, "author": "b1"}, {"bid": 2, "age": query.age, "author": "b2"} ] } @app.get('/book_v3', tags=[book_tag], responses={"200": BookListResponseV3}) def get_books_v3(query: BookBody): """get books to get all books (v3) """ assert query.age == 3 assert query.author == 'joy' books = [ {"bid": 1, "age": query.age, "author": "b1"}, {"bid": 2, "age": query.age, "author": "b2"} ] # A `list` have to be converted to json-format `str` returned as a `Response` object, # because flask doesn't support returning a `list` as a response return Response(json.dumps(books), status=200, headers={'Content-Type': 'application/json'}) @app.post('/book', tags=[book_tag], responses={"200": BaseResponse}) def create_book(body: BookBody): assert body.age == 3 return {"code": 0, "message": "ok"}, HTTPStatus.OK @app.put('/book/', tags=[book_tag]) def update_book(path: BookPath, body: BookBody): assert path.bid == 1 assert body.age == 3 return {"code": 0, "message": "ok"} @app.patch('/book/', tags=[book_tag], doc_ui=False) def update_book1(path: BookPath, body: BookBody): assert path.bid == 1 assert body.age == 3 return {"code": 0, "message": "ok"} @app.delete('/book/', tags=[book_tag], responses={"200": BaseResponse}) def delete_book(path: BookPath): assert path.bid == 1 return {"code": 0, "message": "ok"} @app.delete('/book_no_response/', tags=[book_tag], responses={"204": None}) def delete_book_no_response(path: BookPath): assert path.bid == 1 return b'', 204 def test_openapi(client): resp = client.get("/openapi/openapi.json") print(resp.json) assert resp.status_code == 200 assert resp.json == app.api_doc def test_get(client): resp = client.get("/book?age=3&author=joy") assert resp.status_code == 200 resp_v2 = client.get("/book_v2?age=3&author=joy") assert resp_v2.status_code == 200 resp_v3 = client.get("/book_v3?age=3&author=joy") assert resp_v3.status_code == 200 def test_get_by_id_4(client): resp = client.get("/book/4?age=3&author=joy") assert resp.status_code == 404 def test_post(client): resp = client.post("/book", json={"age": 3}) assert resp.status_code == 200 def test_put(client): resp = client.put("/book/1", json={"age": 3}) assert resp.status_code == 200 def test_patch(client): resp = client.patch("/book/1", json={"age": 3}) assert resp.status_code == 200 def test_delete(client): resp = client.delete("/book/1") assert resp.status_code == 200 def test_delete_no_response(client): resp = client.delete("/book_no_response/1") assert resp.status_code == 204 flask-openapi3-4.1.0/tests/test_restapi_with_doc_prefix.py000066400000000000000000000017541475153525300240300ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/5/6 16:38 from __future__ import annotations import pytest from pydantic import BaseModel, Field from flask_openapi3 import Info from flask_openapi3 import OpenAPI info = Info(title='book API', version='1.0.0') jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } security_schemes = {"jwt": jwt} class NotFoundResponse(BaseModel): code: int = Field(-1, description="Status Code") message: str = Field("Resource not found!", description="Exception Information") doc_prefix = '/v1/openapi' app = OpenAPI( __name__, info=info, doc_prefix=doc_prefix, security_schemes=security_schemes, responses={"404": NotFoundResponse} ) app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client def test_openapi(client): resp = client.get(f"{doc_prefix}/openapi.json") assert resp.status_code == 200 assert resp.json == app.api_doc flask-openapi3-4.1.0/tests/test_security.py000066400000000000000000000022321475153525300207630ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/9/2 15:20 import pytest from flask_openapi3 import OpenAPI # Basic Authentication Sample basic = { "type": "http", "scheme": "basic" } # JWT Bearer Sample jwt = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } # API Key Sample api_key = { "type": "apiKey", "name": "api_key", "in": "header" } # Implicit OAuth2 Sample oauth2 = { "type": "oauth2", "flows": { "implicit": { "authorizationUrl": "https://example.com/api/oauth/dialog", "scopes": { "write:pets": "modify pets in your account", "read:pets": "read your pets" } } } } security_schemes = {"jwt": jwt, "api_key": api_key, "oauth2": oauth2, "basic": basic} security = [{"jwt": []}] app = OpenAPI(__name__, security_schemes=security_schemes) app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client def test_openapi(client): resp = client.get("/openapi/openapi.json") print(resp.json) assert resp.status_code == 200 assert resp.json == app.api_doc flask-openapi3-4.1.0/tests/test_server.py000066400000000000000000000020401475153525300204170ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2024/11/10 12:17 from pydantic import ValidationError from flask_openapi3 import Server, ServerVariable def test_server_variable(): Server( url="http://127.0.0.1:5000", variables=None ) try: variables = {"one": ServerVariable(default="one", enum=[])} Server( url="http://127.0.0.1:5000", variables=variables ) error = 0 except ValidationError: error = 1 assert error == 1 try: variables = {"one": ServerVariable(default="one")} Server( url="http://127.0.0.1:5000", variables=variables ) error = 0 except ValidationError: error = 1 assert error == 0 try: variables = {"one": ServerVariable(default="one", enum=["one", "two"])} Server( url="http://127.0.0.1:5000", variables=variables ) error = 0 except ValidationError: error = 1 assert error == 0 flask-openapi3-4.1.0/tests/test_str_body.py000066400000000000000000000010641475153525300207430ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/5/22 8:05 import pytest from pydantic import BaseModel from flask_openapi3 import OpenAPI app = OpenAPI(__name__) app.config["TESTING"] = True class MyModel(BaseModel): text: str @pytest.fixture def client(): client = app.test_client() return client @app.post('/path/') def create_book1(body: MyModel): return body.text def test_post(client): my_model = MyModel(text='1') resp = client.post("/path/", json=my_model.model_dump_json()) assert resp.status_code == 200 flask-openapi3-4.1.0/tests/test_summary.py000066400000000000000000000023311475153525300206110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/1/1 16:54 import pytest from flask_openapi3 import Info from flask_openapi3 import OpenAPI info = Info(title='book API', version='1.0.0') app = OpenAPI(__name__, info=info) app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client @app.get('/book', summary='new summary', description='new description') def get_book(): """Get a book to get some book by id, like: http://localhost:5000/book/3 """ return {"code": 0, "message": "ok"} # pragma: no cover @app.get('/book2', description='new description') def get_book2(): """Get a book to get some book by id, like: http://localhost:5000/book/3 """ return {"code": 0, "message": "ok"} # pragma: no cover def test_openapi(client): resp = client.get("/openapi/openapi.json") _json = resp.json assert resp.status_code == 200 assert _json['paths']['/book']['get']['summary'] == 'new summary' assert _json['paths']['/book']['get']['description'] == 'new description' assert _json['paths']['/book2']['get']['summary'] == 'Get a book' assert _json['paths']['/book2']['get']['description'] == 'new description' flask-openapi3-4.1.0/tests/test_tags.py000066400000000000000000000017141475153525300200560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/12/19 10:34 import pytest from flask_openapi3 import Info, Tag from flask_openapi3 import OpenAPI, APIBlueprint info = Info(title="book API", version="1.0.0") app = OpenAPI(__name__, info=info) app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client api1 = APIBlueprint("book1", __name__) @api1.get('/book', tags=[Tag(name="book")]) def get_book(): ... # pragma: no cover api2 = APIBlueprint("book2", __name__) @api2.get('/book2', tags=[Tag(name="book")]) def get_book2(): ... # pragma: no cover app.register_api(api1) app.register_api(api2) def test_openapi(client): resp = client.get("/openapi/openapi.json") _json = resp.json assert resp.status_code == 200 tags = _json["tags"] news_tags = [] for tag in tags: if tag not in news_tags: news_tags.append(tag) assert news_tags == tags flask-openapi3-4.1.0/tests/test_trail_slash.py000066400000000000000000000024351475153525300214260ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/5/5 13:28 import pytest from flask_openapi3 import Info from flask_openapi3 import OpenAPI, APIBlueprint info = Info(title='book API', version='1.0.0') app = OpenAPI(__name__, info=info) app.config["TESTING"] = True api1 = APIBlueprint('book1', __name__, url_prefix='/api/v1/book1') api2 = APIBlueprint('book2', __name__, url_prefix='/api/v1/book2') @pytest.fixture def client(): client = app.test_client() return client @api1.get('/') def get_book(): return 'with slash' @api2.get('') def get_book2(): return 'without slash' app.register_api(api1) app.register_api(api2) def test_with_slash1(client): resp = client.get("/api/v1/book1/") assert resp.status_code == 200 def test_with_slash2(client): resp = client.get("/api/v1/book1") assert resp.status_code == 308 def test_without_slash1(client): resp = client.get("/api/v1/book2/") assert resp.status_code == 404 def test_without_slash2(client): resp = client.get("/api/v1/book2") assert resp.status_code == 200 def test_openapi(client): resp = client.get("/openapi/openapi.json") _json = resp.json assert _json['paths'].get('/api/v1/book1/') is not None assert _json['paths'].get('/api/v1/book2') is not None flask-openapi3-4.1.0/tests/test_url_prefix.py000066400000000000000000000024231475153525300212750ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2025/1/15 9:58 import pytest from flask_openapi3 import APIBlueprint, OpenAPI, APIView app = OpenAPI(__name__, ) app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client api1 = APIBlueprint("/book1", __name__, url_prefix="/api") api2 = APIBlueprint("/book2", __name__) @api1.get("/book") def create_book1(): return "ok" @api2.get("/book") def create_book2(): return "ok" app.register_api(api1, url_prefix="/api1") app.register_api(api2, url_prefix="/api2") api_view1 = APIView(url_prefix="/api") api_view2 = APIView() @api_view1.route("/book") class BookAPIView: @api_view1.doc(summary="get book") def get(self): return "ok" @api_view2.route("/book") class BookAPIView2: @api_view2.doc(summary="get book") def get(self, ): return "ok" app.register_api_view(api_view1, url_prefix="/api3") app.register_api_view(api_view2, url_prefix="/api4") def test_openapi(client): resp = client.get("/openapi/openapi.json") _json = resp.json assert "/api1/book" in _json["paths"].keys() assert "/api2/book" in _json["paths"].keys() assert "/api3/book" in _json["paths"].keys() assert "/api4/book" in _json["paths"].keys() flask-openapi3-4.1.0/tests/test_utils.py000066400000000000000000000003631475153525300202570ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2022/12/19 10:34 from flask_openapi3.utils import normalize_name def test_normalize_name(): assert "List-Generic.Response_Detail_" == normalize_name("List-Generic.Response[Detail]") flask-openapi3-4.1.0/tests/test_validation_error.py000066400000000000000000000042371475153525300224660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # @Author : llc # @Time : 2023/7/21 10:32 import pytest from flask import make_response, current_app from pydantic import BaseModel, Field, ValidationError from flask_openapi3 import OpenAPI class GenericTracebackError(BaseModel): location: str = Field(..., json_schema_extra={"example": "GenericError.py"}) line: int = Field(..., json_schema_extra={"example": 1}) method: str = Field(..., json_schema_extra={"example": "GenericError"}) message: str = Field(..., json_schema_extra={"example": "400:Bad Request"}) class ValidationErrorModel(BaseModel): code: str message: str more_info: list[GenericTracebackError] = Field(..., json_schema_extra={"example": [GenericTracebackError( location="GenericError.py", line=1, method="GenericError", message="400:Bad Request")]}) def validation_error_callback(e: ValidationError): validation_error_object = ValidationErrorModel( code="400", message=e.json(), more_info=[GenericTracebackError( location="GenericError.py", line=1, method="GenericError", message="400:Bad Request" )] ) response = make_response(validation_error_object.model_dump_json()) response.headers["Content-Type"] = "application/json" response.status_code = getattr(current_app, "validation_error_status", 422) return response app = OpenAPI( __name__, validation_error_status=400, validation_error_model=ValidationErrorModel, validation_error_callback=validation_error_callback ) app.config["TESTING"] = True @pytest.fixture def client(): client = app.test_client() return client class BookQuery(BaseModel): age: int = Field(None, description="Age") @app.get("/query") def api_query(query: BookQuery): print(query) # pragma: no cover def test_openapi(client): resp = client.get("/openapi/openapi.json") assert resp.status_code == 200 assert resp.json["components"]["schemas"].get("GenericTracebackError") is not None def test_api_query(client): resp = client.get("/query?age=abc") print(resp.json) assert resp.status_code == 400