pax_global_header00006660000000000000000000000064147354423620014524gustar00rootroot0000000000000052 comment=cbac7eeb63af846129d22cd27f2b5c9038cc9944 django-tables2-2.7.5/000077500000000000000000000000001473544236200143335ustar00rootroot00000000000000django-tables2-2.7.5/.coveragerc000066400000000000000000000001371473544236200164550ustar00rootroot00000000000000[run] source = django_tables2 tests branch = true [html] directory = reports/htmlcov django-tables2-2.7.5/.github/000077500000000000000000000000001473544236200156735ustar00rootroot00000000000000django-tables2-2.7.5/.github/dependabot.yml000066400000000000000000000001661473544236200205260ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" django-tables2-2.7.5/.github/workflows/000077500000000000000000000000001473544236200177305ustar00rootroot00000000000000django-tables2-2.7.5/.github/workflows/ci.yml000066400000000000000000000044541473544236200210550ustar00rootroot00000000000000name: CI on: [push, pull_request] jobs: pre-commit: runs-on: ubuntu-latest steps: - uses: actions/setup-python@v5 with: python-version: "3.11" - uses: actions/checkout@v4 - run: pip install pre-commit - run: pre-commit run --show-diff-on-failure --all-files tests: runs-on: ubuntu-latest strategy: matrix: python-version: [3.9, "3.10", 3.11, 3.12] # 3.13 can be supported if lxml supports 3.13 django-version: [4.2, 5.0, 5.1, master] exclude: # Django 4.2 - python-version: 3.12 django-version: 4.2 - python-version: 3.13 django-version: 4.2 # Django 5.0 - python-version: 3.9 django-version: 5.0 - python-version: 3.13 django-version: 5.0 # Django 5.1 - python-version: 3.9 django-version: 5.1 # master - python-version: 3.9 django-version: master steps: - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - uses: actions/checkout@v4 - uses: actions/cache@v4.2.0 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - run: python -m pip install Django==${{ matrix.django-version }} if: matrix.django-version != 'master' - run: python -m pip install https://github.com/django/django/archive/master.tar.gz if: matrix.django-version == 'master' - run: | python -m pip install coverage python -m pip install -r requirements/common.pip - run: coverage run --source=django_tables2 manage.py test docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.11" cache: 'pip' cache-dependency-path: | docs/requirements.txt common/requirements.txt - name: Install and build run: | cd docs python -m pip install -r requirements.txt make html django-tables2-2.7.5/.gitignore000066400000000000000000000004311473544236200163210ustar00rootroot00000000000000*.pyc /.env /reports /*.sublime-* /*.komodoproject /*.tmproj /*.egg-info/ /*.egg /.tox /.coverage /MANIFEST /dist/ /build/ /docs/_build/ /docs/pages/CHANGELOG.md /example/database.sqlite /example/.env /report.pylint .cache/ .python-version .idea *.sw[po] pip-wheel-metadata .vscodedjango-tables2-2.7.5/.pre-commit-config.yaml000066400000000000000000000015351473544236200206200ustar00rootroot00000000000000repos: - repo: https://github.com/psf/black rev: 24.10.0 hooks: - id: black language_version: python3.11 - repo: https://github.com/asottile/pyupgrade rev: v3.19.0 hooks: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks rev: v2.14.0 hooks: - id: pretty-format-toml args: [--autofix] - repo: https://github.com/pycqa/isort rev: 5.13.2 hooks: - id: isort - repo: https://github.com/PyCQA/flake8 rev: 7.1.1 hooks: - id: flake8 - repo: https://github.com/adamchainz/django-upgrade rev: "1.22.1" hooks: - id: django-upgrade args: [--target-version, "4.2"] - repo: https://github.com/asottile/pyupgrade rev: v3.19.0 hooks: - id: pyupgrade args: [--py39-plus] django-tables2-2.7.5/.readthedocs.yaml000066400000000000000000000006251473544236200175650ustar00rootroot00000000000000# Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py python: install: - requirements: docs/requirements.txt django-tables2-2.7.5/.readthedocs.yml000066400000000000000000000007631473544236200174270ustar00rootroot00000000000000# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # Optionally declare the Python requirements required to build your docs python: install: - requirements: docs/requirements.txtdjango-tables2-2.7.5/CHANGELOG.md000066400000000000000000001272011473544236200161470ustar00rootroot00000000000000# Change log ## 2.7.5 (2025-01-02) - Update django.po for Ukrainian 🇺🇦 ([#934](https://github.com/jieter/django-tables2/pull/934)) by [@DmytroLitvinov](https://github.com/DmytroLitvinov) ## 2.7.4 & 2.7.3 (2024-12-23) Correct packaging mistakes: - Remove upper bound for `python_requires` ## 2.7.2 (2024-12-23) - Add python_requires to setup.py ([#982](https://github.com/jieter/django-tables2/pull/982)) Fixes: [#980](https://github.com/jieter/django-tables2/issues/980) ## 2.7.1 (2024-12-21) - Add TimeColumn to built-in columns ([#937](https://github.com/jieter/django-tables2/pull/937)) by [@philipphanemann](https://github.com/philipphanemann) Add support to pass args/kwargs to callables from Accessor ([#940](https://github.com/jieter/django-tables2/pull/940)), fixes: [#939](https://github.com/jieter/django-tables2/issues/939) by [@JordanHyatt](https://github.com/JordanHyatt) - Drop support for Django 3.2, 4.1 and python 3.8 - Add support for Django 5.1 and python 3.12 - Explicitly set `Column.accessor` when binding a column to allow column methods like `order_by` to use its value ([#979](https://github.com/jieter/django-tables2/pull/979)) ## 2.7.0 (2023-12-05) - Update docs dependencies; fix docs search; add readthedocs.yaml ([#935](https://github.com/jieter/django-tables2/pull/935)) - Add example on how to use `Accessor` to documentation in `custom-data.rst` ([#917](https://github.com/jieter/django-tables2/pull/917)) by [@ruddra](https://github.com/ruddra) - Use pre-commit in ci, add isort, flake8, pyupgrade ([#932](https://github.com/jieter/django-tables2/pull/932)) - Add support for Django 5.0 and Python 3.12 ([#930](https://github.com/jieter/django-tables2/pull/930)) - Add missing `{% load l10n %}` in templates ([#919](https://github.com/jieter/django-tables2/pull/919)) by [@tvanekeris](https://github.com/tvanekeris) ## 2.6.0 (2023-06-27) - Fix bootstrap5-responsive template to extend bootstrap5 ([#909](https://github.com/jieter/django-tables2/pull/909)) by [@mschoettle](https://github.com/mschoettle) - Add support for django 4.2 - Drop python 3.7 and django 4.0 support ([#920](https://github.com/jieter/django-tables2/pull/920)) ## 2.5.3 (2023-03-05) - Assign request to table before anything else in `RequestConfig.configure()` ([#888](https://github.com/jieter/django-tables2/pull/888)) fixes: [#865](https://github.com/jieter/django-tables2/issues/865) - Add type hints to get_context_data ([#895](https://github.com/jieter/django-tables2/pull/895)) by [@mschoettle](https://github.com/mschoettle) - Document hidden table attributes setting ([#897](https://github.com/jieter/django-tables2/pull/897)) by [@mschoettle](https://github.com/mschoettle) - Fix building of the docs ([#900](https://github.com/jieter/django-tables2/pull/900)) by [@danielroseman](https://github.com/danielroseman) - Add template bootstrap5-responsive.html ([#896](https://github.com/jieter/django-tables2/pull/896)) by [@mschoettle](https://github.com/mschoettle) ## 2.5.2 (2023-02-07) - Assign `request` to table before anything else in `RequestConfig.configure()` ([#888](https://github.com/jieter/django-tables2/pull/888)) fixes: [#865](https://github.com/jieter/django-tables2/issues/865) ## 2.5.1 (2023-01-07) - `TableMixinBase`: implement `get_paginate_by` ([#811](https://github.com/jieter/django-tables2/pull/811)) by [@Alirezaja1384](https://github.com/Alirezaja1384) ## 2.5.0 (2022-12-27) - Dropped support for python 3.6, added support for python 3.11 - Add django_tables2/bootstrap4-responsive.html ([#874](https://github.com/jieter/django-tables2/pull/874)) by [@botlabsDev](https://github.com/botlabsDev) - Pass record/value to `LinkColumn`'s attrs callables too ([#852](https://github.com/jieter/django-tables2/pull/852)) by [@wsldankers](https://github.com/wsldankers) - Add template `bootstrap5.html` to support bootstrap 5 ([#880](https://github.com/jieter/django-tables2/pull/880), fixes [#796](https://github.com/jieter/django-tables2/issues/796) ## 2.4.1 (2021-10-04) - Add Persian (Farsi) locale ([#806](https://github.com/jieter/django-tables2/pull/806)) by [@Alirezaja1384](https://github.com/jieter/django-tables2/commits?author=Alirezaja1384) - Improved error message if openpyxl is not installed ([#816](https://github.com/jieter/django-tables2/pull/816)) - Use correct mime type for xlsx (fixes: [#810](https://github.com/jieter/django-tables2/issues/810)) - Add support for django 4.0, drop support for python 3.5 ([#822](https://github.com/jieter/django-tables2/issues/822)) ## 2.4.0 (2021-05-15) - Add support for django 3.2 and python 3.9, drop support for django 3.0 - Add Django 3.0 and 3.1 trove classifiers [#803](https://github.com/jieter/django-tables2/pull/803) by [@Asday](https://github.com/Asday) - Strip leading and trailing whitespace from TemplateColumn.value() [#794](https://github.com/jieter/django-tables2/pull/794) by [@jeremystretch](https://github.com/jeremystretch) - Providing link for django-bootstrap3 [#793](https://github.com/jieter/django-tables2/pull/793) by [@TareqMonwer](https://github.com/TareqMonwer) - Fix for crash on windows while deleting temp file [#788](https://github.com/jieter/django-tables2/pull/788) ## 2.3.4 (2021-01-10) - Removed deprecation warnings with django==3.1 regarding `JSONField` [#785](https://github.com/jieter/django-tables2/pull/785) ## 2.3.3 (2020-10-29) - Use `table.default` for empty `ManyToMany` relations ([#773](https://github.com/jieter/django-tables2/pull/773)) fixes: [#769](https://github.com/jieter/django-tables2/issues/769) - Pass record/value to `CheckboxColumn`'s `attrs` callables too ([#774](https://github.com/jieter/django-tables2/pull/774)), fixes: [#762](https://github.com/jieter/django-tables2/issues/762) ## 2.3.2 (2020-10-10) - Fix popping the extra_context of TemplateColumn [#767](https://github.com/jieter/django-tables2/pull/767) by [@bernhardmiller](https://github.com/bernhardmiller) - Fix typo for the translation of the word 'next' in greek [#759]](https://github.com/jieter/django-tables2/pull/759) by [@orfeasa](https://github.com/orfeasa) - Add `format_html` import to prevent `NameError` [#752](https://github.com/jieter/django-tables2/pull/752) by [@MBfromOK](https://github.com/MBfromOK) - Fixed Russian translation [#768](https://github.com/jieter/django-tables2/pull/768) by [@Real-Gecko](https://github.com/Real-Gecko) ## 2.3.1 (2020-04-02) - Fixed the `LazyPaginator` in a simpler more predictable way: an attempt to show a non-existent page, shows the first page. [#743](https://github.com/jieter/django-tables2/pull/743) ## 2.3.0 (2020-03-31) - Add ability to pass `tablib.Dataset` `kwargs` via `TableExport` and `ExportMixin` [#720](https://github.com/jieter/django-tables2/pull/720) by [@powderflask](https://github.com/powderflask) - Drop django==2.1 support, add optional tablib requirements [#738](https://github.com/jieter/django-tables2/pull/738) - Short-circuit `Accessor.resolve()` if the context contains the exact accessor [#722](https://github.com/jieter/django-tables2/pull/722), fixes [#717](https://github.com/jieter/django-tables2/issues/717) - Fixed yaml export [#732](https://github.com/jieter/django-tables2/pull/732) by [@sg3-141-592](https://githug.com/sg3-141-592) - Made Table docstring visible in docs [#742](https://github.com/jieter/django-tables2/pull/742) - Removed the TableBase construct in favor of using the `metaclass` keyword argument, as all supported python versions support it. [#742](https://github.com/jieter/django-tables2/pull/742) - `LazyPaginator` with non-existent page number should not result in a crash [#741](https://github.com/jieter/django-tables2/pull/741) ## 2.2.1 (2019-11-20) - Fix backwards-compatibility with legacy separators in order_by clauses ([#715](https://github.com/jieter/django-tables2/pull/715) by [@federicobond](https://github.com/federicobond)) ## 2.2.0 (2019-11-18) - Use `__` as accessor-separator, add `linkify` Meta option [#702](https://github.com/jieter/django-tables2/pull/702)). This will currently emit a warning but falls back to using `.` as separator. The next major version will raise a `ValueError` if used with `.` as separator. - Add request attribute to table instance ([#705](https://github.com/jieter/django-tables2/pull/705) by [@rubickcz](https://github.com/rubickcz)). - Append ellipsis for `LazyPaginator` if not on last page ([#707](https://github.com/jieter/django-tables2/pull/707) by [@tuky](https://github.com/tuky)) ## 2.1.1 (2019-09-23) - Made `ManyToManyColumn` use `table.default` instead of a local value [#680](https://github.com/jieter/django-tables2/pull/680) by [@srtab](https://github.com/srtab) - Removed invalid scope attribute in `` element of `bootstrap4.html`. [#691](https://github.com/jieter/django-tables2/pull/691) by [@vlt](https://github.com/vlt) - Fixed an issue with incorrectly disabled pagination where `SingleTableMixin` was not used together with `ListView` [#678](https://github.com/jieter/django-tables2/pull/678) by [@nieuwenhuys](https://github.com/nieuwenhuys) ## 2.1.0 (2019-07-22) - Dropped support for python 2.7 (and django 1.11). - Removed `django_tables2.utils.ucfirst`, use `django.utils.text.capfirst` instead. - Removed `class="thead-default"` from bootstrap4 template ([#671](https://github.com/jieter/django-tables2/issues/671)) - Included columns with `visible=False` in export ([#677](https://github.com/jieter/django-tables2/pull/677)) - Fixed pagination when the number of pages is equal to page range plus one ([#655](https://github.com/jieter/django-tables2/pull/655)) ## 2.0.6 (2019-03-26) - Add optional 'table' kwarg to `row_attrs` callables ## 2.0.5 (2019-02-21) - Fixes issue with wrong time format for TimeColumn [#650](https://github.com/jieter/django-tables2/pull/650) by [@IgorCode](https://github.com/IgorCode) ## 2.0.4 (2019-01-21) - The `ValueError` raised if the QuerySet passed to a table instance did not match the value declared in `Meta.model` is now turned into a warning (fixes [#643](https://github.com/jieter/django-tables2/issues/643)) - Make sure the templates do not raise errors when `thead`/`tfoot` attributes are not defined [#624](https://github.com/jieter/django-tables2/pull/624) by [@intiocean](https://github.com/intiocean) ## 2.0.3 (2018-11-11) - Improvements in packaging and publishing helper scripts reducing the package size considerably [#630](https://github.com/jieter/django-tables2/pull/630) by [@wtayyeb](https://github.com/wtayyeb) (fixes [#629](https://github.com/jieter/django-tables2/issues/629)) - Documentation improvements fixing [#625](https://github.com/jieter/django-tables2/issues/625), [#631](https://github.com/jieter/django-tables2/issues/631) ## 2.0.2 (2018-10-22) - Make sure the value of the class attribute in `` has consistent ordering (fixes [#627](https://github.com/jieter/django-tables2/issues/627)) - Make sure that pagination block is available in template regardless of pagination status [#622](https://github.com/jieter/django-tables2/pull/622) by [@apocalyptech](https://github.com/apocalyptech) ## 2.0.1 (2018-09-13) - Fixed a regression which did not allow `Table.Meta.order_by` to be a list. ## 2.0.0 (2018-09-13) Not much changed in this final version, but quite a lot if you are still on 1.21.2. Some [breaking changes](#breaking-changes-200) were introduced in version 2.0.0a0, so before upgrading from 1.21.2, please have a look through them carefully. - Consider `ExportMixin.export_trigger_param` in `export_url` template tag [#609](https://github.com/jieter/django-tables2/pull/609) by [@soerenbe](https://github.com/soerenbe) ## 2.0.0b5 (2018-08-29) - Change order of logic in `get_table_pagination` to make sure we are able to override the paginator using `View.paginator_class` attribute. ## 2.0.0b4 (2018-08-29) - The `klass` argument to `Table.paginate()` is renamed to `paginator_class` - Table views/mixins now take `ListView` attributes `paginator_class` and `paginate_orphans` into account. ## 2.0.0b3 (2018-08-27) - Fixed a bug in the implementation of [#606](https://github.com/jieter/django-tables2/pull/606) ## 2.0.0b2 (2018-08-27) - Added the ability to change the html attributes for `thead`, `tbody`, `tfoot` tags [#606](https://github.com/jieter/django-tables2/pull/606) by [@roelbouwman](https://github.com/roelbouwman) ## 2.0.0b1 (2018-08-24) - Added `LazyPaginator` to prevent making `.count()` queries ([#604](https://github.com/jieter/django-tables2/pull/604)). ## 2.0.0a5 (2018-07-28) - Added `linkify_item` keyword argument to `ManyToManyColumn`, fixes [#594](https://github.com/jieter/django-tables2/issues/594) - Fixed an encoding issue in `README.md` preventing installation in some environments. ## 2.0.0a4 (2018-07-17) - Add `linkify` keyword argument to all columns, to allow wrapping the content in a `` tag. It accepts one of these ways to define the link: - `True` to use the record return value of `record.get_absolute_url()`, - a callable to use its return value - a dict which is passed on to `django.urls.reverse()` - a (viewname, args) or (viewname, kwargs)-tuple which is also passed on to `django.urls.reverse()`. Implementation should be backwards compatible, so all use of `LinkColumn` and `RelatedLinkColum` should still work. [#590](https://github.com/jieter/django-tables2/pull/590) ## 2.0.0a3 (2018-05-24) Hello from [DjangoCon Europe](https://2018.djangocon.eu/)! - Fix table prefix being overwritten in `MultiTableView`, [#576](https://github.com/jieter/django-tables2/pull/576) by [@ETinLV](https://github.com/ETinLV), (fixes [#572](https://github.com/jieter/django-tables2/issues/572)) - Fix `empty_text` cannot be translated (fixes [#579](https://github.com/jieter/django-tables2/issues/579)) ## 2.0.0a2 (2018-04-13) - Another round of template cleanup. - Fresh screenshots - Prevent crash in `RelatedLinkColumn` for records without `get_absolute_url()`. - Raise `ValueError` when `Table.Meta.model != QuerySet.Model`. - Raise `TypeError` when incorrect types are used for `Table.Meta` attributes (fixes [#517](https://github.com/jieter/django-tables2/issues/517)) - Fix: `Table.Meta.sequence` with `extra_columns` can leads to `KeyError` (fixes [#486](https://github.com/jieter/django-tables2/issues/486)) ## 2.0.0a1 (2018-04-12) - Fixed translation of 'previous' for some languages (fixes [#563](https://github.com/jieter/django-tables2/issues/563)) ## django-tables2 2.0.0a0 (2018-04-10) - Cleaned up templates to add consistency in what is presented across all templates. - Added bootstrap4.html template - Fixed translation inconsistencies. ### breaking changes 2.0.0 - Appearance of the paginators might be different from the current 1.x templates. Use a custom template if you need to keep the appearance the same. - Removed the `template` argument to the table constructor, use `template_name` instead. - Stopped adding column names to the class attribute of table cells (`` tags) by default. Previous behavior can be restored by using this method on your custom table: ```python class MyTable(tables.Table): # columns def get_column_class_names(self, classes_set, bound_column): classes_set = super(MyTable, self).get_column_class_names(classes_set, bound_column) classes_set.add(bound_column.name) return classes_set ``` - `verbose_name`s derived from model fields are not passed through `title()` anymore, only the first character is converted to upper case. This follows [Django's convention for verbose field names](https://docs.djangoproject.com/en/2.0/topics/db/models/#verbose-field-names): "The convention is not to capitalize the first letter of the verbose_name. Django will automatically capitalize the first letter where it needs to." (Fixes [#475](https://github.com/jieter/django-tables2/issues/475) and [#491](https://github.com/jieter/django-tables2/issues/491)) ## 1.21.2 (2018-03-26) - Moved table instantiation from `get_context_data` to `get_tables` [#554](https://github.com/jieter/django-tables2/pull/554) by [@sdolemelipone](https://github.com/sdolemelipone) - Pass request as kwarg to `template.render()`, rather than as part of context. (fixes [#552](https://github.com/jieter/django-tables2/issues/552)) ## 1.21.1 (2018-03-12) - Do not perform extra `COUNT()` queries for non-paginated tables. Fixes [#551](https://github.com/jieter/django-tables2/issues/551) ## 1.21.0 (2018-03-12) - Add new method `paginated_rows` to `Table` to replace fallback to non-paginated rows in templates. - Prevent mutation of the template context `{% render_table %}` is called from (fixes [#547](https://github.com/jieter/django-tables2/issues/547)) **Possible breaking change**: the context variables of the template `{% render_table %}` is called from is no longer available in the table's template. The `table` variable has an attribute `context`, which is the context of the calling template. Use `{{ table.context.variable }}` instead of `{{ variable }}`. ## 1.20.0 (2018-03-08) - Define and use `get_table_data` in `MultiTableMixin` [#538](https://github.com/jieter/django-tables2/pull/538) by [@vCra](https://github.com/vCra) (fixes [#528](https://github.com/jieter/django-tables2/issues/528)) - Added `{% export_url %}` template tag. - Allow passing a `TableData`-derived class to the data argument of the `Table` constructor, instead of a QuerySet or list of dicts. ## 1.19.0 (2018-02-02) - `BoundColumn.attrs` does not evaluate `current_value` as `bool` [#536](https://github.com/jieter/django-tables2/pull/536) by [@pachewise](https://github.com/pachewise) (fixes [#534](https://github.com/jieter/django-tables2/issues/534)) - Allow more flexible access to cell values (especially useful for django templates) (fixes [#485](https://github.com/jieter/django-tables2/issues/485)) ## 1.18.0 (2018-01-27) - Follow relations when detecting column type for fields in `Table.Meta.fields` (fixes [#498](https://github.com/jieter/django-tables2/issues/498)) - Renamed `Table.Meta.template` to `template_name` (with deprecation warning for the former) [#542](https://github.com/jieter/django-tables2/pull/524) (fixes [#520](https://github.com/jieter/django-tables2/issues/520)) - Added Czech translation [#533](https://github.com/jieter/django-tables2/pull/533) by [@OndraRehounek](https://github.com/OndraRehounek) - Added `table_factory` [#532](https://github.com/jieter/django-tables2/pull/532) by [@ZuluPro](https://github.com/ZuluPro) ## 1.17.1 (2017-12-14) - Fix typo in setup.py for `extras_require`. ## 1.17.0 (2017-12-14) - Dropped support for Django 1.8, 1.9 and 1.10. - Add `extra_context` argument to `TemplateColumn` [#509](https://github.com/jieter/django-tables2/pull/509) by [@ad-m](https://github.com/ad-m) - Remove unnecessary cast of record to `str` [#514](https://github.com/jieter/django-tables2/pull/514), fixes [#511](https://github.com/jieter/django-tables2/issues/511) - Use `django.test.TestCase` for all tests, and remove dependency on pytest and reorganized some tests [#515](https://github.com/jieter/django-tables2/pull/515) - Remove traces of django-haystack tests from the tests, there were no actual tests. ## 1.16.0 (2017-11-27) This is the last version supporting Django 1.8, 1.9 and 1.10. Django 1.8 is only supported until April 2018, so consider upgrading to Django 1.11! - Added `tf` dictionary to `Column.attrs` with default values for the footer, so footers now have `class` attribute by default [#501](https://github.com/jieter/django-tables2/pull/501) by [@mpasternak](https://github.com/mpasternak) ## 1.15.0 (2017-11-23) - Added `as=varname` keyword argument to the `{% querystring %}` template tag, fixes [#481](https://github.com/jieter/django-tables2/issues/481) - Updated the tutorial to reflect current state of Django a bit better. - Used `OrderedDict` rather than `dict` as the parent for `utils.AttributeDict` to make the rendered html more consistent across python versions. - Allow reading column `attrs` from a column's attribute, allowing easier reuse of custom column attributes (fixes [#241](https://github.com/jieter/django-tables2/issues/241)) - `value` and `record` are optionally passed to the column attrs callables for data rows. [#503](https://github.com/jieter/django-tables2/pull/503), fixes [#500](https://github.com/jieter/django-tables2/issues/500) ## 1.14.2 (2017-10-30) - Added a `row_counter` variable to the template context in `TemplateColumn` (fixes [#448](https://github.com/jieter/django-tables2/issues/488)) ## 1.14.1 (2017-10-30) - Do not fail if `orderable=False` is passed to `ManyToManyColumn()` ## 1.14.0 (2017-10-30) - Added `separator` argument to `ManyToManyColumn`. - Allow `mark_safe()`'d strings from `ManyToManyColumn.tranform()` - Disabled ordering on `ManyToManyColumns` by default. ## 1.13.0 (2017-10-17) - Made positional `data` argument to the table `__init__()` a keyword argument to make inheritance easier. Will raise a `TypeError` if omitted. ## 1.12.0 (2017-10-10) - Allow export file name customization [#484](https://github.com/bradleyayers/django-tables2/pull/484) by [@federicobond](https://github.com/federicobond) - Fixed a bug where template columns were not rendered for pinned rows ([#483](https://github.com/bradleyayers/django-tables2/pull/483) by [@khirstinova](https://github.com/khirstinova), fixes [#482](https://github.com/bradleyayers/django-tables2/issues/482)) ## 1.11.0 (2017-09-15) - Added Hungarian translation [#471](https://github.com/bradleyayers/django-tables2/pull/471) by [@hmikihth](https://github.com/hmikihth). - Added TemplateColumn.value() and enhanced export docs (fixes [#470](https://github.com/bradleyayers/django-tables2/issues/470)) - Fixed display of pinned rows if table has no data. [#477](https://github.com/bradleyayers/django-tables2/pull/477) by [@khirstinova](https://github.com/khirstinova) ## 1.10.0 (2017-06-30) - Added `ManyToManyColumn` automatically added for `ManyToManyField`s. ## 1.9.1 (2017-06-29) - Allow customizing the value used in `Table.as_values()` (when using a `render_` method) using a `value_` method. (fixes [#458](https://github.com/bradleyayers/django-tables2/issues/458)) - Allow excluding columns from the `Table.as_values()` output. (fixes [#459](https://github.com/bradleyayers/django-tables2/issues/459)) - Fixed unicode handling for column headers in `Table.as_values()` ## 1.9.0 (2017-06-22) - Allow computable attrs for ``-tags from `Table.attrs` ([#457](https://github.com/bradleyayers/django-tables2/pull/457), fixes [#451](https://github.com/bradleyayers/django-tables2/issues/451)) ## 1.8.0 (2017-06-17) - Feature: Added an `ExportMixin` to export table data in various export formats (CSV, XLS, etc.) using [tablib](http://docs.python-tablib.org/en/latest/). - Defer expanding `Meta.sequence` to `Table.__init__`, to make sequence work in combination with `extra_columns` (fixes [#450](https://github.com/bradleyayers/django-tables2/issues/450)) - Fixed a crash when `MultiTableMixin.get_tables()` returned an empty array ([#454](https://github.com/bradleyayers/django-tables2/pull/455) by [@pypetey](https://github.com/pypetey) ## 1.7.1 (2017-06-02) - Call before_render when rendering with the render_table template tag (fixes [#447](https://github.com/bradleyayers/django-tables2/issues/447)) ## 1.7.0 (2017-06-01) - Make `title()` lazy ([#443](https://github.com/bradleyayers/django-tables2/pull/443) by [@ygwain](https://github.com/ygwain), fixes [#438](https://github.com/bradleyayers/django-tables2/issues/438)) - Fix `__all__` by populating them with the names of the items to export instead of the items themselves. - Allow adding extra columns to an instance using the `extra_columns` argument. Fixes [#403](https://github.com/bradleyayers/django-tables2/issues/403), [#70](https://github.com/bradleyayers/django-tables2/issues/70) - Added a hook `before_render` to allow last-minute changes to the table before rendering. - Added `BoundColumns.show()` and `BoundColumns.hide()` to show/hide columns on an instance of a `Table`. - Use `.verbose_name`/`.verbose_name_plural` if it exists to name the items in the list. (fixes [#166](https://github.com/bradleyayers/django-tables2/issues/166)) ## 1.6.1 (2017-05-08) - Add missing pagination to the responsive bootstrap template ([#440](https://github.com/bradleyayers/django-tables2/pull/440) by [@tobiasmcnulty](https://github.com/tobiasmcnulty)) ## 1.6.0 (2017-05-01) - Add new template `bootstrap-responsive.html` to generate a responsive bootstrap table. (Fixes [#436](https://github.com/bradleyayers/django-tables2/issues/436)) ## 1.5.0 (2017-04-18) _Full disclosure: as of april 1st, 2017, I am an employee of [Zostera](http://zostera.nl/), as such I will continue to maintain and improve django-tables2._ - Made `TableBase.as_values()` an iterator ([#432](https://github.com/bradleyayers/django-tables2/pull/432) by [@pziarsolo](https://github.com/pziarsolo)) - Added `JSONColumn` for data in JSON format. - Added `__all__` in `django_tables2/__init__.py` and `django_tables2/columns/__init__.py` - Added a setting `DJANGO_TABLES2_TEMPLATE` to allow project-wide overriding of the template used to render tables (fixes [#434](https://github.com/bradleyayers/django-tables2/issues/434)). ## 1.4.2 (2017-03-06) - Feature: Pinned rows ([#411](https://github.com/bradleyayers/django-tables2/pull/411) by [@djk2](https://github.com/djk2), fixes [#406](https://github.com/bradleyayers/django-tables2/issues/406)) - Fix an issue where `ValueError` was raised while using a view with a `get_queryset()` method defined. (fix with [#423](https://github.com/bradleyayers/django-tables2/pull/423) by [@desecho](https://github.com/desecho)) ## 1.4.1 (2017-02-27) - Fix URLS to screenshots in on PyPi description (fixes [ #398](https://github.com/bradleyayers/django-tables2/issues/398)) - Prevent superfluous spaces when a callable `row_attrs['class']` returns an empty string ([#417](https://github.com/bradleyayers/django-tables2/pull/417) by [@Superman8218](https://github.com/Superman8218)), fixes [#416](https://github.com/bradleyayers/django-tables2/issues/416)) ## 1.4.0 (2017-02-27) - Return `None` from `Table.as_values()` for missing values. [#419](https://github.com/bradleyayers/django-tables2/pull/419) - Fix ordering by custom fields, and refactor `TableData` [#424](https://github.com/bradleyayers/django-tables2/pull/424), fixes [#413](https://github.com/bradleyayers/django-tables2/issues/413) - Revert removing `TableData.__iter__()` (removed in [this commit](https://github.com/bradleyayers/django-tables2/commit/8fe9826429e6945a9258bc181fcbd711b282dba9)), fixes [#427](https://github.com/bradleyayers/django-tables2/issues/427), [#361](https://github.com/bradleyayers/django-tables2/issues/361) and [#421](https://github.com/bradleyayers/django-tables2/issues/421). ## 1.3.0 (2017-01-20) - Implement method `Table.as_values()` to get it's raw values. [#394](https://github.com/bradleyayers/django-tables2/pull/394) by [@intiocean](https://github.com/intiocean) - Fix some compatibility issues with django 2.0 [#408](https://github.com/bradleyayers/django-tables2/pull/409) by [djk2](https://github.com/djk2) ## 1.2.9 (2016-12-21) - Documentation for `None`-column attributes [#401](https://github.com/bradleyayers/django-tables2/pull/401) by [@dyve](https://github.com/dyve) ## 1.2.8 (2016-12-21) - `None`-column attributes on child class overwrite column attributes of parent class [#400](https://github.com/bradleyayers/django-tables2/pull/400) by [@dyve](https://github.com/dyve) ## 1.2.7 (2016-12-12) - Apply `title` to a column's `verbose_name` when it is derived from a model, fixes [#249](https://github.com/bradleyayers/django-tables2/issues/249). ([#382](https://github.com/bradleyayers/django-tables2/pull/382) by [@shawnnapora](https://github.com/shawnnapora)) - Update documentation after deprecation of `STATIC_URL` in django ([#384](https://github.com/bradleyayers/django-tables2/pull/384), by [@velaia](https://github.com/velaia)) - Cleanup of the templates, making the output more equal ([#381](https://github.com/bradleyayers/django-tables2/pull/381) by [@ralgozino](https://github.com/ralgozino)) - Use new location for `urlresolvers` in Django and add backwards compatible import ([#388](https://github.com/bradleyayers/django-tables2/pull/388) by [@felixxm](https://github.com/felixxm)) - Fix a bug where using `sequence` and then `exclude` in a child table would result in a `KeyError` - Some documentation fixes and cleanups. ## 1.2.6 (2016-09-06) - Added `get_table_kwargs()` method to `SingleTableMixin` to allow passing custom keyword arguments to the `Table` constructor. ([#366](https://github.com/bradleyayers/django-tables2/pull/366) by [@fritz-k](https://github.com/fritz-k)) - Allow the children of `TableBase` render in the `{% render_table %}` template tag. ([#377](https://github.com/bradleyayers/django-tables2/pull/377) by [@shawnnapora](https://github.com/shawnnapora)) - Refactor `BoundColumn` attributes to allow override of CSS class names, fixes [#349](https://github.com/bradleyayers/django-tables2/issues/349) ([#370](https://github.com/bradleyayers/django-tables2/pull/370) by [@graup](https://github.com/graup)). Current behavior should be intact, we will change the default in the future so it will **not** add the column name to the list of CSS classes. ## 1.2.5 (2016-07-30) - Fixed an issue preventing the rest of the row being rendered if a `BooleanColumn` was in the table for a model without custom choices defined on the model field. ([#360](https://github.com/bradleyayers/django-tables2/issues/360)) ## 1.2.4 (2016-07-28) - Added Norwegian Locale ([#356](https://github.com/bradleyayers/django-tables2/issues/356) by [@fanzypantz](https://github.com/fanzypantz)) - Restore default pagination for `SingleTableMixin`, fixes [#354](https://github.com/bradleyayers/django-tables2/issues/354) ([#395](https://github.com/bradleyayers/django-tables2/pull/359) by [@graup](https://github.com/graup)) ## 1.2.3 (2016-07-05) - Accept `text` parameter in `FileColumn`, analogous to `LinkColumn` ([#343](https://github.com/bradleyayers/django-tables2/pull/343) by [@graup](https://github.com/graup)) - Fix TemplateColumn RemovedInDjango110Warning fixes [#346](https://github.com/bradleyayers/django-tables2/issues/346). - Use field name in RelatedColumnLink ([#350](https://github.com/bradleyayers/django-tables2/pull/350), fixes [#347](https://github.com/bradleyayers/django-tables2/issues/347)) ## v1.2.2 (2016-06-04) - Allow use of custom class names for ordered columns through `attrs`. ( [#329](https://github.com/bradleyayers/django-tables2/pull/329) by [@theTarkus](https://github.com/theTarkus)) - Column ordering QuerySet pass through ([#330](https://github.com/bradleyayers/django-tables2/pull/330) by [@theTarkus](https://github.com/theTarkus)) - Cleanup/restructuring of [documentation](http://django-tables2.readthedocs.io/), ([#325](https://github.com/bradleyayers/django-tables2/pull/325)) - Fixed an issue where explicitly defined column options where not preserved over inheritance ([#339](https://github.com/bradleyayers/django-tables2/pull/339), [issue #337](https://github.com/bradleyayers/django-tables2/issues/337)) - Fixed an issue where `exclude` in combination with `sequence` raised a KeyError ([#341](https://github.com/bradleyayers/django-tables2/pull/341), [issue #205](https://github.com/bradleyayers/django-tables2/issues/205)) ## v1.2.1 (2016-05-09) - table footers (#323) - Non-field based `LinkColumn` only renders default value if lookup fails. (#322) - Accept `text` parameter in `BaseLinkColumn`-based columns. (#322) - Pass the table instance into SingleTableMixin's `get_table_pagination` (#320 by [@georgema1982](https://github.com/georgema1982), fixes #319) - Check if the view has `paginate_by` before before trying to access it. (fixes #326) ## v1.2.0 (2016-05-02) - Allow custom attributes for rows (fixes #47) ## v1.1.8 (2016-05-02) - Ability to change the body of the ``-tag, by passing `text` kwarg to the columns inheriting from BaseLinkColumn (#318 by [@desecho](https://github.com/desecho), #322) - Non-field based LinkColumn only renders default value if lookup fails and text is not set. (#322, fixes #257) ## v1.1.7 (2016-04-26) - Added Italian translation (#315 by [@paolodina](https://github.com/paolodina) - Added Dutch translation. - Fixed {% blocktrans %} template whitespace issues - Fixed errors when using a column named `items` (#316) - Obey `paginate_by` (from `MultipleObjectMixin`) if no later pagination is defined (#242) ## v1.1.6 (2016-04-02) - Correct error message about request context processors for current Django (#314) - Skipped 1.1.5 due to an error while creating the tag. ## v1.1.4 (2016-03-22) - Fix broken `setup.py` if Django is not installed before django-tables2 (fixes #312) ## v1.1.3 (2016-03-21) - Drop support for Django 1.7 - Add argument to `CheckBoxColumn` to render it as checked (original PR: #208) ## v1.1.2 (2016-02-16) - Fix `BooleanColumn` with choices set will always render as if `True` (#301) - Fix a bug with `TemplateColumn` while using cached template loader (#75) ## v1.1.1 (2016-01-26) - Allow `Meta.fields` to be a list as well as a tuple (#250) - Call template.render with a dict in Django >= 1.8. (#298) - Added `RelatedLinkColumn()` to render links to related objects (#297) - Remove default value from request parameter to `table.as_html()` ## v1.1.0 (2016-01-19) - Add tests for `TimeColumn` - Remove `sortable` argument for `Table` and Column constructors and its associated methods. Deprecated since 2012. - Remove deprecated aliases for `attrs` in `CheckboxColumn`. - Remove deprecated `OrderByTuple` `cmp` method (deprecated since 2013). - Add bootstrap template and (#293, fixes #141, #285) - Fix different html for tables with and without pagination (#293, fixes #149, #285) - Remove `{% nospaceless %}` template tag and remove wrapping template in `{% spaceless %}` **Possible breaking change**, if you use custom templates. ## v1.0.7 (2016-01-03) - Explicitly check if `column.verbose_name` is not None to support empty column headers (fixes #280) - Cleanup the example project to make it work with modern Django versions. - Do not sort `QuerySet` when `orderable=False` (#204 by [@bmihelac](https://github.com/bmihelac)) - `show_header` attribute on `Table` allows disabling the header (#175 by [@kviktor](https://github.com/kviktor)) - `LinkColumn` now tries to call `get_absolute_url` on a record if no `viewname` is provided (#283, fixes #231). - Add `request` argument to `Table.as_html()` to allow passing correct request objects instead of poorly generated ones #282 - Add coverage reporting to build #282 - Drop support for python 3.2 (because of coverage), support ends February 2016 #282 - move `build_request` from `django_table2.utils` to `tests.utils` and amend tests #282 ## v1.0.6 (2015-12-29) - Support for custom text value in `LinkColumn` (#277 by [@toudi](https://github.com/toudi)) - Refactor `LinkColumn.render_link()` to not escape twice #279 - Removed `Attrs` (wrapper for dict), deprecated on 2012-09-18 - Convert README.md to rst in setup.py to make PyPI look nice (fixes #97) ## v1.0.5 (2015-12-17) - First version released by new maintainer [@jieter](https://github.com/jieter) - Dropped support for Django 1.5 and 1.6, add python 3.5 with Django 1.8 and 1.9 to the build matrix (#273) - Prevent `SingleTableView` from calling `get_queryset` twice. (fixes #155) - Don't call managers when resolving accessors. (#214 by [@mbertheau](https://github.com/mbertheau), fixes #211) ## v1.0.4 (2015-05-09) - Fix bug in retrieving `field.verbose_name` under Django 1.8. ## v1.0.3 - Remove `setup.cfg` as PyPI does not actually support it, instead it is a distutils2 thing that is been discontinued. ## v1.0.2 - Add `setup.cfg` to declare `README.md` for PyPI. ## v1.0.1 - Convert README to markdown so it's formatted nicely on PyPI. ## v1.0.0 - Travis CI builds pass. - Added Python 3.4 support. - Added Django 1.7 and Django 1.8 support. - Convert tests to using `py.test`. ## v0.16.0 - Django 1.8 fixes - `BoundColumn.verbose_name` now only is capitalized only if no verbose_name was given. `verbose_name` is used verbatim. - Add max_length attribute to person CharField - Add Swedish translation - Update docs presentation on readthedocs ## v0.15.0 - Add UK, Russian, Spanish, Portuguese, and Polish translations - Add support for computed table `attrs`. ## v0.14.0 - `querystring` and `seturlparam` template tags now require the request to be in the context (backwards incompatible) -- #127 - Add Travis CI support - Add support for Django 1.5 - Add L10N control for columns #120 (ignored in < Django 1.3) - Drop Python 2.6.4 support in favor of Python 3.2 support - Non-QuerySet data ordering is different between Python 3 and 2. When comparing different types, their truth values are now compared before falling back to string representations of their type. ## v0.13.0 - Add FileColumn. ## v0.12.1 - When resolving an accessor, *all* exceptions are smothered into `None`. ## v0.12.0 - Improve performance by removing unnecessary queries - Simplified pagination: - `Table.page` is an instance attribute (no longer `@property`) - Exceptions raised by paginators (e.g. `EmptyPage`) are no longer smothered by `Table.page` - Pagination exceptions are raised by `Table.paginate` - `RequestConfig` can handles pagination errors silently, can be disabled by including `silent=False` in the `paginate` argument value - Add `DateTimeColumn` and `DateColumn` to handle formatting `datetime` and time zones. - Add `BooleanColumn` to handle bool values - `render_table` can now build and render a table for a QuerySet, rather than needing to be passed a table instance - Table columns created automatically from a model now use specialized columns - `Column.render` is now skipped if the value is considered *empty*, the default value is used instead. Empty values are specified via `Column.empty_values`, by default is `(None, '')` (backward incompatible) - Default values can now be specified on table instances or `Table.Meta` - Accessor's now honor `alters_data` during resolving. Fixes issue that would delete all your data when a column had an accessor of `delete` - Add `default` and `value` to context of `TemplateColumn` - Add cardinality indication to the pagination area of a table - `Attrs` is deprecated, use `dict` instead ## v0.11.0 - Add `URLColumn` to render URLs in a data source into hyperlinks - Add `EmailColumn` to render email addresses into hyperlinks - `TemplateColumn` can now Django's template loaders to render from a file ## v0.10.4 - Fix more bugs on Python 2.6.4, all tests now pass. ## v0.10.3 - Fix issues for Python 2.6.4 -- thanks Steve Sapovits & brianmay - Reduce Django 1.3 dependency to Table.as_html -- thanks brianmay ## v0.10.2 - Fix MANIFEST.in to include example templates, thanks TWAC. - Upgrade django-attest to fix problem with tests on Django 1.3.1 ## v0.10.1 - Fixed support for Django 1.4's paginator (thanks @koledennix) - Some juggling of internal implementation. `TableData` now supports slicing and returns new `TableData` instances. `BoundRows` now takes a single argument `data` (a `TableData` instance). - Add support for `get_pagination` on `SingleTableMixin`. - `SingleTableMixin` and `SingleTableView` are now importable directly from `django_tables2`. ## v0.10.0 - Renamed `BoundColumn.order_by` to `order_by_alias` and never returns `None` (**Backwards incompatible**). Templates are affected if they use something like: {% querystring table.prefixed_order_by_field=column.order_by.opposite|default:column.name %} Which should be rewritten as: {% querystring table.prefixed_order_by_field=column.order_by_alias.next %} - Added `next` shortcut to `OrderBy` returned from `BoundColumn.order_by_alias` - Added `OrderByTuple.get()` - Deprecated `BoundColumn.sortable`, `Column.sortable`, `Table.sortable`, `sortable` CSS class, `BoundColumns.itersortable`, `BoundColumns.sortable`; use `orderable` instead of `sortable`. - Added `BoundColumn.is_ordered` - Introduced concept of an `order by alias`, see glossary in the docs for details. ## v0.9.6 - Fix bug that caused an ordered column's `` to have no HTML attributes. ## v0.9.5 - Updated example project to add `colspan` on footer cell so table border renders correctly in Webkit. - Fix regression that caused 'sortable' class on . - `Table.__init__` no longer *always* calls `.order_by()` on QuerySets, fixes #55. This does introduce a slight backwards incompatibility. `Table.order_by` now has the possibility of returning `None`, previously it would *always* return an `OrderByTuple`. - `DeclarativeColumnsMetaclass.__new__` now uses `super()`` - Testing now requires pylint and Attest >=0.5.3 ## v0.9.4 - Fix regression that caused column verbose_name values that were marked as safe to be escaped. Now any verbose_name values that are instances of SafeData are used unmodified. ## v0.9.3 - Fix regression in `SingleTableMixin`. - Remove stray `print` statement. ## v0.9.2 - `SingleTableView` now uses `RequestConfig`. This fixes issues with `order_by_field`, `page_field`, and `per_page_field` not being honored. - Add `Table.Meta.per_page` and change `Table.paginate` to use it as default. - Add `title` template filter. It differs from Django's built-in `title` filter because it operates on an individual word basis and leaves words containing capitals untouched. **Warning**: use `{% load ... from ... %}` to avoid inadvertently replacing Django's built-in `title` template filter. - `BoundColumn.verbose_name` no longer does `capfirst`, capitalizing is now the responsibility of `Column.header`. - `BoundColumn.__unicode__` now uses `BoundColumn.header` rather than `BoundColumn.verbose_name`. ## v0.9.1 - Fix version in `setup.py` ## v0.9.0 - Add support for column attributes (see Attrs) - Add `BoundRows.items()` to yield `(bound_column, cell)` pairs - Tried to make docs more concise. Much stronger promotion of using `RequestConfig` and `{% querystring %}` ## v0.8.4 - Removed random 'print' statements. - Tweaked `paleblue` theme css to be more flexible: - removed `whitespace: no-wrap` - header background image to support more than 2 rows of text ## v0.8.3 - Fixed stupid import mistake. Tests did not pick it up due to them ignoring `ImportError`. ## v0.8.2 - `SingleTableView` now inherits from `ListView` which enables automatic `foo_list.html` template name resolution (thanks dramon for reporting) - `render_table` template tag no suppresses exceptions when `DEBUG=True` ## v0.8.1 - Fixed bug in render_table when giving it a template (issue #41) ## v0.8.0 - Added translation support in the default template via `{% trans %}` - Removed `basic_table.html`, `Table.as_html()` now renders `table.html` but will clobber the query string of the current request. Use the `render_table` template tag instead - `render_table` now supports an optional second argument -- the template to use when rendering the table - `Table` now supports declaring which template to use when rendering to HTML - Django >=1.3 is now required - Added support for using django-haystack's `SearchQuerySet` as a data source - The default template `table.html` now includes block tags to make it easy to extend to change small pieces - Fixed table template parsing problems being hidden due to a subsequent exception being raised - `Http404` exceptions are no longer raised during a call to `Table.paginate()`, instead it now occurs when `Table.page` is accessed - Fixed bug where a table could not be rendered more than once if it was paginated. - Accessing `Table.page` now returns a new page every time, rather than reusing a single object ## v0.7.8 - Tables now support using both `sequence` and `exclude` (issue #32). - `Sequence` class moved to `django_tables2/utils.py`. - Table instances now support modification to the `exclude` property. - Removed `BoundColumns._spawn_columns`. - `Table.data`, `Table.rows`, and `Table.columns` are now attributes rather than properties. django-tables2-2.7.5/CONTRIBUTING.md000066400000000000000000000033631473544236200165710ustar00rootroot00000000000000# Contributing to django-tables2 You are welcome to contribute to the development of `django-tables2` in various ways: - Discover and [report bugs](https://github.com/jieter/django-tables2/issues/new). Make sure to include a minimal example to show your problem. - Propose features, add tests or fix bugs by [opening a Pull Request](https://github.com/jieter/django-tables2/compare) - Fix documentation or translations When contributing features or making bug fixes, please add unit tests to verify the expected behaviour. This helps ## Coding style We use [black](https://black.readthedocs.io/en/stable/) to format the sources, with a 100 char line length. Before committing, run `black .`, or use `pre-commit`: ``` pip install pre-commit pre-commit install ``` ## Running the tests With `tox` installed, you can run the test suite in all supported environments by typing `tox`. During development, you might not want to wait for the tests to run in all environments, in that case, use the `-e` argument to specify a specific environment. For example `tox -e py36-2.0` will run the tests in python 3.6 with Django 2.0. You can also run the tests only in your current environment, using `PYTHONPATH=. ./manage.py test` (which is even quicker). ## Code coverage To generate a html coverage report: ``` coverage run --source=django_tables2 manage.py test coverage html ``` ## Building the documentation If you want to build the docs from within a virtualenv, and Sphinx is installed globally, use: ``` cd docs/ make html SPHINXBUILD="python $(which sphinx-build)" ``` Publishing a release -------------------- 1. Bump the version in `django-tables2/__init__.py`. 2. Update `CHANGELOG.md`. 3. Create a tag `./maintenance.py tag`. 4. Run `./maintenance.py publish` django-tables2-2.7.5/LICENSE000066400000000000000000000031671473544236200153470ustar00rootroot00000000000000All changes made to django-tables2 since forking from django-tables are Copyright (c) 2011, Bradley Ayers All rights reserved. Redistribution is permitted under the same terms as the original django-tables license. The original django-tables license is included below. Copyright (c) 2008, Michael Elsdörfer All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-tables2-2.7.5/README.md000066400000000000000000000047451473544236200156240ustar00rootroot00000000000000# django-tables2 - An app for creating HTML tables [![Latest PyPI version](https://badge.fury.io/py/django-tables2.svg)](https://pypi.python.org/pypi/django-tables2) [![Any color you like](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) django-tables2 simplifies the task of turning sets of data into HTML tables. It has native support for pagination and sorting. It does for HTML tables what `django.forms` does for HTML forms. e.g. - Available on pypi as [django-tables2](https://pypi.python.org/pypi/django-tables2) - Tested against currently supported versions of Django [and supported python 3 versions Django supports](https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django). - [Documentation on readthedocs.org](https://django-tables2.readthedocs.io/en/latest/) - [Bug tracker](http://github.com/jieter/django-tables2/issues) Features: - Any iterable can be a data-source, but special support for Django `QuerySets` is included. - The builtin UI does not rely on JavaScript. - Support for automatic table generation based on a Django model. - Supports custom column functionality via subclassing. - Pagination. - Column based table sorting. - Template tag to enable trivial rendering to HTML. - Generic view mixin. ![An example table rendered using django-tables2](https://cdn.rawgit.com/jieter/django-tables2/master/docs/img/example.png) ![An example table rendered using django-tables2 and bootstrap theme](https://cdn.rawgit.com/jieter/django-tables2/master/docs/img/bootstrap.png) ![An example table rendered using django-tables2 and semantic-ui theme]( https://cdn.rawgit.com/jieter/django-tables2/master/docs/img/semantic.png) ## Example Start by adding `django_tables2` to your `INSTALLED_APPS` setting like this: ```python INSTALLED_APPS = ( ..., "django_tables2", ) ``` Creating a table for a model `Simple` is as simple as: ```python import django_tables2 as tables class SimpleTable(tables.Table): class Meta: model = Simple ``` This would then be used in a view: ```python class TableView(tables.SingleTableView): table_class = SimpleTable queryset = Simple.objects.all() template_name = "simple_list.html" ``` And finally in the template: ``` {% load django_tables2 %} {% render_table table %} ``` This example shows one of the simplest cases, but django-tables2 can do a lot more! Check out the [documentation](https://django-tables2.readthedocs.io/en/latest/) for more details. django-tables2-2.7.5/django_tables2/000077500000000000000000000000001473544236200172115ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/__init__.py000066400000000000000000000016621473544236200213270ustar00rootroot00000000000000from .columns import ( BooleanColumn, CheckBoxColumn, Column, DateColumn, DateTimeColumn, EmailColumn, FileColumn, JSONColumn, LinkColumn, ManyToManyColumn, RelatedLinkColumn, TemplateColumn, TimeColumn, URLColumn, ) from .config import RequestConfig from .paginators import LazyPaginator from .tables import Table, table_factory from .utils import A from .views import MultiTableMixin, SingleTableMixin, SingleTableView __version__ = "2.7.5" __all__ = ( "Table", "table_factory", "BooleanColumn", "Column", "CheckBoxColumn", "DateColumn", "DateTimeColumn", "EmailColumn", "FileColumn", "JSONColumn", "LinkColumn", "ManyToManyColumn", "RelatedLinkColumn", "TemplateColumn", "TimeColumn", "URLColumn", "RequestConfig", "A", "SingleTableMixin", "SingleTableView", "MultiTableMixin", "LazyPaginator", ) django-tables2-2.7.5/django_tables2/columns/000077500000000000000000000000001473544236200206715ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/columns/__init__.py000066400000000000000000000015671473544236200230130ustar00rootroot00000000000000from .base import BoundColumn, BoundColumns, Column, library from .booleancolumn import BooleanColumn from .checkboxcolumn import CheckBoxColumn from .datecolumn import DateColumn from .datetimecolumn import DateTimeColumn from .emailcolumn import EmailColumn from .filecolumn import FileColumn from .jsoncolumn import JSONColumn from .linkcolumn import LinkColumn, RelatedLinkColumn from .manytomanycolumn import ManyToManyColumn from .templatecolumn import TemplateColumn from .timecolumn import TimeColumn from .urlcolumn import URLColumn __all__ = ( "library", "BoundColumn", "BoundColumns", "Column", "BooleanColumn", "CheckBoxColumn", "DateColumn", "DateTimeColumn", "EmailColumn", "FileColumn", "JSONColumn", "LinkColumn", "ManyToManyColumn", "RelatedLinkColumn", "TemplateColumn", "URLColumn", "TimeColumn", ) django-tables2-2.7.5/django_tables2/columns/base.py000066400000000000000000000774631473544236200221760ustar00rootroot00000000000000from collections import OrderedDict from itertools import islice from django.core.exceptions import ImproperlyConfigured from django.urls import reverse from django.utils.html import format_html from django.utils.safestring import SafeData from django.utils.text import capfirst from ..utils import ( Accessor, AttributeDict, OrderBy, OrderByTuple, call_with_appropriate, computed_values, ) class Library: """A collection of columns.""" def __init__(self): self.columns = [] def register(self, column): if not hasattr(column, "from_field"): raise ImproperlyConfigured(f"{column.__class__.__name__} is not a subclass of Column") self.columns.append(column) return column def column_for_field(self, field, **kwargs): """ Return a column object suitable for model field. Returns: `.Column` object or `None` """ if field is None: return self.columns[0](**kwargs) # Iterate in reverse order as columns are registered in order # of least to most specialised (i.e. Column is registered # first). This also allows user-registered columns to be # favoured. for candidate in reversed(self.columns): if hasattr(field, "get_related_field"): verbose_name = field.get_related_field().verbose_name else: verbose_name = getattr(field, "verbose_name", field.name) kwargs["verbose_name"] = capfirst(verbose_name) column = candidate.from_field(field, **kwargs) if column is None: continue return column # The library is a mechanism for announcing what columns are available. Its # current use is to allow the table metaclass to ask columns if they're a # suitable match for a model field, and if so to return an approach instance. library = Library() class LinkTransform: """Object used to generate attributes for the ``-tag to wrap the cell content in.""" viewname = None accessor = None attrs = None def __init__(self, url=None, accessor=None, attrs=None, reverse_args=None): """ arguments: url (callable): If supplied, the result of this callable will be used as ``href`` attribute. accessor (Accessor): if supplied, the accessor will be used to decide on which object ``get_absolute_url()`` is called. attrs (dict): Customize attributes for the ```` tag. Values of the dict can be either static text or a callable. The callable can optionally declare any subset of the following keyword arguments: value, record, column, bound_column, bound_row, table. These arguments will then be passed automatically. reverse_args (dict, tuple): Arguments to ``django.urls.reverse()``. If dict, the arguments are assumed to be keyword arguments to ``reverse()``, if tuple, a ``(viewname, args)`` or ``(viewname, kwargs)`` """ self.url = url self.attrs = attrs self.accessor = accessor if isinstance(reverse_args, (list, tuple)): viewname, args = reverse_args reverse_args = {"viewname": viewname} reverse_args["kwargs" if isinstance(args, dict) else "args"] = args self.reverse_args = reverse_args or {} def compose_url(self, **kwargs): if self.url and callable(self.url): return call_with_appropriate(self.url, kwargs) bound_column = kwargs.get("bound_column", None) record = kwargs["record"] if self.reverse_args.get("viewname", None) is not None: return self.call_reverse(record=record) if bound_column is None and self.accessor is None: accessor = Accessor("") else: accessor = Accessor(self.accessor if self.accessor is not None else bound_column.name) context = accessor.resolve(record) if not hasattr(context, "get_absolute_url"): if hasattr(record, "get_absolute_url"): context = record else: raise TypeError( f"for linkify=True, '{context}' must have a method get_absolute_url" ) return context.get_absolute_url() def call_reverse(self, record): """ Prepares the arguments to reverse() for this record and calls reverse() """ def resolve_if_accessor(val): return val.resolve(record) if isinstance(val, Accessor) else val params = self.reverse_args.copy() params["viewname"] = resolve_if_accessor(params["viewname"]) if params.get("urlconf", None): params["urlconf"] = resolve_if_accessor(params["urlconf"]) if params.get("args", None): params["args"] = [resolve_if_accessor(a) for a in params["args"]] if params.get("kwargs", None): params["kwargs"] = { key: resolve_if_accessor(val) for key, val in params["kwargs"].items() } if params.get("current_app", None): params["current_app"] = resolve_if_accessor(params["current_app"]) return reverse(**params) def get_attrs(self, **kwargs): attrs = AttributeDict(computed_values(self.attrs or {}, kwargs=kwargs)) attrs["href"] = self.compose_url(**kwargs) return attrs def __call__(self, content, **kwargs): attrs = self.get_attrs(**kwargs) if attrs["href"] is None: return content return format_html("{}", attrs.as_html(), content) @library.register class Column: """ Represents a single column of a table. `.Column` objects control the way a column (including the cells that fall within it) are rendered. Arguments: attrs (dict): HTML attributes for elements that make up the column. This API is extended by subclasses to allow arbitrary HTML attributes to be added to the output. By default `.Column` supports: - ``th`` -- ``table/thead/tr/th`` elements - ``td`` -- ``table/tbody/tr/td`` elements - ``cell`` -- fallback if ``th`` or ``td`` is not defined - ``a`` -- To control the attributes for the ``a`` tag if the cell is wrapped in a link. accessor (str or `~.Accessor`): An accessor that describes how to extract values for this column from the :term:`table data`. default (str or callable): The default value for the column. This can be a value or a callable object [1]_. If an object in the data provides `None` for a column, the default will be used instead. The default value may affect ordering, depending on the type of data the table is using. The only case where ordering is not affected is when a `.QuerySet` is used as the table data (since sorting is performed by the database). empty_values (iterable): list of values considered as a missing value, for which the column will render the default value. Defaults to `(None, '')` exclude_from_export (bool): If `True`, this column will not be added to the data iterator returned from as_values(). footer (str, callable): Defines the footer of this column. If a callable is passed, it can take optional keyword arguments `column`, `bound_column` and `table`. order_by (str, tuple or `.Accessor`): Allows one or more accessors to be used for ordering rather than *accessor*. orderable (bool): If `False`, this column will not be allowed to influence row ordering/sorting. verbose_name (str): A human readable version of the column name. visible (bool): If `True`, this column will be rendered. Columns with `visible=False` will not be rendered, but will be included in ``.Table.as_values()`` and thus also in :ref:`export`. localize: If the cells in this column will be localized by the `localize` filter: - If `True`, force localization - If `False`, values are not localized - If `None` (default), localization depends on the ``USE_L10N`` setting. linkify (bool, str, callable, dict, tuple): Controls if cell content will be wrapped in an ``a`` tag. The different ways to define the ``href`` attribute: - If `True`, the ``record.get_absolute_url()`` or the related model's `get_absolute_url()` is used. - If a callable is passed, the returned value is used, if it's not ``None``. The callable can optionally accept any argument valid for :ref:`table.render_foo`-methods, for example `record` or `value`. - If a `dict` is passed, it's passed on to ``~django.urls.reverse``. - If a `tuple` is passed, it must be either a (viewname, args) or (viewname, kwargs) tuple, which is also passed to ``~django.urls.reverse``. Examples, assuming this model:: class Blog(models.Model): title = models.CharField(max_length=100) body = model.TextField() user = model.ForeignKey(get_user_model(), on_delete=models.CASCADE) Using the ``linkify`` argument to control the linkification. These columns will all display the value returned from `str(record.user)`:: # If the column is named 'user', the column will use record.user.get_absolute_url() user = tables.Column(linkify=True) # We can also do that explicitly: user = tables.Column(linkify=lambda record: record.user.get_absolute_url()) # or, if no get_absolute_url is defined, or a custom link is required, we have a couple # of ways to define what is passed to reverse() user = tables.Column(linkify={"viewname": "user_detail", "args": [tables.A("user__pk")]}) user = tables.Column(linkify=("user_detail", [tables.A("user__pk")])) # (viewname, args) user = tables.Column(linkify=("user_detail", {"pk": tables.A("user__pk")})) # (viewname, kwargs) initial_sort_descending (bool): If `True`, a column will sort in descending order on "first click" after table has been rendered. If `False`, column will follow default behavior, and sort ascending on "first click". Defaults to `False`. .. [1] The provided callable object must not expect to receive any arguments. """ # Tracks each time a Column instance is created. Used to retain order. creation_counter = 0 empty_values = (None, "") # by default, contents are not wrapped in an -tag. link = None # Explicit is set to True if the column is defined as an attribute of a # class, used to give explicit columns precedence. _explicit = False def __init__( self, verbose_name=None, accessor=None, default=None, visible=True, orderable=None, attrs=None, order_by=None, empty_values=None, localize=None, footer=None, exclude_from_export=False, linkify=False, initial_sort_descending=False, ): if not (accessor is None or isinstance(accessor, str) or callable(accessor)): raise TypeError(f"accessor must be a string or callable, not {type(accessor).__name__}") if callable(accessor) and default is not None: raise TypeError("accessor must be string when default is used, not callable") self.accessor = Accessor(accessor) if accessor else None self._default = default self.verbose_name = verbose_name self.visible = visible self.orderable = orderable self.attrs = attrs or getattr(self, "attrs", {}) # massage order_by into an OrderByTuple or None order_by = (order_by,) if isinstance(order_by, str) else order_by self.order_by = OrderByTuple(order_by) if order_by is not None else None if empty_values is not None: self.empty_values = empty_values self.localize = localize self._footer = footer self.exclude_from_export = exclude_from_export link_kwargs = None if callable(linkify) or hasattr(self, "get_url"): link_kwargs = dict(url=linkify if callable(linkify) else self.get_url) elif isinstance(linkify, (dict, tuple)): link_kwargs = dict(reverse_args=linkify) elif linkify is True: link_kwargs = dict(accessor=self.accessor) if link_kwargs is not None: self.link = LinkTransform(attrs=self.attrs.get("a", {}), **link_kwargs) self.initial_sort_descending = initial_sort_descending self.creation_counter = Column.creation_counter Column.creation_counter += 1 @property def default(self): return self._default() if callable(self._default) else self._default @property def header(self): """ The value used for the column heading (e.g. inside the ```` tag). By default this returns `~.Column.verbose_name`. :returns: `unicode` or `None` .. note:: This property typically is not accessed directly when a table is rendered. Instead, `.BoundColumn.header` is accessed which in turn accesses this property. This allows the header to fallback to the column name (it is only available on a `.BoundColumn` object hence accessing that first) when this property doesn't return something useful. """ return self.verbose_name def footer(self, bound_column, table): """Return the content of the footer, if specified.""" footer_kwargs = {"column": self, "bound_column": bound_column, "table": table} if self._footer is not None: if callable(self._footer): return call_with_appropriate(self._footer, footer_kwargs) else: return self._footer if hasattr(self, "render_footer"): return call_with_appropriate(self.render_footer, footer_kwargs) return "" def render(self, value): """ Return the content for a specific cell. This method can be overridden by :ref:`table.render_FOO` methods on the table or by subclassing `.Column`. If the value for this cell is in `.empty_values`, this method is skipped and an appropriate default value is rendered instead. Subclasses should set `.empty_values` to ``()`` if they want to handle all values in `.render`. """ return value def value(self, **kwargs): """ Return the content for a specific cell for exports. Similar to `.render` but without any html content. This can be used to get the data in the formatted as it is presented but in a form that could be added to a csv file. The default implementation just calls the `render` function but any subclasses where `render` returns html content should override this method. See `LinkColumn` for an example. """ value = call_with_appropriate(self.render, kwargs) return value def order(self, queryset, is_descending): """ Order the QuerySet of the table. This method can be overridden by :ref:`table.order_FOO` methods on the table or by subclassing `.Column`; but only overrides if second element in return tuple is True. returns: Tuple (QuerySet, boolean) """ return (queryset, False) @classmethod def from_field(cls, field, **kwargs): """ Return a specialized column for the model field or `None`. Arguments: field (Model Field instance): the field that needs a suitable column Returns: `.Column` object or `None` If the column is not specialized for the given model field, it should return `None`. This gives other columns the opportunity to do better. If the column is specialized, it should return an instance of itself that is configured appropriately for the field. """ # Since this method is inherited by every subclass, only provide a # column if this class was asked directly. if cls is Column: return cls(**kwargs) class BoundColumn: """ A run-time version of `.Column`. The difference between `.BoundColumn` and `.Column`, is that `.BoundColumn` objects include the relationship between a `.Column` and a `.Table`. In practice, this means that a `.BoundColumn` knows the *"variable name"* given to the `.Column` when it was declared on the `.Table`. arguments: table (`~.Table`): The table in which this column exists column (`~.Column`): The type of column name (str): The variable name of the column used when defining the `.Table`. In this example the name is ``age``:: class SimpleTable(tables.Table): age = tables.Column() """ def __init__(self, table, column, name): self._table = table self.column = column self.name = name self.link = column.link if not column.accessor: column.accessor = Accessor(self.name) self.accessor = column.accessor self.current_value = None def __str__(self): return str(self.header) @property def attrs(self): """ Proxy to `.Column.attrs` but injects some values of our own. A ``th``, ``td`` and ``tf`` are guaranteed to be defined (irrespective of what is actually defined in the column attrs. This makes writing templates easier. ``tf`` is not actually a HTML tag, but this key name will be used for attributes for column's footer, if the column has one. """ # prepare kwargs for computed_values() kwargs = {"table": self._table, "bound_column": self} # BoundRow.items() sets current_record and current_value when iterating over # the records in a table. if ( getattr(self, "current_record", None) is not None and getattr(self, "current_value", None) is not None ): kwargs.update({"record": self.current_record, "value": self.current_value}) # Start with table's attrs; Only 'th' and 'td' attributes will be used attrs = dict(self._table.attrs) # Update attrs to prefer column's attrs rather than table's attrs.update(dict(self.column.attrs)) # we take the value for 'cell' as the basis for both the th and td attrs cell_attrs = attrs.get("cell", {}) # override with attrs defined specifically for th and td respectively. attrs["th"] = computed_values(attrs.get("th", cell_attrs), kwargs=kwargs) attrs["td"] = computed_values(attrs.get("td", cell_attrs), kwargs=kwargs) attrs["tf"] = computed_values(attrs.get("tf", cell_attrs), kwargs=kwargs) # wrap in AttributeDict attrs["th"] = AttributeDict(attrs["th"]) attrs["td"] = AttributeDict(attrs["td"]) attrs["tf"] = AttributeDict(attrs["tf"]) # Override/add classes attrs["th"]["class"] = self.get_th_class(attrs["th"]) attrs["td"]["class"] = self.get_td_class(attrs["td"]) attrs["tf"]["class"] = self.get_td_class(attrs["tf"]) return attrs def _get_cell_class(self, attrs): """ Return a set of the classes from the class key in ``attrs``. """ classes = attrs.get("class", None) classes = set() if classes is None else set(classes.split(" ")) return self._table.get_column_class_names(classes, self) def get_td_class(self, td_attrs): """ Returns the HTML class attribute for a data cell in this column """ classes = sorted(self._get_cell_class(td_attrs)) return None if len(classes) == 0 else " ".join(classes) def get_th_class(self, th_attrs): """ Returns the HTML class attribute for a header cell in this column """ classes = self._get_cell_class(th_attrs) # add classes for ordering ordering_class = th_attrs.get("_ordering", {}) if self.orderable: classes.add(ordering_class.get("orderable", "orderable")) if self.is_ordered: classes.add( ordering_class.get("descending", "desc") if self.order_by_alias.is_descending else ordering_class.get("ascending", "asc") ) return None if len(classes) == 0 else " ".join(sorted(classes)) @property def default(self): """Returns the default value for this column.""" value = self.column.default if value is None: value = self._table.default return value @property def header(self): """The contents of the header cell for this column.""" # favour Column.header column_header = self.column.header if column_header: return column_header # fall back to automatic best guess return self.verbose_name @property def footer(self): """The contents of the footer cell for this column.""" return call_with_appropriate( self.column.footer, {"bound_column": self, "table": self._table} ) def has_footer(self): return self.column._footer is not None or hasattr(self.column, "render_footer") @property def order_by(self): """ Return an `.OrderByTuple` of appropriately prefixed data source keys used to sort this column. See `.order_by_alias` for details. """ if self.column.order_by is not None: order_by = self.column.order_by else: # default to using column accessor as data source sort key order_by = OrderByTuple((self.accessor,)) return order_by.opposite if self.order_by_alias.is_descending else order_by @property def order_by_alias(self): """ Return an `OrderBy` describing the current state of ordering for this column. The following attempts to explain the difference between `order_by` and `.order_by_alias`. `.order_by_alias` returns and `.OrderBy` instance that's based on the *name* of the column, rather than the keys used to order the table data. Understanding the difference is essential. Having an alias *and* a keys version is necessary because an N-tuple (of data source keys) can be used by the column to order the data, and it is ambiguous when mapping from N-tuple to column (since multiple columns could use the same N-tuple). The solution is to use order by *aliases* (which are really just prefixed column names) that describe the ordering *state* of the column, rather than the specific keys in the data source should be ordered. e.g.:: >>> class SimpleTable(tables.Table): ... name = tables.Column(order_by=("firstname", "last_name")) ... >>> table = SimpleTable([], order_by=('-name', )) >>> table.columns["name"].order_by_alias "-name" >>> table.columns["name"].order_by ("-first_name", "-last_name") The `OrderBy` returned has been patched to include an extra attribute ``next``, which returns a version of the alias that would be transitioned to if the user toggles sorting on this column, for example:: not sorted -> ascending ascending -> descending descending -> ascending This is useful otherwise in templates you'd need something like:: {% if column.is_ordered %} {% querystring table.prefixed_order_by_field=column.order_by_alias.opposite %} {% else %} {% querystring table.prefixed_order_by_field=column.order_by_alias %} {% endif %} """ order_by = OrderBy((self._table.order_by or {}).get(self.name, self.name)) order_by.next = order_by.opposite if self.is_ordered else order_by if self.column.initial_sort_descending and not self.is_ordered: order_by.next = order_by.opposite return order_by @property def is_ordered(self): return self.name in (self._table.order_by or ()) @property def orderable(self): """Return whether this column supports ordering.""" if self.column.orderable is not None: return self.column.orderable return self._table.orderable @property def verbose_name(self): """ Return the verbose name for this column. In order of preference, this will return: 1) The column's explicitly defined `verbose_name` 2) The model's `verbose_name` with the first letter capitalized (if applicable) 3) Fall back to the column name, with first letter capitalized. Any `verbose_name` that was not passed explicitly in the column definition is returned with the first character capitalized in keeping with the Django convention of `verbose_name` being defined in lowercase and uppercased as needed by the application. If the table is using `QuerySet` data, then use the corresponding model field's `~.db.Field.verbose_name`. If it is traversing a relationship, then get the last field in the accessor (i.e. stop when the relationship turns from ORM relationships to object attributes [e.g. person.upper should stop at person]). """ # Favor an explicit defined verbose_name if self.column.verbose_name is not None: return self.column.verbose_name # This is our reasonable fall back, should the next section not result # in anything useful. name = self.name.replace("_", " ") # Try to use a model field's verbose_name model = self._table.data.model if model: field = Accessor(self.accessor).get_field(model) if field: if hasattr(field, "field"): name = field.field.verbose_name else: name = getattr(field, "verbose_name", field.name) # If verbose_name was mark_safe()'d, return intact to keep safety if isinstance(name, SafeData): return name return capfirst(name) @property def visible(self): """Return whether this column is visible.""" return self.column.visible @property def localize(self): """Return `True`, `False` or `None` as described in ``Column.localize``""" return self.column.localize class BoundColumns: """ Container for spawning `.BoundColumn` objects. This is bound to a table and provides its `.Table.columns` property. It provides access to those columns in different ways (iterator, item-based, filtered and unfiltered etc), stuff that would not be possible with a simple iterator in the table class. A `BoundColumns` object is a container for holding `BoundColumn` objects. It provides methods that make accessing columns easier than if they were stored in a `list` or `dict`. `Columns` has a similar API to a `dict` (it actually uses a `~collections.OrderedDict` internally). At the moment you'll only come across this class when you access a `.Table.columns` property. Arguments: table (`.Table`): the table containing the columns """ def __init__(self, table, base_columns): self._table = table self.columns = OrderedDict() for name, column in base_columns.items(): self.columns[name] = bound_column = BoundColumn(table, column, name) bound_column.render = getattr(table, "render_" + name, column.render) # How the value is defined: 1. value_ 2. render_ 3. column.value. bound_column.value = getattr( table, "value_" + name, getattr(table, "render_" + name, column.value) ) bound_column.order = getattr(table, "order_" + name, column.order) def iternames(self): return (name for name, column in self.iteritems()) def names(self): return list(self.iternames()) def iterall(self): """ Return an iterator that exposes all `.BoundColumn` objects, regardless of visibility or sortability. """ return (column for name, column in self.iteritems()) def all(self): return list(self.iterall()) def iteritems(self): """ Return an iterator of ``(name, column)`` pairs (where ``column`` is a `BoundColumn`). This method is the mechanism for retrieving columns that takes into consideration all of the ordering and filtering modifiers that a table supports (e.g. `~Table.Meta.exclude` and `~Table.Meta.sequence`). """ for name in self._table.sequence: if name not in self._table.exclude: yield (name, self.columns[name]) def items(self): return list(self.iteritems()) def iterorderable(self): """ Same as `BoundColumns.all` but only returns orderable columns. This is useful in templates, where iterating over the full set and checking ``{% if column.ordarable %}`` can be problematic in conjunction with e.g. ``{{ forloop.last }}`` (the last column might not be the actual last that is rendered). """ return (x for x in self.iterall() if x.orderable) def itervisible(self): """ Same as `.iterorderable` but only returns visible `.BoundColumn` objects. This is geared towards table rendering. """ return (x for x in self.iterall() if x.visible) def hide(self, name): """ Hide a column. Arguments: name(str): name of the column """ self.columns[name].column.visible = False def show(self, name): """ Show a column otherwise hidden. Arguments: name(str): name of the column """ self.columns[name].column.visible = True def __iter__(self): """Convenience API, alias of `.itervisible`.""" return self.itervisible() def __contains__(self, item): """ Check if a column is contained within a `BoundColumns` object. *item* can either be a `~.BoundColumn` object, or the name of a column. """ if isinstance(item, str): return item in self.iternames() else: # let's assume we were given a column return item in self.iterall() def __len__(self): """Return how many `~.BoundColumn` objects are contained (and visible).""" return len(list(self.itervisible())) def __getitem__(self, index): """ Retrieve a specific `~.BoundColumn` object. *index* can either be 0-indexed or the name of a column .. code-block:: python columns['speed'] # returns a bound column with name 'speed' columns[0] # returns the first column """ if isinstance(index, int): try: return next(islice(self.iterall(), index, index + 1)) except StopIteration: raise IndexError elif isinstance(index, str): for column in self.iterall(): if column.name == index: return column raise KeyError( f"Column with name '{index}' does not exist; choices are: {self.names()}" ) else: raise TypeError(f"Column indices must be integers or str, not {type(index).__name__}") django-tables2-2.7.5/django_tables2/columns/booleancolumn.py000066400000000000000000000046611473544236200241070ustar00rootroot00000000000000from django.db import models from django.utils.html import escape, format_html from ..utils import AttributeDict from .base import Column, library @library.register class BooleanColumn(Column): """ A column suitable for rendering boolean data. Arguments: null (bool): is `None` different from `False`? yesno (str): comma separated values string or 2-tuple to display for True/False values. Rendered values are wrapped in a ```` to allow customization by using CSS. By default the span is given the class ``true``, ``false``. In addition to *attrs* keys supported by `~.Column`, the following are available: - ``span`` -- adds attributes to the ```` tag """ def __init__(self, null=False, yesno="✔,✘", **kwargs): self.yesno = yesno.split(",") if isinstance(yesno, str) else tuple(yesno) if not null: kwargs["empty_values"] = () super().__init__(**kwargs) def _get_bool_value(self, record, value, bound_column): # If record is a model, we need to check if it has choices defined. if hasattr(record, "_meta"): field = bound_column.accessor.get_field(record) # If that's the case, we need to inverse lookup the value to convert # to a boolean we can use. if hasattr(field, "choices") and field.choices is not None and len(field.choices) > 0: value = next(val for val, name in field.choices if name == value) value = bool(value) return value def render(self, value, record, bound_column): value = self._get_bool_value(record, value, bound_column) text = self.yesno[int(not value)] attrs = {"class": str(value).lower()} attrs.update(self.attrs.get("span", {})) return format_html("{}", AttributeDict(attrs).as_html(), escape(text)) def value(self, record, value, bound_column): """ Returns the content for a specific cell similarly to `.render` however without any html content. """ value = self._get_bool_value(record, value, bound_column) return str(value) @classmethod def from_field(cls, field, **kwargs): if isinstance(field, models.NullBooleanField): return cls(null=True, **kwargs) if isinstance(field, models.BooleanField): return cls(null=getattr(field, "null", False), **kwargs) django-tables2-2.7.5/django_tables2/columns/checkboxcolumn.py000066400000000000000000000062031473544236200242500ustar00rootroot00000000000000from django.utils.safestring import mark_safe from django_tables2.utils import Accessor, AttributeDict, computed_values from .base import Column, library @library.register class CheckBoxColumn(Column): """ A subclass of `.Column` that renders as a checkbox form input. This column allows a user to *select* a set of rows. The selection information can then be used to apply some operation (e.g. "delete") onto the set of objects that correspond to the selected rows. The value that is extracted from the :term:`table data` for this column is used as the value for the checkbox, i.e. ```` This class implements some sensible defaults: - HTML input's ``name`` attribute is the :term:`column name` (can override via *attrs* argument). - ``orderable`` defaults to `False`. Arguments: attrs (dict): In addition to *attrs* keys supported by `~.Column`, the following are available: - ``input`` -- ```` elements in both ```` and ````. - ``th__input`` -- Replaces ``input`` attrs in header cells. - ``td__input`` -- Replaces ``input`` attrs in body cells. checked (`~.Accessor`, bool, callable): Allow rendering the checkbox as checked. If it resolves to a truthy value, the checkbox will be rendered as checked. .. note:: You might expect that you could select multiple checkboxes in the rendered table and then *do something* with that. This functionality is not implemented. If you want something to actually happen, you will need to implement that yourself. """ def __init__(self, attrs=None, checked=None, **extra): self.checked = checked kwargs = {"orderable": False, "attrs": attrs} kwargs.update(extra) super().__init__(**kwargs) @property def header(self): default = {"type": "checkbox"} general = self.attrs.get("input") specific = self.attrs.get("th__input") attrs = AttributeDict(default, **(specific or general or {})) return mark_safe(f"") def render(self, value, bound_column, record): default = {"type": "checkbox", "name": bound_column.name, "value": value} if self.is_checked(value, record): default.update({"checked": "checked"}) general = self.attrs.get("input") specific = self.attrs.get("td__input") attrs = dict(default, **(specific or general or {})) attrs = computed_values(attrs, kwargs={"record": record, "value": value}) return mark_safe(f"") def is_checked(self, value, record): """ Determine if the checkbox should be checked """ if self.checked is None: return False if self.checked is True: return True if callable(self.checked): return bool(self.checked(value, record)) checked = Accessor(self.checked) if checked in record: return bool(record[checked]) return False django-tables2-2.7.5/django_tables2/columns/datecolumn.py000066400000000000000000000016631473544236200234040ustar00rootroot00000000000000from django.db import models from .base import library from .templatecolumn import TemplateColumn @library.register class DateColumn(TemplateColumn): """ A column that renders dates in the local timezone. Arguments: format (str): format string in same format as Django's ``date`` template filter (optional) short (bool): if `format` is not specified, use Django's ``SHORT_DATE_FORMAT`` setting, otherwise use ``DATE_FORMAT`` """ def __init__(self, format=None, short=True, *args, **kwargs): if format is None: format = "SHORT_DATE_FORMAT" if short else "DATE_FORMAT" template = '{{ value|date:"%s"|default:default }}' % format super().__init__(template_code=template, *args, **kwargs) @classmethod def from_field(cls, field, **kwargs): if isinstance(field, models.DateField): return cls(**kwargs) django-tables2-2.7.5/django_tables2/columns/datetimecolumn.py000066400000000000000000000017401473544236200242570ustar00rootroot00000000000000from django.db import models from .base import library from .templatecolumn import TemplateColumn @library.register class DateTimeColumn(TemplateColumn): """ A column that renders `datetime` instances in the local timezone. Arguments: format (str): format string for datetime (optional). Note that *format* uses Django's `date` template tag syntax. short (bool): if `format` is not specified, use Django's ``SHORT_DATETIME_FORMAT``, else ``DATETIME_FORMAT`` """ def __init__(self, format=None, short=True, *args, **kwargs): if format is None: format = "SHORT_DATETIME_FORMAT" if short else "DATETIME_FORMAT" template = '{{ value|date:"%s"|default:default }}' % format super().__init__(template_code=template, *args, **kwargs) @classmethod def from_field(cls, field, **kwargs): if isinstance(field, models.DateTimeField): return cls(**kwargs) django-tables2-2.7.5/django_tables2/columns/emailcolumn.py000066400000000000000000000021041473544236200235450ustar00rootroot00000000000000from django.db import models from .base import library from .linkcolumn import BaseLinkColumn @library.register class EmailColumn(BaseLinkColumn): """ Render email addresses to `mailto:`-links. Arguments: attrs (dict): HTML attributes that are added to the rendered ``...`` tag. text: Either static text, or a callable. If set, this will be used to render the text inside link instead of the value. Example:: # models.py class Person(models.Model): name = models.CharField(max_length=200) email = models.EmailField() # tables.py class PeopleTable(tables.Table): name = tables.Column() email = tables.EmailColumn() # result # [...]email@example.com """ def get_url(self, value): return f"mailto:{value}" @classmethod def from_field(cls, field, **kwargs): if isinstance(field, models.EmailField): return cls(**kwargs) django-tables2-2.7.5/django_tables2/columns/filecolumn.py000066400000000000000000000056041473544236200234050ustar00rootroot00000000000000import os from django.db import models from django.utils.html import format_html from ..utils import AttributeDict from .base import library from .linkcolumn import BaseLinkColumn @library.register class FileColumn(BaseLinkColumn): """ Attempts to render `.FieldFile` (or other storage backend `.File`) as a hyperlink. When the file is accessible via a URL, the file is rendered as a hyperlink. The `.basename` is used as the text, wrapped in a span:: receipt.pdf When unable to determine the URL, a ``span`` is used instead:: receipt.pdf `.Column.attrs` keys ``a`` and ``span`` can be used to add additional attributes. Arguments: verify_exists (bool): attempt to determine if the file exists If *verify_exists*, the HTML class ``exists`` or ``missing`` is added to the element to indicate the integrity of the storage. text (str or callable): Either static text, or a callable. If set, this will be used to render the text inside the link instead of the file's ``basename`` (default) """ def __init__(self, verify_exists=True, **kwargs): self.verify_exists = verify_exists super().__init__(**kwargs) def get_url(self, value, record): storage = getattr(value, "storage", None) if not storage: return None return storage.url(value.name) def text_value(self, record, value): if self.text is None: return os.path.basename(value.name) return super().text_value(record, value) def render(self, record, value): attrs = AttributeDict(self.attrs.get("span", {})) classes = [c for c in attrs.get("class", "").split(" ") if c] exists = None storage = getattr(value, "storage", None) if storage: # we'll assume value is a `django.db.models.fields.files.FieldFile` if self.verify_exists: exists = storage.exists(value.name) else: if self.verify_exists and hasattr(value, "name"): # ignore negatives, perhaps the file has a name but it doesn't # represent a local path... better to stay neutral than give a # false negative. exists = os.path.exists(value.name) or exists if exists is not None: classes.append("exists" if exists else "missing") attrs["title"] = value.name attrs["class"] = " ".join(classes) return format_html( "{text}", attrs=attrs.as_html(), text=self.text_value(record, value), ) @classmethod def from_field(cls, field, **kwargs): if isinstance(field, models.FileField): return cls(**kwargs) django-tables2-2.7.5/django_tables2/columns/jsoncolumn.py000066400000000000000000000035261473544236200234400ustar00rootroot00000000000000import json from django.db.models import JSONField from django.utils.html import format_html from ..utils import AttributeDict from .base import library from .linkcolumn import BaseLinkColumn try: from django.contrib.postgres.fields import HStoreField POSTGRES_AVAILABLE = True except ImportError: # psycopg2 is not available, cannot import from django.contrib.postgres. # JSONColumn might still be useful to add manually. POSTGRES_AVAILABLE = False @library.register class JSONColumn(BaseLinkColumn): """ Render the contents of `~django.contrib.postgres.fields.JSONField` or `~django.contrib.postgres.fields.HStoreField` as an indented string. .. versionadded :: 1.5.0 .. note:: Automatic rendering of data to this column requires PostgreSQL support (psycopg2 installed) to import the fields, but this column can also be used manually without it. Arguments: json_dumps_kwargs: kwargs passed to `json.dumps`, defaults to `{'indent': 2}` attrs (dict): In addition to *attrs* keys supported by `~.Column`, the following are available: - ``pre`` -- ``
`` around the rendered JSON string in ```` elements.

    """

    def __init__(self, json_dumps_kwargs=None, **kwargs):
        self.json_dumps_kwargs = (
            json_dumps_kwargs if json_dumps_kwargs is not None else {"indent": 2}
        )

        super().__init__(**kwargs)

    def render(self, record, value):
        return format_html(
            "
{}
", AttributeDict(self.attrs.get("pre", {})).as_html(), json.dumps(value, **self.json_dumps_kwargs), ) @classmethod def from_field(cls, field, **kwargs): if POSTGRES_AVAILABLE: if isinstance(field, (JSONField, HStoreField)): return cls(**kwargs) django-tables2-2.7.5/django_tables2/columns/linkcolumn.py000066400000000000000000000137501473544236200234240ustar00rootroot00000000000000from .base import Column, library class BaseLinkColumn(Column): """ The base for other columns that render links. Arguments: text (str or callable): If set, this value will be used to render the text inside link instead of value. The callable gets the record being rendered as argument. attrs (dict): In addition to ``attrs`` keys supported by `~.Column`, the following are available: - `a` -- ```` in ```` elements. """ def __init__(self, text=None, *args, **kwargs): super().__init__(*args, **kwargs) self.text = text def text_value(self, record, value): if self.text is None: return value return self.text(record) if callable(self.text) else self.text def value(self, record, value): """ Returns the content for a specific cell similarly to `.render` however without any html content. """ return self.text_value(record, value) def render(self, record, value): return self.text_value(record, value) @library.register class LinkColumn(BaseLinkColumn): """ Renders a normal value as an internal hyperlink to another page. .. note :: This column should not be used anymore, the `linkify` keyword argument to regular columns can be used to achieve the same results. It's common to have the primary value in a row hyperlinked to the page dedicated to that record. The first arguments are identical to that of `~django.urls.reverse` and allows an internal URL to be described. If this argument is `None`, then `get_absolute_url`. (see Django references) will be used. The last argument *attrs* allows custom HTML attributes to be added to the rendered ```` tag. Arguments: viewname (str or None): See `~django.urls.reverse`, or use `None` to use the model's `get_absolute_url` urlconf (str): See `~django.urls.reverse`. args (list): See `~django.urls.reverse`. [2]_ kwargs (dict): See `~django.urls.reverse`. [2]_ current_app (str): See `~django.urls.reverse`. attrs (dict): HTML attributes that are added to the rendered ``...`` tag. text (str or callable): Either static text, or a callable. If set, this will be used to render the text inside link instead of value (default). The callable gets the record being rendered as argument. .. [2] In order to create a link to a URL that relies on information in the current row, `.Accessor` objects can be used in the *args* or *kwargs* arguments. The accessor will be resolved using the row's record before `~django.urls.reverse` is called. Example: .. code-block:: python # models.py class Person(models.Model): name = models.CharField(max_length=200) # urls.py urlpatterns = patterns('', url("people/([0-9]+)/", views.people_detail, name="people_detail") ) # tables.py from django_tables2.utils import A # alias for Accessor class PeopleTable(tables.Table): name = tables.LinkColumn("people_detail", args=[A("pk")]) In order to override the text value (i.e. ``text``) consider the following example: .. code-block:: python # tables.py from django_tables2.utils import A # alias for Accessor class PeopleTable(tables.Table): name = tables.LinkColumn("people_detail", text="static text", args=[A("pk")]) age = tables.LinkColumn("people_detail", text=lambda record: record.name, args=[A("pk")]) In the first example, a static text would be rendered (``"static text"``) In the second example, you can specify a callable which accepts a record object (and thus can return anything from it) In addition to *attrs* keys supported by `.Column`, the following are available: - `a` -- ```` elements in ````. Adding attributes to the ````-tag looks like this:: class PeopleTable(tables.Table): first_name = tables.LinkColumn(attrs={ "a": {"style": "color: red;"} }) """ def __init__( self, viewname=None, urlconf=None, args=None, kwargs=None, current_app=None, attrs=None, **extra, ): super().__init__( attrs=attrs, linkify=dict( viewname=viewname, urlconf=urlconf, args=args, kwargs=kwargs, current_app=current_app, ), **extra, ) @library.register class RelatedLinkColumn(LinkColumn): """ Render a link to a related object using related object's ``get_absolute_url``, same parameters as ``~.LinkColumn``. .. note :: This column should not be used anymore, the `linkify` keyword argument to regular columns can be used achieve the same results. If the related object does not have a method called ``get_absolute_url``, or if it is not callable, the link will be rendered as '#'. Traversing relations is also supported, suppose a Person has a foreign key to Country which in turn has a foreign key to Continent:: class PersonTable(tables.Table): name = tables.Column() country = tables.RelatedLinkColumn() continent = tables.RelatedLinkColumn(accessor="country.continent") will render: - in column 'country', link to ``person.country.get_absolute_url()`` with the output of ``str(person.country)`` as ```` contents. - in column 'continent', a link to ``person.country.continent.get_absolute_url()`` with the output of ``str(person.country.continent)`` as ```` contents. Alternative contents of ```` can be supplied using the ``text`` keyword argument as documented for `~.columns.LinkColumn`. """ django-tables2-2.7.5/django_tables2/columns/manytomanycolumn.py000066400000000000000000000071341473544236200246620ustar00rootroot00000000000000from django.db import models from django.utils.encoding import force_str from django.utils.html import conditional_escape from django.utils.safestring import mark_safe from .base import Column, LinkTransform, library @library.register class ManyToManyColumn(Column): """ Display the list of objects from a `ManyRelatedManager` Ordering is disabled for this column. Arguments: transform: callable to transform each item to text, it gets an item as argument and must return a string-like representation of the item. By default, it calls `~django.utils.force_str` on each item. filter: callable to filter, limit or order the QuerySet, it gets the `ManyRelatedManager` as first argument and must return a filtered QuerySet. By default, it returns `all()` separator: separator string to join the items with. default: ``", "`` linkify_item: callable, arguments to reverse() or `True` to wrap items in a ```` tag. For a detailed explanation, see ``linkify`` argument to ``Column``. For example, when displaying a list of friends with their full name:: # models.py class Person(models.Model): first_name = models.CharField(max_length=200) last_name = models.CharField(max_length=200) friends = models.ManyToManyField(Person) is_active = models.BooleanField(default=True) @property def name(self): return f"{self.first_name} {self.last_name}" # tables.py class PersonTable(tables.Table): name = tables.Column(order_by=("last_name", "first_name")) friends = tables.ManyToManyColumn(transform=lambda user: user.name) If only the active friends should be displayed, you can use the `filter` argument:: friends = tables.ManyToManyColumn(filter=lambda qs: qs.filter(is_active=True)) """ def __init__( self, transform=None, filter=None, separator=", ", linkify_item=None, *args, **kwargs ): kwargs.setdefault("orderable", False) super().__init__(*args, **kwargs) if transform is not None: self.transform = transform if filter is not None: self.filter = filter self.separator = separator link_kwargs = None if callable(linkify_item): link_kwargs = dict(url=linkify_item) elif isinstance(linkify_item, (dict, tuple)): link_kwargs = dict(reverse_args=linkify_item) elif linkify_item is True: link_kwargs = dict() if link_kwargs is not None: self.linkify_item = LinkTransform(attrs=self.attrs.get("a", {}), **link_kwargs) def transform(self, obj): """ Transform is applied to each item of the list of objects from the ManyToMany relation. """ return force_str(obj) def filter(self, qs): """ Filter is called on the ManyRelatedManager to allow ordering, filtering or limiting on the set of related objects. """ return qs.all() def render(self, value): items = [] for item in self.filter(value): content = conditional_escape(self.transform(item)) if hasattr(self, "linkify_item"): content = self.linkify_item(content=content, record=item) items.append(content) return mark_safe(conditional_escape(self.separator).join(items)) @classmethod def from_field(cls, field, **kwargs): if isinstance(field, models.ManyToManyField): return cls(**kwargs) django-tables2-2.7.5/django_tables2/columns/templatecolumn.py000066400000000000000000000057651473544236200243110ustar00rootroot00000000000000from django.template import Context, Template from django.template.loader import get_template from django.utils.html import strip_tags from .base import Column, library @library.register class TemplateColumn(Column): """ A subclass of `.Column` that renders some template code to use as the cell value. Arguments: template_code (str): template code to render template_name (str): name of the template to render extra_context (dict): optional extra template context A `~django.template.Template` object is created from the *template_code* or *template_name* and rendered with a context containing: - *record* -- data record for the current row - *value* -- value from `record` that corresponds to the current column - *default* -- appropriate default value to use as fallback. - *row_counter* -- The number of the row this cell is being rendered in. - any context variables passed using the `extra_context` argument to `TemplateColumn`. Example: .. code-block:: python class ExampleTable(tables.Table): foo = tables.TemplateColumn("{{ record.bar }}") # contents of `myapp/bar_column.html` is `{{ label }}: {{ value }}` bar = tables.TemplateColumn(template_name="myapp/name2_column.html", extra_context={"label": "Label"}) Both columns will have the same output. """ empty_values = () def __init__(self, template_code=None, template_name=None, extra_context=None, **extra): super().__init__(**extra) self.template_code = template_code self.template_name = template_name self.extra_context = extra_context or {} if not self.template_code and not self.template_name: raise ValueError("A template must be provided") def render(self, record, table, value, bound_column, **kwargs): # If the table is being rendered using `render_table`, it hackily # attaches the context to the table as a gift to `TemplateColumn`. context = getattr(table, "context", Context()) additional_context = { "default": bound_column.default, "column": bound_column, "record": record, "value": value, "row_counter": kwargs["bound_row"].row_counter, } additional_context.update(self.extra_context) with context.update(additional_context): if self.template_code: return Template(self.template_code).render(context) else: return get_template(self.template_name).render(context.flatten()) def value(self, **kwargs): """ The value returned from a call to `value()` on a `TemplateColumn` is the rendered template with `django.utils.html.strip_tags` applied. Leading and trailing whitespace is stripped. """ html = super().value(**kwargs) return strip_tags(html).strip() if isinstance(html, str) else html django-tables2-2.7.5/django_tables2/columns/timecolumn.py000066400000000000000000000014661473544236200234260ustar00rootroot00000000000000from django.db import models from .base import library from .templatecolumn import TemplateColumn @library.register class TimeColumn(TemplateColumn): """ A column that renders times in the local timezone. Arguments: format (str): format string in same format as Django's ``time`` template filter (optional). short (bool): if *format* is not specified, use Django's ``TIME_FORMAT`` setting. """ def __init__(self, format=None, *args, **kwargs): if format is None: format = "TIME_FORMAT" template = '{{ value|date:"%s"|default:default }}' % format super().__init__(template_code=template, *args, **kwargs) @classmethod def from_field(cls, field, **kwargs): if isinstance(field, models.TimeField): return cls(**kwargs) django-tables2-2.7.5/django_tables2/columns/urlcolumn.py000066400000000000000000000016361473544236200232710ustar00rootroot00000000000000from django.db import models from .base import library from .linkcolumn import BaseLinkColumn @library.register class URLColumn(BaseLinkColumn): """ Renders URL values as hyperlinks. Arguments: text (str or callable): Either static text, or a callable. If set, this will be used to render the text inside link instead of value (default) attrs (dict): Additional attributes for the ```` tag Example:: >>> class CompaniesTable(tables.Table): ... link = tables.URLColumn() ... >>> table = CompaniesTable([{"link": "http://google.com"}]) >>> table.rows[0].get_cell("link") 'http://google.com' """ def get_url(self, value): return value @classmethod def from_field(cls, field, **kwargs): if isinstance(field, models.URLField): return cls(**kwargs) django-tables2-2.7.5/django_tables2/config.py000066400000000000000000000046331473544236200210360ustar00rootroot00000000000000from django.core.paginator import EmptyPage, PageNotAnInteger class RequestConfig: """ A configurator that uses request data to setup a table. A single RequestConfig can be used for multiple tables in one view. Arguments: paginate (dict or bool): Indicates whether to paginate, and if so, what default values to use. If the value evaluates to `False`, pagination will be disabled. A `dict` can be used to specify default values for the call to `~.tables.Table.paginate` (e.g. to define a default `per_page` value). A special *silent* item can be used to enable automatic handling of pagination exceptions using the following logic: - If `~django.core.paginator.PageNotAnInteger` is raised, show the first page. - If `~django.core.paginator.EmptyPage` is raised, show the last page. For example, to use `~.LazyPaginator`:: RequestConfig(paginate={"paginator_class": LazyPaginator}).configure(table) """ def __init__(self, request, paginate=True): self.request = request self.paginate = paginate def configure(self, table): """ Configure a table using information from the request. Arguments: table (`~.Table`): table to be configured """ table.request = self.request order_by = self.request.GET.getlist(table.prefixed_order_by_field) if order_by: table.order_by = order_by if self.paginate: if hasattr(self.paginate, "items"): kwargs = dict(self.paginate) else: kwargs = {} # extract some options from the request for arg in ("page", "per_page"): name = getattr(table, f"prefixed_{arg}_field") try: kwargs[arg] = int(self.request.GET[name]) except (ValueError, KeyError): pass silent = kwargs.pop("silent", True) if not silent: table.paginate(**kwargs) else: try: table.paginate(**kwargs) except PageNotAnInteger: table.page = table.paginator.page(1) except EmptyPage: table.page = table.paginator.page(table.paginator.num_pages) return table django-tables2-2.7.5/django_tables2/data.py000066400000000000000000000165041473544236200205020ustar00rootroot00000000000000import warnings from django.utils.functional import cached_property from .utils import OrderBy, OrderByTuple, segment class TableData: """ Base class for table data containers. """ def __init__(self, data): self.data = data def __getitem__(self, key): """ Slicing returns a new `.TableData` instance, indexing returns a single record. """ return self.data[key] def __iter__(self): """ for ... in ... default to using this. There's a bug in Django 1.3 with indexing into QuerySets, so this side-steps that problem (as well as just being a better way to iterate). """ return iter(self.data) def set_table(self, table): """ `Table.__init__` calls this method to inject an instance of itself into the `TableData` instance. Good place to do additional checks if Table and TableData instance will work together properly. """ self.table = table @property def model(self): return getattr(self.data, "model", None) @property def ordering(self): return None @property def verbose_name(self): return "item" @property def verbose_name_plural(self): return "items" @staticmethod def from_data(data): # allow explicit child classes of TableData to be passed to Table() if isinstance(data, TableData): return data if TableQuerysetData.validate(data): return TableQuerysetData(data) elif TableListData.validate(data): return TableListData(list(data)) raise ValueError( "data must be QuerySet-like (have count() and order_by()) or support" f" list(data) -- {type(data).__name__} has neither" ) class TableListData(TableData): """ Table data container for a list of dicts, for example:: [ {'name': 'John', 'age': 20}, {'name': 'Brian', 'age': 25} ] .. note:: Other structures might have worked in the past, but are not explicitly supported or tested. """ @staticmethod def validate(data): """ Validates `data` for use in this container """ return hasattr(data, "__iter__") or ( hasattr(data, "__len__") and hasattr(data, "__getitem__") ) def __len__(self): return len(self.data) @property def verbose_name(self): return getattr(self.data, "verbose_name", super().verbose_name) @property def verbose_name_plural(self): return getattr(self.data, "verbose_name_plural", super().verbose_name_plural) def order_by(self, aliases): """ Order the data based on order by aliases (prefixed column names) in the table. Arguments: aliases (`~.utils.OrderByTuple`): optionally prefixed names of columns ('-' indicates descending order) in order of significance with regard to data ordering. """ accessors = [] for alias in aliases: bound_column = self.table.columns[OrderBy(alias).bare] # bound_column.order_by reflects the current ordering applied to # the table. As such we need to check the current ordering on the # column and use the opposite if it doesn't match the alias prefix. if alias[0] != bound_column.order_by_alias[0]: accessors += bound_column.order_by.opposite else: accessors += bound_column.order_by self.data.sort(key=OrderByTuple(accessors).key) class TableQuerysetData(TableData): """ Table data container for a queryset. """ @staticmethod def validate(data): """ Validates `data` for use in this container """ return ( hasattr(data, "count") and callable(data.count) and hasattr(data, "order_by") and callable(data.order_by) ) def __len__(self): """Cached data length""" if not hasattr(self, "_length") or self._length is None: if hasattr(self.table, "paginator"): # for paginated tables, use QuerySet.count() as we are interested in total number of records. self._length = self.data.count() else: # for non-paginated tables, use the length of the QuerySet self._length = len(self.data) return self._length def set_table(self, table): super().set_table(table) if self.model and getattr(table._meta, "model", None) and self.model != table._meta.model: warnings.warn( f"Table data is of type {self.model} but {table._meta.model} is specified in Table.Meta.model" ) @property def ordering(self): """ Returns the list of order by aliases that are enforcing ordering on the data. If the data is unordered, an empty sequence is returned. If the ordering can not be determined, `None` is returned. This works by inspecting the actual underlying data. As such it's only supported for querysets. """ aliases = {} for bound_column in self.table.columns: aliases[bound_column.order_by_alias] = bound_column.order_by try: return next(segment(self.data.query.order_by, aliases)) except StopIteration: pass def order_by(self, aliases): """ Order the data based on order by aliases (prefixed column names) in the table. Arguments: aliases (`~.utils.OrderByTuple`): optionally prefixed names of columns ('-' indicates descending order) in order of significance with regard to data ordering. """ modified_any = False accessors = [] for alias in aliases: bound_column = self.table.columns[OrderBy(alias).bare] # bound_column.order_by reflects the current ordering applied to # the table. As such we need to check the current ordering on the # column and use the opposite if it doesn't match the alias prefix. if alias[0] != bound_column.order_by_alias[0]: accessors += bound_column.order_by.opposite else: accessors += bound_column.order_by if bound_column: queryset, modified = bound_column.order(self.data, alias[0] == "-") if modified: self.data = queryset modified_any = True # custom ordering if modified_any: return # Traditional ordering if accessors: order_by_accessors = (a.for_queryset() for a in accessors) self.data = self.data.order_by(*order_by_accessors) @cached_property def verbose_name(self): """ The full (singular) name for the data. Model's `~django.db.Model.Meta.verbose_name` is honored. """ return self.data.model._meta.verbose_name @cached_property def verbose_name_plural(self): """ The full (plural) name for the data. Model's `~django.db.Model.Meta.verbose_name` is honored. """ return self.data.model._meta.verbose_name_plural django-tables2-2.7.5/django_tables2/export/000077500000000000000000000000001473544236200205325ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/export/__init__.py000066400000000000000000000001511473544236200226400ustar00rootroot00000000000000from .export import TableExport from .views import ExportMixin __all__ = ("TableExport", "ExportMixin") django-tables2-2.7.5/django_tables2/export/export.py000066400000000000000000000065401473544236200224320ustar00rootroot00000000000000from django.core.exceptions import ImproperlyConfigured from django.http import HttpResponse try: from tablib import Dataset except ImportError: # pragma: no cover raise ImproperlyConfigured( "You must have tablib installed in order to use the django-tables2 export functionality" ) class TableExport: """ Export data from a table to the file type specified. Arguments: export_format (str): one of `csv, json, latex, ods, tsv, xls, xlsx, yaml` table (`~.Table`): instance of the table to export the data from exclude_columns (iterable): list of column names to exclude from the export dataset_kwargs (dictionary): passed as `**kwargs` to `tablib.Dataset` constructor """ CSV = "csv" JSON = "json" LATEX = "latex" ODS = "ods" TSV = "tsv" XLS = "xls" XLSX = "xlsx" YAML = "yaml" FORMATS = { CSV: "text/csv; charset=utf-8", JSON: "application/json", LATEX: "text/plain", ODS: "application/vnd.oasis.opendocument.spreadsheet", TSV: "text/tsv; charset=utf-8", XLS: "application/vnd.ms-excel", XLSX: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", YAML: "text/yaml; charset=utf-8", } def __init__(self, export_format, table, exclude_columns=None, dataset_kwargs=None): if not self.is_valid_format(export_format): raise TypeError(f'Export format "{export_format}" is not supported.') self.format = export_format self.dataset = self.table_to_dataset(table, exclude_columns, dataset_kwargs) def table_to_dataset(self, table, exclude_columns, dataset_kwargs=None): """Transform a table to a tablib dataset.""" def default_dataset_title(): try: return table.Meta.model._meta.verbose_name_plural.title() except AttributeError: return "Export Data" kwargs = {"title": default_dataset_title()} kwargs.update(dataset_kwargs or {}) dataset = Dataset(**kwargs) for i, row in enumerate(table.as_values(exclude_columns=exclude_columns)): if i == 0: dataset.headers = row else: dataset.append(row) return dataset @classmethod def is_valid_format(self, export_format): """ Returns true if `export_format` is one of the supported export formats """ return export_format is not None and export_format in TableExport.FORMATS.keys() def content_type(self): """ Returns the content type for the current export format """ return self.FORMATS[self.format] def export(self): """ Returns the string/bytes for the current export format """ return self.dataset.export(self.format) def response(self, filename=None): """ Builds and returns a `HttpResponse` containing the exported data Arguments: filename (str): if not `None`, the filename is attached to the `Content-Disposition` header of the response. """ response = HttpResponse(content_type=self.content_type()) if filename is not None: response["Content-Disposition"] = f'attachment; filename="{filename}"' response.write(self.export()) return response django-tables2-2.7.5/django_tables2/export/views.py000066400000000000000000000045641473544236200222520ustar00rootroot00000000000000from .export import TableExport class ExportMixin: """ Support various export formats for the table data. `ExportMixin` looks for some attributes on the class to change it's behavior: Attributes: export_class (TableExport): Allows using a custom implementation of `TableExport`. export_name (str): is the name of file that will be exported, without extension. export_trigger_param (str): is the name of the GET attribute used to trigger the export. It's value decides the export format, refer to `TableExport` for a list of available formats. exclude_columns (iterable): column names excluded from the export. For example, one might want to exclude columns containing buttons from the export. Excluding columns from the export is also possible using the `exclude_from_export` argument to the `.Column` constructor:: class Table(tables.Table): name = tables.Column() buttons = tables.TemplateColumn(exclude_from_export=True, template_name=...) export_formats (iterable): export formats to render a set of buttons in the template. dataset_kwargs (dictionary): passed as `**kwargs` to `tablib.Dataset` constructor:: dataset_kwargs = {"tite": "My custom tab title"} """ export_class = TableExport export_name = "table" export_trigger_param = "_export" exclude_columns = () dataset_kwargs = None export_formats = (TableExport.CSV,) def get_export_filename(self, export_format): return f"{self.export_name}.{export_format}" def get_dataset_kwargs(self): return self.dataset_kwargs def create_export(self, export_format): exporter = self.export_class( export_format=export_format, table=self.get_table(**self.get_table_kwargs()), exclude_columns=self.exclude_columns, dataset_kwargs=self.get_dataset_kwargs(), ) return exporter.response(filename=self.get_export_filename(export_format)) def render_to_response(self, context, **kwargs): export_format = self.request.GET.get(self.export_trigger_param, None) if self.export_class.is_valid_format(export_format): return self.create_export(export_format) return super().render_to_response(context, **kwargs) django-tables2-2.7.5/django_tables2/locale/000077500000000000000000000000001473544236200204505ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/cs/000077500000000000000000000000001473544236200210555ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/cs/LC_MESSAGES/000077500000000000000000000000001473544236200226425ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/cs/LC_MESSAGES/django.mo000066400000000000000000000010251473544236200244370ustar00rootroot00000000000000Þ•4L`af‘o nextpreviousProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2018-01-22 08:21+0100 Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2; dalšípÅ™edchozídjango-tables2-2.7.5/django_tables2/locale/cs/LC_MESSAGES/django.po000066400000000000000000000017461473544236200244540ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2018-01-22 08:21+0100\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "pÅ™edchozí" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "další" django-tables2-2.7.5/django_tables2/locale/de/000077500000000000000000000000001473544236200210405ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/de/LC_MESSAGES/000077500000000000000000000000001473544236200226255ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/de/LC_MESSAGES/django.mo000066400000000000000000000007611473544236200244300ustar00rootroot00000000000000Þ•4L`afroâénextpreviousProject-Id-Version: Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2015-04-09 12:45+0100 Last-Translator: Tim Schneider Language-Team: Language: de MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); X-Generator: Poedit 1.7.5 weiterzurückdjango-tables2-2.7.5/django_tables2/locale/de/LC_MESSAGES/django.po000066400000000000000000000017041473544236200244310ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2015-04-09 12:45+0100\n" "Last-Translator: Tim Schneider \n" "Language-Team: \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.7.5\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "zurück" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "weiter" django-tables2-2.7.5/django_tables2/locale/el/000077500000000000000000000000001473544236200210505ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/el/LC_MESSAGES/000077500000000000000000000000001473544236200226355ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/el/LC_MESSAGES/django.mo000066400000000000000000000007011473544236200244320ustar00rootroot00000000000000Þ•4L`af+o›ªnextpreviousProject-Id-Version: django-tables2 Report-Msgid-Bugs-To: PO-Revision-Date: 2013-03-19 21:56+0200 Last-Translator: Serafeim Papastefanos Language-Team: el Language: el MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit επόμενηπÏοηγοÏμενηdjango-tables2-2.7.5/django_tables2/locale/el/LC_MESSAGES/django.po000066400000000000000000000015201473544236200244350ustar00rootroot00000000000000# This file is distributed under the same license as the django-tables2 package # #, fuzzy msgid "" msgstr "" "Project-Id-Version: django-tables2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2013-03-19 21:56+0200\n" "Last-Translator: Serafeim Papastefanos \n" "Language-Team: el \n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "Ï€ÏοηγοÏμενη" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "επόμενη" django-tables2-2.7.5/django_tables2/locale/en/000077500000000000000000000000001473544236200210525ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/en/LC_MESSAGES/000077500000000000000000000000001473544236200226375ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/en/LC_MESSAGES/django.mo000066400000000000000000000006621473544236200244420ustar00rootroot00000000000000Þ•,<PQNZ©previousProject-Id-Version: django-tables2 Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2011-11-06 10:41+1000 Last-Translator: Bradley Ayers Language-Team: English Language: en MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit previousdjango-tables2-2.7.5/django_tables2/locale/en/LC_MESSAGES/django.po000066400000000000000000000014541473544236200244450ustar00rootroot00000000000000# This file is distributed under the same license as the django-tables2 package # msgid "" msgstr "" "Project-Id-Version: django-tables2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2011-11-06 10:41+1000\n" "Last-Translator: Bradley Ayers \n" "Language-Team: English \n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "previous" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "next" django-tables2-2.7.5/django_tables2/locale/es/000077500000000000000000000000001473544236200210575ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/es/LC_MESSAGES/000077500000000000000000000000001473544236200226445ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/es/LC_MESSAGES/django.mo000066400000000000000000000007171473544236200244500ustar00rootroot00000000000000Þ•4L`afLo ¼ÆnextpreviousProject-Id-Version: django-tables2 Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2013-08-21 07:06-0500 Last-Translator: Pablo Martín Language-Team: LANGUAGE Language: es MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit siguienteanteriordjango-tables2-2.7.5/django_tables2/locale/es/LC_MESSAGES/django.po000066400000000000000000000014541473544236200244520ustar00rootroot00000000000000# This file is distributed under the same license as the django-tables2 package # msgid "" msgstr "" "Project-Id-Version: django-tables2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2013-08-21 07:06-0500\n" "Last-Translator: Pablo Martín \n" "Language-Team: LANGUAGE \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "anterior" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "siguiente" django-tables2-2.7.5/django_tables2/locale/fa/000077500000000000000000000000001473544236200210365ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/fa/LC_MESSAGES/000077500000000000000000000000001473544236200226235ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/fa/LC_MESSAGES/django.mo000066400000000000000000000012401473544236200244170ustar00rootroot00000000000000Þ•L |¨©Ç"ç à1ù3+._Ž—That page contains no resultsThat page number is less than 1That page number is not an integernextpreviousProject-Id-Version: PO-Revision-Date: 2021-05-24 14:31+0430 Last-Translator: Language-Team: Language: fa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Poedit 2.4.1 ØµÙØ­Ù‡ شامل هیچ نتیجه ای نیستشماره ØµÙØ­Ù‡ عددی کمتر از 1 استشماره ØµÙØ­Ù‡ عددی صحیح نیستبعدیقبلیdjango-tables2-2.7.5/django_tables2/locale/fa/LC_MESSAGES/django.po000066400000000000000000000021201473544236200244200ustar00rootroot00000000000000# This file is distributed under the same license as the django-tables2 package # msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: 2021-05-24 14:23+0430\n" "PO-Revision-Date: 2021-05-24 14:31+0430\n" "Last-Translator: \n" "Language-Team: \n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.4.1\n" #: paginators.py:72 msgid "That page number is not an integer" msgstr "شماره ØµÙØ­Ù‡ عددی صحیح نیست" #: paginators.py:74 msgid "That page number is less than 1" msgstr "شماره ØµÙØ­Ù‡ عددی کمتر از 1 است" #: paginators.py:93 msgid "That page contains no results" msgstr "ØµÙØ­Ù‡ شامل هیچ نتیجه ای نیست" #: templates/django_tables2/bootstrap.html:66 #: templates/django_tables2/bootstrap4.html:66 #: templates/django_tables2/table.html:64 msgid "previous" msgstr "قبلی" #: templates/django_tables2/bootstrap.html:91 #: templates/django_tables2/bootstrap4.html:86 #: templates/django_tables2/table.html:88 msgid "next" msgstr "بعدی" django-tables2-2.7.5/django_tables2/locale/fr/000077500000000000000000000000001473544236200210575ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/fr/LC_MESSAGES/000077500000000000000000000000001473544236200226445ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/fr/LC_MESSAGES/django.mo000066400000000000000000000007551473544236200244520ustar00rootroot00000000000000Þ•4L`afioÙ ánextpreviousProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1) suivantprécédentdjango-tables2-2.7.5/django_tables2/locale/fr/LC_MESSAGES/django.po000066400000000000000000000017011473544236200244450ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: fr \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "précédent" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "suivant" django-tables2-2.7.5/django_tables2/locale/hu/000077500000000000000000000000001473544236200210645ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/hu/LC_MESSAGES/000077500000000000000000000000001473544236200226515ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/hu/LC_MESSAGES/django.mo000066400000000000000000000007601473544236200244530ustar00rootroot00000000000000Þ•4L`aflo ÜènextpreviousProject-Id-Version: django-tables2 Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2017-08-13 14:19+0200 Last-Translator: Miklos Horvath Language-Team: Hungarian Language: en MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Poedit 1.8.12 következÅ‘elÅ‘zÅ‘django-tables2-2.7.5/django_tables2/locale/hu/LC_MESSAGES/django.po000066400000000000000000000015211473544236200244520ustar00rootroot00000000000000# This file is distributed under the same license as the django-tables2 package # msgid "" msgstr "" "Project-Id-Version: django-tables2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2017-08-13 14:19+0200\n" "Last-Translator: Miklos Horvath \n" "Language-Team: Hungarian \n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.8.12\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "elÅ‘zÅ‘" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "következÅ‘" django-tables2-2.7.5/django_tables2/locale/it/000077500000000000000000000000001473544236200210645ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/it/LC_MESSAGES/000077500000000000000000000000001473544236200226515ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/it/LC_MESSAGES/django.mo000066400000000000000000000007541473544236200244560ustar00rootroot00000000000000Þ•4L`afloÜãnextpreviousProject-Id-Version: django-tables2 Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2016-04-14 11:21+0200 Last-Translator: Paolo Dina Language-Team: Italian Language: it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); avantiindietrodjango-tables2-2.7.5/django_tables2/locale/it/LC_MESSAGES/django.po000066400000000000000000000015131473544236200244530ustar00rootroot00000000000000# This file is distributed under the same license as the django-tables2 package msgid "" msgstr "" "Project-Id-Version: django-tables2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2016-04-14 11:21+0200\n" "Last-Translator: Paolo Dina \n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "indietro" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "avanti" django-tables2-2.7.5/django_tables2/locale/lt/000077500000000000000000000000001473544236200210675ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/lt/LC_MESSAGES/000077500000000000000000000000001473544236200226545ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/lt/LC_MESSAGES/django.mo000066400000000000000000000007771473544236200244660ustar00rootroot00000000000000Þ•$,8Å9Project-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3); django-tables2-2.7.5/django_tables2/locale/lt/LC_MESSAGES/django.po000066400000000000000000000026671473544236200244710ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: django-tables2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-07-28 12:40+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Robertas Murnikovas \n" "Language-Team: Lithuanian \n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < " "11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? " "1 : n % 1 != 0 ? 2: 3);\n" #: paginators.py:72 msgid "That page number is not an integer" msgstr "Puslapio numeris nÄ—ra sveikasis skaiÄius" #: paginators.py:74 msgid "That page number is less than 1" msgstr "Puslapio numeris yra mažesnis už 1" #: paginators.py:93 msgid "That page contains no results" msgstr "Puslapyje nerasta jokių rezultatų" #: templates/django_tables2/bootstrap.html:66 #: templates/django_tables2/bootstrap4.html:66 #: templates/django_tables2/table.html:64 msgid "previous" msgstr "ankstesnis" #: templates/django_tables2/bootstrap.html:91 #: templates/django_tables2/bootstrap4.html:86 #: templates/django_tables2/table.html:88 msgid "next" msgstr "kitas" django-tables2-2.7.5/django_tables2/locale/nb/000077500000000000000000000000001473544236200210475ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/nb/LC_MESSAGES/000077500000000000000000000000001473544236200226345ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/nb/LC_MESSAGES/django.mo000066400000000000000000000006761473544236200244440ustar00rootroot00000000000000Þ•4L`af@o°¶nextpreviousProject-Id-Version: django-tables2 Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2016-07-22 11:11+0200 Last-Translator: Andreas TollÃ¥nesLanguage-Team: Norwegian Bokmal Language: nb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nesteforrigedjango-tables2-2.7.5/django_tables2/locale/nb/LC_MESSAGES/django.po000066400000000000000000000014321473544236200244360ustar00rootroot00000000000000# This file is distributed under the same license as the django-tables2 package # msgid "" msgstr "" "Project-Id-Version: django-tables2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2016-07-22 11:11+0200\n" "Last-Translator: Andreas TollÃ¥nesLanguage-Team: Norwegian Bokmal \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "forrige" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "neste" django-tables2-2.7.5/django_tables2/locale/nl/000077500000000000000000000000001473544236200210615ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/nl/LC_MESSAGES/000077500000000000000000000000001473544236200226465ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/nl/LC_MESSAGES/django.mo000066400000000000000000000007711473544236200244520ustar00rootroot00000000000000Þ•4L`afyoéònextpreviousProject-Id-Version: django-tables2 Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2016-04-19 10:21+0200 Last-Translator: Jan Pieter Waagmeester Language-Team: Dutch Language: nl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); volgendevorigedjango-tables2-2.7.5/django_tables2/locale/nl/LC_MESSAGES/django.po000066400000000000000000000015301473544236200244470ustar00rootroot00000000000000# This file is distributed under the same license as the django-tables2 package msgid "" msgstr "" "Project-Id-Version: django-tables2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2016-04-19 10:21+0200\n" "Last-Translator: Jan Pieter Waagmeester \n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "vorige" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "volgende" django-tables2-2.7.5/django_tables2/locale/pl/000077500000000000000000000000001473544236200210635ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/pl/LC_MESSAGES/000077500000000000000000000000001473544236200226505ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/pl/LC_MESSAGES/django.mo000066400000000000000000000011221473544236200244430ustar00rootroot00000000000000Þ•4L`afÍo = GnextpreviousProject-Id-Version: 0.1 Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2013-08-22 09:57+0100 Last-Translator: MichaÅ‚ Pasternak Language-Team: PL Language: polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); X-Generator: Poedit 1.5.5 nastÄ™pnapoprzedniadjango-tables2-2.7.5/django_tables2/locale/pl/LC_MESSAGES/django.po000066400000000000000000000020501473544236200244470ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2013-08-22 09:57+0100\n" "Last-Translator: MichaÅ‚ Pasternak \n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 1.5.5\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "poprzednia" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "nastÄ™pna" django-tables2-2.7.5/django_tables2/locale/pt_BR/000077500000000000000000000000001473544236200214565ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/pt_BR/LC_MESSAGES/000077500000000000000000000000001473544236200232435ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/pt_BR/LC_MESSAGES/django.mo000066400000000000000000000010171473544236200250410ustar00rootroot00000000000000Þ•4L`afoýnextpreviousProject-Id-Version: 0.14.0 Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2014-02-02 00:44-0300 Last-Translator: Fabio C. Barrionuevo da Luz Language-Team: Portuguese (Brazil) Language: pt_BR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); próximoanteriordjango-tables2-2.7.5/django_tables2/locale/pt_BR/LC_MESSAGES/django.po000066400000000000000000000016701473544236200250510ustar00rootroot00000000000000# This file is distributed under the same license as the django-tables2 package # Fabio C. Barrionuevo da Luz , 2014. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: 0.14.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2014-02-02 00:44-0300\n" "Last-Translator: Fabio C. Barrionuevo da Luz \n" "Language-Team: Portuguese (Brazil) \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "anterior" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "próximo" django-tables2-2.7.5/django_tables2/locale/pt_PT/000077500000000000000000000000001473544236200214765ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/pt_PT/LC_MESSAGES/000077500000000000000000000000001473544236200232635ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/pt_PT/LC_MESSAGES/django.mo000066400000000000000000000007201473544236200250610ustar00rootroot00000000000000Þ•4L`afNo¾ÇnextpreviousProject-Id-Version: django-tables2 Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2011-11-06 10:41+1000 Last-Translator: Bradley Ayers Language-Team: English Language: en MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit seguinteAnteriordjango-tables2-2.7.5/django_tables2/locale/pt_PT/LC_MESSAGES/django.po000066400000000000000000000014741473544236200250730ustar00rootroot00000000000000# This file is distributed under the same license as the django-tables2 package # msgid "" msgstr "" "Project-Id-Version: django-tables2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2011-11-06 10:41+1000\n" "Last-Translator: Bradley Ayers \n" "Language-Team: European Portuguese \n" "Language: pt_PT\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "Anterior" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "seguinte" django-tables2-2.7.5/django_tables2/locale/ru/000077500000000000000000000000001473544236200210765ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/ru/LC_MESSAGES/000077500000000000000000000000001473544236200226635ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/ru/LC_MESSAGES/django.mo000066400000000000000000000011061473544236200244600ustar00rootroot00000000000000Þ•4L`af®o1nextpreviousProject-Id-Version: Report-Msgid-Bugs-To: PO-Revision-Date: 2020-09-18 12:51+0600 Last-Translator: Andrii Pryz Language-Team: Russian Language: ru MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); X-Generator: Poedit 2.4.1 ÑледующаÑпредыдущаÑdjango-tables2-2.7.5/django_tables2/locale/ru/LC_MESSAGES/django.po000066400000000000000000000021051473544236200244630ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2020-09-18 12:51+0600\n" "Last-Translator: Andrii Pryz \n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 2.4.1\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "предыдущаÑ" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "ÑледующаÑ" django-tables2-2.7.5/django_tables2/locale/sv/000077500000000000000000000000001473544236200211005ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/sv/LC_MESSAGES/000077500000000000000000000000001473544236200226655ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/sv/LC_MESSAGES/django.mo000066400000000000000000000007141473544236200244660ustar00rootroot00000000000000Þ•4L`afHo¸ ¿nextpreviousProject-Id-Version: django-tables2 Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2014-12-04 10:25+0100 Last-Translator: Petter Jönsson Language-Team: Swedish Language: sv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nästaföregÃ¥endedjango-tables2-2.7.5/django_tables2/locale/sv/LC_MESSAGES/django.po000066400000000000000000000014511473544236200244700ustar00rootroot00000000000000# This file is distributed under the same license as the django-tables2 package # msgid "" msgstr "" "Project-Id-Version: django-tables2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2014-12-04 10:25+0100\n" "Last-Translator: Petter Jönsson \n" "Language-Team: Swedish \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "föregÃ¥ende" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "nästa" django-tables2-2.7.5/django_tables2/locale/uk/000077500000000000000000000000001473544236200210675ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/uk/LC_MESSAGES/000077500000000000000000000000001473544236200226545ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/uk/LC_MESSAGES/django.mo000066400000000000000000000011331473544236200244510ustar00rootroot00000000000000Þ•4L`afÈo8HnextpreviousProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Andrii Pryz Language-Team: UK Language: uk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); hаÑтупнаπопереднÑdjango-tables2-2.7.5/django_tables2/locale/uk/LC_MESSAGES/django.po000066400000000000000000000020761473544236200244630ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Andrii Pryz \n" "Language-Team: Ukrainian \n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "попереднÑ" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "наÑтупна" django-tables2-2.7.5/django_tables2/locale/zh_Hans/000077500000000000000000000000001473544236200220425ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/zh_Hans/LC_MESSAGES/000077500000000000000000000000001473544236200236275ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/locale/zh_Hans/LC_MESSAGES/django.mo000066400000000000000000000006451473544236200254330ustar00rootroot00000000000000Þ•4L`af!o ‘ ›nextpreviousProject-Id-Version: Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-12 10:06+0200 PO-Revision-Date: 2018-03-19 16:20+0800 Last-Translator: Zhong Chang<726608501@qq.com> Language-Team: Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 下一页上一页django-tables2-2.7.5/django_tables2/locale/zh_Hans/LC_MESSAGES/django.po000066400000000000000000000016221473544236200254320ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-12 10:06+0200\n" "PO-Revision-Date: 2018-03-19 16:20+0800\n" "Last-Translator: Zhong Chang<726608501@qq.com>\n" "Language-Team: Simplified Chinese\n" "Language: zh_Hans\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: templates/django_tables2/bootstrap.html:64 #: templates/django_tables2/bootstrap4.html:64 #: templates/django_tables2/table.html:61 msgid "previous" msgstr "上一页" #: templates/django_tables2/bootstrap.html:89 #: templates/django_tables2/bootstrap4.html:82 #: templates/django_tables2/table.html:82 msgid "next" msgstr "下一页" django-tables2-2.7.5/django_tables2/paginators.py000066400000000000000000000103001473544236200217240ustar00rootroot00000000000000from django.core.paginator import EmptyPage, Page, PageNotAnInteger, Paginator from django.utils.translation import gettext as _ class LazyPaginator(Paginator): """ Implement lazy pagination, preventing any count() queries. By default, for any valid page, the total number of pages for the paginator will be - `current + 1` if the number of records fetched for the current page offset is bigger than the number of records per page. - `current` if the number of records fetched is less than the number of records per page. The number of additional records fetched can be adjusted using `look_ahead`, which defaults to 1 page. If you like to provide a little more extra information on how much pages follow the current page, you can use a higher value. .. note:: The number of records fetched for each page is `per_page * look_ahead + 1`, so increasing the value for `look_ahead` makes the view a bit more expensive. So:: paginator = LazyPaginator(range(10000), 10) >>> paginator.page(1).object_list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> paginator.num_pages 2 >>> paginator.page(10).object_list [91, 92, 93, 94, 95, 96, 97, 98, 99, 100] >>> paginator.num_pages 11 >>> paginator.page(1000).object_list [9991, 9992, 9993, 9994, 9995, 9996, 9997, 9998, 9999] >>> paginator.num_pages 1000 Usage with `~.SingleTableView`:: class UserListView(SingleTableView): table_class = UserTable table_data = User.objects.all() pagination_class = LazyPaginator Or with `~.RequestConfig`:: RequestConfig(paginate={"paginator_class": LazyPaginator}).configure(table) .. versionadded :: 2.0.0 """ look_ahead = 1 def __init__(self, object_list, per_page, look_ahead=None, **kwargs): self._num_pages = None self._final_num_pages = None if look_ahead is not None: self.look_ahead = look_ahead super().__init__(object_list, per_page, **kwargs) def validate_number(self, number): """Validate the given 1-based page number.""" try: if isinstance(number, float) and not number.is_integer(): raise ValueError number = int(number) except (TypeError, ValueError): raise PageNotAnInteger(_("That page number is not an integer")) if number < 1: raise EmptyPage(_("That page number is less than 1")) return number def page(self, number): # Number might be None, because the total number of pages is not known in this paginator. # If an unknown page is requested, serve the first page. number = self.validate_number(number or 1) bottom = (number - 1) * self.per_page top = bottom + self.per_page # Retrieve more objects to check if there is a next page. look_ahead_items = (self.look_ahead - 1) * self.per_page + 1 objects = list(self.object_list[bottom : top + self.orphans + look_ahead_items]) objects_count = len(objects) if objects_count > (self.per_page + self.orphans): # If another page is found, increase the total number of pages. self._num_pages = number + (objects_count // self.per_page) # In any case, return only objects for this page. objects = objects[: self.per_page] elif (number != 1) and (objects_count <= self.orphans): raise EmptyPage(_("That page contains no results")) else: # This is the last page. self._num_pages = number # For rendering purposes in `table_page_range`, we have to remember the final count self._final_num_pages = number return Page(objects, number, self) def is_last_page(self, number): return number == self._final_num_pages def _get_count(self): raise NotImplementedError count = property(_get_count) def _get_num_pages(self): return self._num_pages num_pages = property(_get_num_pages) def _get_page_range(self): raise NotImplementedError page_range = property(_get_page_range) django-tables2-2.7.5/django_tables2/rows.py000066400000000000000000000260371473544236200205650ustar00rootroot00000000000000from django.core.exceptions import FieldDoesNotExist from django.db import models from .columns.linkcolumn import BaseLinkColumn from .columns.manytomanycolumn import ManyToManyColumn from .utils import A, AttributeDict, call_with_appropriate, computed_values class CellAccessor: """ Allows accessing cell contents on a row object (see `BoundRow`) """ def __init__(self, row): self.row = row def __getitem__(self, key): return self.row.get_cell(key) def __getattr__(self, name): return self.row.get_cell(name) class BoundRow: """ Represents a *specific* row in a table. `.BoundRow` objects are a container that make it easy to access the final 'rendered' values for cells in a row. You can simply iterate over a `.BoundRow` object and it will take care to return values rendered using the correct method (e.g. :ref:`table.render_FOO`) To access the rendered value of each cell in a row, just iterate over it:: >>> import django_tables2 as tables >>> class SimpleTable(tables.Table): ... a = tables.Column() ... b = tables.CheckBoxColumn(attrs={'name': 'my_chkbox'}) ... >>> table = SimpleTable([{'a': 1, 'b': 2}]) >>> row = table.rows[0] # we only have one row, so let's use it >>> for cell in row: ... print(cell) ... 1 Alternatively you can use row.cells[0] to retrieve a specific cell:: >>> row.cells[0] 1 >>> row.cells[1] '' >>> row.cells[2] ... IndexError: list index out of range Finally you can also use the column names to retrieve a specific cell:: >>> row.cells.a 1 >>> row.cells.b '' >>> row.cells.c ... KeyError: "Column with name 'c' does not exist; choices are: ['a', 'b']" If you have the column name in a variable, you can also treat the `cells` property like a `dict`:: >>> key = 'a' >>> row.cells[key] 1 Arguments: table: The `.Table` in which this row exists. record: a single record from the :term:`table data` that is used to populate the row. A record could be a `~django.db.Model` object, a `dict`, or something else. """ def __init__(self, record, table): self._record = record self._table = table self.row_counter = next(table._counter) # support accessing cells from a template: {{ row.cells.column_name }} self.cells = CellAccessor(self) @property def table(self): """The `.Table` this row is part of.""" return self._table def get_even_odd_css_class(self): """ Return css class, alternating for odd and even records. Return: string: `even` for even records, `odd` otherwise. """ return "odd" if self.row_counter % 2 else "even" @property def attrs(self): """Return the attributes for a certain row.""" cssClass = self.get_even_odd_css_class() row_attrs = computed_values( self._table.row_attrs, kwargs=dict(table=self._table, record=self._record) ) if "class" in row_attrs and row_attrs["class"]: row_attrs["class"] += " " + cssClass else: row_attrs["class"] = cssClass return AttributeDict(row_attrs) @property def record(self): """The data record from the data source which is used to populate this row with data.""" return self._record def __iter__(self): """ Iterate over the rendered values for cells in the row. Under the hood this method just makes a call to `.BoundRow.__getitem__` for each cell. """ for column, value in self.items(): # this uses __getitem__, using the name (rather than the accessor) # is correct – it's what __getitem__ expects. yield value def _get_and_render_with(self, bound_column, render_func, default): value = None accessor = A(bound_column.accessor) column = bound_column.column # We need to take special care here to allow get_FOO_display() # methods on a model to be used if available. See issue #30. penultimate, remainder = accessor.penultimate(self.record) # If the penultimate is a model and the remainder is a field # using choices, use get_FOO_display(). if isinstance(penultimate, models.Model): try: field = accessor.get_field(self.record) display_fn = getattr(penultimate, f"get_{remainder}_display", None) if getattr(field, "choices", ()) and display_fn: value = display_fn() remainder = None except FieldDoesNotExist: pass # Fall back to just using the original accessor if remainder: try: value = accessor.resolve(self.record) except Exception: # we need to account for non-field based columns (issue #257) if isinstance(column, BaseLinkColumn) and column.text is not None: return render_func(bound_column) is_manytomanycolumn = isinstance(column, ManyToManyColumn) if value in column.empty_values or (is_manytomanycolumn and not value.exists()): return default return render_func(bound_column, value) def _optional_cell_arguments(self, bound_column, value): """ Defines the arguments that will optionally be passed while calling the cell's rendering or value getter if that function has one of these as a keyword argument. """ return { "value": value, "record": self.record, "column": bound_column.column, "bound_column": bound_column, "bound_row": self, "table": self._table, } def get_cell(self, name): """ Returns the final rendered html for a cell in the row, given the name of a column. """ bound_column = self.table.columns[name] return self._get_and_render_with( bound_column, render_func=self._call_render, default=bound_column.default ) def _call_render(self, bound_column, value=None): """ Call the column's render method with appropriate kwargs """ render_kwargs = self._optional_cell_arguments(bound_column, value) content = call_with_appropriate(bound_column.render, render_kwargs) return bound_column.link(content, **render_kwargs) if bound_column.link else content def get_cell_value(self, name): """ Returns the final rendered value (excluding any html) for a cell in the row, given the name of a column. """ return self._get_and_render_with( self.table.columns[name], render_func=self._call_value, default=None ) def _call_value(self, bound_column, value=None): """ Call the column's value method with appropriate kwargs """ return call_with_appropriate( bound_column.value, self._optional_cell_arguments(bound_column, value) ) def __contains__(self, item): """ Check by both row object and column name. """ return item in (self.table.columns if isinstance(item, str) else self) def items(self): """ Returns iterator yielding ``(bound_column, cell)`` pairs. *cell* is ``row[name]`` -- the rendered unicode value that should be ``rendered within ````. """ for column in self.table.columns: # column gets some attributes relevant only relevant in this iteration, # used to allow passing the value/record to a callable Column.attrs / # Table.attrs item. column.current_value = self.get_cell(column.name) column.current_record = self.record yield (column, column.current_value) class BoundPinnedRow(BoundRow): """ Represents a *pinned* row in a table. """ @property def attrs(self): """ Return the attributes for a certain pinned row. Add CSS classes `pinned-row` and `odd` or `even` to `class` attribute. Return: AttributeDict: Attributes for pinned rows. """ row_attrs = computed_values(self._table.pinned_row_attrs, kwargs={"record": self._record}) css_class = " ".join( [self.get_even_odd_css_class(), "pinned-row", row_attrs.get("class", "")] ) row_attrs["class"] = css_class return AttributeDict(row_attrs) class BoundRows: """ Container for spawning `.BoundRow` objects. Arguments: data: iterable of records table: the `~.Table` in which the rows exist pinned_data: dictionary with iterable of records for top and/or bottom pinned rows. Example: >>> pinned_data = { ... 'top': iterable, # or None value ... 'bottom': iterable, # or None value ... } This is used for `~.Table.rows`. """ def __init__(self, data, table, pinned_data=None): self.data = data self.table = table self.pinned_data = pinned_data or {} def generator_pinned_row(self, data): """ Top and bottom pinned rows generator. Arguments: data: Iterable data for all records for top or bottom pinned rows. Yields: BoundPinnedRow: Top or bottom `BoundPinnedRow` object for single pinned record. """ if data is not None: if hasattr(data, "__iter__") is False: raise ValueError("The data for pinned rows must be iterable") for pinned_record in data: yield BoundPinnedRow(pinned_record, table=self.table) def __iter__(self): # Top pinned rows yield from self.generator_pinned_row(self.pinned_data.get("top")) for record in self.data: yield BoundRow(record, table=self.table) # Bottom pinned rows yield from self.generator_pinned_row(self.pinned_data.get("bottom")) def __len__(self): length = len(self.data) pinned_top = self.pinned_data.get("top") pinned_bottom = self.pinned_data.get("bottom") length += 0 if pinned_top is None else len(pinned_top) length += 0 if pinned_bottom is None else len(pinned_bottom) return length def __getitem__(self, key): """ Slicing returns a new `~.BoundRows` instance, indexing returns a single `~.BoundRow` instance. """ if isinstance(key, slice): return BoundRows(data=self.data[key], table=self.table, pinned_data=self.pinned_data) else: return BoundRow(record=self.data[key], table=self.table) django-tables2-2.7.5/django_tables2/static/000077500000000000000000000000001473544236200205005ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/static/django_tables2/000077500000000000000000000000001473544236200233565ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/static/django_tables2/bootstrap.css000066400000000000000000000002471473544236200261100ustar00rootroot00000000000000.table-container th.asc:after { content: '\0000a0\0025b2'; float: right; } .table-container th.desc:after { content: '\0000a0\0025bc'; float: right; } django-tables2-2.7.5/django_tables2/static/django_tables2/themes/000077500000000000000000000000001473544236200246435ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/static/django_tables2/themes/paleblue/000077500000000000000000000000001473544236200264345ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/static/django_tables2/themes/paleblue/css/000077500000000000000000000000001473544236200272245ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/static/django_tables2/themes/paleblue/css/screen.css000066400000000000000000000053261473544236200312230ustar00rootroot00000000000000table.paleblue { border-collapse: collapse; border-color: #CCC; border: 1px solid #DDD; } table.paleblue, table.paleblue + ul.pagination { font: normal 11px/14px 'Lucida Grande', Verdana, Helvetica, Arial, sans-serif; } table.paleblue a:link, table.paleblue a:visited, table.paleblue + ul.pagination > li > a { color: #5B80B2; text-decoration: none; font-weight: bold; } table.paleblue a:hover { color: #036; } table.paleblue td, table.paleblue th { padding: 5px; line-height: 13px; border-bottom: 1px solid #EEE; border-left: 1px solid #DDD; text-align: left; } table.paleblue thead th:first-child, table.paleblue thead td:first-child { border-left: none !important; } table.paleblue thead th, table.paleblue thead td { background: #FCFCFC url(../img/header-bg.png) left bottom repeat-x; border-bottom: 1px solid #DDD; padding: 2px 5px; font-size: 11px; vertical-align: middle; color: #666; } table.paleblue thead th > a:link, table.paleblue thead th > a:visited { color: #666; } table.paleblue thead th.orderable > a { padding-right: 20px; background: url(../img/arrow-inactive-up.png) right center no-repeat; } table.paleblue thead th.orderable.asc > a { background-image: url(../img/arrow-active-up.png); } table.paleblue thead th.orderable.desc > a { background-image: url(../img/arrow-active-down.png); } table.paleblue tr.odd { background-color: #EDF3FE; } table.paleblue tr.even { background-color: white; } table.paleblue + ul.pagination { background: white url(../img/pagination-bg.gif) left 180% repeat-x; overflow: auto; margin: 0; padding: 10px; border: 1px solid #DDD; list-style: none; } table.paleblue + ul.pagination > li { float: left; line-height: 22px; margin-left: 10px; } table.paleblue + ul.pagination > li:first-child { margin-left: 0; } table.paleblue + ul.pagination > li.cardinality { float: right; color: #8d8d8d; } table.paleblue > tbody > tr > td > span.true, table.paleblue > tbody > tr > td > span.false { background-position: top left; background-repeat: no-repeat; display: inline-block; height: 10px; overflow: hidden; text-indent: -200px; width: 10px; } table.paleblue > tbody > tr > td > .missing { background: transparent url(../img/missing.png) right center no-repeat; color: #717171; padding-right: 20px; } table.paleblue > tbody > tr > td > .missing:hover { color: #333; } table.paleblue > tbody > tr > td > span.true { background-image: url(../img/true.gif); } table.paleblue > tbody > tr > td > span.false { background-image: url(../img/false.gif); } div.table-container { display: inline-block; } django-tables2-2.7.5/django_tables2/static/django_tables2/themes/paleblue/img/000077500000000000000000000000001473544236200272105ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/static/django_tables2/themes/paleblue/img/arrow-active-down.png000066400000000000000000000003301473544236200332620ustar00rootroot00000000000000‰PNG  IHDR úðÆtEXtSoftwareAdobe ImageReadyqÉe<zIDATxÚbÔkù¿™Á‡?ØÂR´8æ ^U±Kx|A  ©ÿ»}¿ÿ0€…XþƒéÒÍ\ ‡r>22Ât:üdxÿ ÌäüÇЀáR #㇑­)vÓøfïºÁVbc(„)ÑÈŠ@ ÀÃÁ.F` "LIEND®B`‚django-tables2-2.7.5/django_tables2/static/django_tables2/themes/paleblue/img/arrow-active-up.png000066400000000000000000000003121473544236200327370ustar00rootroot00000000000000‰PNG  IHDR úðÆtEXtSoftwareAdobe ImageReadyqÉe<lIDATxÚbd@z-ÿÿƒèK5ŒŒ úÇ`q&tEnÿ@¦A@@U!H¢Ðá'ƒÿ_0±aŠA€¦¨Û÷X@Œ÷Tâ?H¬”¤ø###HÑâ˜/ ø@ì°‰[€ ü` @€š=.E!c¥IEND®B`‚arrow-inactive-down.png000066400000000000000000000003661473544236200335430ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/static/django_tables2/themes/paleblue/img‰PNG  IHDRóÿatEXtSoftwareAdobe ImageReadyqÉe<˜IDATxÚbüÿÿ?%€‰B0 `Aæ,\¸p3ò! gK||¼/V@šðêÞ°aŠŒÈÑÈÈÈȰ`Á‚ÿ®®®`þ¿ÿ þd‚øt÷îÝ @Ûñ C,--¾ÿæsrr2?~œ!!!=Ý`5fˆŠŠ ˜}çΰf›h`†€h\š1 MÊ䀨RB¥ëÄŒþIEND®B`‚django-tables2-2.7.5/django_tables2/static/django_tables2/themes/paleblue/img/arrow-inactive-up.png000066400000000000000000000003221473544236200332670ustar00rootroot00000000000000‰PNG  IHDR úðÆtEXtSoftwareAdobe ImageReadyqÉe<tIDATxÚ|± À ß( {°Ð¥`(Ò1#ÑA°RD‘—¬÷Kg?áPJi°Ç‰} ‰P'dŒÏ> ô…¬µh­IÖZ£Ö*Ÿù+mÈ9'@ï}U©UVJAˆ!ï=þ”sÆ5ý™Ë=¯Ó .>6WìIEND®B`‚django-tables2-2.7.5/django_tables2/static/django_tables2/themes/paleblue/img/false.gif000066400000000000000000000002601473544236200307670ustar00rootroot00000000000000GIF89a ÄÞ!!ÿþþÿÿÿá33à..ñ¤¤äHHõ¾¾ç``èbbò©©æXXöÀÀçZZêuuó®®õ»»ï‘‘êrrî!ù, -  È@4(D@ë:ãÎ@‘Ю1Aï·;ßpà cM0ÁITa¡…;django-tables2-2.7.5/django_tables2/static/django_tables2/themes/paleblue/img/header-bg.png000066400000000000000000000002021473544236200315260ustar00rootroot00000000000000‰PNG  IHDRStEXtSoftwareAdobe ImageReadyqÉe<$IDATxÚbüÿÿ?‰€‰t0ª‡…NaMŽ=£zÈÓ`>ð …IEND®B`‚django-tables2-2.7.5/django_tables2/static/django_tables2/themes/paleblue/img/missing.png000066400000000000000000000007751473544236200314000ustar00rootroot00000000000000‰PNG  IHDRóÿatEXtSoftwareAdobe ImageReadyqÉe<ŸIDATxÚ|S=KAÝ|`ªÓLþ„U~€`¥XD¸J¯‰ w(Ò†TB9,r zM‘€´,l-ƒJ4—ƒ˜BÖ7›œ¬kî^ffßÜËìî,㜳(ô Ú~OH²©]æSÀ°®}qÖ–ø"pOuL²_­Wÿ{Ä“'ÀíE>+D yq8¦:Y AmÈv¶´¸ çËSo/ÀîÞûÇSmÖ¾NsÙœ’/Dè Óé4¡e*Ú®aV'¢V«Õx|0PLk$ÅÑwħ±`–Ëe*TwçÒxÜŸ¨Þó`` tag. When accessing the attribute, the value is always returned as an `.AttributeDict` to allow easily conversion to HTML. row_attrs (dict): Add custom html attributes to the table rows. Allows custom HTML attributes to be specified which will be added to the ```` tag of the rendered table. pinned_row_attrs (dict): Same as row_attrs but for pinned rows. sequence (iterable): The sequence/order of columns the columns (from left to right). Items in the sequence must be :term:`column names `, or `"..."` (string containing three periods). `'...'` can be used as a catch-all for columns that are not specified. prefix (str): A prefix for query string fields. To avoid name-clashes when using multiple tables on single page. order_by_field (str): If not `None`, defines the name of the *order by* query string field in the URL. page_field (str): If not `None`, defines the name of the *current page* query string field. per_page_field (str): If not `None`, defines the name of the *per page* query string field. template_name (str): The template to render when using ``{% render_table %}`` (defaults to DJANGO_TABLES2_TEMPLATE, which is ``"django_tables2/table.html"`` by default). default (str): Text to render in empty cells (determined by `.Column.empty_values`, default `.Table.Meta.default`) request: Django's request to avoid using `RequestConfig` show_header (bool): If `False`, the table will not have a header (``), defaults to `True` show_footer (bool): If `False`, the table footer will not be rendered, even if some columns have a footer, defaults to `True`. extra_columns (str, `.Column`): list of `(name, column)`-tuples containing extra columns to add to the instance. If `column` is `None`, the column with `name` will be removed from the table. """ def __init__( self, data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None, ): super().__init__() # note that although data is a keyword argument, it used to be positional # so it is assumed to be the first argument to this method. if data is None: raise TypeError(f"Argument data to {type(self).__name__} is required") self.exclude = exclude or self._meta.exclude self.sequence = sequence self.data = TableData.from_data(data=data) self.data.set_table(self) if default is None: default = self._meta.default self.default = default # Pinned rows #406 self.pinned_row_attrs = AttributeDict(pinned_row_attrs or self._meta.pinned_row_attrs) self.pinned_data = { "top": self.get_top_pinned_data(), "bottom": self.get_bottom_pinned_data(), } self.rows = BoundRows(data=self.data, table=self, pinned_data=self.pinned_data) self.attrs = AttributeDict(attrs if attrs is not None else self._meta.attrs) for tag in ["thead", "tbody", "tfoot"]: # Add these attrs even if they haven't been passed so we can safely refer to them in the templates self.attrs[tag] = AttributeDict(self.attrs.get(tag, {})) self.row_attrs = AttributeDict(row_attrs or self._meta.row_attrs) self.empty_text = empty_text if empty_text is not None else self._meta.empty_text self.orderable = orderable self.prefix = prefix self.order_by_field = order_by_field self.page_field = page_field self.per_page_field = per_page_field self.show_header = show_header self.show_footer = show_footer # Make a copy so that modifying this will not touch the class # definition. Note that this is different from forms, where the # copy is made available in a ``fields`` attribute. base_columns = copy.deepcopy(type(self).base_columns) if extra_columns is not None: for name, column in extra_columns: if column is None and name in base_columns: del base_columns[name] else: base_columns[name] = column # Keep fully expanded ``sequence`` at _sequence so it's easily accessible # during render. The priority is as follows: # 1. sequence passed in as an argument # 2. sequence declared in ``Meta`` # 3. sequence defaults to '...' if sequence is not None: sequence = sequence elif self._meta.sequence: sequence = self._meta.sequence else: if self._meta.fields is not None: sequence = tuple(self._meta.fields) + ("...",) else: sequence = ("...",) sequence = Sequence(sequence) self._sequence = sequence.expand(base_columns.keys()) # reorder columns based on sequence. base_columns = OrderedDict((x, base_columns[x]) for x in sequence if x in base_columns) self.columns = BoundColumns(self, base_columns) # `None` value for order_by means no order is specified. This means we # `shouldn't touch our data's ordering in any way. *However* # `table.order_by = None` means "remove any ordering from the data" # (it's equivalent to `table.order_by = ()`). if order_by is None and self._meta.order_by is not None: order_by = self._meta.order_by if order_by is None: self._order_by = None # If possible inspect the ordering on the data we were given and # update the table to reflect that. order_by = self.data.ordering if order_by is not None: self.order_by = order_by else: self.order_by = order_by self.template_name = template_name # If a request is passed, configure for request if request: RequestConfig(request).configure(self) self._counter = count() def get_top_pinned_data(self): """ Return data for top pinned rows containing data for each row. Iterable type like: QuerySet, list of dicts, list of objects. Having a non-zero number of pinned rows will not result in an empty result set message being rendered, even if there are no regular data rows Returns: `None` (default) no pinned rows at the top, iterable, data for pinned rows at the top. Note: To show pinned row this method should be overridden. Example: >>> class TableWithTopPinnedRows(Table): ... def get_top_pinned_data(self): ... return [{ ... "column_a" : "some value", ... "column_c" : "other value", ... }] """ return None def get_bottom_pinned_data(self): """ Return data for bottom pinned rows containing data for each row. Iterable type like: QuerySet, list of dicts, list of objects. Having a non-zero number of pinned rows will not result in an empty result set message being rendered, even if there are no regular data rows Returns: `None` (default) no pinned rows at the bottom, iterable, data for pinned rows at the bottom. Note: To show pinned row this method should be overridden. Example: >>> class TableWithBottomPinnedRows(Table): ... def get_bottom_pinned_data(self): ... return [{ ... "column_a" : "some value", ... "column_c" : "other value", ... }] """ return None def before_render(self, request): """ A way to hook into the moment just before rendering the template. Can be used to hide a column. Arguments: request: contains the `WGSIRequest` instance, containing a `user` attribute if `.django.contrib.auth.middleware.AuthenticationMiddleware` is added to your `MIDDLEWARE_CLASSES`. Example:: class Table(tables.Table): name = tables.Column(orderable=False) country = tables.Column(orderable=False) def before_render(self, request): if request.user.has_perm('foo.delete_bar'): self.columns.hide('country') else: self.columns.show('country') """ return def as_html(self, request): """ Render the table to an HTML table, adding `request` to the context. """ # reset counter for new rendering self._counter = count() template = get_template(self.template_name) context = {"table": self, "request": request} self.before_render(request) return template.render(context) def as_values(self, exclude_columns=None): """ Return a row iterator of the data which would be shown in the table where the first row is the table headers. arguments: exclude_columns (iterable): columns to exclude in the data iterator. This can be used to output the table data as CSV, excel, for example using the `~.export.ExportMixin`. If a column is defined using a :ref:`table.render_FOO`, the returned value from that method is used. If you want to differentiate between the rendered cell and a value, use a `value_Foo`-method:: class Table(tables.Table): name = tables.Column() def render_name(self, value): return format_html('{}', value) def value_name(self, value): return value will have a value wrapped in `` in the rendered HTML, and just returns the value when `as_values()` is called. Note that any invisible columns will be part of the row iterator. """ if exclude_columns is None: exclude_columns = () columns = [ column for column in self.columns.iterall() if not (column.column.exclude_from_export or column.name in exclude_columns) ] yield [force_str(column.header, strings_only=True) for column in columns] for row in self.rows: yield [ force_str(row.get_cell_value(column.name), strings_only=True) for column in columns ] def has_footer(self): """ Returns True if any of the columns define a ``_footer`` attribute or a ``render_footer()`` method """ return self.show_footer and any(column.has_footer() for column in self.columns) @property def show_header(self): return self._show_header if self._show_header is not None else self._meta.show_header @show_header.setter def show_header(self, value): self._show_header = value @property def order_by(self): return self._order_by @order_by.setter def order_by(self, value): """ Order the rows of the table based on columns. Arguments: value: iterable or comma separated string of order by aliases. """ # collapse empty values to () order_by = () if not value else value # accept string order_by = order_by.split(",") if isinstance(order_by, str) else order_by valid = [] # everything's been converted to a iterable, accept iterable! for alias in order_by: name = OrderBy(alias).bare if name in self.columns and self.columns[name].orderable: valid.append(alias) self._order_by = OrderByTuple(valid) self.data.order_by(self._order_by) @property def order_by_field(self): return ( self._order_by_field if self._order_by_field is not None else self._meta.order_by_field ) @order_by_field.setter def order_by_field(self, value): self._order_by_field = value @property def page_field(self): return self._page_field if self._page_field is not None else self._meta.page_field @page_field.setter def page_field(self, value): self._page_field = value def paginate(self, paginator_class=Paginator, per_page=None, page=1, *args, **kwargs): """ Paginates the table using a paginator and creates a ``page`` property containing information for the current page. Arguments: paginator_class (`~django.core.paginator.Paginator`): A paginator class to paginate the results. per_page (int): Number of records to display on each page. page (int): Page to display. Extra arguments are passed to the paginator. Pagination exceptions (`~django.core.paginator.EmptyPage` and `~django.core.paginator.PageNotAnInteger`) may be raised from this method and should be handled by the caller. """ per_page = per_page or self._meta.per_page self.paginator = paginator_class(self.rows, per_page, *args, **kwargs) self.page = self.paginator.page(page) return self @property def per_page_field(self): return ( self._per_page_field if self._per_page_field is not None else self._meta.per_page_field ) @per_page_field.setter def per_page_field(self, value): self._per_page_field = value @property def prefix(self): return self._prefix if self._prefix is not None else self._meta.prefix @prefix.setter def prefix(self, value): self._prefix = value @property def prefixed_order_by_field(self): return f"{self.prefix}{self.order_by_field}" @property def prefixed_page_field(self): return f"{self.prefix}{self.page_field}" @property def prefixed_per_page_field(self): return f"{self.prefix}{self.per_page_field}" @property def sequence(self): return self._sequence @sequence.setter def sequence(self, value): if value: value = Sequence(value) value.expand(self.base_columns.keys()) self._sequence = value @property def orderable(self): if self._orderable is not None: return self._orderable else: return self._meta.orderable @orderable.setter def orderable(self, value): self._orderable = value @property def template_name(self): if self._template is not None: return self._template else: return self._meta.template_name @template_name.setter def template_name(self, value): self._template = value @property def paginated_rows(self): """ Return the rows for the current page if the table is paginated, else all rows. """ if hasattr(self, "page"): return self.page.object_list return self.rows def get_column_class_names(self, classes_set, bound_column): """ Returns a set of HTML class names for cells (both ``td`` and ``th``) of a **bound column** in this table. By default this returns the column class names defined in the table's attributes. This method can be overridden to change the default behavior, for example to simply `return classes_set`. Arguments: classes_set(set of string): a set of class names to be added to the cell, retrieved from the column's attributes. In the case of a header cell (th), this also includes ordering classes. To set the classes for a column, see `.Column`. To configure ordering classes, see :ref:`ordering-class-name` bound_column(`.BoundColumn`): the bound column the class names are determined for. Useful for accessing `bound_column.name`. Returns: A set of class names to be added to cells of this column If you want to add the column names to the list of classes for a column, override this method in your custom table:: class MyTable(tables.Table): ... def get_column_class_names(self, classes_set, bound_column): classes_set = super().get_column_class_names(classes_set, bound_column) classes_set.add(bound_column.name) return classes_set """ return classes_set def table_factory(model, table=Table, fields=None, exclude=None, localize=None): """ Return Table class for given `model`, equivalent to defining a custom table class:: class MyTable(tables.Table): class Meta: model = model Arguments: model (`~django.db.models.Model`): Model associated with the new table table (`.Table`): Base Table class used to create the new one fields (list of str): Fields displayed in tables exclude (list of str): Fields exclude in tables localize (list of str): Fields to localize """ attrs = {"model": model} if fields is not None: attrs["fields"] = fields if exclude is not None: attrs["exclude"] = exclude if localize is not None: attrs["localize"] = localize # If parent form class already has an inner Meta, the Meta we're # creating needs to inherit from the parent's inner meta. parent = (table.Meta, object) if hasattr(table, "Meta") else (object,) Meta = type("Meta", parent, attrs) # Give this new table class a reasonable name. class_name = model.__name__ + "AutogeneratedTable" # Class attributes for the new table class. table_class_attrs = {"Meta": Meta} return type(table)(class_name, (table,), table_class_attrs) django-tables2-2.7.5/django_tables2/templates/000077500000000000000000000000001473544236200212075ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/templates/django_tables2/000077500000000000000000000000001473544236200240655ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/templates/django_tables2/bootstrap-responsive.html000066400000000000000000000005731473544236200311700ustar00rootroot00000000000000{% extends 'django_tables2/bootstrap.html' %} {% block table-wrapper %}
{% block table %} {{ block.super }} {% endblock table %} {% block pagination %} {% if table.page and table.paginator.num_pages > 1 %} {{ block.super }} {% endif %} {% endblock pagination %}
{% endblock table-wrapper %} django-tables2-2.7.5/django_tables2/templates/django_tables2/bootstrap.html000066400000000000000000000106041473544236200267710ustar00rootroot00000000000000{% load django_tables2 %} {% load i18n l10n %} {% block table-wrapper %}
{% block table %} {% block table.thead %} {% if table.show_header %} {% for column in table.columns %} {% endfor %} {% endif %} {% endblock table.thead %} {% block table.tbody %} {% for row in table.paginated_rows %} {% block table.tbody.row %} {% for column, cell in row.items %} {% endfor %} {% endblock table.tbody.row %} {% empty %} {% if table.empty_text %} {% block table.tbody.empty_text %} {% endblock table.tbody.empty_text %} {% endif %} {% endfor %} {% endblock table.tbody %} {% block table.tfoot %} {% if table.has_footer %} {% for column in table.columns %} {% endfor %} {% endif %} {% endblock table.tfoot %}
{% if column.orderable %} {{ column.header }} {% else %} {{ column.header }} {% endif %}
{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}
{{ table.empty_text }}
{{ column.footer }}
{% endblock table %} {% block pagination %} {% if table.page and table.paginator.num_pages > 1 %} {% endif %} {% endblock pagination %}
{% endblock table-wrapper %} django-tables2-2.7.5/django_tables2/templates/django_tables2/bootstrap4-responsive.html000066400000000000000000000005741473544236200312550ustar00rootroot00000000000000{% extends 'django_tables2/bootstrap4.html' %} {% block table-wrapper %}
{% block table %} {{ block.super }} {% endblock table %} {% block pagination %} {% if table.page and table.paginator.num_pages > 1 %} {{ block.super }} {% endif %} {% endblock pagination %}
{% endblock table-wrapper %} django-tables2-2.7.5/django_tables2/templates/django_tables2/bootstrap4.html000066400000000000000000000102511473544236200270530ustar00rootroot00000000000000{% load django_tables2 %} {% load i18n l10n %} {% block table-wrapper %}
{% block table %} {% block table.thead %} {% if table.show_header %} {% for column in table.columns %} {% endfor %} {% endif %} {% endblock table.thead %} {% block table.tbody %} {% for row in table.paginated_rows %} {% block table.tbody.row %} {% for column, cell in row.items %} {% endfor %} {% endblock table.tbody.row %} {% empty %} {% if table.empty_text %} {% block table.tbody.empty_text %} {% endblock table.tbody.empty_text %} {% endif %} {% endfor %} {% endblock table.tbody %} {% block table.tfoot %} {% if table.has_footer %} {% for column in table.columns %} {% endfor %} {% endif %} {% endblock table.tfoot %}
{% if column.orderable %} {{ column.header }} {% else %} {{ column.header }} {% endif %}
{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}
{{ table.empty_text }}
{{ column.footer }}
{% endblock table %} {% block pagination %} {% if table.page and table.paginator.num_pages > 1 %} {% endif %} {% endblock pagination %}
{% endblock table-wrapper %} django-tables2-2.7.5/django_tables2/templates/django_tables2/bootstrap5-responsive.html000066400000000000000000000004721473544236200312530ustar00rootroot00000000000000{% extends 'django_tables2/bootstrap5.html' %} {% block table-wrapper %}
{% block table %} {{ block.super }} {% endblock table %} {% block pagination %} {{ block.super }} {% endblock pagination %}
{% endblock table-wrapper %} django-tables2-2.7.5/django_tables2/templates/django_tables2/bootstrap5.html000066400000000000000000000102651473544236200270610ustar00rootroot00000000000000{% load django_tables2 %} {% load i18n l10n %} {% block table-wrapper %}
{% block table %} {% block table.thead %} {% if table.show_header %} {% for column in table.columns %} {% endfor %} {% endif %} {% endblock table.thead %} {% block table.tbody %} {% for row in table.paginated_rows %} {% block table.tbody.row %} {% for column, cell in row.items %} {% endfor %} {% endblock table.tbody.row %} {% empty %} {% if table.empty_text %} {% block table.tbody.empty_text %} {% endblock table.tbody.empty_text %} {% endif %} {% endfor %} {% endblock table.tbody %} {% block table.tfoot %} {% if table.has_footer %} {% for column in table.columns %} {% endfor %} {% endif %} {% endblock table.tfoot %}
{% if column.orderable %} {{ column.header }} {% else %} {{ column.header }} {% endif %}
{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}
{{ table.empty_text }}
{{ column.footer }}
{% endblock table %} {% block pagination %} {% if table.page and table.paginator.num_pages > 1 %} {% endif %} {% endblock pagination %}
{% endblock table-wrapper %} django-tables2-2.7.5/django_tables2/templates/django_tables2/semantic.html000066400000000000000000000112711473544236200265600ustar00rootroot00000000000000{% load django_tables2 %} {% load i18n l10n %} {% block table-wrapper %}
{% block table %} {% block table.thead %} {% if table.show_header %} {% for column in table.columns %} {% endfor %} {% endif %} {% endblock table.thead %} {% block table.tbody %} {% for row in table.paginated_rows %} {% block table.tbody.row %} {% for column, cell in row.items %} {% endfor %} {% endblock table.tbody.row %} {% empty %} {% if table.empty_text %} {% block table.tbody.empty_text %} {% endblock table.tbody.empty_text %} {% endif %} {% endfor %} {% endblock table.tbody %} {% block table.tfoot %} {% if table.has_footer %} {% for column in table.columns %} {% endfor %} {% endif %} {% block pagination %} {% if table.page and table.paginator.num_pages > 1 %} {% endif %} {% endblock pagination %} {% endblock table.tfoot %}
{% if column.orderable %} {{ column.header }} {% else %} {{ column.header }} {% endif %}
{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}
{{ table.empty_text }}
{{ column.footer }}
{% endblock table %}
{% endblock table-wrapper %} django-tables2-2.7.5/django_tables2/templates/django_tables2/table.html000066400000000000000000000101561473544236200260450ustar00rootroot00000000000000{% load django_tables2 %} {% load i18n l10n %} {% block table-wrapper %}
{% block table %} {% block table.thead %} {% if table.show_header %} {% for column in table.columns %} {% endfor %} {% endif %} {% endblock table.thead %} {% block table.tbody %} {% for row in table.paginated_rows %} {% block table.tbody.row %} {% for column, cell in row.items %} {% endfor %} {% endblock table.tbody.row %} {% empty %} {% if table.empty_text %} {% block table.tbody.empty_text %} {% endblock table.tbody.empty_text %} {% endif %} {% endfor %} {% endblock table.tbody %} {% block table.tfoot %} {% if table.has_footer %} {% for column in table.columns %} {% endfor %} {% endif %} {% endblock table.tfoot %}
{% if column.orderable %} {{ column.header }} {% else %} {{ column.header }} {% endif %}
{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}
{{ table.empty_text }}
{{ column.footer }}
{% endblock table %} {% block pagination %} {% if table.page and table.paginator.num_pages > 1 %}
    {% if table.page.has_previous %} {% block pagination.previous %} {% endblock pagination.previous %} {% endif %} {% if table.page.has_previous or table.page.has_next %} {% block pagination.range %} {% for p in table.page|table_page_range:table.paginator %}
  • {% if p == '...' %} {{ p }} {% else %} {{ p }} {% endif %}
  • {% endfor %} {% endblock pagination.range %} {% endif %} {% if table.page.has_next %} {% block pagination.next %} {% endblock pagination.next %} {% endif %}
{% endif %} {% endblock pagination %}
{% endblock table-wrapper %} django-tables2-2.7.5/django_tables2/templatetags/000077500000000000000000000000001473544236200217035ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/templatetags/__init__.py000066400000000000000000000000001473544236200240020ustar00rootroot00000000000000django-tables2-2.7.5/django_tables2/templatetags/django_tables2.py000066400000000000000000000207221473544236200251360ustar00rootroot00000000000000import re from collections import OrderedDict from django import template from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.template import Node, TemplateSyntaxError from django.template.loader import get_template, select_template from django.templatetags.l10n import register as l10n_register from django.utils.html import escape from django.utils.http import urlencode import django_tables2 as tables from django_tables2.paginators import LazyPaginator from django_tables2.utils import AttributeDict register = template.Library() kwarg_re = re.compile(r"(?:(.+)=)?(.+)") context_processor_error_msg = ( "Tag {%% %s %%} requires django.template.context_processors.request to be " "in the template configuration in " "settings.TEMPLATES[]OPTIONS.context_processors) in order for the included " "template tags to function correctly." ) def token_kwargs(bits, parser): """ Based on Django's `~django.template.defaulttags.token_kwargs`, but with a few changes: - No legacy mode. - Both keys and values are compiled as a filter """ if not bits: return {} kwargs = OrderedDict() while bits: match = kwarg_re.match(bits[0]) if not match or not match.group(1): return kwargs key, value = match.groups() del bits[:1] kwargs[parser.compile_filter(key)] = parser.compile_filter(value) return kwargs class QuerystringNode(Node): def __init__(self, updates, removals, asvar=None): super().__init__() self.updates = updates self.removals = removals self.asvar = asvar def render(self, context): if "request" not in context: raise ImproperlyConfigured(context_processor_error_msg % "querystring") params = dict(context["request"].GET) for key, value in self.updates.items(): if isinstance(key, str): params[key] = value continue key = key.resolve(context) value = value.resolve(context) if key not in ("", None): params[key] = value for removal in self.removals: params.pop(removal.resolve(context), None) value = escape("?" + urlencode(params, doseq=True)) if self.asvar: context[str(self.asvar)] = value return "" else: return value # {% querystring "name"="abc" "age"=15 as=qs %} @register.tag def querystring(parser, token): """ Creates a URL (containing only the query string [including "?"]) derived from the current URL's query string, by updating it with the provided keyword arguments. Example (imagine URL is ``/abc/?gender=male&name=Brad``):: # {% querystring "name"="abc" "age"=15 %} ?name=abc&gender=male&age=15 {% querystring "name"="Ayers" "age"=20 %} ?name=Ayers&gender=male&age=20 {% querystring "name"="Ayers" without "gender" %} ?name=Ayers """ bits = token.split_contents() tag = bits.pop(0) updates = token_kwargs(bits, parser) asvar_key = None for key in updates: if str(key) == "as": asvar_key = key if asvar_key is not None: asvar = updates[asvar_key] del updates[asvar_key] else: asvar = None # ``bits`` should now be empty of a=b pairs, it should either be empty, or # have ``without`` arguments. if bits and bits.pop(0) != "without": raise TemplateSyntaxError(f"Malformed arguments to '{tag}'") removals = [parser.compile_filter(bit) for bit in bits] return QuerystringNode(updates, removals, asvar=asvar) class RenderTableNode(Node): """ parameters: table (~.Table): the table to render template (str or list): Name[s] of template to render """ def __init__(self, table, template_name=None): super().__init__() self.table = table self.template_name = template_name def render(self, context): table = self.table.resolve(context) request = context.get("request") if isinstance(table, tables.Table): pass elif hasattr(table, "model"): queryset = table table = tables.table_factory(model=queryset.model)(queryset, request=request) else: raise ValueError(f"Expected table or queryset, not {type(table).__name__}") if self.template_name: template_name = self.template_name.resolve(context) else: template_name = table.template_name if isinstance(template_name, str): template = get_template(template_name) else: # assume some iterable was given template = select_template(template_name) try: # HACK: # TemplateColumn benefits from being able to use the context # that the table is rendered in. The current way this is # achieved is to temporarily attach the context to the table, # which TemplateColumn then looks for and uses. table.context = context table.before_render(request) return template.render(context={"table": table}, request=request) finally: del table.context @register.tag def render_table(parser, token): """ Render a HTML table. The tag can be given either a `.Table` object, or a queryset. An optional second argument can specify the template to use. Example:: {% render_table table %} {% render_table table "custom.html" %} {% render_table user_queryset %} When given a queryset, a `.Table` class is generated dynamically as follows:: class OnTheFlyTable(tables.Table): class Meta: model = queryset.model attrs = {'class': 'paleblue'} For configuration beyond this, a `.Table` class must be manually defined, instantiated, and passed to this tag. The context should include a *request* variable containing the current request. This allows pagination URLs to be created without clobbering the existing querystring. """ bits = token.split_contents() bits.pop(0) table = parser.compile_filter(bits.pop(0)) template = parser.compile_filter(bits.pop(0)) if bits else None return RenderTableNode(table, template) register.filter("localize", l10n_register.filters["localize"]) register.filter("unlocalize", l10n_register.filters["unlocalize"]) @register.simple_tag(takes_context=True) def export_url(context, export_format, export_trigger_param=None): """ Returns an export URL for the given file `export_format`, preserving current query string parameters. Example for a page requested with querystring ``?q=blue``:: {% export_url "csv" %} It will return:: ?q=blue&_export=csv """ if export_trigger_param is None and "view" in context: export_trigger_param = getattr(context["view"], "export_trigger_param", None) export_trigger_param = export_trigger_param or "_export" return QuerystringNode(updates={export_trigger_param: export_format}, removals=[]).render( context ) @register.filter def table_page_range(page, paginator): """ Given an page and paginator, return a list of max 10 (by default) page numbers: - always containing the first, last and current page. - containing one or two '...' to skip ranges between first/last and current. Example: {% for p in table.page|table_page_range:table.paginator %} {{ p }} {% endfor %} """ page_range = getattr(settings, "DJANGO_TABLES2_PAGE_RANGE", 10) num_pages = paginator.num_pages if num_pages <= page_range: return range(1, num_pages + 1) range_start = page.number - int(page_range / 2) if range_start < 1: range_start = 1 range_end = range_start + page_range if range_end > num_pages: range_start = num_pages - page_range + 1 range_end = num_pages + 1 ret = range(range_start, range_end) if 1 not in ret: ret = [1, "..."] + list(ret)[2:] if num_pages not in ret: ret = list(ret)[:-2] + ["...", num_pages] if isinstance(paginator, LazyPaginator) and not paginator.is_last_page(page.number): ret.append("...") return ret @register.simple_tag def render_attrs(attrs, **kwargs): ret = AttributeDict(kwargs) if attrs is not None: ret.update(attrs) return ret.as_html() django-tables2-2.7.5/django_tables2/utils.py000066400000000000000000000467621473544236200207420ustar00rootroot00000000000000import inspect import warnings from collections import OrderedDict from functools import total_ordering from itertools import chain from django.core.exceptions import FieldDoesNotExist from django.db import models from django.utils.html import format_html_join class Sequence(list): """ Represents a column sequence, e.g. ``('first_name', '...', 'last_name')`` This is used to represent `.Table.Meta.sequence` or the `.Table` constructors's *sequence* keyword argument. The sequence must be a list of column names and is used to specify the order of the columns on a table. Optionally a '...' item can be inserted, which is treated as a *catch-all* for column names that are not explicitly specified. """ def expand(self, columns): """ Expands the ``'...'`` item in the sequence into the appropriate column names that should be placed there. arguments: columns (list): list of column names. returns: The current instance. raises: `ValueError` if the sequence is invalid for the columns. """ ellipses = self.count("...") if ellipses > 1: raise ValueError("'...' must be used at most once in a sequence.") elif ellipses == 0: self.append("...") # everything looks good, let's expand the "..." item columns = list(columns) # take a copy and exhaust the generator head = [] tail = [] target = head # start by adding things to the head for name in self: if name == "...": # now we'll start adding elements to the tail target = tail continue target.append(name) if name in columns: columns.pop(columns.index(name)) self[:] = chain(head, columns, tail) return self class OrderBy(str): """ A single item in an `.OrderByTuple` object. This class is essentially just a `str` with some extra properties. """ QUERYSET_SEPARATOR = "__" def __new__(cls, value): instance = super().__new__(cls, value) if Accessor.LEGACY_SEPARATOR in value: message = ( f"Use '__' to separate path components, not '.' in accessor '{value}'" " (fallback will be removed in django_tables2 version 3)." ) warnings.warn(message, DeprecationWarning, stacklevel=3) return instance @property def bare(self): """ Returns: `.OrderBy`: the bare form. The *bare form* is the non-prefixed form. Typically the bare form is just the ascending form. Example: ``age`` is the bare form of ``-age`` """ return OrderBy(self[1:]) if self[:1] == "-" else self @property def opposite(self): """ Provides the opposite of the current sorting direction. Returns: `.OrderBy`: object with an opposite sort influence. Example:: >>> order_by = OrderBy('name') >>> order_by.opposite '-name' """ return OrderBy(self[1:]) if self.is_descending else OrderBy("-" + self) @property def is_descending(self): """ Returns `True` if this object induces *descending* ordering. """ return self.startswith("-") @property def is_ascending(self): """ Returns `True` if this object induces *ascending* ordering. """ return not self.is_descending def for_queryset(self): """ Returns the current instance usable in Django QuerySet's order_by arguments. """ return self.replace(Accessor.LEGACY_SEPARATOR, OrderBy.QUERYSET_SEPARATOR) class OrderByTuple(tuple): """ Stores ordering as (as `.OrderBy` objects). The `~.Table.order_by` property is always converted to an `.OrderByTuple` object. This class is essentially just a `tuple` with some useful extras. Example:: >>> x = OrderByTuple(('name', '-age')) >>> x['age'] '-age' >>> x['age'].is_descending True >>> x['age'].opposite 'age' """ def __new__(cls, iterable): transformed = [] for item in iterable: if not isinstance(item, OrderBy): item = OrderBy(item) transformed.append(item) return super().__new__(cls, transformed) def __str__(self): return ",".join(self) def __contains__(self, name): """ Determine if a column has an influence on ordering. Example:: >>> x = OrderByTuple(('name', )) >>> 'name' in x True >>> '-name' in x True Arguments: name (str): The name of a column. (optionally prefixed) Returns: bool: `True` if the column with `name` influences the ordering. """ name = OrderBy(name).bare for order_by in self: if order_by.bare == name: return True return False def __getitem__(self, index): """ Allows an `.OrderBy` object to be extracted via named or integer based indexing. When using named based indexing, it's fine to used a prefixed named:: >>> x = OrderByTuple(('name', '-age')) >>> x[0] 'name' >>> x['age'] '-age' >>> x['-age'] '-age' Arguments: index (int): Index to query the ordering for. Returns: `.OrderBy`: for the ordering at the index. """ if isinstance(index, str): for order_by in self: if order_by == index or order_by.bare == index: return order_by raise KeyError return super().__getitem__(index) @property def key(self): accessors = [] reversing = [] for order_by in self: accessors.append(Accessor(order_by.bare)) reversing.append(order_by.is_descending) @total_ordering class Comparator: def __init__(self, obj): self.obj = obj def __eq__(self, other): for accessor in accessors: a = accessor.resolve(self.obj, quiet=True) b = accessor.resolve(other.obj, quiet=True) if not a == b: return False return True def __lt__(self, other): for accessor, reverse in zip(accessors, reversing): a = accessor.resolve(self.obj, quiet=True) b = accessor.resolve(other.obj, quiet=True) if a == b: continue if reverse: a, b = b, a # The rest of this should be refactored out into a util # function 'compare' that handles different types. try: return a < b except TypeError: # If the truth values differ, it's a good way to # determine ordering. if bool(a) is not bool(b): return bool(a) < bool(b) # Handle comparing different types, by falling back to # the string and id of the type. This at least groups # different types together. a_type = type(a) b_type = type(b) return (repr(a_type), id(a_type)) < (repr(b_type), id(b_type)) return False return Comparator def get(self, key, fallback): """ Identical to `__getitem__`, but supports fallback value. """ try: return self[key] except (KeyError, IndexError): return fallback @property def opposite(self): """ Return version with each `.OrderBy` prefix toggled:: >>> order_by = OrderByTuple(('name', '-age')) >>> order_by.opposite ('-name', 'age') """ return type(self)(o.opposite for o in self) class Accessor(str): """ A string describing a path from one object to another via attribute/index accesses. For convenience, the class has an alias `.A` to allow for more concise code. Relations are separated by a ``__`` character. To support list-of-dicts from ``QuerySet.values()``, if the context is a dictionary, and the accessor is a key in the dictionary, it is returned right away. """ LEGACY_SEPARATOR = "." SEPARATOR = "__" ALTERS_DATA_ERROR_FMT = "Refusing to call {method}() because `.alters_data = True`" LOOKUP_ERROR_FMT = ( "Failed lookup for key [{key}] in {context}, when resolving the accessor {accessor}" ) def __init__(self, value, callable_args=None, callable_kwargs=None): self.callable_args = callable_args or getattr(value, "callable_args", None) or [] self.callable_kwargs = callable_kwargs or getattr(value, "callable_kwargs", None) or {} super().__init__() def __new__(cls, value, callable_args=None, callable_kwargs=None): instance = super().__new__(cls, value) if cls.LEGACY_SEPARATOR in value: instance.SEPARATOR = cls.LEGACY_SEPARATOR message = ( f"Use '__' to separate path components, not '.' in accessor '{value}'" " (fallback will be removed in django_tables2 version 3)." ) warnings.warn(message, DeprecationWarning, stacklevel=3) return instance def resolve(self, context, safe=True, quiet=False): """ Return an object described by the accessor by traversing the attributes of *context*. Lookups are attempted in the following order: - dictionary (e.g. ``obj[related]``) - attribute (e.g. ``obj.related``) - list-index lookup (e.g. ``obj[int(related)]``) Callable objects are called, and their result is used, before proceeding with the resolving. Example:: >>> x = Accessor("__len__") >>> x.resolve("brad") 4 >>> x = Accessor("0__upper") >>> x.resolve("brad") "B" If the context is a dictionary and the accessor-value is a key in it, the value for that key is immediately returned:: >>> x = Accessor("user__first_name") >>> x.resolve({"user__first_name": "brad"}) "brad" Arguments: context : The root/first object to traverse. safe (bool): Don't call anything with `alters_data = True` quiet (bool): Smother all exceptions and instead return `None` Returns: target object Raises: TypeError`, `AttributeError`, `KeyError`, `ValueError` (unless `quiet` == `True`) """ # Short-circuit if the context contains a key with the exact name of the accessor, # supporting list-of-dicts data returned from values_list("related_model__field") if isinstance(context, dict) and self in context: return context[self] try: current = context for bit in self.bits: try: # dictionary lookup current = current[bit] except (TypeError, AttributeError, KeyError): try: # attribute lookup current = getattr(current, bit) except (TypeError, AttributeError): try: # list-index lookup current = current[int(bit)] except ( IndexError, # list index out of range ValueError, # invalid literal for int() KeyError, # dict without `int(bit)` key TypeError, # unsubscriptable object ): current_context = ( type(current) if isinstance(current, models.Model) else current ) raise ValueError( self.LOOKUP_ERROR_FMT.format( key=bit, context=current_context, accessor=self ) ) if callable(current): if safe and getattr(current, "alters_data", False): raise ValueError(self.ALTERS_DATA_ERROR_FMT.format(method=current.__name__)) if not getattr(current, "do_not_call_in_templates", False): current = current(*self.callable_args, **self.callable_kwargs) # Important that we break in None case, or a relationship # spanning across a null-key will raise an exception in the # next iteration, instead of defaulting. if current is None: break return current except Exception: if not quiet: raise @property def bits(self): if self == "": return () return self.split(self.SEPARATOR) def get_field(self, model): """ Return the django model field for model in context, following relations. """ if not hasattr(model, "_meta"): return field = None for bit in self.bits: try: field = model._meta.get_field(bit) except FieldDoesNotExist: break if hasattr(field, "remote_field"): rel = getattr(field, "remote_field", None) model = getattr(rel, "model", model) return field def penultimate(self, context, quiet=True): """ Split the accessor on the right-most separator ('__'), return a tuple with: - the resolved left part. - the remainder Example:: >>> Accessor("a__b__c").penultimate({"a": {"a": 1, "b": {"c": 2, "d": 4}}}) ({"c": 2, "d": 4}, "c") """ path, _, remainder = self.rpartition(self.SEPARATOR) return A(path).resolve(context, quiet=quiet), remainder A = Accessor # alias class AttributeDict(OrderedDict): """ A wrapper around `collections.OrderedDict` that knows how to render itself as HTML style tag attributes. Any key with ``value is None`` will be skipped. The returned string is marked safe, so it can be used safely in a template. See `.as_html` for a usage example. """ blacklist = ("th", "td", "_ordering", "thead", "tbody", "tfoot") def _iteritems(self): for key, v in self.items(): value = v() if callable(v) else v if key not in self.blacklist and value is not None: yield (key, value) def as_html(self): """ Render to HTML tag attributes. Example: .. code-block:: python >>> from django_tables2.utils import AttributeDict >>> attrs = AttributeDict({'class': 'mytable', 'id': 'someid'}) >>> attrs.as_html() 'class="mytable" id="someid"' returns: `~django.utils.safestring.SafeUnicode` object """ return format_html_join(" ", '{}="{}"', self._iteritems()) def segment(sequence, aliases): """ Translates a flat sequence of items into a set of prefixed aliases. This allows the value set by `.QuerySet.order_by` to be translated into a list of columns that would have the same result. These are called "order by aliases" which are optionally prefixed column names:: >>> list(segment(('a', '-b', 'c'), ... {'x': ('a'), ... 'y': ('b', '-c'), ... 'z': ('-b', 'c')})) [('x', '-y'), ('x', 'z')] """ if not (sequence or aliases): return for alias, parts in aliases.items(): variants = { # alias: order by tuple alias: OrderByTuple(parts), OrderBy(alias).opposite: OrderByTuple(parts).opposite, } for valias, vparts in variants.items(): if list(sequence[: len(vparts)]) == list(vparts): tail_aliases = dict(aliases) del tail_aliases[alias] tail_sequence = sequence[len(vparts) :] if tail_sequence: for tail in segment(tail_sequence, tail_aliases): yield tuple(chain([valias], tail)) else: continue else: yield tuple([valias]) def signature(fn): """ Returns: tuple: Returns a (arguments, kwarg_name)-tuple: - the arguments (positional or keyword) - the name of the ** kwarg catch all. The self-argument for methods is always removed. """ signature = inspect.signature(fn) args = [] keywords = None for arg in signature.parameters.values(): if arg.kind == arg.VAR_KEYWORD: keywords = arg.name elif arg.kind == arg.VAR_POSITIONAL: continue # skip *args catch-all else: args.append(arg.name) return tuple(args), keywords def call_with_appropriate(fn, kwargs): """ Calls the function ``fn`` with the keyword arguments from ``kwargs`` it expects If the kwargs argument is defined, pass all arguments, else provide exactly the arguments wanted. If one of the arguments of ``fn`` are not contained in kwargs, ``fn`` will not be called and ``None`` will be returned. """ args, kwargs_name = signature(fn) # no catch-all defined, we need to exactly pass the arguments specified. if not kwargs_name: kwargs = {key: kwargs[key] for key in kwargs if key in args} # if any argument of fn is not in kwargs, just return None if any(arg not in kwargs for arg in args): return None return fn(**kwargs) def computed_values(d, kwargs=None): """ Returns a new `dict` that has callable values replaced with the return values. Example:: >>> compute_values({'foo': lambda: 'bar'}) {'foo': 'bar'} Arbitrarily deep structures are supported. The logic is as follows: 1. If the value is callable, call it and make that the new value. 2. If the value is an instance of dict, use ComputableDict to compute its keys. Example:: >>> def parents(): ... return { ... 'father': lambda: 'Foo', ... 'mother': 'Bar' ... } ... >>> a = { ... 'name': 'Brad', ... 'parents': parents ... } ... >>> computed_values(a) {'name': 'Brad', 'parents': {'father': 'Foo', 'mother': 'Bar'}} Arguments: d (dict): The original dictionary. kwargs: any extra keyword arguments will be passed to the callables, if the callable takes an argument with such a name. Returns: dict: with callable values replaced. """ kwargs = kwargs or {} result = {} for k, v in d.items(): if callable(v): v = call_with_appropriate(v, kwargs=kwargs) if isinstance(v, dict): v = computed_values(v, kwargs=kwargs) result[k] = v return result django-tables2-2.7.5/django_tables2/views.py000066400000000000000000000204441473544236200207240ustar00rootroot00000000000000from itertools import count from typing import Any, Optional from django.core.exceptions import ImproperlyConfigured from django.views.generic.list import ListView from . import tables from .config import RequestConfig class TableMixinBase: """ Base mixin for the Single- and MultiTable class based views. """ context_table_name = "table" table_pagination = None def get_context_table_name(self, table): """ Get the name to use for the table's template variable. """ return self.context_table_name def get_table_pagination(self, table): """ Return pagination options passed to `.RequestConfig`: - True for standard pagination (default), - False for no pagination, - a dictionary for custom pagination. `ListView`s pagination attributes are taken into account, if `table_pagination` does not define the corresponding value. Override this method to further customize pagination for a `View`. """ paginate = self.table_pagination if paginate is False: return False paginate = {} # Obtains and set page size from get_paginate_by paginate_by = self.get_paginate_by(table.data) if paginate_by is not None: paginate["per_page"] = paginate_by if hasattr(self, "paginator_class"): paginate["paginator_class"] = self.paginator_class if getattr(self, "paginate_orphans", 0) != 0: paginate["orphans"] = self.paginate_orphans # table_pagination overrides any MultipleObjectMixin attributes if self.table_pagination: paginate.update(self.table_pagination) # we have no custom pagination settings, so just use the default. if not paginate and self.table_pagination is None: return True return paginate def get_paginate_by(self, table_data) -> Optional[int]: """ Determines the number of items per page, or ``None`` for no pagination. Args: table_data: The table's data. Returns: Optional[int]: Items per page or ``None`` for no pagination. """ return getattr(self, "paginate_by", None) class SingleTableMixin(TableMixinBase): """ Adds a Table object to the context. Typically used with `.TemplateResponseMixin`. Attributes: table_class: subclass of `.Table` table_data: data used to populate the table, any compatible data source. context_table_name(str): name of the table's template variable (default: 'table') table_pagination (dict): controls table pagination. If a `dict`, passed as the *paginate* keyword argument to `.RequestConfig`. As such, any Truthy value enables pagination. (default: enable pagination). The `dict` can be used to specify values for arguments for the call to `~.tables.Table.paginate`. If you want to use a non-standard paginator for example, you can add a key `paginator_class` to the dict, containing a custom `Paginator` class. This mixin plays nice with the Django's ``.MultipleObjectMixin`` by using ``.get_queryset`` as a fall back for the table data source. """ table_class = None table_data = None def get_table_class(self): """ Return the class to use for the table. """ if self.table_class: return self.table_class if self.model: return tables.table_factory(self.model) name = type(self).__name__ raise ImproperlyConfigured(f"You must either specify {name}.table_class or {name}.model") def get_table(self, **kwargs): """ Return a table object to use. The table has automatic support for sorting and pagination. """ table_class = self.get_table_class() table = table_class(data=self.get_table_data(), **kwargs) return RequestConfig(self.request, paginate=self.get_table_pagination(table)).configure( table ) def get_table_data(self): """ Return the table data that should be used to populate the rows. """ if self.table_data is not None: return self.table_data elif hasattr(self, "object_list"): return self.object_list elif hasattr(self, "get_queryset"): return self.get_queryset() view_name = type(self).__name__ raise ImproperlyConfigured(f"Table data was not specified. Define {view_name}.table_data") def get_table_kwargs(self): """ Return the keyword arguments for instantiating the table. Allows passing customized arguments to the table constructor, for example, to remove the buttons column, you could define this method in your View:: def get_table_kwargs(self): return { 'exclude': ('buttons', ) } """ return {} def get_context_data(self, **kwargs: Any) -> dict[str, Any]: """ Overridden version of `.TemplateResponseMixin` to inject the table into the template's context. """ context = super().get_context_data(**kwargs) table = self.get_table(**self.get_table_kwargs()) context[self.get_context_table_name(table)] = table return context class SingleTableView(SingleTableMixin, ListView): """ Generic view that renders a template and passes in a `.Table` instances. Mixes ``.SingleTableMixin`` with ``django.views.generic.list.ListView``. """ class MultiTableMixin(TableMixinBase): """ Add a list with multiple Table object's to the context. Typically used with `.TemplateResponseMixin`. The `tables` attribute must be either a list of `.Table` instances or classes extended from `.Table` which are not already instantiated. In that case, `get_tables_data` must be able to return the tables data, either by having an entry containing the data for each table in `tables`, or by overriding this method in order to return this data. Attributes: tables: list of `.Table` instances or list of `.Table` child objects. tables_data: if defined, `tables` is assumed to be a list of table classes which will be instantiated with the corresponding item from this list of `.TableData` instances. table_prefix(str): Prefix to be used for each table. The string must contain one instance of `{}`, which will be replaced by an integer different for each table in the view. Default is 'table_{}-'. context_table_name(str): name of the table's template variable (default: 'tables') .. versionadded:: 1.2.3 """ tables = None tables_data = None table_prefix = "table_{}-" # override context table name to make sense in a multiple table context context_table_name = "tables" def get_tables(self): """ Return an array of table instances containing data. """ if self.tables is None: view_name = type(self).__name__ raise ImproperlyConfigured(f"No tables were specified. Define {view_name}.tables") data = self.get_tables_data() if data is None: return self.tables if len(data) != len(self.tables): view_name = type(self).__name__ raise ImproperlyConfigured(f"len({view_name}.tables_data) != len({view_name}.tables)") return list(Table(data[i]) for i, Table in enumerate(self.tables)) def get_tables_data(self): """ Return an array of table_data that should be used to populate each table """ return self.tables_data def get_context_data(self, **kwargs: Any) -> dict[str, Any]: context = super().get_context_data(**kwargs) tables = self.get_tables() # apply prefixes and execute requestConfig for each table table_counter = count() for table in tables: table.prefix = table.prefix or self.table_prefix.format(next(table_counter)) RequestConfig(self.request, paginate=self.get_table_pagination(table)).configure(table) context[self.get_context_table_name(table)] = list(tables) return context django-tables2-2.7.5/docs/000077500000000000000000000000001473544236200152635ustar00rootroot00000000000000django-tables2-2.7.5/docs/Makefile000066400000000000000000000113131473544236200167220ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = -W SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-tables2.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-tables2.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/django-tables2" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-tables2" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." spelling: $(SPHINXBUILD) -b spelling $(ALLSPHINXOPTS) $(BUILDDIR)/spelling @echo @echo "Spelling check finished, look at the results in " \ "$(BUILDDIR)/spelling/output.txt." django-tables2-2.7.5/docs/_static/000077500000000000000000000000001473544236200167115ustar00rootroot00000000000000django-tables2-2.7.5/docs/_static/example.png000066400000000000000000000444121473544236200210570ustar00rootroot00000000000000‰PNG  IHDRL‘'ÛM„iCCPICC Profilex­YgTM³î™Í»,aÉ9眓ä%'AXò’„% HQQ‰bD@QEA0""(¢PDPQEDÒPßï½çÜïßs˜y¶ú©êê®NÕÀ}‰3Gw²0òðôÂ<`Œ€©±ÑF¶à¿>?F´Q8"¿aë¿Òþï–À Ø ¤Ø?06 Á—€Û¢éq 7ì‰&ÆEoàCf£#"¸v‡üÆØÿ7Øä¸8™ œ ð T*=†YD.”‚Ø!3€¥DÒ¢`B°~@(5n?„#¹cç#XÊÿ_vBþ…©TÿlR©!ÿàßmA4‘ŠMi±ÑÔ¤ÍÿŸ¯Èˆx¤¿6äÍgì„|¹~ã¢ÅY¹ ˜ ÁJ¡ñ–®°ir¨‹û‘{DùÛoE0Á±&H_Ä”¾ÃfÃÎ'?0ÈÔ ÁȨ€ÊbœÿâúäPû?0ªõFÌN•Ž ßõÞŒŽsØðaÃæ³¨{Û?x.˜n¾a‘ÃØ X3g#>À|qt— 9â3¬L3·B0R/l±9æ68Nôx§¶ˆ!80(Êõ¯î¾@ª© "çCäÀ˜S „¼w€äh ùþ•üKî ’Á'‚@,¢±Éð¥eÑÿb`¨ˆ~R.ÿGßxS­Õ¿¼ÁÙöÙ¿øŽÿ?æàí¦?”Î(½WZùËbúëÖ kŠµÄšc¥ÿJš~·‚¾éŸ Òš Ø BêþëÏ¿[ÿãßÒß}à´©Ž0hën›žÑþ±eóOÏüé ´Z­Ž6Fë¡õÑÚ@Íæòh5´Úm€ÖEÊ´ÿÕÏ´þø/‚7û*aÓûpðñ™ÕqA;ãX“ÑItZHhœ²ZÉ YE(È ©()+ƒµgƒÀ7§Í5âxðYX š‚ȸºñYÐ(]/éFüLb2,ÑÜñ ˆ§'ü¶‡üˆ€ iÜ@ˆ)¤ý*@èC`¬ÁVà<ÁvBé ì™ äCà¨ÇA ¨ÍàhW@¸ î!ðŒƒ 0>‚9ð,C„ƒÈ+Ä Bâ,¤iAúd 9AžEAñÐ.h7”BÐI¨:uB=Ðè!ôz ½‡æ¡_0 f€Ù`~XV„µ`#Øv}à8N†³á¸ ®†›à6¸¾?†'àð  H(”0J¥…2AmEy¡‚QtTj?ªU:‹êBÝB &P³¨%4ÍŠBË#±´D»¢Ð1è4t>º]nC÷¡GЯÑsè5 Ç‘Åè`¬0˜L"&S‚©Ã´bú11S˜X,–+‰ÕDƯ'6 ›‚ÍÇVa[°×±±“ØÇ“Åéá¶â¨¸8\®ׄ»†ÆMá~âIxA¼ Þï…ÂgáKðønü0~¿L`&ˆt[ „$ÂAB-¡‹ð€0EX&²%‰zDb1“XF'~#‘H"$m’#‰FÊ •‘Αn“^“–( 2 & Þ ñ  §®3ãƒùdøœøRøjøøøø-ø£ùËùoðÏ p „  t ¼dÔ¤  ^ü Ä.d$!T&Ô'4'Ì'l)/|RxPxYDRÄU$K¤Eä…(QTK4X´X´WtNLPÌNl—رgâq-ñPñRñ[â‹’î{%Ú%f$¹$­$“%ÏH>—"KHÅHUK=’ÆJkI‡KWIÉÀ2ê2¡2•2daY Yšl•ìC9Œœ¶\”\µÜyy#ùù3ò¯8l²Ú>+Š)z)V¼¥¸¦¤®¡T«4®LQ¶VÎRîRžW‘Q P©Ty¤JV5WMWíPýª&«¤vLmLUÝN}¯z¯úª†¦]ã¬Æ{M1M?Í£šO´Ø´´òµnkc´µÓµ¯h/éhèÄé\Ðù¢+¯®Û¨;³ErKЖÚ-“z"zT½“zúBú~ú'ô' „ ¨Õo E  ë §¤ÂŒšŒ>+Ó[MtLRM®›¢L-L÷›šQÌ\Í*Ì^š‹˜‡˜Ÿ1Ÿ³P·H±¸n‰±´±Ô ´$t–fB« } ³ ;¶¾5ütøz„{DK$>Ò/²3ŠÕ·C`ÇΣe£s¢'btbŽÄÌÑmèu±P¬OlGrȈ—Šßÿ:A?¡2ág¢[âÅ,;£v$É$å&M'›'ŸJA§¤ôîÞ•¹ëuªQêÉ4(Í?­7]4=;}*Ã"£>“˜žy?K)«0ëûn÷Ý]ÙüÙÙ“{,öœÉaÌ¡ç<Ù«»÷ø>ô>Ú¾Á\ÕÜòܵýûïæ)å•ä­ääß= | ìÀzApÁàAƒÇaE=lp¸¾¥0¹p²È®¨­X¨xñ÷#¾G•/%–Æ—N”Ù–u”‹•*_©­x\i\Ùr”ïhîÑŪÀªác†ÇÎç?žwü× Ú‰±“'Ûª%ªKj°5 5ïjÝjoÒ:ÕPÇS—W·z:êôD½S}_ƒfCC#_ãÁ3ð™ø3›†šM›;ÎÊŸ=ÙÂÑ’wœ‹?÷á¼ßùÑ 6z/j]<{IüÒÑVÖÖýmP[RÛ\{hûD‡gÇÃNëÎÞ.Ý®ÖË —O_¾Ry•ýêÁnbwv÷úµäk ×£¯Ïö„ôLöúöŽßð¸ñ¨Ï±o°ß¦ÿöMó›7nݺv[ïö•;:w:ïjÝm¿§q¯m@} õ¾úýÖAÁ¶š:†´‡ºnyØ=l0Ü3b:ró‘Õ£{í?u{âýdb,plæiÄӯϞ-g<Ç<ßÿ‚ùEÉK¾—Õ¯¤_µLhL\}múzàó›ñÉ€Éocß®Le¿#¿+™œn˜Q™¹òÞüýЇm¦>F\žÍùÄòéèg©Ï—¾~˜ó˜›úJÿº>ŸÿûÛéïjß{^þˆü±¼¸ÿ'÷Ïú%­¥[¿ÜM/'®àVÊV¥W»ÖlÖž¯G®¯GSéÔͳ yÃÁÁÌŸFrO$€Èø;7Ød€‚‚Q€Œœš¶P! äÕÃ0 O¢‚Póè<Œf[… Û$ˆŒ$˜Efa”e²b¦³œ¤¼``÷ç¸À…æöã¹Î'ÈŸ+ðUÈGøž¨ŽØ) 6É ©i{ÙyF…Å‹JË*ºª±jÇÕû4^k.i3èðèÊlÑÒ3Õ·7ð2 5J0Î1)1­7ë2¿kñÌrÆjÑmËlÇg/¹UÙAÇÑØÉÊÙÞÅÉÕÕÍÝÝÃÃÓÓËËk›—·—×v_7?'ª¿y€~ zL°`k(.t™ö9ìuø£ˆ[Ȭ<³£*ú@LkÇ÷9¾'¡4qÇNë$ѤÕä')-»ö¥ú¥i¦3"sërfaVèn½lÖì™=Ý9E{C÷mÉåÈ]ÍCçëh>¨uèÂáÕ"ÁbÙ# %J¥Êeªåjê•êG5ªtŽ™:Qvr¬†½Öè”O]Ôéäúœ†Ã•gN5µ4wž½Ñ2|îÓá‹Ñ—†Ú¤Û#:Ê:Ûº\ž¾²ÖÍqMùº[OaïLŸeåÍû·^ßž»‹½'>`q?p0öAÄëCÍaâÈÒ£ÉÇ÷G¯=é»òôÚ³žñîç-/¿Œxe<Á=1ÿzèMçdýÛÊ©CïöL'ÍD¾÷û`÷Qu–2ûñÓÍϵ_ræÂ¾ÚÏ«}ù.½àó£û§ÒRñ¯W+Ü«kµëëã/rJtBò…&ð’„v@×a^8 žGE£~¢÷a„1ýØ8œî¾—PEL%2x=˜ü™ãYò(õ¬Cl?9$9}¸Š¸ð’ùlùó…ÈÂŽ"‡E‡ÄIf’ RuÒe¾Ë1ËK)¨)j+i+«ªH« ¨1«Cêß5¦Ýê¶v§NƒnÅ–<½ý0ƒm†öFÆÆš& ¦bf<æÌX‹eË9«)ë1›Ûn»söÕ[‹²c¨Î.ú®²n\î÷¯Ï=o{]ÜvÜ;×'v»¯™Ÿ••úÓÿE@O`mоàð»PE í[Øã𶈲Ȥ(Ñ”è÷1×èE±qêñ˜øÑ„S‰ñ;Í’Ø’&“/¤dì²OåKýÖ•~ #,Ó)Ë:{4r”öÊîÏÜÏGÉ'@X-øqðë¡ùÃKE¸b®#R%š¥¦eåÛ*B*éGS«ö+<~ôÄé“ÕÃ5K§¤ë¼OçÕ·6é{÷Œ8®øÜåEÒËc¯nM,¾Qž¤¿½85?-7ò¾æÃ«YÞOŸ~™ûšðM~²H\‚}\¹¼Fû"àrÀ„ƒRpÂBÐahÖƒO¢È¨=hº#¹Ž ÄQpwðûöDAâéCùc9S!óA–BJë)¶6öÛ/9—¸)<ò¼æ|Tþ]¥‚g…z…‰L‰~›ŸCNMcR½Ò§dvËzÉ)ÊCòà µŠ‰JÖÊBÊ‹*ƒªujiê®rš°æ˜V³v–Ž›®Œîê–!½ýDkCAããS&i¦nf æóç—,÷[ùYkØl&l[írí}•ãðÔ±É)ÃÙÙEÜå‡ëm· ÷p]O’ç¸×™mÉÞ–>>o·Ÿ÷M÷³¡rR'ýÏ$š±×…Ä„êÒдÁ°Òp¿鈯‘Q;Ì£ Ñ1ùtëX|l\V¼~ürB{bìN…ï“j“}SxRí*HµLƒÓºÓÓ2,3ù3—³&vßÎ>¿§2'{oä>·\ýýyä¼…üçn4,>”y8±^]Œ JbJcʢˣ*h•~G«¬Ù÷9‘t²ªº¿æó)ö:ÍÓ¶õN ŽÛΤ4]j^n±8WtþÕEÙK ­=í¤çβ®ñ+ÂW#º¯^gí ë½ÑÇÛwsð¶ÄÔ»dîg N¹= x´0ºoŒçió¸áóÑ—voœßz·ø¾x¶ÿ«ËâÓøÿ¾#ÚØ°œ2À­gm ^ì[p ࢠ`îr]·Ô?û‡0BöŽÝ ô#«Y?¬ pèÔ‚äzßaNXö…wÃõð ü Ń2B…¢¡:PoÐ$´šŠ>€îDOcØ1f˜x$ëÃ2`°‰Ø³ØœÎw ÷/‚şïl '?ˆÄf™EfÐb8I&‘È“ŒŽŒ=L*LµÌÜ̇X,{(0%‹ÃšËÆÌVÎ.Î~‘ÔcŒsž«–Û„û-Ï^^yÞÇ|éürüÏ MW…º„“EôE1¢ÄŽŠ‡KèIR$?HõIWËdÉÉÙÊk+((**é+»ªD¨îF–üVÍÚü:º [ô^pº•¿2•2‹7¿iÉcb}ĦÔ6ÁÎÐnݾgk¾C˜#Í)ÛùœË[7wgÏmdoGŸ’íc~LTU‹×ÀÀ ôà3!34å°Ìð‘H)dä=‹Ñ¤—ÄþŒwOhJü”Ä™¬”b¼Ë35=­3ƒšu?[cOõ^¦}i¹ÓyFùÙZ &1¶/úËFƦ³Hó'–ÖV7mlmÇìiÀ±Òy‹Ë·Ï…mç}è¾~óþ•²AÍ!2¡uaá ‘ŠQÑ1c±‘ñØ„êFI¯Rv¦bÓ 2Ø2Ëw‹d7çèåŸ)ð>„=\Y$P|¤WšX6]áU9Råqìû‰†ê ZÜ©ýu?ê=Zϰ5Å5¶hŸ;vs1êÒÓ6‹öÎN¥®Æ+bW+¯1^Oíùpý¯ï¦Ê­“w(w³ï-Þ|;äóðéˆû£'£.OîÃnþ²õ[7¯b\uí±y¯QMæñu»O>GNsW.š3£»ƒÏ¯.æWsŒZË0Y-üZªÊˆ>xwÈOcÄM8’X#­CQ>å;ó‹jmø—_ÝþÑy B‘¢Šh|>EåÑ ϯ.âVqñ5¿œËÍN8óÄj48^•q&<<Ñi‚»1B·OÝó2o†÷ô_µj¾{öµƒ‡n+Ø¡È9·#:› ½5ê¬Pe÷™ ÏùkVÍ·-ÎØºù>ö+ʸð¿ëhˆ§“1÷yÂ¥ŒÀ0ô`‚¡§ÿúUs˜ÏBÑÃ(elë|¼}_2týÏ2)‰™°•‘wþ·Û§Ãa.…dIÌnˆpûEë7¯ ôw4Â3huQqqn5t› ÀË&&ô–é6º¯“cB„]´X?v4RÒ×·3^>Èñœ%› È{ŽšýϱÝÀ½‡â_ÂD€ô÷xv-æXì<q`Ò£ºÜù¡³&Nê‹Í¶üç÷ 'NûزPãen*žøŸß»ÿàHå>ʯ–U¥ø ûè£ ÀM©¸bCnZì­º}p]èîÛÅÆ#f,ý¼g56Ãr¬÷àîÝÿ1Þ Œ«ÊÈ)eü8¯9Ÿ{¨Üç• 2”¿[—þŒg׎‰}†.¯ä ^d>„Ü(ÿû÷æÑת#6€v¶Ç™3V¯<ñ³á š©ÓAÎ\E—¿ùW¬~!»Ã£üƒ \±!¯È؆%¢á@\¬ø®_C†9d~𬉣úâ­‡ÿ\1mâçØ<æ½Ì¤š-ãöõëžéá£bt†ûübµúY8Q“è*–>Þƒ­lO=§8=·5VÔÆÆ…“?›8òŒB Ÿgܾvƒ‰R)Õ'hŪ¶¬/i÷? l¼y%½üñlÄüx-6ÛM#©kC#ÊÞóßý)lãæ°¢;%ªƺ'רûG[}¶±Ž {öŤ°íz˜+ãŠJÆSj…´ ŠÈf…rV¸ö>þ#nÿûúõ }ü‹A! ËÁ~“ÜPav î 8A5£Nº°˜çQ0Q˜…}ŸklckkheÛÛ YÛà.!Ò¤øïáÇ}||–ñСC~~~rEòˆ*ãQ_m˜ÖOÈY›IeÀ)ŒlaSÆa“xB£ÄÂþó_‚6f Ëñó&Y£Â˜Dù ²…•iá ðGèlC-Àhdf x¨©L]7öqïÛ×6éŽu| ûoéÀecÈ m$¤Âa=úuM;·&å9Õ–t³Qå”(k+h6¿ÞzÖ6"TÁë¼ý~‡ÜGß,vÔ£K„*(ˆ`Ü£ÐI227ƒ,—GuLÄ[ÛXØ~ì×›¡gÝ|Ÿ Ô¾ÅGKûB,Çû£Ë))G¶<䢮Ÿõaý0Lb[°+–φMvçcO¿³7#æžßÿþ”T!ŸGu_ €íGŸ5Á]ÎǜŬ×cî}ÿë&]RÕxiaÍ9n"â &3ƒü’׆¾ ^q«Ê@Š«»»½ÁóØ*è·/a&‡[ ¾ÂþÒž Àœ§ªô,`UkÙ—ÈüCW‹ê’çõMù¥„´q·±±9yò¤‘¾„",]6ÉS„Úq° õ,zÁ&Qñõ„Ì!Ã:eßÅ‹‹n–Ð;)ãoŸKjá’yá:Pì-‚¡ñܪ˜…\݇ô48s;Ìæð{Ùõ‚9êòÁ£}¾™dɯàtêjI ÁÞ7¢¦)lXƒ“(ƒIͧä•üa¸½ #ö¦a5Jn2í=#Ngé¢,ÁF× › WÄöãÿ´yzöŽ—°_2ì?tFÕ%ÅõP*”÷úwþ'ê ¶cdÞ‡å?ô§ƒ]/héâs ™6Ãt¯ßÍäl-!x%Ѝ,Vdaïˆþ,æ²_šÛhÑ¡úy!¿ù>ÑÀ9% -íKXÕ‡c,R.–TqÑðqýa¦Æ@Ðh@ž_zóz¾½ûôå}¬¾ßô·°œKC…?!²u  »Tç?),ïcÝ)ëæ=†½û¢•¿,ý1VqVôfI#Ψj¼´˜fƒ\€USF"3·eÓ†½‹tz]ÿчy%õŸS™½\»2Són\¸?чڪÕÁÌtp5ÝÚøg1ÕÄwUoGÄ•Ã?_¡L´¹— ÞÓn"5ª‰R*W*O‘hsšÌ\ýõ«m¿œÚ±öT6r Xî“K-U®[~å‡`L¶>ëóþ&‚úb¡ñz]GöažJýcmª‘«kW”š÷Û…ŒgNÿzÒ«_NÝÙõè2òë (8)k©Â»õT”ЖëRÐÊùÐøåÝxlà´LyèBºŽ!5IS”%¢tpsèã)áÍÊ`=QŒÞ£ðAÃì:ôã1#úJfÛúîšèÌBÈdʺ](42‹‹ZHé¶ÃÇ5Ð]¢‡{9b£óæEN0  Xî¹iÇŸ\ÔB¢Dº’9Nzì&„Žu5ÑÍ40w>ÔŠúÉBÕc?£Yk—yFîËÇâÆ&ÆÆ,sˆkScêR8(©®³q_Ø„<çNÂG ¹~¾Â­¼ðX4éi¶ÛÍ9÷qLðÏàåß/ZåƒoÌ~KO¬Ch]RNÂÔ^,'çÀ ß|Ï›âæ£Ú;‰©u8ÈŸmê=l“EPjQí³œü‰vz|†Íÿ͇QðÂf´lY¬dIû.âIŸ"Íc× g3Z-~Þ‹ kø¨…D¡|úØ+úŠÇ©AN¦ÂõÖ“[ÆE-$ª Öó¿ÎÁšk ‚³…¸Zé¼yÕ7Ë¡)·¸#¹—QYÇߣu|“ƒ>5>"êt,Nm]›p½ù ¿}d>š8­3Ÿk9lzt‘½Ì¬á:#k3èëkв‹ÿöjf s£0ñŸÞý¡}Ëýl ˜f6f ¦u?G½¼ß÷nǫβwò¢Zïîo§ŽtcÉhl!QF–ðB¿£ø&ARÜB¢D29Þ¤V1’EWžFór‘õÚ¾hmd\Û˜•$¼—œ0Æß‹î·VÚ\snýº¡Ó»œÍõç~åŒÐö#7@™h¯ ë…ÉËÙXþÆfޤŒÃ³{¹nBŽžc=ÜPE½‚{k'”¨(}¢Ëݦo)Ÿ c¨Ç@-$*4¡£!º[TKa= Áë*j‚h3À=Ø›Öàö1õdÔڻض‹õgÑÈÝ|½HºU›ù7-bà#ÍRaчScî¨óŽ{ñ["ßLçœýå,Úö„ËìÄ›)ÇN§oÝ!Þ›mB'³ç %mˆŒ)ã#x.UÆ©º½3 - ˜:¼ÿ>¦ÿv(°€ ó/®°Z‡ÑèÁW—ŸÂÔÅ>½ó+´ì'fK‰ ¬‡4Ë¥ô°¼¥¯ø-ôpîï LX²b¬j!ëQ)÷€Ò&º*µOÕ|neêÅóùœ‡»VĬ/O¼ù¦œ’x3f JÛ~2M%É7ffï±KP±ø'àxÖñgѺñ¶NHMyééQ'¦Š§i+÷ÙsQÌ‹ØOñ":³Qâ*ŒpT²[žç´Õ÷mxXu<»Ã˜uhç'¦¬Ž¿>îáü 蛽Ԏ®¤^kå8é{LM,ƒ MãlMYóðëqwÏrÕ7e-Ì^w÷[/¬»…D„2N¶ìêq %Lè¡o:{?l:XÙµìÌ smSk×'«â–´=-$ª £×ºœ»ßvßèÕü…'|¦^ß'ÞŽFÁ¾ƒÅç1ÚMßãmÅáœw8ôªìÈû¯`³<5æêt€ÑmAÖ±ŒY½“”²@þÐuÿ›ÏáÔ1˜,&µ®/Šp nãù6_DTÊLå˜Tú 0ŸS ë›™H/¹ZHTh&§ª”‹˜ÒÀÖB¢BE@l¬Lf²Œñ> IÍ Ð(’|vY—iln Ýošö®‹Áø¦P·9 É’z·(%I’5€!¹æZHl ¯y‚BÍW#r0Xff’.-W¨™—M/×5ÓfbA€  $ÈU‹°Ú"$ÈÛb«› * @‚\°+A -"@‚¼-¶±™  2Ð*+ɘUÀް4ñ— „ïHš A.ýxMchÌ0xac›¶¿1¿”§”ǪiÎ6$/äd¹Þt[“R‚@›G€y›oBâA iH7)%´yH·ù&$šF€yÓøR‚@›G€y›oBâA i” rneiaii%[§vóïý~äê3|’A+'~e)¤Êw¡‰]ZX(–;» “?w]!±•qPZ<Ÿ‹MÄ”im8†Ã+ï ÒÊÚ cƒ3ü5Ü%‚œýpûl-½Î],,:êéhùÜSOƒÖ]ôuùEk…ÇŽtÕél©³ž–Ï9Ù1åþþ…ZZ³ïÉFóDö-/-éäs‹äÙ¹C‹.]:ëxm~&<}{.ÄËР3ô ‘œ»®ˆØjý„{ÏKË‹¶t°ïo×rLŠô‘v€Îo¾‡¿ õðÜv-=l2 ¦§ãy¯)Ë@¸t H†Z¯%5Ø÷#%Š\··õïZñ ã—x¹>Û“Û”+©ôäÉ©+:[°šÌ%8¹¤®ëjJ’“ x ¸Þ†P·Ï…%W¼MÕFê(²ŸWR@™.( wAžiâª5iû¨Îè\#¦ ”#Öd§¥eBÊνî GSg %—@ZTȪØ´õ‰ ´äR0Bþ˜V“ì‡i%%´,§ºdOä™$ò±&-¡ðŠºšŠŠŠšºšKë]\Ö_^ò—pkG§áÆæÕ•d''¦a›M¼š‚ªK=ŠÀ®Ê$J]PrAA.$šS¦\s/ ý?7)&..©¹ßª9ã‘´¹2(ªI‹€V+æÂùܸ0*D‹"ô™QAþá1Ñ롃#ä–I5wC6/7ÂWuñÄœáT+`“W¨ÔuCû¥ªU@ûEe )5É >,:Ê[* •% ¥dû‹\È„Èv‰À±]!@óˬ«é.ái4of„'òŽª($Ò,jøT€€¢ ÇbZ¸§KX²Hq ؈‚ãD—ªü­I‚FʦT¤Ç±:IÆç )´¹.Â¥y3ËõÜä8èÆCñÙd2I§Ë°ÃÐv%ᦻ×ÄfB³;Ð;ÆhqIIZPÌÊèŒJ 6d‹_Ý= kAnïâæC¢SC6Q‰þrŸœ[²p¡—Vç@±}f/J"{ÿì5áÉ+¦¨#…‡&+OZu5Ä~·Ì?à/ ójj‹)]ÀÃg¬TóðQî.fRç®ç”ÕQç»7 ¾û?ÒÇÄ#nV\ Ÿ:B%3žœÛ¾pál-C7°göjx°œph¨¥åóÝÙ[%•”h3Z·m¥æNa‡„S¶Œ_6ý†ÕÝ:ù{në©®§¯ÅZìÇ¢:º jªo7`{»…%Ͷƒ3ßÇN6Jj6]-¼`v¸ðk‹Âœ‘oÀúßGþ<Õñ^äìy1Þ‰á] ïß,G 3 ]ؤ)M¤!ã>Üï±Ú%±b’A}騅V+Äz ÝT²º‚SàÅ5y†Ò—ÕÛzoú~ª#º ôþÚbèGÎf¾óŽŒ½6ÓQæh$V¿¼º¹ìŠŒ_ývÿ¿Žu‡¦6ÄRI¦•tQ… l{¡´=•.ö2—.¯ÜïÓyÜcnòµ~áB¿ ÝÅÚ‚bu Øøupd1Sþ€úlÒªZžgš;öƒl"Nö ¹°zj—§‘§gÍšIcÊ‚Sˆg \ׯîG¥‰‘ýp?-ÝÞgÜá(ú©Ž¡!J— :ÔYîú:Hh…øfH˜fŠÏ]WH¤yZåÀ )Ü¡Ôꊥ#—бzø¸ Àßï,#µtÒäûuj$Qy:¦]è<ÓÜ®üà¾G/ìTÖLG9á “ab3`qxøJ—ÃY¿N¥Ð‰i›¡%e÷j5Þ é›#÷!¼lØfÀoô^LEr\\neÜ»á;õ‚¸í K!É’»»¸ûÅ;ju Ùj`³ ùG•ðxÙ—`ˆº'WÀ&m” yö ê„;C‚Š(¸öÜ'sÈKƒM©d¹I%ˆ%IaÊ%é=©¸¯A1ø¦´+ ¾và=š`Š–FÑðÞ†B¢ N6ɪ\0Â;"‘ò²"Ú¶ Ä7áÒ­†åæÆàÖަ!áUd'Å%å6¥°®¢ ‚œŒÑ¥ö5y%ÉaÉÔN ¯®ŽÚ¥å%†AÇ©s—µ)³ÔP¦IËððîúDhJõì?«ÁÖ"äŒofã »U’Lï¦Aóãä [JqB’KPlãmÕ´ñNhgB[EZ´ðVÜÓ2ÔÆ›¶f+Eó סFÊnø€=BـƛR2»ëT•æ‰0¢A¬ÈuÙìØK§’çzÑT]L°ðnÇs}œH·B¢R6ˤØîO‹bÖAóIs²o ž—7ØÅ)(Z´U©Hwµ/+dö‹ »Ð›µáÔC /å#­X‘4¢)D’vM䑟üô 1È/shD¿¡eW Oí6a 視l6bá 6›­Çjôˆelðõ 6e"SE›Kåÿ*¶ÔU²áÄqs™#Ö•«<'Ÿ] ß¹‘S„¡CLs™›U…Då5Æ©Ì ßo©@HOÎ6ÅrølüÕ!ƒebÒì-4œ¥_‘‚óÜåI†¯Ê¼#ðEÕó·q$Õ#¿U¥ÈßÌ=¹Ø–‰¹L'…[k–ÀeÄÌÒl fÃΣ€MZJKò Î¼Ù¾Ú’ºò0!¤ˆ¦˜(¤þÓܼÁ3’Æ´0Xæ Ýh„ÎÒ7gÊu iÖw¾´V’—F ™GhÒ¬$O ´EH·ÅV#6T@€¹ `V‚@[D€y[l5b3A@H«a%´Ed¡‘s×Ûb›  >’Y湺ßOÞP{+R}§t+êÔ,ÑuµG›FŒ—r²\WW¯ rŠ r mbA@] W’DA@C A®¡ CÌ"¨ äêB’È!h($È5´aˆYu! )A^–{êÆ3uy%'‡SUZV¿÷”I|Né‹"ù3Ò[ƒÈ)+}Q&еB¢Œ}mí¢U[P!ï^#˜¡°/)4OsˆÊBúçë IDAT9÷áR–¶Ç¦k­gwÁeïy‰/0ˆ)[LY[ªÔ¥‰ÿpKÛ®«å=,»°&Ÿ{* ¶ü‹!–֖νM-'lÉž‘ŽZȽ´i´]KçFÞ›$ç®+"ªËáFähX rR¶›ÎØÃ.'}¿)K›þ°é2Q[ˆÝHݦmºé–øR:#î3ÒÄVÌ7Ò—ZQ£šD+ä/®:€Pzh$}œ›²ªùg³´÷§ËÍ ÔÖõtfâS ìg]MšfЗêdÃOϧ>c¿)g—ï˜3k÷ ,¡,ÞuòšÈ¤jL4\éú#5xµ±ìJÈ´P‡«EoÊ‹îZ‡ŽÙq£+WDTÝ/ÕjhZ >8ä;Ý“ú•?>*ôXFqÒémÑ¡¾ÇÈ/yzMϸ:«·boE}Fq©ú©Šú’úµ¨_¢2A^yêûãW­¢ŽÅ WÔY‡öÞ§Ìaÿþåèýé¸m²Înñ FåÙ{ïÁQ$§–9Eh™›¾Gà÷᪠§Nn‡1{GJeþE!§é°êøC‰cÜ’Ëw_ÇÿÙŽ/ûÓcüÒ½× ñ’*Må6nÃûQC†Éàážè~.ì<·õÝ5ÑŽ:0™²n Ìâ¶‘{9b£óæEN Þ`À²pÏM;þä"…ĦOÜ=/¬8>#ž£ ÃzXÄc˜H‹7vÞ½1Û.Nüš²³{iØuÏ?“ð“øm·*¹ Ë‘ÏÅ/LX²äSˆ8|º³ÍÀÇg¤Öð[ƒÈãÔ 'Sá!ÈXOn)$Ê«ÞkMkAîÓ„pF®°Àט¨m[L9/‹›ÓßÉv•𢸻e¸§½HÚ½öè„ÔÊ7y? Ïw×µIõ+pÛû('cmͯoc š ù:½ôMyÞM´÷«SðÜÃãäXá}Öè›G9©§WžÌ„7< éí¡÷},åJæ/9öP5À¥ú’jßwóA~ûÈ|4qZg>×rØ,ô èâcùåwG¡é–Cç£Ó½·¾Œñy@pàŒÕf]Œ ˜pR1œé}"6Ôµ« \Y9»éå_Þ¿ëxa@Û.ö_p¸±Í 'ýk‡#]Ï…B>5*‹¹TÊpÿaÚ{Ì =©ÿ#:©“‚3ÒQ+5áÜuMkÁG繇l,Õ„VÝ{˜ï™~þÄí<8.N¦«ÀAÖ4£q·Amœr½Ð?¹¨/t¹ù/ŸffŽ3–¡£ÄtÒ» þÈîÿ%B5<÷@«A‹â"¾ò23spè‹à¦Ÿw7¡çwþ°öǸÓèìSúøZO3Ÿ úR35ÞqsAιõëV„Nïr6ןûmÌÌÛP·µ`9?M¥Wè¿ÌIáµWO=Ù9æ“Ux7‹/wX¿g'áQk•GftôSŠãè±vA0Å)JœôHË®÷ œ=Gp–“¤ˆ¿1çñ»¾;’ÊšÖæÑa¢Û…ô Á ÎH×c Ö v4Dw‹¤Î]ïŒÏ]WHlÌx5Ð5®K/¬H˜= Özâä=zâgþág¶vÞ=yÝ»Ä]Ẽ˜½gòí›0v:¢râV?‡:[½{™ÁS´fÀB¬ÇÝ=<ËUß”e´0{ÝÝo½°ØV ZÙµìÌ smSk×'«â–Ä7 ‰Ø€VIׂ9W¡UŸXÉ8›6¹·‘]ïAW§¯»0w”Hº Žd!ká•PWkØ*3Z–8ë‡é®"÷tíhÊ2]˜¾.ií8ƒ¾Þk=дÚ¦ŸFv›î²Ök><K€*¢!ÄnyÆEó–xŸ¸ãÜC"iMþUØ—š¬¡!…2‡FÀqÍ*üžœÏåp‘ÜaK%.‡ÍgèÀ«ÎD‰ËŇrK„À‰ ð±í›i s»¨*,øE*øl_F¬„G*§Òo€ùœJ˜×Ídco "|}Î]7£ö)ÄÆ*$ŠKß:£x¥ôµr ²w³LQRµ?~´¡zÂý'Õ[`£†Á@ܪ*ž±¸ ñ9œ:¾\ÿ”U‰9‘lw3¨†¤¸šfdÀøž={ŠmiA‹ehL¦M7ŒZPÔtø…§^0g¦­dP‹ßê¢éH6é±\k<ØM:C Û ›Ióg¶1›Û¾¹Mß“·}ÿˆ¿=$Èÿö]€ÐÞ AÞÞ[˜ø÷·G€ùß¾ Ú;2»ëäÜõöÞÜÄ¿¿ ÒG2Ë쮓 ÿ»tâg{G Ñ Ç¥Ÿ¡·wˆö‰|«MÚ1rO.ÉÚ!$ÈÛa£—Ò —Fƒä íäí°Q‰KiHK£Aòvˆ òvبÄ%‚€4$鴄 y‚@;D€y;lTâA@äÒh»²´°TIJ*$l6‰€ŠAν7› ,üá:û÷{¥jrš{5r‰«––žûnx¯¬R‰ÿp³ïê°ÄÜš’ÿ8è‰kp_¢¥cØÙ¢‹…¡žÖæSOÄMe^½X½.Öoû_åB¦WǶÆ.>‡ß¶EA   bÃ+Z)»¦$sŸÛaßûÔå­Ç…Eù 2çQ50·¤°yêfˆXâäûS jx yŸßJï=…K/©Ì“lák2å¹]óOGÄú­»˜N¿2S¡Þ#*9¼fÜÈÜ‚eî8arBOÊ9Ï6φ9§…‘WéÐxI‘\}|¼¼¡WÓ…Wi¢ÖÂý·8Ì:éãQýœQZƒŠPxk¿­ÃkÉÕBü6Òÿ€Fwï<{·tf¿?´¡‚ÞÉÍ¡Œ²+yÅÒEÔÂ[wNäÉ÷˧?n…ŽõÛ˜xµ‚˜ä¢ßÆóÇÖ!ôêôž³‹dWädüV "ê6nŒ=‘C]„&ù«1¨äðÆÉêª ö³«>Ù†ü'Ø3u†ù®*’ÂwlMƒ,<Õ}tÀ¬K¹<Þå€á(!© ;[xª‹G@Hn &¹vžÛþ' î½yÏ,Ô=×ÅmÞÇ—²y‚šDŸ.ÁÏkáoÉžÈ3©FprñE`n™€‚\”{¯XÓÇÉ ^uýÇÞ;ÅH[ø&Ûº‚u¿¦§è؆óêXùkJ1bxwèüêÕ©£I‡O^ÿ-Í׳³•UlA‡I{X)¹Qd2¡Z•ƒ¢fµGwÃîµaÑ;'16Ãúê'ìô_퉶]W4FHı~§Õeµ[Tņ•Œ9r·Ë†Âû–˶žÌzIEùËåxµ_Y~))¯K/Ë«íVy[»XÏ¥rÜ'Žp3ÕFzƽL ´£s?+S=•ñÄ‚I"´&JFÄXÿ†%U¬Šû5$öýHC—€õщ¾.¦.((z0#Šn­yõ˜ §Z„ü&ϘaÁãMúµ•ƒr3,U“Ϻ2ï>~Õè¤÷þBC—Ú}É‚/Îð”DE¯êÑñJO͆ضž×ŸA§îVFÝMŒ<º=:b “)ŒäW¢¥}æP"ÈAà}" òÌS½\¸œÅvç&Ç!¼`ꇦ¼rúæØÂa0B+W‰¿õÈx—@ä‰ß"ßkø„?®2É¥;KJí=Ä ·&T.—D±C— »ÞÆÞÇûîOG& ?7{™—¢ ùðþ©`—Ý(x•·Ce)¤J9QRœ ³Óæጇ(*u²4é ™W<#óNæXØß-²q°îÓÝ>wýjù„´:a¾œ§Õ/éq®O> š€ÊA÷äº:’ù¿g€'ÚØEKkÌ/ý\ܼ÷×õš™})"'l´Ëš;_F£òzi6“r/…z£<ãÚ—!·\gïñѲp[ £ƒ…ŽÖÔÈûÒà0ì¦fF¯ŸåÒªöñÍŠNÛî(Ñ/Íùº"|Ó°±OgxŒi^šro’1ôçša5N-EX]Cæ÷鉪#^^ôÓ壙e¯_ŽºWa`»ræ  _;˜ë#£T#Æ ¡8ÿÛÑÄ£Yr~aI$Þ/2¯I‚3Ùßæå |.»±XLÄg³ùz,&ƒÍæâK„îm÷xrNMüÂÕ9æäé`Õ½¦ê걤Ÿ—©.ämj¼yYÔv'½&~Íù éê1šdzõ¤A@UäY}’ÁdÑAÌ Bÿd¹¡ýn¡]žÑ™ŸIî¿1§dÖTÍô–ÔUM“7„7u/.G–»ìÀè$ùNŽ\¹$¼O䃾FÒRs:ôü¥¶:¤‚ ;í¬.ø¡µd¶Ô&RŸ ð÷E@>ÈÕ‚Lºæ6’ù[-2‰‚Aàír2ë¾ޤA@c A®±MC #¨äêÁ‘H!h,$È5¶iˆaõ @‚\=8)E€¹Æ6 1Œ  H«G"…  ± ר¦!†Ôƒ rõàH¤4ù ó~;©R‚¯Áj©T!R… ¦R—!FÕ#o˃ĽÅ×ÝI™fPâ‚ ¦H2,sÄ$XÈ9ùZ«’#´ äƒ\õåz»€8Ah¿Èye¥²o7h¿˜Ïí ™“aÚ•gÄ‚A€B@å3Þn‚@ÛB€yÛj/b-A@eH« ©@h[ o[íE¬%¨ŒÀÿi6­Nÿ8þIEND®B`‚django-tables2-2.7.5/docs/_static/tutorial-bootstrap.png000066400000000000000000000712761473544236200233120ustar00rootroot00000000000000‰PNG  IHDRPŽi ÄiCCPICC ProfileH‰•—T“Ù€çÿÓ-!RBo‚éÒké`#$„cBP±+‹+XQÁ²¢‹" ®µ l‹bþAu],ØPÙXÂî{ç½wÞœsÏýþùçÎ̽çÎ9sdžTšjäHre1¡œ¤ä© ð@xÇ—Ký££#“‘ùŸòþ6 ƒó »A_ÿþÿ¿Š¦@(ç ѧ äüŒ`ã_*ËÀíÆô¦sr¥ƒ|c– Kボ1̽ƒœ6ÄxüM\L Æ:d:'Ë ›azN?óCÂØA"K0ƾÁ‡/â 0Æâ¸œœYƒ¬ÄØ*ío~2þá3Må“ÇËPñð^†„$–K³yóþÏãøß’“­‰a ºHƒÍlìÌîdÍŠP±$mrÔ‹CöC,R„Å0_˜2Â^P„jmöäÈN‡pU~r¹q#,”ÇŽ°lVŒ*Vº,Єy²Ñ¸Š¬x•^$äªüç‹âG8Oœ0y„åY±£6*½L£Ê_( ¢Ú{ŽüoûsUksEqaª½óFóJüG}Ê“T¹ „AÁ£6ñ*{in€*–4;Ze/ÌUéåy±ªµ¹Ø…]­:ÃL^xôC$„â!rA<ŒÃ»©¹Â¹ƒwgIçÉÄ¢\Ž?VeBW·Çqrpô¬Ùá+ñöÎP-"lò¨Nîà¹SJGuÓYGDjüQE VŽ4€3)|…,oX7XN@*¨ tÁLÁ ìÀ \Á ü Â! â fDƒe>ÀR(„bX› vÀ.Ø à4Àq8 çá2\ƒ[p”Ð / ÞC?‚ $„0]Ä1Gl'ÄñA‚‘H$IFR‘ D‚(Èr¤)AÊ‘H5òr 9\DÚ‘»H'Òƒ¼A>£8”޲PÔº£þh‡NG3ÐÙh>Z€®AËÐJt?ZžF/£·P%úíÃކcãŒqv8w\ . —‚KÇÉp‹pE¸R\%®ׄkÅÝÀ)q/qŸðD<ÏÁÛá½ðaøx<?¿¿ _Žß‹¯ÇŸÅßÀwâ{ñß ‚>Á–àIà’„9„BB)¡Šp”pŽp‹ÐMxO$ÙDK¢1Œ˜LÌ$Î'®"n#Ö›‰íÄ.b‰DÒ%Ù’¼IQ$)—THÚBÚO:EºNê&}$ÓÈFd'r9…,!/#—’÷‘O’¯“Ÿ’û)sŠ'%Š" Ì£¬¥ì¦4Q®Rº)ýTMª%Õ›Gͤ.¥–Qk©ç¨¨oi4š ̓6…&¦-¡•ÑÒ.Ð:iŸèZtz }]A_CßCo¦ß¥¿e0 ?F #—±†QÍ8ÃxÄø¨ÆT³W㪠Ô«U¨Õ«]W{¥NQ7W÷WŸ¡ž¯^ª~XýªúK І…F Oc‘F…Æ1>M¦¦£f”fŽæ*Í}š5Ÿi‘´,´‚µZZ»´Îhu1qLSf “Ï\ÎÜÍ<ÇìfY–,.+“UÌ:ÀjcõjkiOÐNО«]¡}B[ÉÆ±-Ø\v6{-ûû6ûóƒ1þc„cVŽ©s}̱:~:B":[:Ÿu9ºÁºYºëutêáõlô¦èÍÑÛ®wNïåXÖX¯±ü±Ec½§êÛèÇèÏ×ߥE¿ÏÀÐ Ô@j°ÅàŒÁKC¶¡Ÿa¦áFÓ†=FL##±ÑF£SFÏ9ÚN6§Œs–Ók¬of¬0ÞiÜfÜobio²Ì¤Îä¡)ÕÔÝ4Ýt£i‹i¯™‘Ù$³f5f÷Ì)æîæ"óÍæ­æ,,--VX4X<³Ô±äZæ[ÖX>°bXùZͶª´ºiM´v·Î²Þf}͵q±ÙTØ\µEm]mŶÛlÛÇÆyŒ“Œ«×aG·ó·Ë³«±ë´gÛGÚ/³o°5Þl|Êøõã[ÇspqÈvØípßQË1Üq™c“ã''¾S…ÓMg†sˆóbçFç×l''lŸpÇ…é2Ée…K‹ËWW7W™k­k›™[ªÛV·w–{´û*÷ ÅÇ=>yºzæzòüÃËÎ+ËkŸ×³‰–…wOìò6ñæyïôVúp|R}~ðQúûò|+}û™ú üªüžú[ûgúï÷à 8ð!Ð3pa`s.(4¨(¨-X+8>¸<øQˆIHFHMHo¨KèüÐæ0BXDØú°®—Ï­æö†»…/ ?Aˆ(xi)‹lš„N Ÿ´aÒƒÉæ“%“¢ е!êa´eôì蟧§DO©˜ò$Æ1fALk,3vfì¾Ø÷qqkãîÇ[Å+â[Ô¦%T'|H J,IT&OZ˜t9Y/YœÜ˜BJIH©Jé›yyç$Ì9¾ßÅïzñ›ü·/ÝOOJŸ=­~æôìxOHϵçSŸw¿¾èYø»æï[_Y½:ò‡ßWz“z»_Ë^¼YõV÷ížwÞµôE÷=zŸó¾ÿCÑGÝ{?¹jýœøùiÿœ/¤/e_­¿6}‹øö` g`@Ê“ñ†Z6Ðôt€7{ÉÌkÔ©Ã=õ Ãï€!‚ÿÄÃ}÷¸`® ¾`°EÛõWK«Ž}GûÄùê쬉<ÝÙiØ­kMJÞbý#ÉàkÇÀ@ÃÀÀ×*,Ù{Íï‡{ùA1ÄÞS)@N˜x³zþEþŸ·¼Ü´Õú pHYs%%IR$ðiTXtXML:com.adobe.xmp 774 336 HOiDOT¨(¨¨HßxÕÿr@IDATx융ǟ , *H“" (Eº 6,*¢""¢"EEýÛ°w EDPì)JXPPŠTTT:üß/w²s3{»ÇíÝíòËçs7-É$ßÌÎä%ï½P³ö{„H€H€H€H€H`¿&pƒýºýYy    0(ðA      |H€H€H€H€H€(ð      |H€H€H€H€H€(ð ¤!°{÷.Ùµk·ìÞ³GöìÖ¿=»õNÅ’¦YP  ÈãhcLjÅÛ¿ ìÚ¹S…]²SÿH€H€H€H ‘($’.ó&,ÀlÀÎ];eçÎ:#… ˜„H€H€H€²@€‚A 1 $‚Ô‚vê ÁŽ;‘=ó$   ¨(DÅË$3 2´]Ú ä oÞ…H€H€H # ™ð ä(íÛ·›™‚½)oF$@$@$@> |@xH9E`·zÚ¾m‡ÀÛ ä6 ¹Ý¼ÿ~I`÷îݲmÛ6ªí—­ÏJ“ @Þ$@Á o¶ K•ÂÒ„‚­ô8”Âm̪‘ @2 `Œ­Æ2'-¨mÛÊ™‚¤m@œH€H€R˜ƒn\V-ïتBm ò^»°D$@$@$@" ø@ ÷¡ÍÛ d‰ƒ,ac"ˆÖ)ئnIH€H€H€H ¯ `W[†åJX´lëVïI™:±"$@$@$@©G€‚Aêµ)k”ÇìÐñÇ@$@$@$@y™@ž 8à¹ù¦åòË.•·ßyO^ðR^f™°²U¯v²T¬XA~^²T,X˜°û䕌˕++6”:µkIñâŤh‘"‚gáå÷ß—E‹¾—©Ó§Ë7óD]1¸k—Î2àåWsµZ{vï‘-[·äh UHjש-õëÕ“ãŽ+%G>ZŽ<òHùûï¿e㦲bÅJ™9k¦Ìœ9Kþúë¯-oF$@$@$w äYÁàÀ”‡¸OÎnÑÜÐÃbPõ™wI&°d—]r±|ÈÁæèÔMŸ13w˽¬!ôíÓ[jÔ¨S!Ö­_/÷Þ÷€Ìýz^†ø·vï&W¶o'§Õ©—áZNžÈÉÙ‚Â… ËÕW_%œ¤¿ŸÌ®]»äÓÏ>“×^*þõgfÑyH€H€H Å äIÁBÁãô“3›4öðOœ¾®órøáå¿ÿ¶È«/÷O(‹XÚ¢P¡BR@Ûì}n1ˆÉ@ÉL Ï ABF„»ßvû~ûƒCÇBRÑ¢E¼gí·ßVÉ´é3’^88Z?ÀÏ>ý¤T­RÙ«ÜzNœ4ÙÔïç%Kdõš5êû”Pµ¢Ê•*IËsϑڵN"~»HŸ‘rK·›äâVyyå¦`SžˆÚ]ÑN:_Wgì,X°@fÏ™#‹¾ûNÖhÇdãÆMÚQ),%µVµJU£¦U½z / „ÌAƒˈ7ßôÎ%bgÒ„/ð‚™”š$â¹’ç±Êõ£Þ3÷þùç%Ò᪫½rD»æEÚÏvŽ;þx9çœóäÈ£Ž ¬ùz ýù§ÚÑÚxÝžÁ‹ñò+¯ÈÈ·ßö޳{‡‚A¤ÐÝ|•_ï^=å¢ /0ÙßÜýV™÷Í7Y¾U… å‚‹ZešÂêëC‡ÈF혌î·o¥ªYtٜ۽{·¼÷ÎÛòÛo+CãT®\EÎ=ïüÐ븀‘éaCëö¿ÀxgŸÓRª:ï±ÀHzrÚ”)2kÖŒ°ËÞù+´^(ÿý÷?y好óÜI#“‚AP[Ô8åT#Fk-[¶Ê W_VÇÛ£EËÓ×ΨW_jÕ®cÊ8ò­²~ݺ<]^.{äÁß§ŸxL 6c…K"m‹Ù”&e`s€`²…>½n7†å¶ÜC_#nóŽW_)7w½Ñf‘a››‚AX'"C!³x¢v­Zòè#ž „ÉÚéèׯ_\ë%äÏ—Oúöí+ÔØÏÑwÞif̉lþGÁ 9ƒ'{TéÀÂwÞ%&NÊÒ“qÈ!ù¤[÷[¼Ö¿þüKÆŽýBViÇÏñ‰eËIógËa‡¥©Ä¡sõRÿç3ÜË8¦PÅ è¸O2I–ýú«”:î8©W¿¡.\Ȥƒ€Ñÿ…çGü!\\ݱ“—ÿÚ5keÖÌé²^Õ‘Ê—¯ 6òî²ôŠ×îøïu–nöì™ò·ö+V\Zœ}ŽQU3†{]Ö¯ï`¡n·è 9x,úö[;f´MÊm:œ ‚Úâˆ#Žë:wñžá¿ÿÞ,ß.\`ž¯N(-åÊ—÷Úi¥ª£½ûö[Þq²í´PuÏjÕÓlþÞU{åŠåÉV–7 ò„`$`äø¶Û{í÷3þ6 Ö¬^+S¦MK*á \¹²òΛýê úºô)ãG׋eç¹gžŒ˜9p£æ–`°{÷.]» ºê[Îx÷óçÏ/ï¿óŽ®)„¯çÍ“Û{öŒ7/þSO>!§Õ<ÍoVïE­Û¶5k/x²i‡‚Áþ-¸#­ ¨3†þAC;L:ßp£§’ƒY¿JÔvÎ>ç\óTBMgÈàA:ºþ÷”b6²Óu½ÄY3gÊ´©“½ëvç‹.–ò*˜C¨/ 6Ô^2[W^ÕѾG &ë|³™;]§jzG›øS§L–ÙêñËPè¡#dÖÙ/]æD¹ô²Ö&.;cC†9%µ…ÛY^±|¹¼÷îÛå+§åEª–fÃsÏ<•´*En]ù,ÚMým® x?õø£3 n¹­g\ÆZ©ßT{ká QÃjàVÜ;™lÂÁ Ï>-õΨkÊöîÖý¶,ÙKÀ`ÆÆa!·ƒD{#ºþºë¤}»v¦Ú›6m’Ž:…ºøÕWž&M›zûîôd‡¢£¬…ÍiØ 4È’-ûñ(lIŠ+fTW/þ)C'2³BU®\IjžZÓ¸iýVíP~ûí·Ì’˜ë 4ö,åuôo§®ZýƒâþØ Ñì®áwÜ A})U²¤,\ø­ÞãǸêW©ÒI*ÌÕÔÑçõ:Ã37´ýƒÊ[²ä±ªÂWͼCV¬X¡¶(ßWÀþ¸hƒ|:«tï=}µ-j™Ë=þ„L™:ÍìÃÝ-tùc -ÕcV¥J•MôÉ“&ÉÜ9³“žÓò|©R¥Š¹6á«/å›y_GÄ»öºä¨BG™s£>þH~þù§ˆë8pg¶mÛ®³ÏFÄA½nºyïìÅ‹Ï?8«P§ÎR?}F 3¼ÿnD>ÀÂÈ2”ž¼‡èva³ðúkƒí¥ ÛsÎ=OªT­jžØ_Ù€Ùx§ÛµsWD9a{ü '˜еjOä´lú°í1Ç#ÅŠ—?õ=‚ôñêÅÃÈäp(ÃñÌl10¨q j€—û{B{a†eݺµ¡êc¶ñ¸_©ãŽ—CTålõêU‚XCP[¸‚àK^”-êe»tõ„ÒwtƆìAïÛc´Îèm‚;n5^†ÝD¬!^öºí³jÙÃallÐ&?ª Ú?_¾ürÐÁjÛØ´™÷{ýtÔ'ƈeƒšl¼ÏI¬uŠ7^Pðn=^go h}שúÓÆÁêˆþ{a³{àŠßœ  ]Â~S¸Oþt‡ÛÔ‰‡÷àÇ‚¶û ã&ÜãY?F¿mü±A6¨vÛAqÝsûò<»ùDÛÏUÁ?ŒöÖ­]Û+ãÔiÓåöÞwÄ ÉK¸Ÿí$³pØQ½oZ ŠV—^.p=o€;R¸%rK0ئõ‚ñq"ÔF}ò±yy#ÿÇ\FñEè­b øÜsΑ>½{›|ðq¿à‹âRK -€s!Áà²K/Uû‘n¦Cê$5‚ãÒ_~‘nݺ˦(g0ÕßÿÅ祢Žã#è Ï¿ð¢®ÙÙñtË_H;Ã/>ÿ¼ª¹”‹H‹8¨Û‹ýDMߢy3¹½Çmžèæ µ‡û=±VÊ5 Ì§˰0iòdéÝçΈËe…±lÑoó³ÎV¶iFï0Æ]¸`~DZ{дY 9åÔSÍáWê-kþ7{]cä#ðèœ<¯ƒ h› Ð¥ëÍR ÀaæÒ{ï¾#+–/ó¢¹F¥è<Œxãuïš»ƒŽ‡õ2…û ³nï‡4<äà1Cçï£ÓÞknzìÃPµÃ•ié°…zõåþ(Þ±-ójídtfU;vºÞ8°Èéµêèz%uÕ˜?¿—;H÷Á{ïFí £Ü­.¾L=(•ÌðAeêý÷ÞQJ›"òu³-0"wŸaÄÁsˆ¶E'3(ôè™öŽ@ú¾Ž¶Ÿwþ…Æ£ú6 ƒõíÂ…òåø±öTÄ6Á¶#Í[œ%‡èûÒ È{ŽFÍ ¹ñ°ÔÕ«Ÿ¢^« É6³±éGÐŒWÍš§Ký 2” ÏÕÏ?ý¤üÆ„ÎØf{Ìl«¸k¸?ð•—dóæÍ;"?7Ôņ…ª:5~ì{˜«[÷yûÅçrÙåm½Ù9[0¼[QÞï¿ÿÎžŠØ~øÒ´Ys£æžÑ&ót`bêäI„!wFiÒ„ fÍ¢3›67†ñöИ4ñ+ùnÑ·æ·‚ßMµ‘„¦Œ ¸Ç´©Sgmœìxžm^™msM0 zôê“~f•Ø_¯ k×®S]ó©žäšÙ´kÛFn¿íS´Ñ_Œ‘»ÿwÜÅŒE(@¦¹%àeu¢D„&MšÈ}÷Þk²†ú•ºví¨Ý/VÁÏÓ0Ué¢h÷=ð€Lœ8Ñìg׿ÌŒÀÕLï†Ý³1wÞÕ×µvã~ÚiòŒNÝGëÌ"þ¬Y³¥»^»¡\Ù²æ#Ž‘ÅháW5nÛ®CDç?Ì+\ )r´~Œ F¤w¼ô²¼>ì ÷”Ù¯Wï yüÑGÌÈg†‹Î‰…ª—~½ê@ûÃUWv›ºÞè?qŒNS—®7yÂÁT ¢ñÀ»¦Wï>^—·m§ÏÏqæxæŒ2}ÚïvªT=YÎ9·¥9çïøÚˆø0_F\Ó ×»kô莾ÿª‚⇼g“fØâ#}ªÎª àÃýõ×s¼8®Áð—ãÇ©'™o¼kþÛ)ÇùA_1£óþ8ÑŽëÕo uϨg¢„ÕÑAéÜ%­ð»7o®—í5×v6vQ†j”Åð"8;蜿9üÀ‘RŒî·nÝ&jÇï“Ï>%?-þÑÉ5mîX/SU'kß‘!Bú‰¥êQîã>ÈpÙväìì}^2DÔXÐò­od(ÌL0€°q’z°‹~_ÿ»Œþzè·2Z[D˳×\{½‰b…87þù¶ÒK+º§2ìÿóÏ¿:ƒ;0ÃDv±‡f'Ø Ýo¹-jû~ûíB7&|0*C…xÂ>Oë´ïs¤ÎpZû¤ [ŽSáö nÀ@ÃUW_µ¾ˆ¿zÕ*¶\Áà§Å‹¥‚¶«Ÿ©ÿö[oêo¶‘÷~´çÝíø±:X²0ã`Iv<Ïî}2ÛÏ5ÁêC0¤µ3 ,Ø·èÌ5¬__Ž-YÂK„Î"xæÕàªõ½÷>ùbLð¨PXùc >·ƒ-êûP™ h;oa¶'H­-lÞþ-ÖÑhݦ­góËÒ¥³H˜a¹¼Í&ʳHG‘ÔßâÖ­[týŒòR§n]/í×_k½'|åÝ"ì!¸|§¿A¨Ú@UíDÑî¥J'ùtV‰¬à6cút£j†AýÆõ6àÙ9è ½3>ö|Øuß´icØå¸ÎûŸ§¿ÿúÛÌä ÿ"EŠêÌL£ˆç³É®yJª:'x,ÐYÌåj;²k×Nà*Eü¦ ,»kЏ‚-4lˆ–,ùÙü+W©êÝÛ^ǽgLŸfÔî È4hØØ›µ $³ëy¶÷e›+‚ÁM7Þ W_ueL«³ÆR‰ 8Yñp”O"ÎU¯v²êÏVNDÖiyê¬úwú2üvQð´Yân[Îï«Îe™2i°Kôã ›©Üzv×íUu) 5ŒzcZ7»B5ý¨¾ð\šžôOÚéèì¸BÍŽ{„ èŒWï4vt#æ9w ©÷#Ò]Ò~øÑGòècOxQn½¥»\¡³Qګꆕ´‘žV#ktv`›]v0:Ž™L¼¸1jûêÝ»¡Ë 嚎iê s´³ÒMõÓmˆG0˜?ÜpcW›Ôlýõ÷{þéyûmÚy¿ÌÄ….w›+Úg° ÀZ×vºÆÄÁÇÝ.Z]t¡ÜyGÚȾ$€ñìǪ½{ÌÔ×¶6Äê•j5Gy„I†´OG}l³ð¶Ü xØ1: "Ðñàc;mPiÀ¨·kTŒLÜY€‰Úq𧍰P¶l9iuɥ沿“1  n%ÿŽ¢{ÏBøm :š !ÿjÕ®+ 52WÑ‘~ñ…ç"غɰ&C‰cKFŒý wùƒÏ?û4ƒºfÚê³AÁ¯>õ£F›kXÛ.XÝvÆëžûóÕ-íW*PÚP§n=£ƒc´¿Žý©êĺéF¸Ð޶6¸9œ‡A·ß¸ÜÍåðâ ªQa‚ê}“ªZ5˜>ü@~YºÄÞÚlÑy¿¢}³¼_Rá'hQ²hm‘aúAÅ“*©ZÔÞó •¬7Ԙݵ¹°6 H2i¢Î`©På†ãU/ý²ËÛ˜<üBqv³ß°aƒþ¾†E´[ìÇj|lílüé£Ðçïé} îó4ªwzW}&¬ªÖ$Y³fµw[ÌŽ”þÞz¡öemó«ØgŸ~â¥õ c¾mT†l<»Üä©3â]‡G×Îö7ÞÔÍS-zQí”0ã‡Ï³-S,Û\ fLQ½UŸÞ_,…'FÛ4I{9Å“.'â^zq+}q’Ð[á…ûÞû&ôYÍ|Ú¤¯ÌÈÒ7<³yĨXVóÌké)|ðÞ{ž”ÖºŽFŸ£…XU‰ ßM_Ç`£]]’Þ–<×ÂRw¿ù&“•¿ÃîæV‹òà÷™Síl’ÞÁ ŒˆÛÙÿˆ¾I ÿðÞûÀ‚*\ÀäÜó.ÈÐ1²qý[ŒœCxAX«£ê¥ªã8VÁ‘gŸ«¿½tá¡î—-Òf‚ü‚‘[·|HgBF§%òýÿ\?ZvVÄUI:¯å¹j@|·‰ý»v0*OˆU0(P  Ž2Ö6¿i »B÷~9=ÿ‚VÞÓ½†}¸€üðƒw3tq­ÕÅ—tìà 2HÝ×`àgÕº :òư×Ò.èÌ\Ø‘oØ ø;È^DÝqUfÏše\£º×Ãö1B}ñ¥i⼯ºÿË—ý‘uS¤ªá «tVøí‘#óiÖü,3ʉ‹Ðãï‘^ÐÇïÌ Y –¨A&Tœ‚¹瞣Få÷˜K°o¸ûÞÿ™}xJ‚—„ÌêågžzÒÄ…*|ðÛdØ3<‚}3ïúú0Y¦ºµA!»ƒ&g6“šj Ž€Î ”gëJÀ¶s=gèØCWÞÿˆ³;—©*Q¹ò:Ãf/‘Q•hïúX¡6š*‘;Òf hË‹­1hÔ…Ó¬úB˜!®›Æºh ×M©ã a®UmÜ[{ô4Ï,flൠnM]6Ëܦq·îón RÝ™¸C‡ r“DìÃH¶ííÌ9ÿ¬…íÈeÖ9-®¿¿ö®2yø ¶ÃƒK.m-ðøbCXÝúAŸÜ¾;lºÌÚÂÆÃÖ]›cù²eÆ£“{ÝÝÇ}aXïzÂu¨˜­×÷ìEÒKKS%±i³›½¿ƒkïã߯*øÓe娣¥úÉÇ#.Ûç)Z`·5/„ Á¨Ù•,Uʸ*=4>ã¾Õ}NÖW0X0~ -W0šB¾®#W0È®ç÷ˆ'äš`€&×/ˆ§©2ÆÅCë ’a=ƒ}5>ÎH"ïI¤ññ@µ1¨‡A<3® ¯Ï!·³Ž3:ÐaaØë¯ÉIêýÁ n‡x‰ŽÂÙEXAçÛ\ÞZ`§àÿÅͪ`¤ßoó ⩌§aD$DÜÙ§·œ¯FܶÃjïm·(ßͺ2±ßlvøøºëDS«qGøü#5OSß3›š"CGºÚaÁ¹öëË_¦žyNP^#u”nuú(]P^®ñ±_oßðk®½ÎA]£jð¬“Y°£ôAzÒHë þΑ?o¨>Ø\·”vÖÛ¤wÖýñ£[Õ·³ÛwÒŸ³ ´ðãŽÕüB„íÈÁÆžw‚+Èø;da‚ÁÕ¯Õõ+Š„exÞ?“„H™µ…›‘ëeȯcîÆ³ûhŒcÖËÚÓØkØBX‡{L×ð8»Ùûyº÷w÷÷Á ’ºµ…§´XÞû~n‰ ²ëyvÛ4–ý\ P8ŒÄ=ýÄc\Ü,––òÅIV¡Õ¸¢ÍåÒ³GšÎÞêu¦ï=i#£¾*Æ}ˆ5 à±hÄ[#åégŸ;}v&H¤»Ò¾wÝe¼Ð ¼±,D`à.œ–“^‰† (UÓ´ºã®¾2aÂÄÐæøD] Wx PØF|úÉG朿ÓnNfò¯œzœá™ýPÃË [¦#€Ô» :7èÈ<ÜïÁÀ{ĪJ”ÁÀÍ;3û€† êË“O¤¨Á«ÜðùÞðbÔ¼YS©ª3#Ö&ÁÆs½ÙsÙ!`Á¡ÖjX‰€6жÈâ¸[xÇٚœÎ\”> ×±úCZÜõüÆžî,@fîJÝNù`5ÄÂnA>æ×®dØ5„\Û<0Su½®öŒ¦nà–¡¿0ÌÚü¼}ŽŸîsÚíhc´~ºúM%üøã÷F]ÑMŸY§þ8]Pìò¶iÞwü«[Á6B/õO»FÂ~A$L0¸Xg ¬G#¨mÔç,³ï5®ÚV,máæ‰ç Ò!¼1lh ]Œßîcv î-Ëêó ^ö™±×]§ìfïïàÚ{ú·± %K–2¼þô¡Ç*ü¸ÌƒP¿‹0¶~ýºˆ(öyŠV§°ÌìÁµ¬}ïcæ3HÔ k±`æƒ|—µ¾ÜÜÓD ÙñÝöí;b¬Æþ v¸z…›B’a¦À–+½~òá{棗µi'«tQž} …uÁ™Ï>þPÝ æ7#.§×­¿/ÙísZÔ+Ìðr_3o¢^Eîû_š0µJWòì zÌ®®¦?ÿX¼‡¦®êJ™,î»ÿ~™¨«Ófg°öèx7pì Bƒs¸…+Ѱ0eÒ3Š:»63¦MñÔ' ‡ŸYgÌÍ¿WÏÛå²KÓTN Ãß~ÙacÁå´6~nn°w¯°3@pՑ̉Xh ^§{ÎA´»v3«MÛ$Ù!¸*~µ{wëv„Ýéuè"_{ý &*ì`b]àÌ?CáÎnqmG,Ñvhàà>/~cì0ÁÀÕÉÎLØ º'ÎÅÒai÷å<7à‚ÙzëÂûËU)ËNöþnX¹c lÙÂò :ŸÝ^‰¢Õ)L0¨{F}u,ÖWÀÌшá×µÀ 5>öß#Ñ‚Av<ÏAì3;—ë‚ $ÌýzžÜ¬®)D6aP€u ¦Ï˜WG(2ל?rÕ‰fÏ™+]Õõc´Îmf%|ø¡äìt.3fÎ’nê‚,7:pøKDð¯|üø“OÊçê¯?,Ä*´lÙRz÷ìi²Éé•Ûè,R[ÓÜnV=ÍõZµºH ƒ€Ñœ3u…\Æ­®2Ó:eƒ‘ƒÛKÞ/y㕨`AÓ!!0>ÀUǼzº·'ž|Z=z½ï¥±;P5êq[ÚL—VÂÕ÷wþ£]³yc¦J„k_ë-Šæq ñ\Ï¿Ðß[á+&ÛEÕÖ¬Yø®€÷»~€ëÑùº‚ÁÓÏ>'o¿ýNÇ0ª›xeжú/„Ôî·ö0ßÄ:d°ñµŽ}·“<êãäçŸJ»àü/R´¨\­zþ~u$œÃˆm7U›²£…azûuêœá-&¦CŽ<:\ÙQŽI¬A‡|¨ºòD:–оÃÕR¼Dñ@7¥6½[çh‚ŒkÈè/¯U‘AžÑ ˜ñ;‚EtÚ]ónúhƽ»`ºÃMÑýê5V0ÀÅ EðL"ý‡ãþY”0ÁÀõââ÷ c2Jÿ!®P¡Âæhóæ¿#f_bi 7¯xöá5íÀÒßß.óþò§Å÷ýf¸ÏTm ·óœìý\9ì±+ŒV÷¸?„¬f¬‚ëÈ ìy†ªQËóÎ7HüÜ-dÇólÛ2žmž P`ü{ø!9³Ic¯ü<f'L(€¡ñ¾tª#ï’3G+”—·TuÆ}ñ"å_ð¬\.^œ±£`ï•[¼ø¡N”¨àªü@¼ã5ׄª7Ä" #ðºz©)¬S«±¨(e¥na3øPÃ%§Õ‹5êS]\푈[À³Î0-£]äkĈ7åùû{q°6J×Óôš!”ÁÐÒ¿Á êI¥v­Z&ëõ‘~IÓtg˜%ésÇ]^¾ØAç…]g!§ƒ®fí—4CLøþF‡×o(|GŸ^rq«V¦ÜðÌÔL…&l\»Œ UŸªXµDð»Dugt‚Öa0‰ôt¡a< ßö&|±:ÏPq±ñiªÊ2kæ ›4bë®ô4ºí® ŒÎ÷àA#ÜbUÞNªÒsøáM¾ažb.P#u¨s À&cø°¡ïÒ¢E1îNñîE°úöæÀù×Z×c9>Ý¡„æB‰% o¸)¬I2~ì˜Àd®`€þÎ2ÎPºŒ\ª«[Æþ…ÛN;]‡kr&¢jÇt‹Ññ÷û’wGêýìÝY\ÃŒˆßhÛu튛° ”;{ç 8Ù 5Ž_y”íŒz ô¯vpÕ)w/L0Kø¬·ï¼ûÜÕ£‘žŽ®óž wf!Ö¶@>6 ãˆgß`¨«EsŸ{³Ú~Ø5‚:¢˜ñSV0p½ee'{×Öſų‚g6g£tV>(`ý¼Ãc »´ÝW,_kô¨ñìó­Na3®ªáÂúÛÓ2Ý€Ù·k¯»Þ[ÙD ûú<»u‰g?Ï(4 <®ÞŠüÂAw}iFÓ§Œ§ÂÉ/ŠÆF¨a¦ …Û½{ö6­÷úø2ôuéÿÒ+örLÛ¶mZK¯{g>øècé÷Èc1¥Mt¤Dz&‚W‹‘Ú1>"}„üëyóäöôÑþ¬Ôë™§ž–Suu`„ta©6í®ÐŽÖYÉ*jš0Á‰ÜYC¯xÒä)Fà9養ª>רû`‚-¦ÝÝÒŒý™ñƒ}|¨§L&èÈV¬XANWo8EÃDwTüüóZÊ=w÷E2`ÀèÔÝËFØ£;˜Ù±qܵÞù–r[i£Flý‚.®\¹R–j[ìÞ³Ûè©»åX«3CoŽØ;øb3sGžÑÖpÿ¸víZÃüµq½ú`Ui¿{ÈnìæyÕÁsˆÕ￯W5ÎCõwv’Y ÍÞË?[€ó¶#gã }~øá] v•é\žtRåˆ<°âî uEìûœ©4U=¹šzy9׋¯Hp‰Át`áZÓ.@pÁ‚s(B¬máe®;X ‚(BÐÌ”¹þÏ5TÆ=¡"õ‹:ZØ¡íP¡BEã·>¿zÄA2ZÏ.öþnzñ2l\–(ïJe‰Ù9<£þEé2$ΡöyŠV§0ÁÀµWBqñ΂g(|ÓgÞÑÜ÷¾ÿ‰ P&· pÏóŒøY yJ0@‚„ƒ ¿•uÀ¾H³RÑdN¡Â’ýp£.É. aôÊ©vòÉ84aæìÙòP¿Ge~¨¢ŒÝ¢>ß!ØcÑN×w‰y¤Î¦KÔvû¶m²3D6;îYGý#?òp?ïÅ5yÊéׯŸl‹q¤e€ZRß¾}Õ»UCS$|èï¸óN™=gNv1CÑD~ü±G"tÝ3d '0ÂyËm=L‡ßÞŠ £mÕfü×í1Tïnëq»=4[—×Î`XX°`¡Y±³¹!T¯^]u²Ÿñ:5aåœ2uªôìÕ'Ãeøß®šn$™ábú |ü±~ÂX]+à P Ðåœ Ø€ßO˜ â`F+nûÝ•Úôv‹ÙçÿóÏf{Êl³‹½¿ƒqçêvX•×ÿ»÷Û|8Ir|×>OÑê& °îâbA…_¢‚w™2eŒðç¿GN(SVŸç úÄr.Ï (tp€…xþ÷ÀC±Ô)åâÔÑ•DO<±´W/xa=^BÉŽÖ;¦K«V©ìU£.u4³!™„°yó?æåT®\Y©¯ aõ誓k„Ç›µSòï¿ÿÚS¹¾Ý¥£@ñtÒ³Rà¶mÛJ—ν¤Ë—¯PLÏÈÅË,Ô¨QCõúo“Ò¥Oð¢¾üê«2räHï8»w&|5^ v˜ÞÂêjѼ™ôéÝËÓ/¶eÀèæ¢Eß©:ÊíÆS„=ïßæÓõSO>!5kžšáƒ†™†þ^´!Àšw÷½Ëxë±[äçB ³†Ï~W ø`ÛNÔâÅ‹åªtÝv¤v ×m@½zðs8zôrßÚKÞe|Zœª¡B‚ÿc ÛŒÇ2C§Þ&Æ{µÛM]åÒK.öV·×°ýNWå|XgÛÂ\ÅÖ<õT¹õÖîRQ]åZ>˜UéÕç/»Æ:rc¿-ßëHmP¨V½†Qi±*þ8舩3$K—†»­ELõ_¡‚†5øuóªÌ»ï¼ÓÈ&<&µjuq ±^×y€ºËŽÁªA¶câÞ;³}ièd Ø…Ö‚fXÜ|\Á.Ha8 #_;ú¸à¾|Ù2¥jaø½„tøÎ¿ð"e:Á›…³q¡ …E·ÜÕŠí5»Å³_ÿeÕ›—ÿ9ÄLã˜/>—e¿þb£Gl-/Ô*WPç*­ncí¨;"c&`®º¢E9‚‚em„¾\ù y¿·|7¡3yâW'›ofmá–ÉUGûQg>>ÿl”{9Ã>„ƒFꡪˆá7é´üáCÍv^Aa_Ø[[¿ Ù ûØs$©£¨ÊÙßý· ʸôUàm¼ÜÚÆR§ :“u>ïþ(ðÄ‚˜§èûÍÖñðl}·h‘ªì÷<¤ù¹•*µ×pØJÖ®ú^Ð îåÚr¼5bxÕ:ÄÉÊóŒtY yR0@EüÂf ê5JÓÌJE“9 :Á‡äK›ª„P€ÑÎT i졨¹?ÌXêˆQŸ×†“W1#n±¤ÉÉ8[¶ü§\bïxe‡örm§k#nÁ`Îܹ²ðÛE²vÝãjóè£ KIÕ•¯ª«~Ö©]KªkçÌ ƒ‡ –7†pOåê>ÞÕ«W3v‹¾ûÞè€Ç[ tÊO;­¦ü©bÐñÅ(vf÷…êÒqê ÷Å”~^ ¨ÛÉênô/y¯ííUVf¤…zU!õðu"¨NÅšyå»wï,<åO‡NçUm‰Ö1EÙð[/ªå8Fõø éžÑ™˜ F-%7“ný 7^ZGõàîƲè”fE•Æ®'žXN×íZÕ{_±by¦õpËï>T¯O÷÷ÿÕ—ãeþ7óB³ð 6":š°íøSë »™xÏBç³!7þ÷Ì<¼6Ác›Žþÿ®BTfV®`à®" xÖ®]“¥¶ «7~Ï%J+ùU¨Þ¤õƒ«Ù 2ÆÓþ{Áí%ò scëoaÛUøè"æª9þ/l/û°|b9ïtuÚ é=Kžy)ÞG…U…ñˆ#Ž4ª„ÿþûO^*žW–XŸg/Avò¬`€º@÷n]¥µºùö»òB†Y¨{Ò$©^ídó_²äÓHš‚ÇYP$ßÑ«§Ô¨Q=¦”sæ~-=ñ”üª#dy5$Ò;‘[ç³Z´P9·Ž»ñ‚ö¡šóô3Ï„Ž4¥á9 }'P«v]­ld2r×jÊ9L0Š›—Ï… ¹]æxÚ"·ËÊû“@" äiÁ ‘gÞy—@¹²'=sŒj/^LŠ)bfV­Z-¿éåJ5=f¬åÌ»µH+ÙUݲuKŽ›×tì(p;z Õ™x†€›ÓÁ:Ûòç_ij ™¥áu ì#€U¾±X_fî[qG ÙÇ=(§xÚ"(=Ï‘@ª `*-ÉzäY95k`:ªÔ®S[ê«»?,VVDLYíàFU7€:ÚLõò2SíTâú¶÷à–H`ß Xýè W‰þÜ)ø‰dïq ó—m”®§g‰á[·6‘ÒÇž¥´n¢©?®“ÞoÌqOÉÔÏ“<@¶îØ%MïqíÙŽu¤v…c"Îñ€H€H€H€H€öw ö÷'€õ'    %@Á€ @ö … æ—ã‹ÈímN“bGši¼Ì"P•(3B¼N$@$@$@$@™Èöƒ«——.gUÊüÎÙ#·ƒ·í”Uü+Gž_Š»€³eûNùýï­²}çn9â°C‚ ¦³VmüO=ä )rDþÀä;wí‘åþ1× È'ì$Ò®üã9Fëvø¡‡„Eãy    !«‚ÁÚ?·H·A3"P¾xÝR¢ÐaçÞ˜´D>ž³Â;WåøBò@›šæ8Q‚¿lG8D†tm(oMûEÞ™þ«¬Ó²ÛpX¾ƒåê&åBQPXöû?ò®¦ùbþ*`àŽ=RΫy¼\^ïĈKïÎX&oëýlhS¿¬œYµ„<úÑBã Â:îuÕ º×EÕŒ°1}ñzycòY´b“ìRÏL©16òïÔ´b¨W¦-ÛwÉËc”?­—ßT豂ǩeŠÈUZÇò%Ž´§¹%   H!¹*,×óÏNŒÀä­èÉOɳ–yñÊ?BÞèÞØ'J0*[íX¿­ü°Ð¸J y¤ýé—§©×¤^>¯Iœƒº‘Ç;Ôöܸ>=j‘¼7s™ãôrEåÇUÉ?[wxçÜŠ”ÊËÃ,pOGìŸX ìÉDºŠ]¸|£ô>Wà‚6,@¸@ýT*…çI€H€H€H€’”ƒ†  B¢FœqKcAç3í|‚Σƒ}äaùdÓ¿Ûpžºº¶œ¡k- øƒˆˆûppëyU#f'V¨ºQÛg&ÆœãCWœ&MO>6æøŒH$@$@$@$÷ d»`Y•R]÷Ñ}Ï2Ñ‚:ßyyÆ…ÆlÅEµKËÁÚ¹sê/*7¸ÞVÕ}º·¬‚]ôåO2䫟̾ý÷¨Ž¸××wKÖþ-×ôŸâ©û ÎÅš7T‚ÂÜãÄb‡›ÅÛfþ´wq7“(ýß Êšu"¦ü°N0káÌL<}uïT§SÌL„=Õ¨®gW2ÒŽúzeÄ,T‹>éÓB|“69·$@$@$@$@IH€‚AH£ -Xm„ª0añ4Ø œõà˜ˆŽ½«NÔù•iFÏßÞâÔ‹Hµ¡pÕÏO’¥ë6{§°øaC î¾ìiyêqæú5h÷ÜDAYÝp×%5äüÓŽ÷Nù;þðõQïææú”Ö"˽š£c{.ˆœFª:Å@$@$@$@$(„´cPg¸O«êrQ­"RÜòÚL™³dƒw®òq…dð ¼ã°?U—Ü‚Õò̧‹"¢D ÐiŸüÀy#õÏ|ú¼;#ÒîaÒ-åƒöz:¬³ƒuöÂxAšxKsøâ?È›S–ÚK‚Ù„>U÷ŽíN]áÚ5¸†Pá€H€H€H€H 5d»`€Ng™tû DÅu´ú±µÌ¥ Îw^V%Âh?FýÝÐO }?SU\Ãh{~&¿Vª— eë7Ÿ7ПIDAT <YB6ŽÝF Ž+RPÞéq¦j¶Ç/–×&üìsUµìIxQzö³ïì¡qjƒîCfGÞÅwb€bÌŽÑH€H€H€H€r™@¶ ñ¬c«`ðÄÇßʇ³—{¨ÜÎwNz%r ‹ma¢yLÚ¡îDïzëë :þ6mÐ6š`£f”Á þÙèÿº£…EÞWÏFO©‡#܃ 'lÎhmã†m‹«KÙUåˆH€H€H€H 5ä9ÁnHÑñwÃ=#çÉ—ß®öNå–`ïlÆ#æÊ$)ð¬[PV;ùu¯õÂz6ä´`píKSå‡ßþ´·7³ åbX« »=qeÚÌ—˜;$@$@$@$@IK W¬ÜÛú©¯"à=ß©®À_¿ü×d 0[ÐøŸ»Õ0Þˆî¿üT)ÿ`ïü­CgÉìŸ÷zÊiÁ‹¥}â,×°rqOÕË+$wH€H€H€H€Rž@® ðêÓìþ/" ·oXNn:§²wnÃß[åÂÇÆ{ÇØIÁ໕›äú—§E”ö°°^…Î~hL„+М °pÔ¡l€ó¨;[H!u+놑º³»¸Ûê2Õ¿R³Ÿû$@$@$@$@ÉE W jxÏgî>Ñ1í­~ük”9Z×øOÿx¡¬ÿkkÕd °FÁU/LŽ(÷=êjôÜtW£¸ðÂçßË[ÚávC­òEå¹kêšS~w¥‰°1€pvþ#ãëU[¸e½ûÒrRÉB‚ÅÏÆ-\%¯O\b/›í°›IùTŽ"ñ€H€H€H€H ÏÈuÁ Ïð9‚E¸â É ìÚ½Ç=þzÁ®  ª}·òψθçzûÉ Á÷·pµüïíy¶™n¡ê•/    Ô!ë‚Á«þ”N¦F%Z½taY¸|¯n2¨Ð+ã~Ì0Òµ¢z±pÁüòÙ]i^…rJ0@™ŒùA†OÞ»žAX91k1°Ký;‰°¸‡?Î÷=ÏÒã½÷—d!@€ @ ©zM}ýUž @€@ üG @€ @ þ @€ ø?@€ @€@úpƒÁܹsÓ /¼?÷¹Ï¥UVY¥f>kÖ¬ôꫯ¦>}ú¤õ×_¿ö¹ @€–¾À‡Ú”hÊ”)i„ ¹V‡~xÚwß}k5<ꨣÒ<ß?ú裵ϗteþüùé­·ÞʇYc5–ôpö'@€ Ð  N8á„4mÚ´|ñf̘‘zöìÙR%  @€,‰ÀG N9å”ôÇ?þ1-¿üòé–[nY’:´ØW0hÁá  @€º>²`P×Ùu¢`Ð 4» @€4½À2 bræÿüç?iå•W®lÔ™`ðöÛo§7Þx#¿³ÿ#^ýõÔ«W¯´Â +töö#@€ ð‘ |dÁ`Ò¤I):¯´ÒJ)Ö[/n¼ñÆÜ_à‰'žHo¾ùf.ûå/9 >=­¹æšéôÓOO‡vX-„DÁ‚A 2o @€–n âIÁA”üñL7á;î¸cŠ_ýcN„«®ºªv3¿ì_xá…y>„ø5?–ûî»/½üòËy}ìØ±©G©_¿~ù&>>|å•WÒ7¾ñÚ16Ûl³´Å[¤Þ½{§§Ÿ~:]sÍ5yßøsä‘G¦}öÙ§ö¾Ѥ):MßÖ[o½tÖYgå§ µ¬ @€XFº]0¸ýöÛÓI'”ùF•N=õÔÜv¿ðŒ§ G}t­ùÏÅ_œ6ÞxãbsªêcpöÙg§ë¯¿>—ãì¹çžµ}c%ž&ì¿ÿþµàp÷Ýw§¾}ûæ2E0(vˆ€£+E@1,j¡â• @`YèvÁ`Ûm·Í¿ÄG߃›nº)ÿÚß¶Üt(ÂC¹ÿ@{Áॗ^J;ì°C>\<)¸è¢‹Z:¿áSã†?–O<13&¯··Þzk0`@Þæ @€eY [ƒèD¼ÕV[eÏhîsòÉ'/ÖöàƒN=öXÞþàƒ¦W\1¯· ¢™QtRŽ%úDik‰™“£C,n¸aºôÒKóz9Œ7.qÄùs @€,ëÝ*<ùä“i¿ýö«™ÆSƒÅ-1JQ±Ä“…èƒK{Áà—¿üeš8qb±[å¨ö¦ÕJqürÇår0¸òÊ+Ó!CZíå- @€eS [ƒrÿ‚Žp^}õÕ¹óoìÓ^0(÷/èÈñcXÕXÊÁ@3¢Ž*K€ ÐÝ: ®»îºZ›z´­f9S¦LI&Lȇ~xÚwß}km WMƒ¢‰P,ñkü6ÛlS+ßÞÊvÛmWë‹Ð^0¸üòËkçç# U-ÿøÇkC¡–ƒA̰ÜrËUín; @€eB ÓÁ †-†›ü½öÚë.—©'üë_ÿJ£GÎÇÙu×]Ó±ÇûcV}Ð^0˜6mZ~¢Lj9¶Ür˪õØ^ÅS„¼!@€ °Œ t:ĬÄÑ7–ÅÝÄǰŸ1kq,åÑ}â}[O bƒ‘#Gæ¡Bc(ÐøŽ^½zEñË;3<ðÀ4wîÜÔ¿ÿ4uêÔ<_Aj/<ûì³i·ÝvËÇŠPá ­%Ž[<݈ÎÐ1Z,‚A[Z>#@€hNƒ˜€,nÎc‰NÂwÜqG‹›ø ä›ü©õ|mƒ({ÅW¤K.¹$ï¶Ç{äÑzŽ€r¥hF4~üø\>þ|ï{ßKwÞyg~ÿÐC噑kß_9ôÐCÓÌ™3óG‹{jðï|§6ërÌ£°ýöÛçò‚AYÒ: @€@# t:,\¸0Ï <þüì1lذÜ?`5ÖHÑ$(‚@ñ´ Fö¹á†jCŠÆ‹ 1Pô-(FÚh£RÌ`¼ÖZk¥˜‡àá‡N×^{míDØ`ƒ jïãf¿˜½8nèã¼úôé“¶Þzë\fÖ¬Yiï½÷®•cÇSŠÕW_=Ŷûï¿?MŸ>=oo=—‚`Pc³B€ Ð`áPž =—¶~™_\0ˆãİ¥ßýîwS:wì¸Qße—]Zl.7q*6”‡Ïbæã¡¨j™õ©C”Z @€4“@—ƒfSW @€( 4âUU' @€ :¦8 @€F ñªª @€ Sœ @€@# xUÕ‰ @€@ƒ‚)N€ @ ƒF¼ªêD€ @ ƒ‚AÁ'@€ Ј‚A#^Uu"@€ ÐAÁ ƒ`Š @€hDÁ ¯ª: @€è €`ÐA0Å  @€4¢€`ЈWU @€tP@0è ˜â @€Q@0hÄ«ªN @€:( tLq @€( 4âUU' @€ :¦8 @€F ñªª @€ Sœ @€@# xUÕ‰ @€@ƒ‚)N€ @ ƒF¼ªêD€ @ ƒ‚AÁ'@€ Ј=ž{î¹÷±bêD€ @€@ý‚AýVJ @€hXM‰öÒª @€úƒú­”$@€ а‚AÃ^Z#@€ P¿€`P¿•’ @€V@0hØK«b @€ê ê·R’ @€@à  {iUŒ @€@ý‚AýVJ @€hXÁ a/­Š @€¨_@0¨ßJI @€ + 4ì¥U1 @€õ õ[)I€ @ aƒ†½´*F€ @ ~Á ~+%  @€4¬€`а—VÅ @€Ô/ Ôo¥$ @€† öÒª @€úƒú­”$@€ а‚AÃ^Z#@€ P¿€`P¿•’ @€V@0hØK«b @€ê ê·R’ @€@à  {iUŒ @€@ý‚AýVJ @€hXÁ a/­Š @€¨_`‰ƒÁC=Ôî·õìÙ3­°Â ©oß¾iРAi¥•Vj·|wØ8þüôÌ3ÏäS6lX>÷â¼}ôÑô¿ÿý/ 0 ­»îºÅÇ^  @€,ÓK †Þ!€Í7ß}zêׯ_^Ÿ7o^:ñÄóú·¿ýí´þúë§›o¾9ÅMúƒ>˜¾ô¥/¥ /¼0o pï½÷¦©S§¦Ç{,Vü‰ð0jÔ¨4nܸÅvp~ñÅÓUW]•ž|òÉÚ¹Äþ#GŽLûï¿zõÕW;Ýùø¥—^J¿øÅ/ÒO<‘þþ÷¿çÓZo½õÒ&›l’¶Új«4dÈâTó÷ÿèG?Êï¿øÅ/Ö¾³V ´rýõ×§?üáù“ƒ>8mºé¦¥­V  @€,™ÀR ¯¼òJ¾iSŽa@ãºX",Dhˆåì³ÏN÷Ýw_ºí¶ÛŠÍ)F3Š`¿ÀŸrÊ)-¶Õ •V¢ƒóÏ~ö³„ƒ'œpBí©Ei—Úê–[n™"´ÄÒ‘Q‰îºë®tÚi§µ{ì‰'ÖnêãIG|W±DØéÝ»wñ¶Åë˜1cÒœ9sògå@Õ¢7 @€:)°Ô‚Á /¼oø‹_øc8н÷Þ»vÚå`°Áä_Ü‹ñ`‡vHGqDºúê«kO¢ŸB4MЧ LûÛßÒ•W^Y»1þ {î¹gq˜4k֬߿äÇS‚Ï=÷\š6mZš;wn­|¬Ô f̘‘;ì°Ú¾;í´S>¯·ß~;?ñˆ›þb‰™â H,qüßÿþ÷yýôÓOOÛn»m^/ÿyöÙgÓn»í–?êζËçl @`ÙèÒ`7é­;ÿ÷¿ÿMÿüç?[ÜpÇ ô>ûì“–[n¹šV9ÆMò1Ç“ÛÿŸESŸh¦Ëu×]—\lʯO?ýtnFo¾úÕ¯¦óÏ?¿¶=ÂH<1ˆ%ŽOŠ~ñÙo¼‘›3=üðÃñ6/­oÖÛšÇ nþwÝu×Ú/úñ¤â _øBqˆüzË-·äï‹7|®¸âŠüy4gÚo¿ýòúf›m–.ºè¢¼^þA"þÅrñŧ7Þ¸¼Ù: @€%èÒ`PÏÙįÿñËÿ׿þõvƒAùÉO¶Ø'Þļ±ÄÓ†ò‡ø,žND§ââ×ør0ˆî“O>9Š¥8 rÈ!y½­?1£ñ”)Sò¦z‚At þÕ¯~•Ëßxãi5Öhë°éOúS:è ƒò¶øþ8X"$œwÞyyýØcÍOò›÷ÿüå/Ißüæ7óÛò>Åv¯ @€ºB KƒA{óD3Ûo¿={î¹µó>î¸ãRüZK9,îi@mÇ÷WbÒ´§žz*÷ˆÑâ‰@Ñ9·(W—_~yºôÒKó¦ .¸ ÷-(ʵ~-‡ˆz‚Áᇞyä‘|˜è÷°¸¥¦5¶Ggâb¦òSŠè÷ý(Š%šMž<9¿m/tå½ @€èŒÀR ÅÉ•oº£ÓoÑñ¶ vÞyçtüñÇ»´x‘.¹ä’Ãw¶µD3¡âéD9œuÖYé†nÈ»”;ÿ¶uŒ8‡zhÞTO0ˆþ /¿ür[‡ZìgñÄ$J±ÄhFÅp¤1CôÚk¯G`ŠÎÆQŸîtÒ¤IEq¯ @€ºT`©ƒøÕ<:)Ktô¶þå`“‹¹E¹x>Ñ'FŠ%~¹bèÓOúÓiÍ5×LÑ¿¸™.ƒrÞŸüä'iĈùmý‰á@ãiF,õƒ<0=þøã¹üQGUW€µÖZ«Å9”;!Gã_ù³Âuë­·Îßá @€®XêÁ Ü98*“—­¸âŠ-‚Áâ:ð–oØ# Ä/îñ„ õRÌ®\åÂqó¾×^{µÞ­ö>FúéOšß× ~ðƒ¤ø•?–h.µÊ*«äõŽü‰NÆcÇŽÍÍ¡"ÜDçèè»}"ÝqÇ©W¯^9¤² @€êXêÁ æ1ˆ™{c‰_ø‹&Aå'‹ Ñ”¦˜-ú*|ík_û@EchÔí¶Û.^å9 âÆ;ší´î¼;ÅУ1bRÑ©ž`ðÛßþ¶Öw¢Üo¢õÉÅ< E'ã'­ç,(wBŽ>Ñœ*Î#†BNÉ @€–ÀR 1ÉY AZL"“Å$d±Ô Ê£µÕ´&nêãúÈÇ2dHžð,¿yÿO¹ÉϸqãRt.Ï»³*G3£rçßz‚AtŽ0ͤâ×ý˜d-š •—ØÃŽ#ú;|æ3Ÿ)ɪ£iT,^Š~ 1ÒÒç?ÿùe½!@€ Е] bÒ²òvq¢1ÄèóÏ?_›\,>&@1AYß¾}s±z‚A4¯9õÔSsù¸ÿþ÷¿Ÿ†š'&‹™‹c˜ÒòÈDQ&:*¯»îºyXÔèá Xb´øÕ>&I‹ó‹ŽÑwß}w±9¿Ö ¢`4ù‰àR,1WCÌÈÜ»wïOIn»í¶Z߈ÅMdûŽ?>ÏUP'Îý׿þuñÖ+ @€E KƒA½g£Åñ‹~±Ô bˆÒÝwß½öKz±où5n¤ûõë—oÆ‹Ïc˜Ò 7Ü0¿ýÝï~—Î<óÌbS›¯å‘ê ~¢ÏC„ö–¨{Ñf±?ÿùϵù ¢@{M“Ú<€  @€tB`‰ƒAÑÑ·ê»ãf;: G»ÿ˜» uGÚz‚A|G4Gúñœî»ï¾_O¢©N4ŠÑ|ŠáF£PëáIcþƒh2TŒ$T(šïD£ᨘu9f+=ztQ$ ><¯G½ËOŠwÝuWŠÎËQŸÖKŒ´ͧ ¢|tBŽ>E3¢è—пÿÖ‡òž @€@— ,q0èÒ³éÀÁ¢­~t4ŽeÀ€ù_y÷˜ù¯ýkn&´êª«–7ÕÖß}÷Ý“£-X° ŬÍñ¤¡«–¢ùTLì¶úê«§¦å—_¾òðñT¤èg0jÔ¨üd¥r' @€,¡À2 –°ÞÝv÷©S§¦óÏ??ŸßĉÓ¦›nÚmÏÕ‰ @€4Ž€`Юå¿ÿýï4f̘<ºQ åàöìÙ³¡S!@€hTÁà#¾²ÑähçwN묳Nš9sfqJ§vZîk🞯'@€hÁà#¾Ð FŒÑâ,FŽ™~øÃ¶9ôk‹‚Þ @€è"Á ‹ ;{˜èOæÍ›—GÚh£ÒرcÛœ•¹³ßa? @€U‚A•í @€š@@0h‚‹¬Š @€ªƒ*!Û  @€4€`ÐY  @€T UB¶ @€hÁ  .²* @€¨ ª„l'@€ ЂA\dU$@€ P% T ÙN€ @  ƒ&¸ÈªH€ @ J@0¨² @€@Mp‘U‘ @€@•€`P%d; @€& šà"«" @€*Á JÈv @€M 4ÁEVE @€U‚A•í @€š@@0h‚‹¬Š @€ªƒ*!Û  @€4€`ÐY  @€T UB¶ @€hÁ  .²* @€¨ ª„l'@€ ЂA\dU$@€ P% T ÙN€ @  ƒ&¸ÈªH€ @ J@0¨² @€@Mp‘U‘ @€@•€`P%d; @€& šà"«" @€*Á JÈv @€M 4ÁEVE @€U‚A•í @€š@@0h‚‹¬Š @€ªƒ*!Û  @€4€`ÐY  @€T UB¶ @€hÁ  .²* @€¨ ª„l'@€ ЂA\dU$@€ P% T ÙN€ @  ƒ&¸ÈªH€ @ J@0¨² @€@Mp‘U‘ @€@•€`P%d; @€& šà"«" @€*Á JÈv @€M 4ÁEVE @€U‚A•í @€š@@0h‚‹¬Š @€ªƒ*!Û  @€4€`ÐY  @€T UB¶ @€hÁ  .²* @€¨ ª„l'@€ ЂA\dU$@€ P% T ÙN€ @  ƒ&¸ÈªH€ @ J@0¨² @€@Mp‘U‘ @€@•€`P%d; @€& šà"«" @€*ÿC;$Õ„œ÷{IEND®B`‚django-tables2-2.7.5/docs/_static/tutorial.png000066400000000000000000000321321473544236200212630ustar00rootroot00000000000000‰PNG  IHDRjž”Î×sBITÛáOà IDATx^í} \TÕÚ÷†Á¹àÀŒƒà“ ˜N^e4±Ž†GòöÕ‰Þú%§úÈN5žz;ÔÛI<•bŸùV’õž,‘ʆOÃ!QÇûxG¹ 7@¦™ 3ð={ï¹2Ì8ÃMѵÊaöº=±ácqã…š†@ØÀé…‘F ©¹¹©©I«½1Ò!ùÑDÑLj£ÝÐÐØØÔ4cÆŒI“&b7âõ¡ £…@Ì,Ö Üð`ùg3kXÒ2jôk!Ì\úúˆÙ FäÖ0¦'5ÀsçÎ ™8¬ a[€ÿ­oÂÞN‡¸ãN¿Éwiÿ}Ü¥7u!0t} C$!p—"€èã.½ñ¨Û¡#€ècè" »DwéGÝF DCÇI@Ü¥ ÉmìÆî?ú{÷wF¿žžžk×®†††ŽPummíâñåË—ëëÈ*¦Í™={Á‚…ãÇ’1 E=dX¶L8ìmÐëõçÏŸW(W®\!…³X¬øøøyóæ±Ùl¯«3bÕëÌÔÑhĨ£^é0µ}èbL]mí: ‹8¤ñ4ô†€“ ð½Ýê6‘Ê cÓ|jÄàµàŽ>ü\*á5ŸªJfðlëêÒ¶··Eˆ»²¿ü"~ó­7Åâw@ÎÊÊË»¾þæïoþýôé3p ÜñÏ÷ÿÙÝÝíNÈ ãårùgŸ}VRRbã¥ÑhNœ8ññÇC<ËM…WïxîdïUÞ4ãP2(‹³ÿðÈŸwT[d(ů㗕C9¶ËvÉÿxàP…æ–÷¢é·Ýߺjòµ!¦¦~,‘6ÁƒÇ·à;Oò»»u w€3vTTÔ믽æ[ƒÍ=nܸðpnK‹d ¯òiþgGޱ ,X&òxÑd35~ãúÈæ-[V­ú£X,ÞlÜ–“ÉdÅÅÅ ºhdVµZ úˆT*­äé§Ÿ¦Óén¥`˜V«Ò¹2 G’Q«Õ:þÐð:‡Cn?FUCuµ–“”à¹?†Ž«çÎÊ›TZ3…Á ›2óÄÈ ‡_µ©«þœô|½Jg¦2ÃbgÍ›nQ"ÉÚL5§ÏV4©t•?kδ0§Ç¯¡íòé³òcp"g͹/d€£®­jÇñ±¡¤.îEš £Ùì>Ý]Š©¹ªIG }`J»îâ£}w€ÞÜ1yòä×þö7›VﮎaŒ§ÑhÀ ðÊïðê  wwÀ´èýþó/YëlÜ-Ÿ3gö½õæºçŸ‡ïßÿÃHpè$w¬X±ÂÆP#ÌYRSS×­[ïË´¶¶îÙ³gÁ¼ÍEÜüÜ oTâO ·ÁÔ"ýñÇ#òv,$rJdU[{º¤èÐÕ.[þ®«‡Š]hÖR9¦©…gìáz;ù›:ÎøQRѬcp8Ls»üØ¥-öG7.üÀ1y»™Éá0tÍ’œïp}²«ëkU3:6üV³‡[”n’`j¾Úd¤„N‰v"Ö›"“}¦›Þ1úÜA¶xØì?ìû„¿õ曎Äáˆ(#Ä’0%‰ ÔÜÁçóÉJAé(++•„¼Z;H}}½-Ò«Û{‡gê’Ÿ’k(¥¯X4?yþ¢ÇÒMa›Ï]h#ûm¨‘žh62ãÓV­XºT¸2]ÅÐÖJÏY¢«Jz®ãÌX¾ê1H^þ—¢©8z®ÃZǹ£ ÷¡ôÕÂ¥K[µ|k?'­²s™¯£ªŠ`°±Šµ¡þªÒHáÆ‚=0߸có–ÍMMÍÀ{õÕQ7½$ƒ ×,F\R:ÌYÜqiï ½z t 'w@$IŽ‘0gyüñÇwíÚ´â˜ó¦Xa˜Q)+-.=Ù Â8Qü”Â$n?§ªº´T|²R©Å˜Üþb¡0ƒaªJ±¸TV­„y •“$$cÝV§m(Ý»C ÙAFBŠP˜å\‰±á$.¯A‹°ÅÂÅ|Ž=ݨ<).ÆÓŒT‡U•åÅ{UDVjTÊŠ$˜Ç˜ºÔF›´QjT3ÆŠ´ÍGh¬¶¹«Ë„…`†úªf#:çprBx߬Øs jåÊyá‘XW½\i¦D͘a™ÅΊ>w ª¶ªcnH†µUÕj0Fì¬XR£™1#¢BÒPUß5mºƒ’ßQ[¯Á˜‰${˜ºÛš›àq³9ƒ £r¸S¦L wœKá=én»ZUÕÜÖm  ›âl®„䊪f5$…O‰uL6t4Õ·´¨TZ!žÉö<† õry-Q;"vÆTçÙ†š% w0ºæ }wämÍ#í£ ªÁŒ•;^ÈÞ]qbb¢˜XÐ'…hJwl+Å¿R9¨I[Ž~@®e&._› {&ƒæå¦–îéA„âmjoû%+„Q*¥ È%,Ì®“‡„‡Q/Tu´h°ÈSüÁ8áöáÁ¡À¸nëÆB»;Ú´%"ÂnР……s°UK›iº’a͈¾ ­:zè´ Ã(T•b6êj«äçÎE=”¶tªuÝÌÔvê@É…v J“¢kn¨•Ëc—,_h}òZ¤J*T¸‡I1BrÅî¼´´é$Ãê¥%GÀ,NˆÇŒ:]•üÂ9‡t2‡O“úò¡Çšu&‹Eˆ“WÕ.X.œê@À±@¢ {ø¶äb©Ä3s9µ'ÿ³Ï€; ª±±ñ¯/¿ìøý€ßW¦§¯\™îÒ%¯"ZZZt:ûBƒçöÁ;¥M˜0Á+Ñ.™ÈìNõ€øÿٱåаEë,Žö ºPd¿j ØP½§•87WÜÀYüvžh1®sd’ûnBÂöÕ8hOn{k7ÆKŸ¿½:†ìF£‘T ^ÚõÓK–ê•â·^È-ß]\-|>ÆMÇ9+ò ×óñ¢Æêݯ¿°C¼m÷ê]dî†Ý¹›ËUQ+þE†±Aünvnñ»›ù»Þ[ l²·¸Ú˜ðR!Ù ‡Q“Þþá_‹­4ü€‘¦A‹8#B~¤ùØÃæùs¦0ZŽýÔ›6ƒ̆®np ƾÑi!>µfŒÂrH ‚è4`n Ôj 5Èq%–Á`P0³/mmUõZ`)${Õ0â}b~8Ñ‚ŽË¿:Öpìð©•sñ,¦–c‡/´ÃlkÕ’i8Åu]ÿx¤êè¹Øˆù¸åÄPìp…ŠÂ·œ$ŒîiIIʼnçÃVãdIJÔ’'—FããÖ¤®9vèHÕ‰C'BVåû‡Žs‡Ž5›Cç,O›‰“dwýo?ª:!­^j#‹nœ=¨±‘ýjýe |íƒí^<X†›Øú‹ë„›tOѰª§d—4Ø®Õ%n GA|õÕW¤Mıä² ( Þõ­A¼÷¤–™òüz‚; pRÖ¿C¶²¸˜XuU•ï-UQù™" w@u ï îâ|&¦¬np¿Àå,³jÌêÕIT¬¡²Ã ²J#G¸þ%BÙÀ'!BÑóIT-Ô¯/á?L«TYEØ¢ ®Z•¾&ž0ʧ.IKd™5µÇüû›ïÔjY‰ ² ³—I¡ØgIp€™M úQÂìÎé8ìˆÝœÌxùé¼»S-€H¶£ÐB°G´E÷°6Òö—2mÑüX†YUUÑ„ÿ> õ—jµXèŒùw@š:w Ó5ÕPu×^ªÇ@Z” ,0ÈmMmŸ¤ìD* žU†ºëá˜a@Qƒ†ìíL£QCBlê®gÁwSª±ºÈFEáK¶·P^î€N-\¸ºÎéee¿¹Þ[Ì®¯¿î˜={ö0r'×\À¯ô¦ i ™9s¦‡FâO1ÜüIfIHņR°=X‹«‹Å••ŸBÌ%bR`Þ¢*/.'­v¹*-ü†¹øRˆÛ`«Äm2IÔUY,¶´²âòŒ“”’àT–“ ½fRL˜ð&ªWÁÜÉ9ÀÂm·E% ’ê…ÖACЃ(¨T|D°##Y@ŸWm†üi‹iáL g`ºú*›£áÁAåFÃ)þTUYÝ@È© #r É‚¸§&ákå8Õè×T¸„!ª„åŸpbm™ÎÄÌÍ—\|GÈrœèHhPÓ%CFW,.cœhgWY{5†úË-àÅÁVU„ Mm—W¹QlÕµàîÆˆ {`”ØØØ““É3\ˆs\,ÿséª ,+NOœ~êÔ©ŽUEÅ¥¤¹I Aæ üå—~¶›ÙÂÃÚ8™–×j†;@8•:îž{î9.•ž9s :111¶ö_€5¾Üù%é– Ê×øñ¾)\µuuÓ¦Më'Óv ÖPðƒÕðIŸ>}:,!A¹pëèßî!ÇŽƒµ˜U«Vyð[77—í=Y×®aÝw/—Ɉº—);x°ôà“EÑ4\Ü·5wO¥9æÙ·ÁŽ O& 7†U)>X^ZV©‘¨i¯»r¦ü åþ˜cåÏeg*Œ°naÔ´C|yéÉæÐ”'–ÆíPÚË÷–]i×€“FT‹ª­,Þ{Ò˜ðØê$닌ª3ûŠ/b÷¯X9~¿Œ{£tÇÅeK+µð U5œÙ³ùƒâ:*ÿ/o?›üPY¼yßøÅk¡ö‹÷î;ÞLá¯ÎL `LóEð¹X§å0Êê“â‹Æ¸¸‰G¿/:r¶Æ9=rwôDc÷ø¸…)÷áOMÿ N@KU]C=§÷u5œøí|›yB¢à¡(âaÀaë«`—üŽ€ š±ãÊÑòËjnRê¬I¸!ÒÔxêX•v"?ezè8û­Õ·Ê/_ëêlë0ÂÆá†®æJiùù=êÁ…‰<[ ‡ÑQS×\WÛ¢ƒ FöweCUmWp—é4‘Övµ¦ àGoÜ’¶Ò!Ú;úô1qâD ó.œ>sÚïööôkÀúñoGŽlûd[uu h(à–q¯@{¦Y]] >é—.]§¦Î/ׂV²ÿ~àÈ Ü™=4€a”/?YªŒZ)Œc`¬¸”$Žò$AÅ@#啪Дgß~®ËŒIMá¶Wž<^VZzr”¿bL.½?£½R^vð øg<<Ù ž q©«SïÅ'÷2®”—/« ®¸Ÿuú€Çýì”û©uÇÅ¡ K_ÑE-~é½wVDá&HÕů·mÛóóA¢–²3í¬¤µo¯_·•ÀUBgÊK!”¼Ò•º"‘ª¸T¥2QCb¦GÁcŸ6y¢ñz½¢¦Z~¹â²¼¦ù÷¾ ± ž79ã8‘“¨55ÕUUµ*cð””GâYaþf¾®¨®©®©©mÖ`âK’->fЀÀˆÈ ºkµP¸¦FÑ ÃtÆÃKfZ<0êN«ÑÂJ¢#{`}©tƒ² Õ6¶wabzda¬u„ŽãDóBL×uŠÚ¨µ¡ùú zÑãBxÑÌî¤ài­Ý”I ‚%EZ&}h錾ë5WA|Ã5M_pÔœ‡%r,Óñ†¶±¹¹¹Ñ:ã>Ž@PÔ”0óï-ÍŠº:¢6E#t#rê½0™ë¸xü|븘¤džgõÉà ’ü–=úèúì—ᡚ#™³4gUIÜž2×?)Û?Ù6 ôªGsó5Xg^î°óðm¹~ýº+d >ÀÂSXØ`6 8ôkéÊ•+]e:Æ€ï³(äKú¤Š¶RÒÞzxösq'Ó«LÜæ` Z°phqß1.a´t PB©Q°ã9ºƒj¡~-nødâvç’F-$áneÞ(ÿ4h„S²r°È¸&áF^¢ ö$ƒºÃÈ rôS0uu€Ÿ§™Be²œ,Õº:4ðF»ê!^¸c°8_V発Õ* 8^qDjÄßJ”¡ Ö<6Õɬ >¿ïûÓ:ðû˜ÇéÒhÐ ¶SCí’ ]jh1JÌ-“8{9·ÈpõçoŽ´ã~\]‡{ñ†î.³³Ç ôAÆ[¨D#¨¦Cº·¨Â¿|íü!9Û[hËÛŸ€s¾è¨É›rs}õ\]P †<ü]©-°_AØÚãã­y°sêôi‰$L›þìƒ#ïdoµÀ |ÒÉ—ôË‚½ «Þoùa±Œ:ˆ€Ñ×JæÔ8(^©ý>žÝƒD×ý%8^ãéJ¸rKQ—68-Ü’™‚BÂ<=CižÓ!Ùi±Ô¹àë輸bóÔØ$ å@¦GG,Z§5ðÒž:„yO ì_úâÔVpw³»Ú{ºi7I}€l0ŒæâËÈq‡ 'Xñ¼sD‡ Æð´…@ú†Á­·ë,C¨õ nÜ:h?oŸª¡Ì¤³¬åE¡Ô1TúJݨì€ÀÔ Â€I(ò¶@ $þ¡3ÃðÔdFÏ]À0˜ #-|–`AwÐ÷1Q£XÀeG:ºÚXDƒÃ •º{ »oêÀƒv¤í帑À'(ü¾þó’ÁT3|ÍüšÍ`ÚÊ wˆ>î ›‰º‚]}Œ.Þ¨6„À„¢¿™à¨âÍ&é#ÞTB`¸@ô1܈ºÈƒÓÀéÑ%E Æ<håeÄoaÌ}÷]¸x©´ôWð‹GÞ¯U€-}Œ8Òàê6wÎl8Yª½ýº¶œA!p§ €èc”î$¼q5J•¡j£‚Ò¥GfT BàNDÑÇxWQŸ£‚¢QU‚¸@ôq'ÞUÔ'„À¨ qÏ=MÍ×` |» Ø)ÈŸØ/ˆø„€¬;Yö+tØÁñ´|s¸$[nKu¼$ó»ëYÄ5Õ]¼kNƒ@ŒUUUÏ<ó ¹ÛÉ @d ¹ƒdÀä%ùÅ‘2\éÑ8l#ßÃ[oÝÑ„»øQƒ U„@¸"€&/®˜ „BÀ+}xÊ„@¸"€èÃ@x…¢¯`B™W}¸b‚b¯@ôáL(B!àŠ¢WLP B!àˆ>¼‚ eB \@ôአŠA ¼BчW0¡L„€+ˆ>\1A1„€W úð &” !€pEч+&(!€ð D^Á„2!® úpÅÅ ^!€èÃ+˜P&„BÀ/j0×6JŦÃÕ±¿˜¿2ó1ú‚¥ŒˆCéÏÆCúˆ}ðD ó§ÑýYÑþ óhKWÐc&‘®U›Ž¥/Ï*ûliý¶ ^üyÕµ¤sLoEa×öüž6<–ò§‚ k¸=%yÚ¯ÅfD°i9EA‰ýD¨ûóo|GfÀóP7Ï¢[2idºï tY¯îŸ(d<›Í˜|³ t„À݉€_jjêûï¿ïÕnc=Æ ÓµâN ÿøì ?äЬûŒ7ÎÒ|U…ÓÇ”WÙÅoÓ^íùéÃ|×k`ù/ÿ/Öÿ•Æ"ö4$!vú"Ógë.ã þÿy44çA?Lß§Qš¤EÚ-ï;3ÙǶàÃúf»™ÿ-lµ2RD—'¾Bt/r5Å ‹¦ÿ[Á~p€{Û[‘«Z³ÁÔI$E® Þ_fÏfúZp½8câwY^Òë („À€/“šÐ‚¢ úßöåG§SBg2žùšý¯å~˜¦÷Ç¿ýþÄ›½; ¹”`šcšš š–ÍÙ³“ê®Tÿx–½DÝ/¸_ŒK‘Ätj¤µê¦=/æIIL—ȧô/„®|¡·ÀùaN@æ XþO:Ÿˆ¯ü óƒ½nK»&(Í 7°„Lr7ñq-3˜˜È Æ“3-¥"Í;bû ’4Ìo€> ¦T!pÇ"0’Êyý‘8ì`gÞû±þåäñVÝÅ3š}g ntŠ‚tÚ“Y}˜B÷ya‘˜ø'JÐ+ÉÓUz›ú§,ºÃŒÃ³ØRÙôRÚ²V 3˜¿ÉÔ< °†7 gõ6JtÅ…†³ŠÞN̲€ñB6#†0—hdÝ߉M¸&AB¦@¯Û’§¯PûAžWD`71Ÿ-Ð~QØÓF§¤e='p¼¯Qrã‹|c£cÅÓŸ&ÞDc¨ (!pëIúÀÆ%À³§¬ó7ƒL?>ÕjžôÔ_µþ÷˜—‹ ‹‹í‡±éL»lC/ŒÏ„7èO ¨‚tý'ñ7¤p×÷àéf9ñÌO zO7Ô€ÀVãßÓ;c¤,›ÕÚNÓþÌß_•P>’°Ÿesê/6KĦý’ Ð’øôÄüëk>ÃÕ«‰é=6™Þ{®Ä,-1V({ÿ¤ÔIèƒÜ$­7IA»‘O|ŽGJí=›«zjCoÚ¾/=’Õ‹{¾–°D âéÇÒn/†eòâ¶KA,«|µ¹Qí6‘Ð×X¦ÛŸ¯yi‘æ;…cN¿Èxg/ Ìò\›C*KÈþ$7 ˜ˆ1œ×½˜©#qìA/Ѿó•Ù 4S`t6--WO:ËôßÉÈ5#ÿÈxËL§“MÿDÂÙ\Ä\A(Z•ùÝMY¾)˜ðIÎ8|*d0I$–ù‘^Òù"n£iO¦S`yh…ÀªÎÉ7:UŒ.·7#K4‡qn$õ{÷pt*L•çÍŠþd¥C½IDAT9\¹b¸­~‰Ù6¯µ@Ñ´Gób^ccé`ˆ…* }íjœ/htrvÓÛi'DË|ç>! ŸLÑý-†[v€@€s_0¨QDhW’ôa.Ë×7(%’ˆfãµ×é«É|è!0ÑÉ Ö &PKðŸ8É3~‰OmxÐÃÚ§–õxÎ:©”´–Hñ{î \¶T¤ÞÈ÷Ÿ•n­'™%–3*Õþ \Ó¿³o|Sd1ߌíÍ´3 …qLÇexªA®Ûa„T¢/ìÄê¾v ƒ  1ÀˆÒGo“Â2ÒhóÆ=àªE ˆoü ÂNïGæ€2I§½X|6¹óÿáfÔ^é ÌÑÙŒÎóïÌíæ™gå±6`ê5[}YH 5}’G¸Ôõ…¬þ^mäGQÛ‘œ¼è'Î[ú¼ðù@¯}7ýÖÒH•~´ãn.d$¸NôÆÏOm0‹Ø›3+ÆPƒuv#7U(‡* •GÜ*F>Úÿ­;H!Ca¾½Æ,V2ÝIgûYìš$Hê>û”h(° äÊÆž?®Ÿ»Z[aWnX=üùÃ¥¬kÙ^½• ¬æL«4µ-O\õJs¥÷Os~“3Ù›×QTŒ¾v9ÙȾï²T/f¨rÄ–>6JõÇeøwÖ/ÖJ¡…DÃ4ÖæY3ø DŒHŸºMê§²´%bÝ×Yš6Õk­»Ñ5B`ô ðx¼‡~Ø @¾Ò)~~½ ‰þ—<Ýž“˜/ÎO}ÕÔMÇ&`õ¶ëvýÜÛEÄwU:`ØŸß½³¸/2ž•ÇzçeF8ñÌvyçÅ$ÝÕý¿éºja¢ú+fj,FŒë¿.˸·ÃPr¢¯³¾§p¯)Fįshz5°´‘ì_SØõÁ6“ç’>Õõ¾¸dÿÊí¿övC„¡W¥§ÌMÇrÔðWæ´¹oé/ž€öz1&%‘çH£þ¼%ãË K‘x§Âb°Š"c?1 E¼1ÃïøÞ¼ºÊŠçèò¾§¿XͽÞ™Bùý›Ÿí1ãoÓzÛL”Y‘=ÿWÔ}®ÏЭî0‡:3Ò›H}xNï…C¦¦XÓcñO¦ñÏÿwƸáRoðÊP@Œ0¾¼2GP °ùi}_Îé/´–¼&¿Ø>=±õÑÝ«qàtªQôÔ(±H>µ?¹Œ@êžFlÜd»W¯F±ˆåU<èÍmz¿0ÛåàÛ`n”™Úõ~£×¯Á7•DôG`¬<íüà=ºY¼þ­Ákö8çy„ó[ytÊ0±e2Ÿ‚&,#x‘è‘DÀ+ú¨–›Ö.þ݃g/üú<ù íµWïþÇ#‰ÿÉ/ö¶ƒêÅëvß7ž×ìùÒÕzItí±»xלc;棬5™Œ±ÝÔú»ŸL§w *¨£„€x¥}ÄÄœm #­«C·}­Ú´ƒõ¯ÿéÿ~˜{ÛÇÝ¢}xqËP„Àí‚Ò>n—;Ús ús· 5!p» €èãv¹¨1‡¢1wËPƒ· ˆ>n—;Ús ús· 5!p» €èãv¹¨1‡¢1wËPƒ· ·+}èåâB±ü&»+ß. ¢v îN¼ò:u„æRþSoþBío¼ÐçnøöÝ…ƒGC//ùj‘ØBìÒÓÓ„Âxº"ÿ™•ëeü÷ÏÅß7õ!pû#à#}´íÛ´õ×Óø1·x E=±a®—[˜ =>m]|ÚLeÂü¯Lúóq¶ÇKÿÇ?êeñÞr‡Z¡Àx¼þ>ð׉báAÀ7ú¸ôYë£Ë-˜à°ÉÇ0´ƒËcÃÎ[\î$;ñ„¢-B¯EËòEba¡ˆïu”!€:¾Ø>4E[ÆþÇ|DÓØƒTdäyÙyòAÔ‡Š CBÀú¸ôÕÇÎ|õø´i W¯ßYÞï$¶!5b€Âz…¤ ·€8 ÅÔrqA®(;[”W QX¶ QËòÓ…¢²VµL*† ³í[>Pf\’ZV”Ÿ'V``›ÍåH‘uvôQBÀ+¼§ÆÆ¶˜‡ÍŽ¡k.•¬_>w‰è±¿ç°µ¬à•eÓ¦-þÏwöËmû éeyé¹ ®0##Y_$ZÄOΖÀÈWÊäôd>ÚÖ¥R©& ¸É¬çf&óXù—|±$?3#S´iÃ3YùÎ5ì½Aw0©©©G=qâÄ™3gΟ?éҥ˗/_½zµ¦¦¦®®®¾¾¾©©éÚµk---­­­íííׯW–mËšƒŸÅ<'§¼³³³««K«ÕÞ¸q£›:NOŒÖÐc &k0[CÍæy`û˜ù~E¯5\ÛžJÃhïî&#t‡×Å¥~Z×g -ŸB*÷òqâúòÆ™4læÆsÖT™¿§ZÇ­ÝY×§»üË·[¬ÅÐ_„BÀG|34vÿïÿ”4õÿüaý¯›7ÿôlÁòXð`sqÂÔEy…z6ORP`‰‘c`&QH$r,ÙemÆsf.¬´zAV&D 3]J;ÔŠ¾"ž}ài±ÏlÏùyî §Ž\Å–'y®b0©ÎFT™T¦§ ãù|žE?O’ 'Qs­×ŽUx‘™>X#í`º‚Ê îXI€Ç¤‡›A»èöl'Ÿópö£^­†3aÙ|¾7+³>eö¹•¨B!`CÀ{Ó© ht:vÿÜ©.ñƒ`¹Õ Øl6Ö*)’:oÌ®ÉZ6ñ)ó`ÛŠÊ!Ä‘ ƒ ­‡µ®Z¿Ê~ô‚ÏrÔr©Li£v|ϾP0 »’Ÿ%Â[,AY˜[¨´Ïqô¶/2»©E#>!à-}t~;cåó[4’ÇʶýúÎ+ßߟ÷NÊ =½ úýyhþì‡^‘è1X¦ÁâÖ>lo¹š8´Ç](ÍcÎoò…Y¹ùù¹YBaA|&éᎫ°‚+•+å…b¥çÌ„HµÒv&®O`¡Ì„€^.Ü*¾)‘8ç>xrÌôé³Íþæ¤R­Ñh†°p{ióBp`¥Í\ó×5¬Ù^q–q‰eÚîÚÒíëæá¾­¬y붨„Å[XNj9¼1-Új™”úò/ö×–o×â)´èÇ?½¬#VžÎÜrüÛÇá2X3ŸÞ²ïÜï>®R¡ì„€¾œqÛÕ\YÓîÇžÇcxÂ-œ9çÛ·¹äD==39žK¨17;çE­Ézv<ß’ßN„`0ÅØÎÆ÷™íÅÐ7„B`ðøB·òˆltLÔàï1*‰!¼µ}ŒPõH,B!0v@ô1vïj9Bà#€èãßT=B`ì"€ècìÞ;Ôr„À-FÑÇ-¾¨z„ÀØEÑÇØ½w¨å[Œ¢[|Põ±‹¢±{ïPË·D·ø êcDc÷Þ¡–#n1ˆ>nñ @Õ#Æ.C¦ç-|nc àè‡"±}ëv Nr(’ØÎu¸Žš†¸]Üf…m§wçùk+=jÆŒ…KŸ\7˜CnÕò’âÿ'.*²nF‡fý ØÁ”;/õñôtaüpmÁ¬—ååääî9¯OÛ©„tÕÒ‚œœœü’zöºÃéÇ=™o×»„Ú…¸-ð>¬ÿó«§çæ~¹='ŽA¼·?¸Ž±ãÓžž–Ê•Myt—zÞûgK߈§Ã û¥´hÓ+¯<ú÷ i›÷íËNüvDöVÑãÓEyjIÑ32Žœ™—+—lh¯ÃÁu•BÜø6y1T¬I{îôœ/~ÚüÇ©Ã2²''2Ðèt‹4:79cËáo×ŵ–¬fìD6LË%w±Šcó¸öf¦J‘„À€/ô¡9öæŸß,ËúdcʰsKh†B<-ŒÆ°+âý²aCŸŽ cÃÁ}ÃÖ6$!06ð~òb8óÁk_U³VîzùþnÃÞa¸ÞaÓI`{R¹¸P¬dg°%ùb,9;‹0ZÀ‰µ…bØåTOç% ÓÓ<'Zkia‘X¦ÀxÉéÞ˜8”²¢"±D®aép&Ñ+¤¹uŽÑ £”YÓåò…Ä ™( îJ¼Ö>:ùxO56ùÑŒØK»óþ¾þÙg_ÉÙ]>2‡Üêåù›ŠZ1VêÓOóñÁ*ÉÏÄO[ö—±´03=C´aÓ_2r$&ÏòÓ 0avŽ(ƒ'É^Äæ+ì7QQ˜™,ÌSó³D¢ ®$;«ÐscÕ‘0³@/ÌLç« ²ä òdz ì¹ê"Qú²e˲‹°xžEUâòØ’œ ‘ã¤;Ý•¿#Ôé»oµÃ1ñ±NØc¸êÀ†?,øãcì99/ìÜýó§¾}NZ0ÔmÝ„ñ`«R½ºþ|,¨ªyk7úiv<.—+ÈÊ¿× ‹E&)TI9ÒB±>9“dç”(ù;3ø0ˆ¹YÙé¹û ÅŠ¬,^H–›‘%ˆe¤ñ5#?_&y`“Âm3•…YYòLiQÎüü¥ô ¢¬|¡4;>#Ê>¸ §›jC׫õé¹¹Bg{Š[é(!pg"à-}TŸ¹Ô‰Qg¯ÛöQVìyê7·jKYÿóÿøß¥…OU§óøü™°[ºAÓJo=/¥c ¥¼l¿” XŽÁq=9+“6VAf'ÌX‹% , žÃ@Îy`NS˜“+ãfåÛnø‚d–{úPæaÜä"ë!ºz#K•Ù°)sr¶(-ÿ8W&GIôT-Η ²óýäΨW^#à-}À‘ 3,,Ì&9ê‰ÿ\’óëGÒ?¹|0Žö6Ò&ñSÓbgÅ46¶9 ÁbÁú-kRذ­âÚeÓyq< *ƒºUíz½$[^ÈË-ÊKïïŸ O(+‡%ï8ºÒ19¸´;g—ËÀxj ¼LQF´Fœ›'•äq3I•È;é(BàNEÀ[ú^¼vQ¨ñâ¯Gø£µ­›¼dõ‚¡hñn´¥´LÇaF'§º}ÊK ®`ñÉ|§ÚI²áòùP¬¾¨ÀÕílÀúè¸eÄp"7 _l±y~žXog_fÍÃÎçgdIøYéÖIÎú³@ýBxƒ€·ô ÿë-!ëh~þiò”[Lj÷^Í’wÞY2öÀÔ õB¯oø %ZôÚ-Éclá´[è Ì12ÓÁãÌ ÍÏ•ªõjyQnN¡Â€)2iQ¡TÍÏ=> «/ÈÌ, ^’Ó+Š á݃ÎäV.%ÄY·Ö3t1nFNæLš¦ì?=;¯ÑÍdˆ™ÉŽÆg‰Ò'iê1a–`H]öæ¶ <1€—gܶ´´´¶6þ =&桬¶}òþ3Ïÿíå­ƒ?ãöú¥_n~ž8ËŒ*“¢gÎ[øHZj꼸è踙©kßøò˜Š8ò¶Wuö‡-kgâVZÜÚß·n[·“8Ú–8±v繺}kÁZŠ{úÛ:âÎßoyœHfÁ¡š3ˆ ™—ºn˾˿_þeËÓ„@8+÷Ó_êÈsqëö½<$¶ñøGà^—º…”;E |?¤ÒØ^SÝd`OÍ&ߘs<î†yI~±}zþBŽYÇ<¶ò‹Û÷òÔ`$åò,îøâ ÏÉ“K­T:7žÏc«å25ïì•Ú¯PG”r™\‰qùÉýÍ)DVy®@Ä-,"Wo] £„À݆€· ·v\ha±÷sÿ¢íðÌæñìá5¸~ÒÙ¼ädK8Wûæ `šd®;k‹Œ¦¼,Iÿ:†·CHB` !à;}Œ¡Î GSõ²‚œe¼€‰sdÈWl8@E2î}ÜäFJós6}VmCn¾„ô¢¿I ”Œ¸[@ôq“;-È-ÚÇ—ªy‚ اì&yQ2BàîBÑÇÍî7›ŸžÅ¿Y&”ޏðÚïãnõ!€ð„¢Oè 4„BÀˆ><€ƒ’O úð„JC < €èÃ8( !€ð„¢Oè 4„BÀˆ><€ƒ’O úð„JC < €èÃ8( !€ð„¢Oè 4„BÀˆ><€ƒ’O úð„JC < €èÃ8( !€ð„¢Oè 4„BÀˆ><€ƒ’O úð„JC < €èÃ8( !€ð„¢Oè 4„BÀˆ><€ƒ’O úð„JC < ðÿìgÁ±#êòIEND®B`‚django-tables2-2.7.5/docs/conf.py000066400000000000000000000035511473544236200165660ustar00rootroot00000000000000import os import re import sys from pathlib import Path import sphinx_rtd_theme os.environ["DJANGO_SETTINGS_MODULE"] = "example.settings" # import project sys.path.insert(0, str(Path("../").resolve())) project = "django-tables2" with open("../django_tables2/__init__.py", "rb") as f: release = str(re.search('__version__ = "(.+?)"', f.read().decode()).group(1)) version = release.rpartition(".")[0] default_role = "py:obj" # symlink CHANGELOG.md from repo root to the pages dir. basedir = Path(__file__).parent.parent filename = "CHANGELOG.md" target = basedir / "docs" / "pages" / filename if not target.is_symlink(): target.symlink_to(basedir / filename) extensions = [ "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", "sphinx.ext.viewcode", "sphinx.ext.doctest", "sphinxcontrib.jquery", "sphinxcontrib.spelling", "myst_parser", ] intersphinx_mapping = { "python": ("http://docs.python.org/dev/", None), "django": ( "http://docs.djangoproject.com/en/stable/", "http://docs.djangoproject.com/en/stable/_objects/", ), } master_doc = "index" html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] html_static_path = ["_static"] # -- Options for Spelling output ------------------------------------------ # String specifying the language, as understood by PyEnchant and enchant. # Defaults to en_US for US English. spelling_lang = "en_US" # String specifying a file containing a list of words known to be spelled # correctly but that do not appear in the language dictionary selected by # spelling_lang. The file should contain one word per line. spelling_word_list_filename = "spelling_wordlist.txt" # Boolean controlling whether suggestions for misspelled words are printed. # Defaults to False. spelling_show_suggestions = True myst_heading_anchors = 3 django-tables2-2.7.5/docs/img/000077500000000000000000000000001473544236200160375ustar00rootroot00000000000000django-tables2-2.7.5/docs/img/bootstrap.png000066400000000000000000002132571473544236200205740ustar00rootroot00000000000000‰PNG  IHDRg=§ß€gAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿÿÿ ½§“ oFFs(U¹ô‘tIMEâ ;,-8Í· vpAg4lqê€IDATxÚìÝw\VeÿÀñÏ=ØCDTĽ˜¢¸¦%–™ƒÔ_6ÌQ¦¹ÒräSjšZ–æx\™–³,Sý7( ²D™²÷ùýÜ‚€ ¢ÐÓ÷ýzñÒûŒë\ç:ç>÷÷\ã•¢( B!„¢BP—w„B!ÄCœ !„BT œ !„BT œ !„BT œ !„BT œ !„BT œ !„BT œ !„BT œ !„BT Ú'YØõó}df?|¡€™¡×zÖLêÛÊ&úe’!ÿˆDR2²i^âÄeøFÒ²fe,MËfÛ%ñ »Ï‚Ý׈H¤Ž­S^mD#ûJ/d_½|"XôçuþšÒ¥Èù_ìð!;GaÖ€¦Ï¼Ÿ×îÜgا81»'Ú¿»ºº²k×.lll ͻΛÓþѺφzjT«Ä'}Ò šy™l?>%ïÀº7±-qÙ'9ve!&)ƒ¯~óål@4•Môy·[ú¶´!Û~R‡¯Eòåo¾ìŸÖõ™Ózô˜ þ©Á€—ª?÷ý¿á<µmL׫þsß–Bä÷Ä¿¼#::ñýÿµfÙ&ôiȵ°xfo¿Zfš¶ù¡ÑÉ%.“”Á”Ÿ/’–™ýB *>%“Ö{ÓÚ© ›?t£µS>\çMRzÖsßW‘«]=«ÜsïÿZ3ëõfê©ùp½w™Ëÿ¾Éq¿¨R-û¢Ýô_.‘–‘ÅúÑíÓ³swúp)$î…mÿIÔ´6eH{Ç2IëÑcÒ¿uu–Q0.„ÕÕœ8Ù˜Ò¦NÝg}­šé¿\&%#c}Í3g¨´/ú|ѯ=|-#}-czÖC¥‚Ñ=ë±íl(§ý£KUÓò,û*rY›8÷Z8VÆ}Þ!¼cq«oýÌé?É)õ"]Xl Þ±ìšØ K#jY›°÷ò]ö]¹K‹š•_`NJ§–µ µ¬Ê$­GÉÀ¶5Ê{÷„â¹{âàìQ&ZT*P=øœ–™Ã /ö] '15“Öµ«0啯ØZq?oþ¼Î¹[1¨U*z5·ã£Þ 0ЪyoÕB¢“™½ý*—‚ã˜æÑ˜…»¯áåAZf6--™új#,qŸw€WaÎÍШUür*„j•8îÅG½ëÓ±¡ ‹÷\çäÍhR2²p´2aBŸ†¼T;÷~àÒã¸Õ·æì­b“Ò©aeÂŒþMq°4*´Ÿ½[TÃÅÉÕƒU99 99O÷3ýè¾~öZ|ÃîóݾøÜ¾¢(4¯Y™Ï^k‚ÅÃü¬ðògËÉôõԼݹ6ƒ\k™þ.ï0Ö¾ELR:õìÌ™ðrCšT/º 6)=‹¯vùrÔï–&ú |$ÍÇ3€‹Áq,ýËÿˆDì,Œݳ]Û•£{üÊ‚‘~î¹§Î;(ö{ã± îÆ§âTÕ”_n€s-Ëçÿx €çnp)$7Úvö6똆£µ)º×ǵ®U¡c÷Aº¸Ï;ÄøÞõYsèÙ0£Vxù³÷R8Q‰éX™0´½£®Fiþîk$¤d’šIpT2Fú&¼Ü€¶u­ ígµÊÆlß¡Ày™£<ý¹%Ÿû%‹÷’ør—/×îܧ–µ =šÚ±û¶ܡ@³¦—OáâdÉoÞa¨T*º6¶aÒ+ЪUdå(Å–SQÇ$³fIךKÓ»E5ùFx/GkS&öiHKÇ¢ÚC×"ùnï "ï§áÞ¼YÙ9…¾S;·vž»Íú£AD'¦Ñ Z%¦ökLSî%¤óÅŽ«\‰CO£¦Kc>éÛC½g¿™BüozâfÍŒ¬R2²INÏævL [N…У©FjÍæî¼ÊÑë‘|ñFsÖŽr%+[aüÆódç(¤gåðþª3ddå°êÝ6ÌÜ‚7¢X¼Ç€ïß~‰UŒ™îјiÙ~&”‹!q,û¿ÖlÛJÅÜ>ìžÔ €mwÀ½y5|nÇ£¯U³`hKÚÔ±bæÖËÄ&e°òÝ6lùÐ '3Ýúyvy‡1k@3öLéB >ZïMV?zZµ.¨¸ŸšÉ¢=×16ÐÒ®ÞÓÕØ<º¯)ÙŒ[çMƒjælùÐßiClR:+èÖ‰JHçêíxV¿ß–‰}²lß ¼®FJûÄÍ(ï¹ÎØ^õÙ<ÎÎløà¿g‰¼ŸVd^æîð!(*‰•ï´azÿ&l:¤›WÒ1 KeìÚs8;Yòó¸ö nïȧ¿\"ð^ÒcßÓÈÎQHÉÈ&%#›èÄtÖ¾…m%#œr wž»Í¢?¯3²Km~מ6ur›ž#âÓJœ?²Km<\èÝ¢[Çw$ "‘%{üøÔ£1Û?îH‡U™òóER3² »<^>,ôlÅ€65Øx,ˆ¿.…óÅÀæì˜Ð‘7]k²ø/?îÄ¥ê–ßwå.}ZÚ³{R'>ê]Ÿ ?] ,6¥Ð~«U¹µQyÇcÏ¥pÎDÓçûœwî—t.fdåðázol- Ù4¶=ƒÚ9²ê`@±Ûñ »OBj»'ufñ0gþºÎaßH€Ç–ӣǤÐyû˜kMžMǃ˜üj#öÚzvf|¹«èó/ "‘)?_äµ—j°i\{Œô5œˆÑÍ/éÜÚå. ÿ¸Î;]j³yœŽÖ&ŒPž ÿ¸†™‘?kÏÒá.œ ˆaã± „¢8O\s6w§OXc} «ßk @lR{/ßeùÛ­qyðƒ9÷Íæô™ˆSþѤed›œÁ¦Í15ÌÝô”W3~ƒ7c{ÖÃÔP‹J¥B«Q£U«¸—Š‘¾{KcÌ µÌèß„ˆû¹?nzšÜ¸R_£Fý â$Gñ½`n¤€k=k:4¨J*¹AÕvŽŒXqŠ´Ì õr×ЦulLx·[~9ÂùÀØÍgùE%óÆ’cü_''L ¡¢äpºBÖ¨0}d9=ªÀ¾ÞÏÈÆÓÍ‘aœÐjT8Xáþà®_—ŽZÅìͰ23 Ž)WoÇóëéº7-جºáhCÚעǃéÃ:Ôâl@4;ÏÝfT÷º–½ŸšÉßV¾Ó††ö¹}yF÷¬Ç¬m¹ý]¿÷ØcöÇÅ;Ô°2Ñuš®ieBBj&©Ù=~Oã‹wøãâÓ>­©®ïçÁ mïÈË-rƒõq½ês>(–-§Bß»~‰óÕ*• =Mî¹§Qƒ]e#ªU6âÝ®uhUËZUèØåÞÑIW“•Æg¯5ÕÕÒ ëP‹½ü ŽJ¾rníSó¸7· }=kZÔ¬Ìïçï0ºGÝbË ï‚ÃÄ%gàZ× §ª¦E.“˜–U @±0Ö+r¹âÎý:¶f=߈"9-‹ý›`¨§¡–µ ×ïÜçL¾`æQã{7ÀH_C‡J8;Yrõv<Ý›ÚâhmòØrÊLò+éZ“×ÌÝ·•=MrkŒ¶­Éïç‰AÁZ«ß·Ѫ–%žnŽLèÓã7îéæ—tîl?{›þ/U§o«Ü€ù“¾ø~ÿ S3¹›B󚕱µ0ÂÁÒ˜ÅoµBï´Büóÿõ2o<ϪwÛZæ½UgðHroNÍéUdZ;÷w.úG$âTÕ´@³\³•‹ ÎÌô C†z24–¦œŠü`0Fqך¼àÌ6_—€¼üæn»`pt/‰úùއZ \JsîÞKâgû|ÛR3±OCÞíZ‡i[.±çR8®u­èÖÄö©û© !þž88«aeR ²[}k^Yx˜ÝîбAÕ"×QÈVô´jT¼Î “ŠºרbÌŽ 8~ãÇý¢XáåÏïçÃXÿA»"·c˜o@BŽãÖys;&…^ÍíèÛÒžmk2~Ãù"·Ÿ'-3JEqô4*ô4š×°``Ûšì¹x§Ppf¤¯áûÿk­ûlQŠÇŒD'¦óÖ'±³0­¾5VÅ/<Ã×Öœ=hÔª"ƒ…ìl…1=ëÓ©aÁãaô˜J¾’È_KQÒ1ÓÓ¨)®¸wü´šâ˸8UL œ{mëT!:1_4­õØtµH%ÍÏÏPOÍŠ‘/q!8–#×ï±çR8¿žeí¨¶Ô´2)2ùƒ•Õ‡n±þh }ZÚÓ®ž5ô¨ÇðN>²ñ‚ÛMÏÌF£~|¹˜h15Ð2ν>ï¬~âjhpåÆÝDB¢“Q©ÿ#x/‰³·bøv„ c{Ö£g3;R3r¹ÿwäúûºÿG'¦ŸJ];³Bé­9|«P`—”–‰yMFZµŠ6uªèþê‘Þ£ûzÀ'EÕïµåíεq«oMtBÁ>bwãS ,ƒ¬l…¾_æêíû,Ûw“Ä´L¼T%Ü™òj#þ¾r!„(Îgþ‰œ¼ÍɛѺɴ͉IÊ [[ÌôèçìÀü߯q)$ŽÀ{IÌÜzksC\ëYÓ¾¾56• ™µí AQÉ\ ŽcÁîkôj^M×/ÆP_ÃùÀX"“ˆNLgÁï׸GÄý4þº޹‘ö–FºZ C×"¹—^(Ÿf†¹#ùNûG“˜–Åù X–îÍíÄž™oÖ¶3¡ô$8*™9;®R×ÎŒf5 æjWךÓþÑü|"˜°ØTþ¸x‡­§CضèÑ’¥‘_+듚Éå8R3ùýü~;FF¾äÔŒlV GÉ  ¶Ÿ½Í[ ?²`¨[-vŸcûÙÛÜOeã± ~=B «Â£$Íôx¹¥=_ÿqkwîsõv<ßﻡ›_Ò1ëߺ:ÁQI¬>t‹ð¸Tvy‡qòf/=¨Õ*îø=È„4ݹwüFßï¿ÉŸz4Íí·åéV‹ŸO³ïÊ]ÂbSùáonÜM ¿KõRÍ7Ò×à‘À…à8Ô*X¶ï{.…y?£×ï“”®kÂÍìŠbn¬ÇÅà8âS2 ŠJfƯ— Oß°û¬9|‹°Ø¾ß“;±)¼Úªp³o#‡JhÔ*þ³Ó‡èdÎÞŠaÑŸ×x¥•Ã3ø+îÜ/é\ìР*f†z|õ›ÁQÉì½|—gCK XŸ¦œò“GÏÛÇ]kžTÿ—ªãϺ#„Ŧ²ÂËŸ[ùš˜K:wµsdǹÛx] <.•…\C«VÑ š9×îÜgÁï×¼—DhL G¯ßÓ5™ !DQž¸Ysã± ]_&­šºvf,òl¥ëL>±OCïñãã çÉÊQh[ÇŠ#_Ò5 ,æÌ‚Ý×ð\vc-}ZVãƒõtéhSƒE^'5#‹ÿ¼Ù‚ؤ ¦n¾HBj&õíÌY2ÌY÷ƒÔÏÙeûn Q« ½¡À¦’!Óú5fõÁ¾ßƒêULÓ£_þæ‹_xmô)ëÓÊžøs'6…—j[±ø-gŠj jhoÎ×C[ñÃß7ùáï›T«lÌ4ƺÂO#ÿ¾~9¨—‚c™°ñ<Š ì͙طßüyäôÜ¿ZÖ&$¥eòêׇÉQÆõªWd߸nmˆíÓõGYøÇ5jZ™0HËbßf0õÕÆ,üã¬9‹‰–aXøÇõÜD­zì1³23`ñ[Î,þË5‡¨ñ`[NUM©QÅä±ÇïIöÖ½%@«VaWÙˆq½ê3¸#û¸“Øä ¾Û{ƒ˜¤têW3燷_¢öƒNï%Íwo^½—ï2yÓ¼¦wcÒ+Xy €ˆû©ØV2bZ¿Æ4{ðF€üÇnRÍS3_oʼ߯ñòüCX™è:ŠßOÐõ7j[׊«¡ñ¬9t‹Ú6¦üðvk¬Í ¥e¬¯aÙÿµfÑ×xëû“˜éáÞ¢ïv­óÔçî÷hjûØsÑÄ@Ã7oµâ?;}ôÝqêØ˜Ñ·•ÃS=·¤rzô˜äWÒµæI8Z™°x˜ ‹÷\gåÁÚÕµ*Ð/¬¤s§sêŒéY%ùŸ’AÓ•Y2Ü=êÁ>úòöŠÓd+ mêX1£¿4i !ЧR^ôÓ\+K3еæ yŒ(Ùã^ßô¿fþîkÄ'gðÕ å²ýg9÷£Ó ¼—¤{^ ä>Ï'ì~‰_„B”LÆs !žHfvãÖyóÛù0îÆ§râfÛ΄ÊD!„(#Ïü†!Ä¿‹…³4cõ¡æýæ‹•™!Ã;:Ñï“"„â¡u³¦B!DE#ÍšB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆgB!„ˆ¶,[½z5+V¬ _¿~|öÙg0hÐ LMM9|øpyïo…¥( ·oߦFÏœV·nݸÿ>›6m¢~ýú¥ZgìØ±œ>}š3fàááñÂ÷?33“mÛ¶±wï^‚‚‚ÈÈÈÀÆÆ†öíÛ3|øplll^h~¦M›ÆßÿÍG}Ä[o½U`^JJ IIIT­ZU7mòäÉ ÏNrÀ7²\Ë$(*™‘+OóëégNë˜_o,9Ƶ°ørݧ§y?¡ËNðëéPb“2¨emJvŽÂþ+wù¿§ùýü'+ p¾UdR>âU™ÖœÅÒÒ’wß}ƒæ]ð‹ðÇpåÊ5jTÞY)‹/ÆÇÇþóŸÿТE ¢¢¢øì³ÏðööfÒ¤Ilß¾}}ý’'wwwêׯOË–- L_³f ‘‘/.jhoÎo¿¤û¬( ©Ù„F'³úÐ-ÎÅ2kÛœkuÆÂXï…å+¿?.„q%4žFO^+ô¨5‡ˆ¼ŸV.ûQ¦l¾HLR®u­øb`s,ŒõPØt"ˆ%ÝàËß|hU«2–¥«a|ÑçÛ?”ø_õÜkÎ,--yÿý÷1bDy﫨€’’’ؽ{7Ÿ}ö™.0°¶¶fÁ‚XXXp÷î]Ž=úÂòÕ©S'FŒA“&Mʵ|4*f†ZÝŸ¹‘6• i]» K†9SÙDŸÔŒlþ¼ød52¢ìù…'àsû>Æúæ¾Ù\,«TàéV‹6uª•­ðÇ9VBˆÇ“fMQ®|||ÈÊÊÂÔÔ”¶mÛšonnNóæÍ¸yófyg·B1Ò×кv#“Ê;;ÿzIiY¸Õ·Æ½y5*®Å¬og@x\jygUQÁ=U³æÙ³gÙ¸q#ׯ_'##ƒ¦M›2jÔ¨"—}Ü€€ôôtvíÚÅ¡C‡ 11###éÞ½;,ÐŒõË/¿ðõ×_3jÔ(ú÷ïÏ?þÈñãljÅÚÚšÎ;3räH,,, å#))‰Í›7³oß>îÞ½‹™™:ubÔ¨QÌž=›ãdzbÅ \\\ ¬wìØ1¶nÝŠ¯¯/ÉÉÉXZZÒºuk† FíÚµŸ©ðãããéÞ½»îó–-[زe :ubÑ¢EºéGŽa÷îÝøúú‡V«ÅÖÖ–víÚ1lØ0¬¬¬ŠL_Q~ùå¶mÛFXX•*U¢}ûö¼ýöÛØÛÛ—:Ÿááá¬[·ŽS§N…‰‰ Mš4aРA¸ººZ>''‡;v°oß>BCCIHH J•*´jÕŠ¡C‡¤Ð°aC¾ÿþ{ÒÓÓ‹Ý~Þ<µúñ÷§NbܸqØÚÚòǘ—œœL×®]ÉÎÎ.rÐÃâŋٴi£FâwÞ)4 ÀËË‹©S§ê–ÿä“OŠpýúuV­ZÅÅ‹ÉÌ̤zõêxxxðÆo”¸OJõà_#M‘óùE±õL¾·ï“œž…¥©>­kWaX'jÛ˜¹N\r?âÈõ{„Ç¥¢U«pªjÊË-íy­uu´šÜ­Æ§dÒý?tëm9–“!tjX•Ež­€Ü€eýÑ@NùG“BVŽBµÊF´«g§›#Vf¹Ý¼|"˜ºùÒÃòÝt1·|_nÀöŽLüéG®ßcÃhWÎÝŠeýÑ@r…5+óÍ[ΨUž•îs·9t-’€ˆDÓ²0Ò×àheB÷¦¶ l[}íÃòˆLbзÇi嘛Ʋý79|-’ÄÔLì*áÞ¼ƒÛ9bRLÙ>ÊÅÉ'ËbçG%P­²Q‰i•ö|{’ïfÞµ¸mÛ¶,Z´ˆµk×ò×_qïÞ=¬­­éÓ§#GŽD«ÕrúôiÖ¬YƒŸŸjµš–-[òá‡âääT(½V­ZñÍ7ß°lÙ2>Lbb"vvv¸»»3xð`LLLJ}>—öZ÷<ʧ4J›^ZZƒ ",, WWW¾ûî»é„„„0tèPÒÒÒ˜>}:ýû÷×Í»yó&¿üò .\ **Šììl,--iÙ²%žžž4hР@Zyƒ‘Ö®]Kvv6«W¯ÆÇÇ€Æ3fÌ5jDrr2+W®ÄËË‹˜˜lmméß¿?o½õVëRÞ€²³gÏòóÏ?³cÇîÞ½K•*UhÛ¶-ÿ÷ÿ÷D¿ÉÉÉlÞ¼///ÂÂÂP«Õ899Ñ·o_<<<ÐjŸ{瘝'.•7²téRªT©‚½½=çÏŸçwÞ¡iÓ¦¥N'))‰Q£Fáçç‡J¥ÂÞÞ+++"""ðññÁÇLJ3gÎ:©"##:t(ÑÑј˜˜`bbÂÝ»wÙ¼y3§Nâ§Ÿ~ÂÐÐP·|LL cÆŒ! @wb$&&²}ûvNœ8Qd0§( _~ù%;wîÔíkíÚµ¹}û6þù'ûöícæÌ™ôîÝû© _OO®]»rëÖ-BBBppp ^½z4nÜX·Ìœ9søý÷ß°²²ÂÉɉ˜˜‚ƒƒ æï¿ÿfóæÍEîÃÂ… ¹téÔ©S‡àà`~ûí7öïßÏ÷ßO³fÍJÌã©S§˜µmL¹“ŸÃÙwù.3_oJïÕ ¬çžÀ¸uÞÄ%g Q«¨emBZf>a÷ñ »ÏÞËá,î‚™¡=š®m¸™DHt2–ÆÔ³3£±ƒ;êräÊÓÜŠLÂ@«¦zcP©‰Jâ§ãIì½ΆÑí¨jn€M%Cº6¶Á;0–„ÔLšV·ÀÚÜ€VØ÷^¾ËÏ'‚uŸÓ³rP«rƒÀQkÎ➀Jö•°27$".U—÷31|7Â…G%§gñÎÊÓD&ÑȾum͸Ç /úFðýÿµ¦²ÉÓ÷uÌÊQØz:”£~÷01ÐàѺ䊥9ßžæ» ‘‘Áû₩¦¦ΪU«ˆŽŽ¦qãÆÌ;µZ¥¥%ÑÑÑ?~œ«W¯ò믿R¥J•‚å—œÌ;ï¼C@@5¢nݺ\ºt‰+VpðàA¾ÿþ{*W®\â>?ɵîy–Oqž$=CCCf͚Żï¾Ë©S§Ø·o½zõ ;;›™3g’––FÇŽ f»wïfΜ9(Š‚™™$''ÉÞ½{ñòòâ‡~ U«V…òwàÀ~þùg ·;QLL gΜáòåˬZµŠY³fqëÖ-ÌÌÌÐ××',,Œï¾ûŽäädF](½Ù³góçŸâàà@›6m¸qã»víâàÁƒ|ûí·¥êòΘ1c¸}û6*•ŠêÕ«£§§‡¯¯/>>>8p€Å‹ø½(OÀ××WqvvV\\\”­[·*999Š¢(Jtt´2jÔ(ÅÙÙYqvvVæÌ™£[Çßß_qvvV:uêT ­Å‹+ÎÎÎJÿþý•Û·oë¦gee)?ÿü³.­«W¯êæmÙ²E7½ÿþÊùóçuó¼¼¼”6mÚ(ÎÎÎÊŽ; lkÒ¤Iг³³2dÈ%,,L7ýàÁƒJûöíuiž;wN7oãÆŠ³³³Ò¾}{eïÞ½º}MKKÓå½M›6Н¯ï“a‘¾ýö[ÅÙÙYùúë¯ L?zô¨.§N*0ïܹsJ‡gggeݺuæuíÚU·O?üðƒ’™™©(Š¢$&&*'NTœ•>}ú(iiiºuÆŒ£8;;+;wîÔM»sçŽÒ±cGÅÙÙY™7ož’’’¢›wæÌ¥K—.г³³ò×_é¦_»vMqvvVºuë¦覧¥¥)³gÏVœ•7ß|³ÔeóÕW_)ÎÎÎJ¯^½”ÔÔÔ—ÿøãgggeëÖ­¦/\¸PW&}úô)0ïîÝ»Š³³³Ò»woÝ´©S§*ÎÎÎʆ  ,;|øpÅÙÙY9tèPéy瘳³³2fÌ%<<\Q”Üóùûï¿×ÍË®?ΪƒŠó§)C—Pâ’3t±IéJx\Šròf”2bù)ÅùÓ¿”ñ¼•§§ÎÆcAŠó§)ígîSö^×ÍOËÌVïñSœ?ýKi3c¯â¯['9=KéõÕAÅùÓ¿”qëÎ)Q Ïß°x¥ï‚Êó§)ºP`[ßîÍMïë?®=’‡@ÅùÓ¿”‘?žVâS2tÓ£Ò”¡ËN䮳»à:Ã8©8ú—rÈ7¢Àô Ï+Οþ¥´žþ—2æ¿g•S7£”¿.…+ÇoÜSEQï¹®8ú—ÒÑåvÌÃó4+;GùùD°âüé_Šó§)Wo?Ü_ÿˆDÝôö3÷)'nDéæE'¦ëò8ý—K¥:fòŒQ>Þp^W¦}R.Ç>QÅoOóÝÌ»;;;+;vT<¨»®mذAwmoÓ¦òõ×_ëÒô÷÷Wºwï^èZ“?½öíÛ+'NœxX~ÑÑÊСCgggeúôéòÞ¶m[ÅÙÙY ÕM{Úk]Y–Ïãùä%::ZQE‰ŒŒTú÷ï¯8;;+®®®Jß¾}•‹/*Š¢(™™™Ê×_­8;;+:t(°­ü¿«V­Ò#Êœ9sggg¥_¿~Jzzºnµk×:ÖYYYÊ!CgggåÝwßÕ]EQnß¾­;?æÎ[ªcðoóDm,6l`À€ 0•*·y£J•*|ýõ×EÖÞçôéÓ@n5´ƒƒƒnºF£aðàÁº&Ãààà"×_´hQ»‡nݺáîîÀåË—uÓƒ‚‚8xð ,Z´¨@ul—.]˜0aB¡´³²²X»v-'N¤W¯^º}500`üøñtïÞ¬¬,V­ZõÜçS§N¡R©xã7 õÇrqq¡oß¾-£îÝ»óÁèªMMM™;w.¶¶¶DDDàååõØíoذääd\]]™2e FF›c^zé%&NœÀÊ•+uÓhÞ¼yfß¼r{饗hÚ´)™™™%îÿîݻٶm{ž”æîÊÍÍ ÈmzÏïܹsR¯^="""×Í+©ÖìIØÚÚ²páBììì€ÜóyÔ¨QT«–[C•ÿÜ, ¿ðºÿç€î¯Ç—yåë#Œ[çÍÕÛñtndÃWƒZ¢R=\'+[aíá[LìÓ^Íìtó ´jÆ÷®O÷¦¶då(¬: [oç¹ÛD'¦ã`iÄ×C[éšÙWbñ°ÜæÃÃ×"ñ O(1ï¹ýàº6¶)ÐËÊÌ€ÜëãVßšª•žìŽÙÒÔ€oÞr¦m]+Ü›Ûéj OäÖ NèÓˇç©F­bp»šº&ÜਢûæíYŸvõv¨bªÏü!-ѨUì½|—»ñOÞOìbpœîYg;¸£4åVOóÝÌo̘1téÒEw]2d(ŠB‹-øä“OtiÖ©S‡W^y(¾ßçØ±ci×®ÝÃò«R…ùóç£ÑhØ»w/wïÞ}ìþ<ëµ®¬Ë§¬Ò=z45kÖ$&&†eË–áïïÏêÕ«øüóÏ Ô(z{{“••E£F1bDæ¾êÕ«3räÈÇ–AÕªUùÏþ£«Ù¬Zµ*rkKçÌ™£p¥Õj=z4*•Š””îÜ)¤gåè¦ù„Ås?5#} }[9¹ÞÐöŽœ ˆ!ãÁºÇoDðª³Ú—†:6¦¼T'7€9æw¯Ä¼9^Å›y¢Ÿ4oâP‰u츜–™Mtb§ý£X¶ÿ&^ ')-K× ?$:÷Ô©ª)zš¢ËºAµú¬"ï§Q½Š1¡Ö«_ͼ¸ìаš9§ý£uÛxœ×_ªÎ.ïÛDħ1mË%´-jVƵ®5UÅѪôÅóÔxÌ:&âS2ñ ‹çvt a±)E%qýN ©¹5µ9Jáõ­L0Ò/ºÓm3¼c¹›Â“²~ð†#} CÚ;ba¢Ïç[¯°ùd0ÚÔÈíƒ÷žö»™zQOÓÏûa´OðØNÛŽŽŽ…npuåW»6ÞÞÞ¥ €žåZW–åSÖé5mÚOOO6nܨ{ Lq×4µZ‰‰ ~~~Fhh(ܺuë±ePTåCÞïWq}þòŽ«¢þb4lذÈuò~ÿK:¦y¿½Ç/öQZyi”¶FôߤÔÁYþڨ⾈¦¦¦¥Mޤ¤$¾ûî;vïÞ]àK__ŸfÍš^ é)¿'yi^MEqy†Ü;ˆüòî"7ÊHOO===233‹¼ë( Š¢°iÓ&Ö¯_O\\œnºJ¥¢nݺX[[sõêÕRï×£ÓÓÒŠØgþ;伦ÊÇÉ~,Z´ˆ_ý•ß~û[·náïï¿¿?ëׯÇÁÁ)S¦;RjÙ²e¬[·€Áƒ3a„ƒ¹G¹¹¹ÀÙ³giÔ¨‘®‰3/8ƒ‡5g—/_&99™Ž;–Ƀ’_ÄCr õ48X1 M l-Œ¿á*‘’ž[CebPü×[O£BO£"3[ÑÕh%§g•¸^Þ¼Ô|µ`Å©l¢ÏOcÚóß÷Øå.щéxÆâËwûnà\Ë’ý›>>œ?µZÍš5køè£èÑ£uêÔÑU³æï«ô,ò€âîx²²² U©æ}ù‹í¸ž÷®Ç¼ç𔵬¬,]õù”)S˜5k¯¾ú*Mš4уQQïZäô7n<ö9mfffº/ßãÞkyöìYBCCuMÉIIIøøøè:W«V æÍ›ÇîÝ»qpp ++«Ð`„¯¾úŠÝ»w£Ñh˜3gÎ3½Q¢iÓ¦˜››séÒ% £E‹ºs+ïYvçÏŸçĉÀ?78Ëß‘óàƒ£un@x/I€=*¯cºV£ÂÖ"÷"ZÓ:÷B|ã1ÖýÂsk¢«W)¹I2¯¦,5#• T3ç­µXþvkÖÐZŸT.‡Æ•˜ÖãøÜ¾Ïù XÔ*Xó^[>r¯O¦¶Ô±1Õ}Ñ ÅC!Ñ)dSN7î&¹MÄ%™³ã*ƒ¾=κ£Å.™[[°Å“zÚïæóRì6ò®5Åu…²¹Ö=ÏòyÖô–/_Npp0NNNÌœ9Èm]xôµS6l@QzöìÉòåËyóÍ7qqqÁÒÒò‰Ë ,}çÓ¢ú}ç—÷X“ëׯ»Œ¯¯/mÅù·*up¦R©èر#;vì(4?==}ûö•*­°°0 ÷¤/*@ðóóÓLyϦzZy_O:UdÀçååU¨†¬qãÆXXXššÊŸþYdºy/)wvv.±s{iÊ ¶ûÇÇÇëjôò¿Ò(Orr²n°CqeôèƒX!7xÚ»w/ðpdcqòæoܸ±Èm9r„Ñ£G3hÐ ÝÝÔ·ß~ˈ#tM“ùYXXèúÁåï7±yófvíÚ…Z­fÁ‚¼üòËÏTžWWW222tùÈÿpá¼ÿïÝ»6lXìƒ|‹;V¥éûò"»q¤5] ísû‘5v°ÀÂXﱯuÊ{I¹s-K]¿4·ú¹é?§ÀƒrÙÙ…÷+-3›¿¯M‘ÈÕÛñhÔ*Üî£õ¨Ö@»ÿò]Ý1ÉïÈõ{Dħ¡VAÛ:Ïv¾=ÍwóyIKKãï¿ÿ.\~\½zFóØkͳ\ë^Tùý“{öìÑ=rð?û쳇KçÉë,yÿþýB_ê+W®èžö O“^ZZ³fÍBQÞxã ÝC¿§M›†¡¡!§OŸÖ=äþ.îÙ³§@…Ajj*ß|ó.@}QµL»víbË–-ºkxBBÓ¦M#""‚&Mš”xÝìÕ«Õ«W'--)S¦è*e ·ÚĉIKKÃÑѱȧ6üÛ=ÑjÔ¨ÁçŸάY³øþûïÙ¼y3¶¶¶êžv\š—S7nܘ:pìØ1¦M›Æÿû_¬¬¬¸{÷.ÁÁÁФI|||Êäù'Ÿ~ú)øøøðꫯâääDJJ ááá8::’““CrrrIC‡%((ˆß~û)S¦PµjU¬¬¬ %)) ===>ýôÓ2y1v½z¹?×®]£G4iÒ„uëÖñÎ;ï°dɶnÝʱcÇptt$!!???rrrpqqÁÛÛ»Ø2êܹ3óæÍcÍš5T©R…ÀÀ@222°µµeÞ¼y%vurrbΜ9|öÙgìÞ½›ýû÷S«V-’’’t_´–-[xV\Þ+š6mÚÄŒ3øæ›o°±±!..N—Ï¡C‡ê.T[·nÕ Y¿~=ëׯ/6?o¼ñS¦L)U™ººº¢R©ÈÊÊÂÄĤÀÈÕ¼7&ä5u—¤æW·n]Μ9ÃÖ­[Ùºu+ÇgܸqÏ|çúº|ñøçÑY™ðÅÀæ¦ mïHн$~;Æ”Ÿ/RÕÜ+3CB£“IJÏBO£âS&4©^I·Ž©–Ež­øp7Çü¢è=ÿulÌHËÈÖÕ<5«aÁÌ× ¾Y¢ÞƒwF^»sŸ_Ô0Þщ“7£¹z;ÏïOb_Ù3#=îÆ¥r?5ZÅg¯5)ÐÉ¿®­9gbØz:”­§CÞ±ãz=~xc‡Jth`Í1¿(¦m¹Ä›aefÀݸT‚£“1ЪiR½>·ïq¿pÿ(}­šJÆzŒZs–šV&èiÕÜŠLDQrkGuü›)òêiXèÙŠ±kÏqöV }¦†• ÉéYDÞÏýAíÚØ†ñî J•¾=ÍwóyÑ××§R¥JŒ5Šš5k¢§§Ç­[·P77·b‘G«Õ>õµîE•ÏÓ¤÷ÝwßqûömªV­Ê˜1ctÓíííy÷Ýwùî»ïX¼x1®®®ØÚÚ2räHΜ9ƒ¯¯/}ûö¥AƒdeeáççGjj*-Z´ÀÇLJôôtâââJõÖ…gQ»vm.\Ⱥuë°¶¶&00ôôtto(é¼X´hcÇŽÅ××—þýûëš·óšÂ«T©Â’%KÊüµvÿ ž¸DÜÝÝY»v-]»vrÛ¥ëÔ©ÃâÅ‹uÏÏ*¯¿þš &P¯^=îܹ£{_ÿþýùùçŸu¯“8tèÐ37!U©R… 6àéé‰ÁÁÁdddðæ›oòßÿþW×,™´žJ¥â³Ï>cáÂ…¸ºº’––†¿¿?æææxxxðÓO?êÐù´ÜÜÜxï½÷°²²B«ÕêîŒ<==Y¼x1-[¶$-- oooîÝ»GÇŽùá‡X²d zzz9yΜ9Œ=}}}°´´ÔNùüû8Ý»wgóæÍxxx`iiI@@ÑÑÑ4lØñãÇóý÷ßå8~üxfÍš…³³3™™™Ü¼y“´´4ÚµkÇÂ… #Ï{\Y³°°Ð½N¬eË–h4;°çÕžYYY=Ñ#`ÞyçÜÝÝ155ÅÐа\FiÕ*,ŒõhQ³2c{ÕcÛø…K¡RÁg¯5a¡g+\ëZ‘–™ƒDæÆzx¸8ðÓ˜ö¼ÒªðûñÙWâ—:àéV ûÊÆÞK"69ƒæ5,øÔ£1+ßmS¨É­¾5ïu«ƒ•™ZŠ´µ]úZ5߿ݚQÝëRÏÎŒ˜¤ "1Ô×Чe5~ÓŽ. >z䮵qon‡©C=5Éi¥ë/õõVLx¹õì̸›‚w` Y9 ý[WççqíÝ#÷èod¡&S}šµ£\ñpq !5“°˜T3gºGcy¶ÂP¯ô—ÉÕÌÙ<ÎA®5©Z)7NNÏÂÅÉ’ÿ¼Ùœùƒ[>Ñà‡ÇoOóÝ|ôõõY»v-$$$Fƒ ˜>}:‹-*ÕãŸöZ÷"ËçIÒ;þ<¿üò S§N-40kèСԭ[—””fÏž¢(4kÖŒM›6Ñ¥KLLL¸pá·nÝ¢Aƒ̘1ƒ•+Wê*<øÜëçŸÎèÑ£100àÖ­[ØØØðÎ;ï°aÆRÿ~899±yófÞyçj×®Í;w ÁÎÎŽÁƒóóÏ?—:­•RÔNÊPÞËqÍÌÌ8tèPyïo!Š¢Ð®];233Ù³gO‘ÏÿBüïÉ{ñ¹©–ßw/ïìüãä]ÛMMM9|øpygG”‘¼Ÿoڴ鉟]*ÊÎs¯KÌf]^/6=wî}ûöeÆŒÅÎÏÌÌÄÂÂB3!„B”»çœå Èë$š÷nÁ­nݺDGG³wï^¶mÛV ‰ÔÏÏOב³¨WU!„B¼hO4 àI,\¸¿þúK7ª¥¤a·Ï‹……£FbÙ²eÌ›7åË—cccCBB‚®si»vít/•}ׯ_/ö‘Ó¤IÝèF!„BxŽÁYbb"YYYXXXðòË/3xðàrÛÉ#FвeK6mÚÄ7 ÂÔÔgggúöíKŸ>}ži´HPP[¶lyâõúöí+Á™B! xî„B!DéÉÃE„B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!* ΄B!*mY%”’‘MÇÙ£VÁÙ¹î¼4c/9ÊÃe õÔ8Z›òn×:tjXµ¼÷ý©¸~¾Ìl¥ÐtKS}öOëZª4\¦ï¥’‘ft+ò³B”Väý4Vxùsòf÷S3±¯lLÿÖ i_ µêÅæ%)-‹ýWïòZëêå],Bü£•Ypö8µ¬MPˆMÎÀ/<‰?]`¡g+:ÿC4GkÔª‡W> c½òÎ’¢9s†«W¯>vccc† RfÛ KåíO˜ŽF­ÂÜHèd–üuƒ°ØT¦¾Úè…íLRC—À¾²‘gB<£œý4¶=Z5Y9 ?zù³öH ‹ÿ¼N§UQ½à;»²²v”+f†/¤ø„ÿ•+WfÅŠ]æ­·Þ*Óm.Ø}èÄt:7²aö€f˜h8êwI›.²íL(ou¨…}e£²ÿ‰©™D'¦¿°í ñ¿ì…FZµŠQÝë²Ë;Œ;q©D&R×Ö¬¼Ë L}·ïë1£<\ðXt„°ØT}Öý‰º31ŒY{Ž7ÚÖ 5#›ƒ>T2ÖçMךxº9¹Íó~÷åbpY9Ô¨b̘žõéÐÀšÛ1)ôÿæ(ÝÛ`b¨åï«êix¯kT*XsøIiYôiQɯ6Ö5ƒl>ÂOǃˆMJ§Ž­öªOëÚUÊ»x…¨°êÕ«GçÎ9|øp‘ó 6lX™m/.9ƒã7¢ÐjTÌ|½)&:6¨Ê„—`a¢O%£ÜýÌìÜã=—î—œAm3Fu¯‹[}kNÞŒæÃõÞ¸7·cîÀæ|±Ã‡ß·±phK:7²aä§¹Ïü!-Yþ÷M"ï§Ñ´ºŸ½ÖKS,9ÀåÐx\¦ïÅkz7~<àÏÖÓ¡¼ß­ÛÏÞÆPO…1çcX2ÌY·ýù»¯±õthë¦ÿf/|@€F­ÒdÁQÉå½ÿOíÿVœbàÒãº?¿ð„纽ígBIJËbH{Gî%¤±ä/?ÂbSø|ëŽùEѬ†ê[™Ä§¿\"+_߸¾‘Ä&e0¤#qÉÌß}Õ‡nñ¦kMª˜°íìmþ¾z€¿.…³èÏëhÕôj^;±©|¸Þû}¼„xÞ{ï½bç½ñÆT®\¹Ì¶uón"µ¬M Ýô½éZ“^Íì0}0ýó­—Yw4ì…¦Õ-ðHäãç9åýÄÛ÷›//·¨F][3ÎÞŠa…—?•Šöõr- c=z4µEOóðçeÕÁì,ŒhRÝ‚Þ-ªpøZ¤nþ±ë÷ЪUtilSÖ‡Dˆ¤r­i¬Ÿ{‡—œžUÞûÿÔ‚£’ ¼—¤ûKËÌ~®Û³·4fÞàŒê^—®.`yá@ךLy¥ßwaþ–4²¯DjF61IéºõM µÌÜ‚zÔ¥ž]npü‘{}Fttâ¶5ˆÈ½Ø¯?ÀŠwÚ0­_c¦õkLf¶ÂïçÃʻ؅¨ÐòjÏUÖµfI®Ÿ&¯¿™ÄßW#°67`ÛÇYùnfh†¢Àò¿ýŸx»õ®ÏÛkóùkM¸~'­FÅÇ/7 ¦• _ j¡«ÉèÝ¢kGµeîÀætmlƒ¾VÍQ¿{ä(pãn"÷ÓhSÇJWÓ'Ä¿]¹tšJ}Èêiž1¥òó$M”ŠRªÅ«vUS´Ú+›è™•@ûzÖxùÜeæ¶+\ Ž#<.5w~vŽn}Kc]yç]ÌkU5ÀÌPO·|vŽBptn ÙËóÈCh´Ôœ Q’÷ß¿PÓfYךÁÛܤ"nr3³ô4¹×‹«·ãèP¿ªîšÕ³™³¶]ázø}²sо@)=½ž­9®CY9”¤iu ÝÿM µ´¯gÍ¡k‘\ ãì­z4³-ÓòâŸì…gŠ’{'¹#ÿWåä‹È2²K¾x•DOû°’Sý HS€ì…‘?žâÆÝD\œ,Ц{/‡són"*• åA>´ùÆÔçýOû Ù!ÿ Œ¼4õµj~|ç¥y0“»Z!JT·n]ºté¡C¹77†††e> ŽM^÷$Ó² Ü,¾¿ú j•Šéý›U^’ü7’Å]ú®EÝ5¥ä»ÏGk÷Ü[TãеH_»‡w` zJ“¦y^x³æ/§Ct#zêÙ™—÷þ—9ƒµS÷î§p7>•ؤŒç¶½w¸q7‘VŽ•Y1ò%†u¨¥{¶ÜÓ „ÕªUÔ¨bBFVj•ЦÕ-Kå×Ó¡º}B<^þ¾g ÀÒҲ̷amnÀKµ«•­0gûU’Ós[$¶ åJh<~á÷±67 ~µÜëì±÷HLË­eÛå.Y9 *¡Q«0ÐËý)¸—ûÏÊV¸ñ„ýhóbÀì"š Ô¦ZêÛ™áVßšã7¢°ø(ŽÖ¦\ ‰C­‚1=êàô ÛÄÅà8&þtؤ "žðf¬’qn3çõ; ¼õÃImUì²úZ5ÛðÇ…;ôhj÷\‡ÿ4/¤æ, "ÿˆDS3iVÂ%ÜéÜè³ »Sƒª jWµ ÎÝŠah{Gš×,Û¾&ùY›ð©GŒô5ÌûÍ—€ˆDÝPô«¡qO•æmk0®W}ô´jþ¼x»ÊF,òl¥H „(Ù{ï½ÇÀŸK­YžšV&¬Ý÷æv˜hÉÊVhPÍœ9o4cp»šºåæ nɰµÐ¨U\£žߎh­{Ì7{üèÚØ†CZ–wv„¨P¤‘_!ž Ì ;y3šgC9q3 €×^ªQÞY¢Â‘àL!Ä £RÁé€hÌôÜΑ¶uäÍ#Bâ×_¥Zµjå]FÎÁƒY½z5¥Z~ùò大§Ó½{÷òÎú Ô)Søý÷ßKµü“–g~úúú¼ýöÛXXX”÷n‹©h]» Š¢•˜ÎšCÌÞ~•%ÜË|[5­MÒÞñ¹íKC{sÆö¬_hº­…ásÛ¦Q™gþþþ̘1µºp…œ——_}õU«V-4ï?þÀÃÃwww>øà8ÀÑ£G4hPy—Q…£(Ês]þŸîE–£G.ï]ÿbN6¦´©SE÷Y_«fú/—IÉÈÆX_S¦ÛªemB-k§ç¶/•Œõ ì‹ÿVeÚ¬yþüy:tèÀêÕ« Í;uêãÇgüøñ…æ7Ž¡C‡˜¦R©ÈÎÎ.ïò)`Ú´i̘1£À´¯¾úŠ)S¦ÁäÉ“éÒ¥ ݺucÁ‚¤§§J'22ÂÃÃuÓ6nÜȈ#€Ü@vðàÁ|õÕWôïߟ—_~™Ÿ~ú ooo¦OŸNDD...DFFÃŒ3èÚµ+mÛ¶eРAœ={€ü‘;vèàÒl{øðá¬X±‚.]ºÐ¥KÖ­[DZcÇððð cÇŽÌœ9³Èc³mÛ6^~ùåÁÎþýûéÙ³'ÙÙÙ¤¥¥±dÉz÷î››ü1E–õ£Mœ×®]ÃÅÅ…ôôtâããqqqaýúõ 8wwwæÌ™CZZ€.ÈõÕWÙ³gÛ¬üæ›oÒ¶m[ºvíÊ_|Afff‘å °k×.úõ뇛›o¿ý6>>>EæóÑfÍòí·ßâééÉË/¿Ì¨Q£ +PF¯¼ò ®®® <˜S§Néæùúú2jÔ(ÜÜÜhß¾=£GæîÝ»/êôÿ#L ´¨T zðÙcÑv_¸£›íÎ}\¦ï%=+‡èÄt\¦ïå§ãAtù‹YÛ wE™½ý*‹Ž˜^ Y3>%—é{ùí|¯.ºÎ=À;|ÈÌ~ú›!ß°ûŒZs·YÓ~æ~Fÿ÷wãSÈÊQ˜÷›/Ýÿs·Yû»Î›°Øݺ—Bâø¿§q›µŸW¾>ÂÖÓ¡O› !ž»2 Î ÄØ±c100(4ï³Ï>ã•W^)r½Úµkcgg§û|öìY‚‚‚pqq)ïò) W¯^?~œÌÌLrrr8tè={ö$==÷ߟŒŒ V­Zżyó8qâ‹/~ªmùûû°}ûvV­ZÅO?ýÄŸþI«V­˜={6¶¶¶œ8qfΜIll,+W®dË–-8991wî\Fމ‡‡½{÷fëÖ­¥Ú¶¯¯/ÉÉÉìÛ·I“&±lÙ2Ö­[ÇêÕ«Y·n‡æðáÃ…ÖëÞ½;111øúúê¦yyyѽ{w4 sçÎåèÑ£|ñŬ]»–¬¬,ÆÿÔAøX³f »ví"<<œ 肺mÛ¶áîîΕ+W˜3g#FŒ`çÎÌž=›ýû÷³oß¾"Ë3ï¸;–Í›7Ó¹sg>øà]àV’]»v1kÖ,öìÙC“&Møè£ÈÊÊ" €%K–ðé§Ÿ²}ûv:tèÀ”)SHMM%%%…qãÆÑ A¶lÙÂ?þ¨;¦B}úpêÔ)ÜÜÜž¸ŒÆŒƒ™™£FbÔ¨QLš4I·Ÿúúú¨Õjôôô˜:u*½{÷ÀÎÎŽ-ZˆZ­.Tž6l`È!ôèÑ€aÆqöìYvîÜɨQ£JÌ×€¨S§ï¾û.¿üò çÏŸ'-- FƒÕªUãÝwߥU«Vh4ñôôdذahµZpwwçСCO\.âßeîN]`¬¯aõ{mŸ(áp®e Àák¹5пže×¹Û¬z¯-Õ*»îÈÎu°©”Û'ìåö콜|éiÔLíטÞ-rû ÛYÑ¢fe#“ŠMëì­:Îþ»À´I}ò¦kMR3²ñtsdX'´–F¸·¨Æ!ßÜ›¦;q©ék°·4ÆÌPËŒþMˆ¸Ÿ[«¶íìm;XðAº8Z™•Äúctoj[ÎGPˆÂ*TpÌèÑ£iÔ¨“&M*ï좯¯OçÎ9tènnnxyyÑ©S'  ÂÑÑQ˜4kÖŒœœœ"G”¤ZµjÒªU«›6m*rÙAƒqèÐ!¶nÝJHHˆ®æ*''ç©öÓÌÌ #£Ü‹q^ “¿¯ V«-4¨#O¯^½X½z5}ôǧR¥J4oÞœ‹/ФIݲ•*U¢F=UpV¯^½å“••Å;w uÎoذ!fff¬[·ŽÀÀ@nݺÅÍ›7©]»v‘éqåÊ6nܨ›–™™‰¹¹ùçËÀÀ{{{‚‚‚ððð Aƒ 0€úõëÓ¡C<<<Ð××§J•* 8]»vqóæM‚‚‚ðõõÅÉéùõïÿÞéR›¶u­HLÍb—÷mÞ]ušŸÆ´§zãR¥á`Yp¹û)™,úó:ÆzX›_†z¿ó*`é0gL ´,ÿÛÿ±Û×j ~çòNÿ³·b°ä~whd_‰I¯4¢]]ëǦej¤§Û—¼¿¼æÔèÄtÞXzŒ=—±µ0dxG'Þîüð«FcvLèÄÜÍ©jnÈ /Þ]yš¬l=šB—0E)²,„¨*DtïÞ=ÆŽ‹««+³fÍ*28¨(Z·nJ¥bÓ¦MdeeѶmnóA­Zµ "))IWãuõêUÔj5jÏòj£’’Vï?Z»NBB‚®¶ÆÇÇGW#“?P äìÙ³lݺ•Zµjð÷ß¹ÍyABþåK³ígajjJ»ví8pà'OžÔõ™ªQ£*• Z·n Àýû÷¹}û6ŽŽŽ…ÒÑÓÓ+1ׯ_§}ûöºò122ÂÁÁÄÄÄËmÛ¶Î;3o޼囷ÝGÏš5kBõêÕuÓ¾ùæ5j¤lð8ׯ_×5‰FGGAݺu¹pá×®]ÃÓÓggg>üðCúõëÇ™3gPEQX½zµ.À<~üx™ñï¡™Y9ä<<ô4*’Ò²tóC£SJLÃÜX¶u­˜Ð§!6žç•Vö4¨Vºšã<Û΄ҹ‘ óµÐM KÁÑÚä©öë€OŠ«ßk‹úÁWö¸ß=Ýüß·QÙDŸîMléÞÄ–h'^_|ŒèdjU5åjh|ô®Þާ¦ÕÓåEˆç­BÔœ-Z´ˆœœzöìÉ™3g8yò$'Ož¬öÔh4tëÖ5kÖеkW] _ûöí±±±aÖ¬YqñâE,X@¯^½ 5³YXX`ggÇš5kð÷÷gçÎ…:Øgdd0wî\BBBØ»w/»víÒ=VÄÈȈøøxŽ=ª«±;}ú4‰‰‰œ?ž¥K—è.áïïÏ… JµígåîîÎÏ?ÿL•*Uhذ!æææôë×ùóçséÒ%™9s&ÖÖÖ¸ººJ£qãÆìرƒ«W¯ròäIÖ®][h™Å‹ãëëËåË—Yºt)¯½öúúúº&ÙC‡qïÞ=ÌÍ͹uëaaaDGGóÍ7ߤkšÍ_žiii :”Ý»w³}ûvîÞ½ËÆùõ×_u}úJ²mÛ6|˜ììl>þøãó<==‹|ôFyswwgÛ¶môìÙS7M«Õ²dÉ,X€§§'ÆÆÆôéÓ‡>ø Ðú*•Š™3g2þ|† †³³3£Gæ?þÐ-S¹reªW¯Ž§§'L™2…Ž;вeKœœœ˜:u*ÿýï™6m«W¯æûï¿§zõêŒ3†/¿ü???Ú¶m‹»»;{÷îeòäÉxyy•¸íg•×,¯É7ÏĉY¼x1ü±®ÖqÅŠEŽî5j³gÏæ½÷Þ£FE>†¥gÏž|üñÇdffòꫯ2nÜ8ŒéׯË–-C£Ñ0jÔ(f͚ŠAƒ066¦M›6xzzråÊ•"˳[·nÄÆÆ²~ýz.\HÍš5™?>5*Õþ÷éÓ‡ü‘;wîðÒK/±xñbÔj5-Z´`Ò¤I¬\¹’ˆˆlmm™6mÍš5£qãÆ\ºt‰ & ( 4`âĉ|óÍ7$''cb"wø¢h±ñXî­šºvf,òlECûÜš®QÝë2{ÇUÞ[u†V&ŒïÝ€ñJÿ– }2pé1vz߯ÒD¿Ôëê^—YÛ¯2èÛãhiSÇ O7G®}:Z­–õë×ãïïÏ矎ƒƒ-Z´ ((ˆÐ¥KÝ:öööäääÊ´iÓpppÐͯW¯^y—Q…sðàAV¯^‡‡G©–_¾|9ééétïÞý¹ç­ÿþ4lذœK¨üèëëóöÛocaaQÞYÿ#::ѺvE!*15‡˜½ý*K†9—jýi›/1¼“ÍkX”÷®!(³à,,, ooovíÚ…ƒƒµjÕbïÞ½ìÛ·Oœ2pà@Ú´iShý;wî‘‘A¯^½055-ïr©ÐEy®Ë?‹¾èâ¨P =ztygCü‹8Ù˜Ò¦NÝg}­šé¿\&%#c}M‰ë¿¸«ƒ¢´Ê,8«V­[·n-Pë•““CNNÉÉÉDEEQ³fÍ"× ¢J•*:0›6m†¹sçê¦}õÕWÄÇÇ3þ|"""øæ›o8wîjµš^½zñÑGa``P ÈÈHúôéÃï¿ÿNµjÕØ¸q#`ݺuxyy±fÍš5kÆÙ³gIOOgÈ!xzzâííÍôéÓpqqáÏ?ÿD«Õ²xñbNž|8µk×fÑ¢EÄÆÆÒ¥K>ÿüs4šÂþÁƒóúë¯3`ÀæÏŸOBB cddÄ„ hÛ¶-.\`Ñ¢EaiiÉ Aƒðôôr›È‹Û§GÅÇÇÓ½{w¾øâ ~øábcc騱##GŽäË/¿äæÍ›4hЀ/¿ü’ªU«’••ÅŠ+Ø»w/QQQXYY1tèP† ¢KsõêÕüúë¯dddп.\¸À!CèÕ«¤wïÞ:tˆÀÀ@™8q"-[¶$11‘.]º°mÛ6Ëû”ÿB&ZT*P=øœ–™Ã /ö] '15“Öµ«0啯ØZòÞª3„D'3{ûU.ÇñÙkMØvö6똆£µ)º×ǵ®ƒ¾;A?g·Ë½†Oüé‘÷ÓøiL;øFòÝÞìšØß°û|·ï>·ï£( ÍkVæ³×š`gaD|J&Ýÿs€Ï^kšC·ˆOΠyÍÊ|þZS¬Ís¯•{.…³þh !ÑÉëkéÒȆ©ý£§Q=M‘ñSf}ÎÔj5µjÕ ==={öpæÌúôéä_6l {÷îxxx°iÓ&]­NPP†††|øá‡tîÜOOONŸ>]ÞåS@¯^½8~ü8™™™@nðyèÐ!zöìIzz:ï¿ÿ>¬ZµŠyóæqâÄ /^üTÛò÷÷`ûöí¬ZµŠŸ~ú‰?ÿü“V­Z1{öllmm9qâ666Ìœ9“ØØXV®\É–-[prrÒ#GŽÄÃÃ޽{³uëÖRmÛ××—äädöíÛǤI“X¶lëÖ­cõêÕ¬[·ŽÃ‡søðáR¥µoß>úôéÃîÝ»ù裘0aaaaäää0yòdz÷îÍöíÛ?~<Ë–-ãÂ… ݧâìܹ“7²yófŽ9˜1cøä“Oسgééé¬[·È Fÿúë/¾øâ vìØÁ›o¾ÉâÅ‹¹s玮Ì7mÚħŸ~ÊêÕ«¹qã¾¾¾¶µiÓ&&OžÌþýû©W¯_~ùeŸmB”NFV)Ù$§gs;&…-§BèÑÔ£µfsw^åèõH¾x£9kG¹’•­0~ãy²s¾û%jT1fºGc¦y4& "‘%{üøÔ£1Û?îH‡U™òóER3²q­k…w` ŠƒãðH$9=€³Ñ´¯oMJF6ãÖyÓ š9[>tãÇwÚ›”ÎÊò½óÜmV½Û†:“”Κ÷¸ÏœíWÑщ:2{@3ö_½Ë¾+áå]ÔB¼0Ïe´fß¾}ùüóÏqqqÁÉÉ @×AºZµj,]º”7ß|“ï¿ÿžíÛ·¹ÁÙýû÷yùå—ùöÛoiÚ´)ãÇ×)A»v¹wˆgΜrk~ÒÒÒpssãØ±cÄÆÆ2wî\êÔ©CëÖ­™2e ;vì ))鉷e``À¸qãP«ÕØÛÛóÆo°cÇÔj5Z­V· €««+S§N¥N:8::2dÈÂÃÃIKKC«Õ¢V«Ñh4èéé•jÛjµšÑ£G£¯¯¯ë8xð`¬¬¬prr¢aƺ`»$Í›7ÇÝÝ€öíÛÓ¢E ~ÿýwRRRˆÇÚÚ;;;ºwïÎòåËuµMÛ§âŒ1 jÔ¨AíÚµéÒ¥ 5ÂÜÜœŽ;€££#Ÿ}ö-[¶ÄÞÞžaÆ¡¯¯¯;G·nÝÊðáÃéܹ3uêÔaΜ9º2ÏÓ·o_š4i‚±±1$((ˆäää²=á„(…¹;}è8ûo:Íù›þßåJH#:æ^wc“2Ø{ù.Óú5ÆÅÉ’º¶fÌ}³9a1ÉœòFO£B¥R¡Õ¨ÑªU܉KE£»ÊFT«lÄ»]ë°`h+4j®u­¸KŽ‘‰˜éa_Ùˆ«¡qœ ˆÆµ®©Ùxº92¶g},hR½î-ªx¯àupdç:ØT2ÄÊÌ€—[Øãs;=š©ýÓ»E5ì,ŒèÐÀš5+ùä×Q!þ©Êt@@ž;wrÿþ}>ÿüs>þøcV­Z…»»;mÛ¶ÅÊÊ €ÆÅÖ­[0`S§NeüøñºŽÔÍš5ãúõëìܹ“É“'—w9¹½;wîÌ¡C‡pssÃËË‹N:a``@PPŽŽŽše›5k¦èð¤ªU«V ­Zµj±iÓ¦"—4h‡bëÖ­„„„èjyòš”Ÿ”™™FFFº€®jÕªºùZ­–ŒŒÒ«[·nϵjÕ"((SSS† ÆôéÓùî»ïpss£oß¾XZZ>õ>åÏ£¾¾>666ºÏzzzºÏ.]ºpéÒ%~øá‚ƒƒ¹qãéééºfø[·nÔ`ee…]mÙÚÚêþohh@fff‘M½BNU vY±³0ÔýßP_Cfvîw»¡½9fFZÖ $ð^·"¹‘Hm›ŠÛåEˆ²ö\jÎLMM±··gܸq\¼x‘èèh´Z­.0ËãääDtt4û÷è·ZµjéæWîîî9r„ÌÌLûøøè+’?è äìÙ³|ûí·Œ;–ž={’šš < (ò/_šm—¥ëׯøìããCݺu‰ˆˆ`áÂ…8::2räHÖ­[GÏž=ñòò*Õ>=‹Í›7óÑG1uêT<<<077'%%EQP«Õ8::Èw||<Ï­Œ„(K ™•CNŽB +cT*tM†÷S3¹“Œ£• ðpàÀ…à86ƹ–%^nÀŽ 1Ò×p& ·¯Yn¿³X.…ÄÑÒ±2-+ãs;žc~÷hW/7€:à¢Àê÷ÚòvçÚ¸Õ·&:!­”¹‡mgBéÜȆyƒZ0¨]MZÔ¬Lx\ /pйå®Ì‚³F¡ÑhøÏþCHHgÏžeÑ¢E¼òÊ+Ò¡C.]ºÄºuë cçÎlÛ¶aÆСC¶lÙÂáÇ aáÂ…„††òÆo”w ÑhèÖ­kÖ¬¡k×®º”öíÛcccìY³ ââÅ‹,X°€^½zª´°°ÀÎÎŽ5kÖàïïÏÎ; u°ÏÈÈ`îܹ„„„°wï^víÚÅ Aƒ022">>ž£GêjìNŸ>Mbb"çÏŸgéÒ¥ºf<###üýý¹páB©¶]–|}}Y³f aaa|ÿý÷ܹs‡W_}sssþøã¾ýö[îÞ½‹>>>4lØ33³÷éY˜››sñâEâãã bÆŒº2‡Ü&Õ7rôèQnݺŬY³ÈÊÊ*³ÚC!Ê’D"'oFsòf4‡®E2móEb’2èÖÄs#=ú9;0ÿ÷k\ ‰#ð^3·^ÁÚÜ×Á”¡¾†ó±D&¡VÁ²}7Øs)œÈûi½~˜¤tT3 m=+NúG¡ªW1ÆÑÊc-Çü¢tÁY%c}R3¹GBj&¿Ÿ¿ÃoçÃÈÈ*]7 s#=nE$›Btb:ßìñ#(*™Œì§ë¦!Ä?Q™gÆÆÆ,[¶ŒØØXÞzë-æÌ™C÷îÝuýÅ7nÌ‚ Ø·odÆ LŸ>N:0tèPFŒÁÂ… 4h>>>,_¾këŠWíîîNjj*={öÔMÓjµ,Y²„ŒŒ <==™þøc:uê„§§'666%îÓ³˜9s&·nÝâå—_æ£>¢eË–´nÝš7nЯ_? ÀìÙ³yûí·©W¯¥P!Ä‹´ñX®÷æÃõÞÌøå2‘ i,òlECûÜ€jbŸ†´ªeÉÇÎ3|ù)ô4¹M—ÚÜËÿ€658àÁªþ´¨Y™I¯4båú-:·{o0­_cš=x@­£• Öf†´t´Ôm¿eÍÊØZâh[×£©-¯¶²gÂÆó¼úõö\ºÃľ‹MÑì|œQÝëbcaÄ o3dÙ â’3ðtsäFxB‰ë ñ¿B¥¼È'”ŠRñòòbÁ‚ìß¿¿¼³òLæÏŸO||<_}õUyg剜;wGGGÝAzz:;wfåÊ•4mÚ´¼³'„âœôjâ{÷î%$$„)S¦ ¯¯Ï† ¨Zµê¿úµTB!^œç2ZSˆ²qãÆaeeÅ{g§§'‘‘‘|÷Ýw2BS!Ä !ÍšB!„ˆÔœ !„BT œ !„BT œ !„BT œ !„BT œ !„BT œ !„BT œ !„BT œ !„BT œ !„BT ¥~M```yçU!„âËÉÉ©TËÉ뛄B!*iÖB!„¨@$8B!„¨@$8B!„¨@$8B!„¨@$8B!„¨@$8B!„¨@$8B!„¨@$8B!„¨@$8B!„¨@$8B!„¨@$8B!„¨@Ê48‹gÚ´itîÜwww–-[Fvv¶nþ79r$nnn¼þúëìÝ»·¼÷ÿ…Ù³g}ûö-“´²²²ØµkW©—ÇËËë…ìgBB#FŒÀÕÕ••+W¾m ñoåúù>\¦ïÕýuù‹O¹L\rÆsÙÞák‘ôüê ‘÷Óp™¾—ð¸Ôò.!þçhË2±¹sç’’’Âÿû_bbb˜1c•*Uâ­·Þ"--ñãÇÓ«W/fÏž··7³fÍÂÁÁ&Mš”w9ü£ÁÍÍ>}ú°téR22r›köíÛÇÛo¿ÍŒ3èܹ3;wî`×®]ôë×777Þ~ûm|||Š=žÏ’·âæ•T¾YYYÌ›7îÝ»ãææÆØ±c Ó-»gÏÞ|óMÚ¶mK×®]ùâ‹/ÈÌÌ`þüùÌ›7É“'ãææÆ+¯¼Â©S§X³f Ý»w§{÷îlÙ²E—–‡‡»wïÖ}¾ví...ºsrÛ¶m¼òÊ+¸ºº2xð`N:õ¼¾:â21ТRêÁç×c…—?½çbÀ’cdå(ø†ÝgÔš³¸Íú›ö3÷3ú¿ç¸ŸÛ<9~ÃùM¥y—Cã 4k !žŸ2ísöé§Ÿrüøq:tè@¯^½°¶¶æ½÷Þ fÍš¼÷Þ{|ýõ׸ºº2bĆNÛ¶mË» J­W¯^?~\÷c™““áC‡èÙ³'ééé¼ÿþûddd°jÕ*æÍ›Ç‰'X¼xñSmËß߀íÛ·³jÕ*~úé'þüóOZµjÅìÙ³±µµåĉØØØ0sæLbccY¹r%[¶lÁÉɉ¹sç0räH<<<èÝ»7[·n-Õ¶}}}INNfß¾}Lš4‰eË–±nÝ:V¯^ͺuë8|ø0‡.´ÞŒ3ðôô¤C‡¥Ê[vv6ãÆ#--ÿþ÷¿|ñÅlݺUhMš4 µZÍúõëùꫯ8sæ ß~û­n{>>>èëë³`ÁÚ´i£+ï±cDzyóf:wîÌ|@ddd‘ûù´y+)ß³}ûv.^¼È²eËØ´iF·Í+W®0gÎFŒÁÎ;™={6û÷ïgß¾}ÖïÙ³'‡¦mÛ¶L˜0ˆˆþøã&NœÈâÅ‹‰ŽŽ.1,Y²„O?ý”íÛ·Ó¡C¦L™Bjªôú§ÉÈÊ!%#›äôlnǤ°åT=šÚa”¯ÖlëéP¦{4abŸ†ddå0n7 ª™³åC7~|§ ±Ié¬<ÀWƒZpè³îú¬;gtÃÅÉçZ–4q¨TÞ»*Ä¿F™ö9 ¡U«VŒ9’èèh¾üòKV¯^ͨQ£ÈÌÌ$44OOOÜÝݹxñ"K—.¥iÓ¦ÿ˜­]»vœ9s777.\¸@ZZnnn;vŒØØX6mÚ„©©)S¦LaüøñŒ;ö‰·e``À¸qãP«ÕØÛÛóÆo°cÇúôéƒV«Õ-àêêJ‡t}¼† ˆ#HKKÃÐеZF£AOO¯TÛV«ÕŒ=}}}ºtéÀàÁƒ±²²ÂÊÊŠ† Th=­V‹F£A­V—*o¾¾¾„„„°|ùr,--ue–““Ãùóçñ÷÷gÅŠ0uêTÞ{ï=>úè# 78?~<æææÌž=›!C†Ð£G† ÆÙ³gÙ¹s'£F*”ß§ÍÛ¥K—ŠW’;wî`dd„½½=fff̘1ƒˆˆôôô˜:u*½{÷ÀÎÎŽ-Z¨[¿qãÆº¾ƒ:ubçÎŒ=CCCÜÝÝ™9s&·o߯ÊʪÄ|h4ììì¨V­ï¾û.­ZµB£)Ûf0ñüÍÝéÃÜkˆõ5¬~¯à5µw‹j¸Õ· &)O7G†upB«Qá`i„{‹jòͽ‰ÉÔ­:@pT2›Æ¶G£V•"7Bˆ²PfÁÙ7øá‡Ø·oŸîÇò“O>á³Ï>ÓÕ„††êj êÖ­Kpp0«W¯þÇgúúútîÜ™C‡áææ†——:uÂÀÀ€   u@³fÍÈÉÉ!44ô‰·U­ZµiÕªU‹M›6¹ì Aƒ8tè[·n%$$___€R E133ÃÈÈ@ÐU­ZU7_«ÕêšKò¸¼QµjU]€è«mÛ¶9ˆ!++‹;wîèò™w®qåÊ6nܨ›–™™Y`™²Ê[qóŠ«¥Ë3`À¼¼¼èÑ£­Zµ¢S§N¼òÊ+4lØ333Ö­[G`` ·nÝâæÍ›Ô®][·~þã §§‡V«¥råÊ@n×FSªcÓ¦M4hÀ€¨_¿>:tÀÃÃ}}ý'>_Dùz§KmÚÖÍ ÆS³Øå}›wWæ§1í©^ÅKcÝòULõض&»¼osón"AQIø†ÝÇ©ªitOÜŒbÍá[¬ùULå¼âE*³àìÚµkXYYø!¬_¿>DGGãëë‹“SÁޤõë×çÈ‘#å]OÄÝÝ3f0uêT<ÈçŸäþPªTï,ó:â?$=ºP¨×£øÓÓÓQ« ·Bçää0nÜ8nß¾M¯^½èÛ·/düøñEæ¿4Û.ªö¤¨õJRRÞ´Zm±éfggckkËòåË Í³±±áƺµüëŒ3†N:˜žh–UÞ7¯¤ò­Q£;vìàøñã?~œ+Vðû￳~ýz.\¸À‡~HçÎiÑ¢¯½ök×®}ì±y’ã’?†††¬X±‚ .päÈöìÙï¿þÊÚµk©Y³ækQ~jX™Ð¢feÝg·úÖ¼²ð0»/Üatºæ« ‹NLç­Nbga„[}k:6¬Š_x‡¯=¼±¸—ÊŒ_.3®Wýi !^Œ2 ά¬¬ˆˆˆ 11333‚ƒƒÑjµØÚÚbee…··wu‚‚‚°··/ï2x"­[·F¥R±iÓ&²²²tµ~µjÕ"((ˆ¤¤$]×Õ«WQ«Õ888¨=Ë«JJJÒM{´v-<<œ„„]°ëããC½zõ€‚?Èœ={–­[·R«V-þþûoàa€—ùÒl»¬””·5jI||<üôÓOøúúÒ¯_?îÝ»‡¾¾>666@î À† ˜5kV‘Û«Y³&!!!T¯^]7í›o¾¡Q£F¸»»—YÞÞxãbçMž<ù±åûÛo¿Q¹re]þ^ýuBBBضm;wfÞ¼yÎGGǧ*==½bóqá®]»†§§'ÎÎÎ|øá‡ôë×3gÎHpö§™Y9ää=Jû€OŠ«ßkK^Kåq¿{ºùi™9LÚt—jWa¨<6CˆrQf^zé%ìíí™9s&\¸p… 2`À´Z-¯¾ú*¬X±‚°°0þúë/¶mÛÆ!CÊ» žˆF£¡[·n¬Y³†®]»êúµoßfÍšEPP/^dÁ‚ôêÕK÷žÇÂÂ;;;Ö¬Yƒ¿¿?;wî,ÔÁ>##ƒ¹sçÂÞ½{Ùµkƒ rk‚âãã9zô¨®ÆîôéÓ$&&rþüy–.]  ¸`dd„¿¿?.\(Õ¶ËŠ™™ÙcóÖ²eKêÔ©Ãܹs ÆÛÛ› 6ЦM^zé%j×®ÍôéÓ¹yó&~~~Ì™3‡¬¬¬B5fy†Êîݻپ};wïÞeãÆüúë¯E>oíYòö¸y%•ott4 ,àâÅ‹DDDð×_ann޽½=æææÜºu‹°°0¢££ùæ›o *uò£7nÌŽ;¸zõ*'Ož,P §V«Y¶l{öì!22’£Gó=-rùG$ròf4'oFsèZ$Ó6_$&)ƒnMl‹\¾’±> ©™\‰#!5“ßÏßá·óaddåÖð¹Ë‡„ÔLÞï^—°ØnÇäþ%¦e•÷® ñ¯QfÁ™ßÿ=;B0ïy·œœX¶l§NbРA¬Y³†©S§Ò¹sçò.ƒ'æîîNjj*={öÔMÓjµ,Y²„ŒŒ <==™Í’%K044dÔ¨QØØØ0hÐ † B\\žžžÜ¸qã©ÊÔ¨QXYYñÞ{ï±téÒMÝ-Z´`Ò¤I¬\¹’~ýúñí·ß2mÚ4š5kö\Îñül<ćë½ùp½=çÉЀIDAT73~¹LdB‹<[Ñо辖=šÚòj+{&l<Ï«_aÏ¥;LìÛˆ°Ø’Ó³ùûê]"âÓ¸ô8‹ŽÒÿ›Ü¿]çn—÷® ñ¯¡R^äJE©xyy±`Áöïß_ÞYB!Ä &/>B!„¨@$8B!„¨@¤YS!„¢‘š3!„Bˆ D‚3!„Bˆ D‚3!„Bˆ D‚3!„Bˆ D‚3!„Bˆ D‚3!„Bˆ D‚3!„Bˆ D‚3!„Bˆ D‚3!„Bˆ D[ÚË;¯B!„ÿXNNN¥ZN^ß$„BQH³¦B!D"Á™B!D"Á™B!D"Á™B!D"Á™B!D"Á™B!D"Á™B!D"Á™B!D"Á™B!D"Á™B!D"Á™B!DR¦ÁÙÝ»w™8q";wÆÝݯ¿þš´´4Ýü+W®0|øpÜÜÜ>|8—.]ÒÍ{ýõ×qqq)ô7mÚ´ò.£2±gÏúöí[&ieee±k×®R/——‰‰‰¸¸¸ ÀÀŸ(­‹üeV7ndĈ¥Zö‹/¾`Ö¬Y彋âD|J& ÿ¸N߯Ó~æ~.=ÎÖÓ¡È[“…øçÒ–UBÙÙÙL˜0[[[Ö¬YCBB³gÏ&;;›©S§ÉèÑ£yùå—™={6ÞÞÞŒ7ŽM›6Q£F fΜIjjª.½ððpþóŸÿгgÏò.£ çàÁƒ¬^½R-¿|ùrÒÓÓéÞ½;úúú¼ýöÛXXX”÷nThùËLˆŠ*:1+Nan¤Ç¨îu±­d„Ïíx–îõãN\*ã{×/ï, !žB™g7oÞÄßߟ+VP©R%ÆŒ×_~ÉÔ©Sùí·ß°±±aêÔ©¨ÕjñööfË–-Lž<™fÍšHï½÷Þã•W^¡K—.å]FŽò„·Äù—700`ôèÑå½ Þ“–±åá›=~˜êñß÷Ûb¨§ÀÅÉksCfm¿Â€65p°4*ïl !žP™gÕ«WgõêÕºÀ @¥Ré~äîܹCãÆQ«¶¤Ö©S‡Ã‡JëÀ\»v/¿ü²¼Ë§€iÓ¦¡Ñh˜;w®nÚW_}E||<óçÏ'""‚o¾ù†sçΡV«éÕ«}ôÒ‰ŒŒ¤OŸ>üþûïT«V Èm;pàëÖ­ÃËË‹5kÖü?{wUÕ?püÃ0l ˆ ²‰lî+**•úk3s#MÅGsrÉRyÜR) ) Ë\C1W$qI\QÙDAÖ~£#¨ØcAõ}¿^óÒ™{î¹ç|ï0óåœs/´mÛ–èèh >|8¾¾¾œ¾nŽ íÒ¨ºO˜ç¶æÌØØÍó²²26oÞŒ««+uëÖåÖ­[Zûܼy“ììì u­_¿žW_}ssóꎎ9Bqq1¥¥¥DFFâîîNaa!£G¦¨¨ˆõë׳hÑ"Ž=ʲeË~×±âââØ¶mëׯgÓ¦Mìܹ“:0wî\¬¬¬8zô(–––Ìž=›¬¬,Ö­[ÇæÍ›qrrÒ$o¿ý6ÞÞÞ 8ÐÐÐJŠ›6mÂÐÐ &TiähÛ¶mœ>}š•+WR!q}´lHHóæÍ#88˜_~ù…ÄÄÄ*ÇãI}˜2e …‚7²páBNœ8A@@€f{ll,úúú,Y²„.]ºÌîÝ»™?>aaa¼þúë,[¶Œäää§Æ¬°°‰'bmmÍ7ß|Ã!Cزe‹fûÕ«W3f ;wfÓ¦MŒ=š+VTº†­*eøáÌŒ3prrzb_¯^½ÊòåËù÷¿ÿͶmÛèÙ³'Ó¦MÓZ2 þÒïQ\RF;³ Ût:ôke‰±¡’¼¢Æ¤¹)›'¸ñÅ;]ÈÊ-dÝþ«Zû„ÿrƒà±Ýùv‚?_Lcì—Ñ|äÕ‚]ÓúR¨.!èçk½’β]çÑŒoǻѧ¥%c¾ŒæÖë‹C£®3û5“· ¤´ŒÑëOP¤.eý»]Xô† G/§³l×%®¦æ0&0šÎÎõÙ4®£û5fÅžKDœKÕÔ·ýd½Z4`ÿÌ~Œìåħ?^$ýnauŸ!þ0ØÕšÄÅÅ1aÂú÷ïOLL Û¶m£  €ãdzÿ~ÔjµÖ~§OŸæÚµk¼þúëÕ› ºwïÀ‰'8u긹¹qøða²²²X°`7¦S§NL›6°°0rssŸùXŒ?…B­­-¯½öaaa( ”J¥¦ @·nÝð÷÷§qãÆ8880|øpRRR(((@©T¢P(ÐÕÕEOO¯Òcy{{ãåå…³³3³fÍâúõëÄÄÄ<µÉÉÉakk‹½½=3gÎdìØ±•– gøðátíÚ•† 2{ölJKK«'õ1&&†¸¸8æÍ›‡³³3mÛ¶Åßߟ­[·j%Ò~~~tîÜøøãiß¾=¶¶¶Œ1}}}ž³ãÇs÷î]þýïãàà IäîÛºu+­Zµb̘1888àååÅë¯¿ÎÆ+ÔU•²NNNøøøÐ·o_Ξ=ûľ&''£««‹µµ5666¼ûî»,Y²]]]ÄßKn~ù{ÛÄHï‰åò‹Jðus`œ{3Ö3¢µ]<]lˆOÓþ\ÕÛ³Zz4ª_ gKcú¶²¢¥mLôèÕ¼¦üׇT ïáÈ€6VØÕ¯ÅˆžŽ´kdFø/74u t±Á­™Ýš˜søbY÷ŠX0¬­Lèä\Ÿi/¶",ú:¹j¶Fß UC3Æ h‚ƒym¼:Øòz7{6Ž×Ô×ÜÆ”!l1P*x¹“JSîT÷)âóܦ5¶bÅ BCCY¾|9•=·nÝš>úˆ¥K—²páBðññá§Ÿ~ÒÚw×®]tìØQ3V“èëëÓ§O"##qss#""‚Þ½{c``€J¥ÂÁÁcccMù¶mÛRZZÊõëןùX666Zu9::RiY"## %11‘óçÏT9ùiÓ¦æÿõêÕÃÊÊŠ„„ìì잸ßСC‰ˆˆ`À€tèÐÞ½{3dÈJË&$$ЬكÅÉVVVÔ¯_¿ÊñxRU*U¥‹÷Õj5ÉÉɘ˜˜`jjªÙÖ·o_Μ9ÃêÕ«IHHàòåËV)f*•ŠFadô`-O«V­P©Tš¾>:ÍÙ¦M¾ûî»Jãò´²O?­¯]ºt¡yóæ :”f͚ѳgO¼½½5Sžâï£N­òsz'¯H3}Y™úÆú ëjÏö“7¸r3Uz.ç“îàÔÀX«\ƒ:†šÿëë*°|蹞®‚buùφ*=—_¯ß&ø°J³½¸¤ÓZÞc ëÕÒü_•ž‹ƒEmŒ |Ý´mdFi\ϼGBz.­ýkÓ¨.ßE=øÜ´6Ó^7g¨¯Kq‰¬ _Ï59+++cÑ¢EìÞ½›€€:tè µÝÇLJW^y…ôôtllløòË/±¶¶Ö*sôèQFŒQÝqy,OOOfΜ‰¿¿?`Ö¬Yèéé¡££S!P1Iz´”_íZÙ¾÷j­×»¯´´”ñãÇsãÆ <<<ðòòbذaøùùU¹OŽÝ1zZ;5jDXXGŽáÈ‘#¬]»–~ø7jF÷î344¬ÐLJË<éXOëcII VVV¬Y³¦B–––\¾|CCC­×7lØÀƆµ™nÍ,èÕ¢—Rîrð‚ö2¥Bû}¨ó˜ã–””1Ö½½[4ÐzÝHÿÁè¬áCÿ×S*Ðy¤¶ûŸn¥¥å‰_…²2JJ|*u+¶F.ÚgÏuZsíÚµìÝ»—Õ«WWHÌŽ;Æœ9sÐ××ÇÖÖ>¬Y“ššJZZšÖk5M§NÐÑÑ!$$µZM×®]ò‘-•J¥5…yîÜ9 E…ò÷“¡‡Ë>:º–’’ÂÝ»w5ÏccciÚ´) ÈÄÇÇM@@ãÆÃÝÝ]³¾èþ‡We_þ{øØ¹¹¹Ü¼yGGǧ¶óûï¿'::šþýû3g΂‚‚¸|ùr¥kÉ5£]·oß&==]ó\©Tj'55U3%ù´>ÚÛÛ“––†¾¾>vvvØÙÙ‘““êU«;öí·ß2qâDüýýñööÆÔÔ”¼¼¼*ÅÌÙÙ™ÄÄD­ö^¹rE«¯çÎÓÚçܹs•&GÏRxj_O:EHH;vdÒ¤I„……add¤™ŠJ}[Y²éˆŠ‚bíÄ×édÎ$Þ¦aýZìM¥¬ 6¼×•·ú8ãÖÌ‚Œ»¿ó¨`oQ›ÄŒ{ØÕ¯¥y„ž¸ÎÉø¬JË;Z£JÏ%·àÁ–s׳Qè@Ãúµpl`̹ëÙZûœ»Q1áâŸä¹%gqqq|ùå—xyyq÷î]Ž;¦y”””ШQ#öíÛGxx8III|úé§$''óòË/kÕ¡T*qtt¬î¸<–®®.ýúõ#00^xA3bÒ£G,--™3g*•ŠÓ§O³dÉ<<<*ÜSÌÌÌ kkk‰‹‹#<<¼ÂU«EEE,X°€ÄÄDöìÙÃöíÛñññÀÈȈììl:¤y‰ŠŠ"''‡˜˜V¬X InŒŒŒˆ‹‹ãÔ©S•ö)44”¤¤$Ôj5+V¬ E‹´mÛö©íÌÈÈ`É’%œ>}šÔÔTvïÞ©©)¶¶¶Ž1bÄ6oÞLDD‰‰‰,X°@kt¨U«VDEEqðàAbcc™7ožfÄÈÄÄä‰}ìܹ3ÎÎÎ̘1ƒ+W®péÒ%æÍ›‡Z­®0bvŸ©©)§OŸ&;;•JÅÌ™35qZ̺u놅……æüìÛ·;vh¶6ŒØØXÖ­[GRR»wïfË–-•®£|–²ÀSûªP(X¹r%»víâÖ­[:tˆÌÌLš7oþÇü@ˆj5νwòŠyû‹(vŸI!úZ&k~Šã“ïÏ3²—#浩SKŸ»ùÅœM¼ÍÝüb~ˆIæû˜$ŠÔU_óù°7ÝÙ“ĶèÜÌÎ'ø°ŠïŽ'ÒȼV¥å{4³À²Ž!s¶þŠ*ý§n³dÇ<ÚÙ`VKa]í‰MÊfÝ«$eå³ûL [Ž_çõn2Ò+þ¹ž[rvàÀÍš&LÐzÒ°aC>ù䂃ƒñññáÚµk¬Y³FëÖ˜››×øÅËžžžäççkÝ W©T²|ùrŠŠŠðõõeêÔ©ôéÓGó¥ÿ0fÏžM||<#FŒ`ÿþýî=V·n]ìììðõõeÕªUL›6^½zо}{œœœð÷÷'??ŸéÓ§ÌÀYºt)cÇŽÅÈȈK—.iÚ›™™ÉÔ©S+íÏ Aƒð÷÷ÇÝÝ´´4-ZT¥vŽ9’Þ½{ãïïÏË/¿LTTË—/¯4!êÕ«ãÇgéÒ¥Œ1‚V­Zi•óôôÄÓÓ“?þ˜>úˆþýûkÖ¼YZZ>± …‚Ï?ÿÞzë-ÆŽKóæÍŸxþÙ³gsíÚ5 Äĉiß¾=:uâòåËO™žžܹs‡ádzqãFÞxã Íö† ²|ùr<ÈСCY·n“'O®ô¦ÁÏRxj_]\\˜2e ë֭㥗^" €éÓ§W¸ ø{°23äËÑ]ileŠ=—ùðë^¼ÅäÁ-ë^¾Æs@+^ì`ˤà^üôgvIf²WK’²ò¸WXòÌÇì×ʒɃ[°ñP<¯|~ˆ§“Y<¼=-mëTZ^©ÐaùˆŽ•”â»ò(S¿9MŸ– ˜ùrùZˆõŒXþ¯Ž¼p‹¡Ë±îÀU&nŽ·kÃgi–+:e2q_ãDDD°dÉöíÛWÝMùC=|3!„B”“?|.„BQƒHr&„BQƒÈ´¦B!D "#gB!„5ˆ$gB!„5ˆ$gB!„5ˆ$gB!„5ˆ$gB!„5ˆ$gB!„5ˆ$gB!„5ˆ$gB!„5ˆ$gB!„5ˆ²ªããã«»­B!„YNNNU*'¾I!„¢‘iM!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„Bˆä¹&g™™™|ôÑGôêÕ‹—^z‰üQkûÒ¥KquuÕz|ñÅší;wîÄÛÛ777¦L™BVVVuÇç¹Ùµk^^^Ï¥.µZÍöíÛ«\>;;›ˆˆrrrpuu%!!€aÆ=S]¿×Ò¥K™>}z…X<ϸü•=|Ž„¨ªn³öâ:cæÑw~ÿÞr–Û÷Šþã¼p ÷…¸u§×{H¹_ÝaâoGù<+›1cJ¥’7ǬY³hذ!...¨T*†Jß¾}5ûØÚÚpêÔ),XÀœ9shÖ¬K—.eÖ¬Y¬\¹²ºcTã8p€ 6àíí]¥òkÖ¬¡°°þýû£¯¯Ï[o½…™™ÙŸÚæ®]»’Ÿ/âóð9âYŒêåD'çú”••‘žSH`äUæn;ÇòŸû±ì-ŒÞẻ,ÄßÞsKÎ’’’8yò$Û·o§aÆ8::²gÏöîÝ«IÎâãã6l]ºt©°xx8îîîxxx0nÜ8|}}¹}û6uëÖ­î8Õ(eee¿»¼|ðÁŸÞf77·?ý˜%ÏzN…¸ÏÉÒ˜.ëkžë+ÌØr–¼¢jéë>×c9ZÔÆÑ©º»,ÄßÞsKÎlll ¥aÆš×JKK)--àÞ½{¤§§coo_éþS¦L¡°°Pk߇ÿ­ ¦OŸŽ®®. ,м¶páB²³³Y¼x1©©©|þùçüòË/( <<<˜8q"Zõܺu‹ÁƒóÃ?`cc@pp0û÷ï'((ˆˆˆiÛ¶-ÑÑÑ2|øp|}}9yò$3fÌÀÕÕ•;w¢T*Y¶lÇŽ#//&MšDçÎùâ‹/ àÌ™3Ó·o_¶nÝŠƒƒP>¢éëë˵k×h×®sæÌÁÊÊê©íT«Õ,]º”ˆˆ pqqÁßß_ë=pßÒ¥KÉÌÌdáÂ…OŒqUcåÓ»k×®eÏž=¤§§cnnΛo¾ÉðáÃHKKcþüùœ={===úöíËG}„¡¡!‹/æîݻܽ{—„„ŒŒŒ˜4i]»v ¸¸˜•+W²sçNJJJèÔ©S§NÅÜÜœŒŒ <==ñóó#00Þ½{ãææFpp0®®®|ÿý÷èèèð /0eÊ”JåÛúè9úâ‹/ž÷ÊŽ?gΜêþñ5Dm%:: óÛóW—f@+¾I¢¶’Íܸœr—ÿî½Lì;”••Ñξ.¿Òk3#ü¾ŽáÈåô õŽîÊíÜB>ùþ<û¦¿PÝÝâoí¹­9S(8::PXXÈ®]»8qâƒÊ€¯¿þšþýûãííMHHˆfÄÀÔÔ  |nÅŠôèуúõëÿŽÖü1<<<8räÅÅÅ@y≻»;………Œ=š¢¢"Ö¯_Ï¢E‹8zô(Ë–-û]ÇŠ‹‹`Û¶m¬_¿žM›6±sçN:tèÀܹs±²²âèÑ£XZZ2{öl²²²X·n›7oÆÉÉI“@¾ýöÛx{{3pà@BCC+=Vhh(>>>lÚ´ CCC&L˜P¥‘œmÛ¶qúôiV®\IHHH…ÄõY=k ƒƒƒÙ½{7óçÏ',,Œ×_eË–‘œœ ”'„&&&|óÍ7¬X±‚'N¬ÙïÞ½ <˜;v0qâD&MšDRRP>Íxüøq>ýôSÑ××gâĉZ¿,DDD°téR† Àùóç¹{÷.;vì`Ù²eìÞ½›ƒ>µ­U9G•yôøâŸ©H]J^Q ÷ K¸‘™Çæã‰ hcÑC£f¡Q×™áݚɃ[P¤.e|ÐIšÛ˜²y‚_¼Ó…¬ÜBÖí¿ ÀB"?îOäÇý90³®NõèèXÖ ëTwW…øÇøC®ÖôòòbÖ¬Y¸ººâäT>~º +V¬àõ×_gÕªUlÛ¶Mkß­[·âííÍÙ³gyå•Wª;>ZºwïÀ‰'€òur¸¹¹qøða²²²X°`7¦S§NL›6°°0rssŸùXŒ?…B­­-¯½öaaa( ”J¥¦ @·nÝð÷÷§qãÆ8880|øpRRR(((@©T¢P(ÐÕÕEOO¯Òcy{{ãåå…³³3³fÍâúõëÄÄÄ<µÉÉÉakk‹½½=3gÎdìØ±¿;¾ÏC>þøcÚ·o­­-#FŒ@___ó^KNNÆÌÌ +++Z·nͲeËpww×ìß®];<==èÑ£...üðñyóf¦NJûöíqrrböìÙܸqƒS§Niö9r$;v¤uëÖš×üüü022¢uëÖtìØ‘sçÎ=µ­U9G•©ìøâŸgAx,½æþDïy?ñòç‡ø5ñ6£ziO=t±Á­™Ýš˜“_T‚¯›ãܛѰž­íêàébC|ZùϘ‘¾.&†JL •l9žHBú=>ñqAW¡ó{š'„øžë÷…‡‡sçÎf͚Ň~Èúõëñôô¤k×®˜››ЪU+ÒÓÓ ÕúÍðàÁ <˜ˆˆ¦NJ@@@¥kÔªƒ¾¾>}úô!22777"""èÝ»7¨T*066Ö”oÛ¶-¥¥¥\¿~ý™ecc£U—££#!!!•–õññ!22’ÐÐP9þ­­¿GeÓÇâŸç¾ÎtmRþ¹š“¯fûɼ»>ŠMc{`W¿ ëÕÒ”¯o¬Ï°®öl?yƒ+7sP¥çr>éN Œµê=z%Àƒ×XûvgêëWw7…øGùCFÎŒ±µµeüøñœ>}šŒŒ ”J¥&1»ÏÉɉŒŒ ­×ŒŒŒ022bÈ!tèСÆÝ^ÀÓÓ“Ÿþ™ââb8 ¹€AOOíß,ïO >úüh9€’’’J÷½¯°°…¢âé*--eüøñ¬X±¼¼¼˜;wî3õéÑÑšû#8Okg£F cÁ‚4hЀµk×òî»ï¢V«WlŸ%†6l`üøñܽ{—îÝ»³|ùrjÕzð%Ô§O~üñGÆG~~>3fÌ`ñâÅ=~aa!ºººšö¯\¹’o¿ýVó¸ÑÊ}†††ZûßѬ¬ýOkëêòþ¨ìøâŸ©‘ym\ìëâb_—žÍ-XúfLŒôØq*YSÆð¡)ÎŒœB^[q˜]gR°23dd/'Þêã¬Ugòí|fn9Ëxf¸ØËYBüÙž[rvòäI<==µõçää P(066fíÚµŒ=ZkŸË—/kF!ÆOPPÖöœœêÔ©Yë:uꄎŽ!!!¨ÕjÍòû£.O¿;w…BQa„ã~2ôpÙGG×RRR¸{÷®æyll,M›6´¿¼ãã㉎Ž& €qãÆáîeÅýÄ ²/û‡=|ìÜÜ\nÞ¼‰££ãSÛùý÷ßMÿþý™3gAAA\¾|™ÄÄÄßÛg‰!À·ß~Ëĉñ÷÷ÇÛÛSSSòòò(++£¬¬Œ•+W’““ÃСCY¾|9Ó¦Mã§Ÿ~ÒìñâE­úbcciÒ¤ 6DWW—ÔÔTììì°³³Ã‚€€RRR~WßžÔÖGÏQUÞBθ5³ ãnf{Aq)SBNÑÙ¹>oÊm3„¨Ï-9kÙ²%ºººüç?ÿ!11‘èèh>ûì3† ‚¡¡!={öäÌ™3‘””Dxx8[·neĈ@ùz®   Ž9Âõë×  !!¡ÆÝ TWW—~ýúÈ /¼ -éÑ£–––Ì™3•JÅéÓ§Y²d î)fff†µµ5ÄÅÅ®Y8~_QQ , 11‘={ö°}ûv|||€òÑÅììl:¤mŠŠŠ"''‡˜˜V¬X ™R322"..Nk½ÔÃBCCIJJB­V³bÅ Z´hAÛ¶mŸÚÎŒŒ –,YÂéÓ§IMMe÷îݘššjî]÷¬ž%†P>xúôi²³³Q©TÌœ9S;.\¸À’%Kˆçúõë:tˆæÍ›kö?þ<$%%±jÕ*’““yñÅ144äÕW_eÅŠDEE‘””ļyó8þ<5ú]}{R[=GUyq_\jÇ®dpìJ‘n1ýÛÓdæѯµU¥åëÔÒçn~1gos7¿˜b’ù>&‰"uùèô'Ûc¹›_ÌèþMHÊÊãFfù#§à÷ˆ !žÝsKÎjÕªÅÊ•+ÉÊÊâ_ÿúóæÍ£ÿþL:(_c¶dÉöîÝ˰aÃøú믙1c½{÷Ê×M5ŠÅ‹óæ›orîÜ9Ö®]«Y«I<==ÉÏÏךâR*•,_¾œ¢¢"|}}™:u*}úôÑ| ?LGG‡Ù³gψ#Ø¿…{Õ­[;;;|}}YµjÓ¦M£W¯^šEêþþþäçç3}út‚ƒƒ8p K—.eìØ±qéÒ%M{3335çâQƒ ÂßßwwwÒÒÒX´hQ•Ú9räHz÷î¿¿?/¿ü2QQQ,_¾üwO·=K fϞ͵k×4h'N¤}ûötêԉ˗/k¶ðÖ[oñæ›o¢T*µn9ѵkWÎ;ǰaÃˆŠŠbõêÕš+†'L˜@ß¾}™9s&>>>ܾ}›•+Wbddô»úö´¶>|Žªòþâ¾àÃ*&l<É„'™¹å,·îð™oZØšVZ~@+^ì`ˤà^üôgvIf²WK’²ò¸WXÂOçn’š]À°Gðþì/^þØþËêîªÿ:er÷Ë'""‚%K–°oß¾ênÊßÖâÅ‹ÉÎÎ~ê}ׄBˆ?›üás!„BˆD’3!„BˆD¦5…B!j9B!„¨A$9B!„¨A$9B!„¨A$9B!„¨A$9B!„¨A$9B!„¨A$9B!„¨A$9B!„¨A$9B!„¨A”U-_ÝmB!„øËrrrªR9ùóMB!„5ˆLk !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ HrVPPÀ+¯¼Â¹sç´^‰‰aÔ¨Q¸¹¹áííÍÖ­[5Û^}õU\]]+<¦OŸ^Ý1z.víÚ…——×s©K­V³}ûö*—ÏÎÎ&""€œœ\]]IHH`ذaÏT×óäææÆ±cÇXºti•Îõ³ö]ˆ¿³n³öâ:cæÑw~ÿÞr–Û÷Šþðcç¨q±‡„ô{Õ.$ßÁuÆ Õ¥•n÷úô Á‡U^ßûëM:ÏÜSÝÍ®^¸…ûÂÕÝ ñ?R>ï ð÷÷çúõëZ¯§¤¤0qâD|}}™?>.\`Þ¼y˜™™Ñ¿fÏžM~~¾Vùÿüç?¸»»WwŒjœ°aü½½«T~Íš5Ò¿ôõõyë­·033«înhéÚµ«Öù^}âïnT/':9×§¬¬ŒôœB#¯2wÛ9–èø‡W_©à­>ΘÕ֯Ø[3¼‡Cu7CüžkrÇÌ™3Q(*È8p€† òþûï`ggÇéÓ§Ù³gýû÷§mÛ¶Zåß{ï=† Bß¾}«;F5NYYÙï.o``À|PÝ]¨ÀÍÍíé»wN–Æti\_ó\_©`Æ–³ä•PK_÷;®RÁšTw÷Å#-jãháTÝÍÿ£çšœÅÄÄгgOþïÿþÞ½{km0`]ºt©°OiiÅaèýû÷sáÂ>ùä“ꎖéÓ§£««Ë‚ 4¯-\¸ììl/^Ljj*Ÿþ9¿üò …&NœˆV=·nÝbðàÁüðÃØØØÌþýû """‚ÀÀ@Ú¶mKtt4……… >___Nž<ÉŒ3puueçÎ(•J–-[ƱcÇÈËËÃÁÁI“&ѹsg¾øâ ÂÂÂ8sæ ÁÁÁôíÛ—­[·âàà€J¥Â××—k׮Ѯ];æÌ™ƒ••ÕSÛ©V«Yºt)àââ‚¿¿? 6¬»²²26lØÀwß}GII £GÖÚ¾téR233Y¸p!ááálܸ‘ŒŒ š7oŽ¿¿?ÙÙÙú^¿~}Ö®]Ëž={HOOÇÜÜœ7ß|“áÇ?óùKOOgñâÅDEEQ§NÜÝÝ3f úúúìÝ»—-[¶`ccÑ#G˜8q"/¿ü2Û·o'88˜›7oâääć~HÇŽ숅ORÛ@‰Žèüö¼ ¸”µqìý5…œüb:9×gÚVX™°5úŽ'#§ c&x6£[s²óŠéÿŸý|üJk#¯‘}¯ˆvöu™õJ,L È)PÓw~[ýzâ`Q›óIwøïÞËÄÞ¸CYYíìëòñ+­±63ª´»Î¤°ñP<‰÷¨¥¯¤oKKü_j…ž®‹w\ X]ʼbŽÇe`n¢¯›#C»4 ·PÍÂíç9t)zµõÖÍþ¹Ä.ýn!‹w\ *.:µôqok͘þMÐW*ØûëM¶OĦ®G.¥3q`3>Ûy‰‚â­:” ¢æ{°ýd_¼Ffn!M­M™4¨­íêp¯°„…ßÇrèb&Fz¼×¯1óÃbÙãßsRïðù΋ür-…Ží¬™8°9zº -ŽäýþMðv}ðY;tùa†uµ§©Ÿ|ž}Ó_xê9|ÚyÕç¹®9óññaܸq’KKKš4yð[Vvv6{öì¡S§NÊ®_¿žW_}ssóꎎ9Bqq1PžXFFFâîîNaa!£G¦¨¨ˆõë׳hÑ"Ž=ʲeË~×±âââØ¶mëׯgÓ¦Mìܹ“:0wî\¬¬¬8zô(–––Ìž=›¬¬,Ö­[ÇæÍ›qrrÒ$ o¿ý6ÞÞÞ 8ÐÐÐJŠ›6mÂÐÐ &Ti„jÛ¶mœ>}š•+WR!ñyØÖ­[ùæ›oøøãùâ‹/8pà•–Ý·oK—.åwÞáÛo¿ÅÁÁ???Ú¶m[¡ïÁÁÁìÞ½›ùóçÆë¯¿Î²eËHNN~¦ó0eÊ 7ndáÂ…œ8q‚€€Íþ±±±èëë³dɺtéBxx8Ÿ}öo¿ý6ß|ó ]ºta„ ¤¦¦>ÿ7ŸQ¤.%¯¨„{…%ÜÈÌcóñD´±Æè·Q³áç8tñó_kÇWïwC]R†_p %¥e\MÍaù®KüÛ»Û>ìEÏæ ˜öÍiò‹$á¿Ü`ý»]›Ô‹ÌÜB^«Ð†¼¢Æ¤¹)›'¸ñÅ;]ÈÊ-dÝþ«•¶ù×ëÙÌÛvŽQ½œŸÔ‹¹CÛ²ïÜMöþš¢)³ýd½Z4`ÿÌ~Œìåħ?^$ýnayŸÂbQ¥ç²î.Ìx¹5!GTg®ÞÊåzÆ=úµ¶ª´ï;‡U9¢zTËÕšL™2^}õU­m§OŸæÚµk¼þúëÕ› ºwïÀ‰'8u긹¹qøða²²²X°`7¦S§NL›6°°0rssŸùXŒ?…B­­-¯½öaaa( ”J¥¦ @·nÝð÷÷§qãÆ8880|øpRRR(((@©T¢P(ÐÕÕEOO¯Òcy{{ãåå…³³3³fÍâúõëÄÄÄ<µÉÉÉakk‹½½=3gÎdìØ±•– çÍ7ߤW¯^4iÒ„Ù³g?¶ÞmÛ¶ñòË/ãåå…}ô}ûöåÞ½{úîààÀÇLûöí±µµeĈèëëk.x¨êù‹‰‰!..ŽyóæáììLÛ¶mñ÷÷gëÖ­ZÉœŸŸ;wÆÆÆ†o¾ù†7ß|“AƒáààÀøñãiÒ¤ ›7o~žo;!žhAx,½æþDïy?ñòç‡ø5ñ6£~K²r‹Øsö&Ó_j…«S=šX™°àõv$eÞãx\É·óÑU€u]#lêñî YòftFMÞîÓË:†˜›0ÈÅ–ØÙÚ_T‚¯›ãܛѰž­íêàébC|ZåŸ}zº ü_jÅ@¬ÍŒèÙÜûºÄßzP¾¹)C:Øb Tðr'; ” .¦ÜáN~1ûϧ2Å«%-lMéì\ŸÜ›>5N+÷]¡Çì}ZÙ[ÕlQe—šÃ¼×ÚáliLÛFfø¿ØŠ­'®Sü[WZ~›ÓÙ¹>6u06Tbb¨¤¶’…ߟ§±• £û—B|}HÅðŽ hc…]ýZŒèéH»Ff„ÿrƒì¼b~:W~^ZÚÖÁÕ©~ƒškÚrøbY÷ŠX0¬­LÊG;_lEXôur Ôx¶µá—k™ä¨ˆ8wW§úÔ7®|ýßãÎaU΃¨Ïý‚€§¹wï“'O&==õë×£¯¯ýfÚµk;vÔL£Õ$úúúôéÓ‡ÈÈHÜÜ܈ˆˆ wïÞ R©pppÀØØXS¾mÛ¶”––V¸8¢*lll´êrtt$$$¤Ò²>>>DFFJbb"çÏŸ*Ÿ2®L›6m4ÿ¯W¯VVV$$$`gg÷Äý†JDD  C‡ôîÝ›!C†TZ6>>^³Þð~ÿêÖ­ûز×chhÈäÉ“+-Û·o_Μ9ÃêÕ«IHHàòåËVÚ÷§¿ûM-—k·r¸’šƒ³åƒòN‡–»ŒÄô{”•ASkSͶV Íxš×»ÙãÙÎZëµW3Y·¿|–B•~Bu)ýÿ³ÿ¡e¨KËH¾€‰¡S£Š¿ä®Þw…„ô{„ŒëÁý¼V•žË¯×ok]%Z\RŠi-}®¦æPVÍmêh¶µ}è©Òsq°¨±áƒ¯è¶Ì(-ƒë™÷hmW‡u 9t)A.6DĦâëæøØ¾?îVå<ˆêñ§&g¹¹¹Œ7ŽÜÜ\Ö¯_……E…2GeĈÕ—ÇòôôdæÌ™øûûsàÀfÍš€žž::Úsô÷§M-PRRRé¾÷Vz¡Eii)ãÇçÆxxxàååŰaÃðóó«rŸQ»?Êö´v6jÔˆ°°0Ž9‘#GX»v-?üð7nÔŒp=©ÏÉ{ܱ+³aÃ6nÜÈàÁƒéÞ½;cÆŒaäÈ‘Ï|þJJJ°²²bÍš5ö±´´äòåËj½^Ùô}YYY…s)Ä©‘ym\ìü¢ãÖÌ‚!K²ãT2½š7¨tŸ²2()+ÃPOÁÚ·;s*!‹Ÿ/¦±ëL ßE]ç«÷»R§Vù/ÎJ]E…}•‘SÈ¿VÃÚÌ·fôjÑ€K)w9xáV¥Ç¾–É„'éÓÒ—Fuy¥³_Œ×*£¬dÍÓß‹e<øUÖGYÕ1¤ÍC @ÊíWˆ—””befÈš·:WØ×²Ž!—SîbXÉ‘nrTÅúw»bVKï¡úÊëÞŒÞ-´Ï‘¾.IYyõGç·þ< §T Ã#ß'¿ý{ÿ뼭5bSifmJRf/´²|lßw«rDõøÓ¦5KJJ˜Mjj*»wïÆÔÔ[[Û ÇðññáÛo¿%22’„„æÏŸÿØÊÇLJ°°0"""HIIaéÒ¥(•Jš5k¦Õ÷‚‚LMM9}ú4ÙÙÙ¨T*fΜ©‰ß³œ¿Î;ãììÌŒ3¸rå —.]bÞ¼y¨Õê #f÷ùúúòÍ7ß°wï^’’’X½z5—/_æå—_®–÷¦øgŠKÍáØ• Ž]É òÂ-¦{šÌÜ"úµ¶ÂÔH—:6dñ8“x›ø´\f‡þŠ…©!ÝšZ Ð•{/³ëL ·îpèb™¹…4·1}¦6Ô©¥ÏÝübÎ&Þæn~1?Ä$ó}LE¹)¬©‘×RsHÊÊ##§Ïw]B•~¢’§/Å05ÒcP{[>ýñ"’ïpîF6«ö^~ê~OÓÙ¹>Ζ&ÌØr–+7s¸”r—ya±¨Õ¥êUüªÌ+*á£M§hïP·fÜÈÌÓ<ŠÔ¥¼éæÈŽ˜$¶Eßàfv>Á‡U|w<‘F浨[»üJÐ%;.p!ùgo³l×% ü¿Í,°¬cÈœ­¿¢J¿Çé„Û,Ùqv6šÑ¹&V&X›r4¶ÖÏÔ×çqÄëO›ÖŒˆˆ ¬¬Œÿüç?Z¯7oÞœM›6å_öæææèêþq÷æy<==Ùºu«Ö r•J%Ë—/gÉ’%øúúR«V-̘1c*쯣£ÃìÙ³Y¼x1#FŒ cÇŽ|ðÁüøãš2uëÖÅÎÎ___ÌÌ̘6m½zõ }ûö899áïïÏ—_~ÉôéÓÙ°a«V­ÂÎÎŽ±cÇòÉ'ŸpéÒ%ºv튧§'{öìaêÔ©„‡‡WhÏ Aƒð÷÷'%%…6mÚ°hÑ¢*µsäÈ‘deeáïïÏÝ»wi֬˗/¯4™ñòòâÎ;,^¼˜üü||||»6«OŸ>Œ;–åË—“M›6mX¾|9zzzú>{öl-ZÄ Aƒ077×ü†Ë—/WX?ö¤ó§P(øüóÏY²d o½õôìÙ“>úè±ïƒ’••Åÿû_233iÖ¬«W¯ÆÙÙ¹zÞ˜â)ø°J³®É@© ‰µ Ÿùv …my‚5yp –íºÄ‡_Ç .-£kcs־ݥûºLÒ’uû¯’z'«:FL©m™‘W\å6 hcÅ™„,&Ç”¯¥²5e²WK>ßy‘{…%Ô6ÐþL¿æl;‡OÀj(éÒØ_7~½ž]¥ãù¿ØŠ¥?^`L`4µ ”ŒèåÄÒ/þOqTèÀçÿêÀ’xë‹( ” z¶hÀGƒ[TZþÚ­Òï‘~#—ÖÚöõÝè×Ê’¬Á-Øx(ž¥?^ÀÞ¼6‹‡·§¥mùÛÔ!-ùdûyÞ]w³Úú¼ÜÉŽµqèéê Tè°|DG–츀ïÊ£Ô2P2¸½ ch_øàÑΆ¯^£wËÇOi>ÉÿzÄG§LîêYãDDD°dÉöíÛWÝMBñœ••ÁÁ‹·èÖÄC½òÄõd|7žäÈwª¸äVüýéWk !„ÿd::°b÷%N\µÀ×Í»ùŬÜ{™Z[Ib&I΄Bˆ?Ý¢7Ú³ôÇ‹øÅPOAßVVø lþ¿W,þdZS!„¢©–¿ „B!*'É™B!D "É™B!D "É™B!D "É™B!D "É™B!D "É™B!D "É™B!D "É™B!D Rå?ß_ÝmB!„øËrrrªR9ùóMB!„5ˆLk !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ Ï59ËÌÌä£>¢W¯^¼ôÒKüøãZÛÏŸ?ÏÈ‘#éÑ£#GŽäÂ… ZÛ}||puuÕzœ}:«V­Ò<š6mZÝ1ªq8À† ª\~Íš59rä¹·#33“iÓ¦QPPð§ô{úôé\¿~ýO9–£z9±êÿ:±r”+“·àBR6s·«îf !þÊçUQRR'Oždûöí4lØGGGöìÙÃÞ½{qqqáàÁƒ1vìXtttøàƒغu+QQQôïߟäädŠŠŠðððÀØØ¸ºãR£•••ý¡å«»Þšr|8¾¾¾œÓ†´ÂÊÌ€­Ñ7Øøs<98X3Á³Ýš˜WwX…øS=·iM…B££#………ìÚµ‹'N0xð` 4_ðwîÜá³Ï>£V­ZtïÞ(OÎ ™0a}úôÁ××—¨¨¨êŽŽ9Bqq1Pž|FFFâîîNaa!£G¦¨¨ˆõë׳hÑ"Ž=ʲeË~×±âââØ¶mëׯgÓ¦Mìܹ“:0wî\¬¬¬8zô(–––Ìž=›¬¬,Ö­[ÇæÍ›qrrÒ$o¿ý6ÞÞÞ 8ÐÐP¦L™‚B¡`ãÆ,\¸'N 9ÞéÓ§Y¹r%!!!’Ñ‡íØ±€­[·âéé©éï¸qãøöÛoéÓ§cÆŒáÖ­[š}¶oßN¯^½Ø¿?#GŽäÓO?%==(Ÿ~=~ü8Ÿ~ú)èëë3qâDJKKYµj5bÆŒLŸ>½J1ŒE__Ÿ%K–Ð¥K— ÛŸ·ÂÂB&Nœˆµµ5ß|ó C† aË–-â»Mˆª)R—’WT½Ândæ±ùx"ÚXcôÛ¨Ùìгdå±îÝ.lžà†“¥ ÂcµêˆˆMe©o†vi@úÝBÎÝÈfÃè®LÜ‚•{/q.€àÃ*vŸIaþ°v„MêÅëÝìY¶ûÉ·óX‹*=—uïtaÆË­ 9¢Ò:Ö‚ðsºx‹ù¯µã«÷»¡.)Ã/8†’Ò2®¦æ°|×%þíÝŠmö¢góLûæ4ùE%Õf!þTÏmäìa^^^ܾ}›nݺáä䤵M¥RñÚk¯ðÿ÷Ô®][óú;w4hï¼ó»wïÆÏÏàà`š4iRÝqÐ$’'NœÀÍÍS§NQPP€››‡&++‹Íèß´iÓðóócܸqÏ|,ÆB¡ÀÖÖ–×^{°°0ŒR©Ô”èÖ­={ö¤Q£òÖáÇ3jÔ( 044D¡P ««‹žž111ÄÅűvíZ ËSõ÷÷ç½÷Þcâĉ$''cdd„­­-&&&Ìœ9“ÔÔÔJÛ¨§§€¾¾> …‚¯¿þšáÇ3`ÀFŒAtt4ááá¼ÿþû4oÞœ!C†ðòË/³lÙ2.^¼H:uؼy3´oß(OžúõëÇ©S§4£®J¥RÓÿ§)--ÅÏÏSSÓJ·?)nQQQܽ{—ÿûßáààÀÙ³gQ©TU:¶–á±ZÉV-}]6¼×Uó¼[S z6o@£úµÞÝQkSPü`þÈ^Ntt¬@jv>J…s‡¶ÅÜĀƖƜ»‘ÍwQ‰ôoc…ƒEm>~¥ íê0¢§#_DÄ‘ž‹±¡’ýçSY÷NZØ–ÿÜ}àÞ”9[Ë×Àeå±çìMÖ¼Õ W§òã-x½ƒGr<.ƒ’Ò2t`]×›ºF¼ûBc:8ÖCW¡ƒÿ$HrÎ;w˜5k~ø!ëׯ×lkذ!GŽáòåËL™2]]]Þÿ}üýýñóóÃÌÌ €¶mÛrñâEÂÃÙ:ujuÇ (OBúôéCdd$nnnDDDлwo P©T888hM˶mÛVs¡Ã³²±±ÑªËÑÑ‘JËúøøIhh(‰‰‰œ?@3eü0•JEaa!ýû÷×z]­V“œœÌСC‰ˆˆ`À€tèÐÞ½{k’©§Q©Tüúë¯k^+..ÖJެ­­µö144¤¸¸˜””ŠŠŠðóóÓÚ^PP@bb"®®®ÏC“Ç&fO‹›J¥¢Q£FiÊ·jÕJ’3Qã¼Ó×™®¿Mûåä«Ù~òï®bÓØØÕ¯…O7{"Ïß"4*‘ÄŒ{œ¿q€Ò‡Öp6¬WK«N+3#ÌM,Çhi[‡½goз¥%go³ú§ò„ìrÊ] Õ¥”–Bbú=ÊÊ ©õƒŸ»V Í4ÿOȸ@k»¯Õ1Ò£‘ymTi¹¼ÖÕžæ6uºì0ͬMèÙ¼Þ®vè+å®OâŸåIÎŒ166füøñ¼óÎ;ddd`n^þá¡§§‡žžíÚµcذaìÚµ‹÷ßCCCÍHÎ}ŽŽŽdddTwŒ´xzz2sæLüýý9pà³fÍÒôëþzºûî/`4Iz´@III¥ûÞWXXˆBQñª´´”ñãÇsãÆ <<<ðòòbذa’œ‡ceeÅš5k*l³´´D__Ÿ°°0Ž9‘#GX»v-?üð7n|êˆUII cÇŽ¥wïÞZ¯?œàTVGYYjµ€•+WR¿~}­í÷ö§Åð~÷=ú~ú_âö¸¶ QÝ™×ÆÅ¾®æ¹[3 †,=ÈŽSɼ߿ ãƒNr#3vÖxµ·eXW{ü¾ŽÑªÃPOûÂG“!]…Žæµ ‘רx(žÁíméÞÔ‚1š2rõ1­òe<øüÒÓ}𳪯[y’UV%eeê)XûvgN%dñóÅ4vIỨë|õ~WìÍkWw¨…øÓ<·_GNž<‰§§'………š×rrrP(Xá‹/77W3²1zôè Õ+Wppp¨îiéÔ©:::„„„ V«éÚµ|úÀÑÑ•J¥¹5À¹sçP(Óߟ|¸ì££k)))ܽ{Wó<66Vs[‘‡“øøx¢££ `ܸq¸»»“Ÿ_¾öã~‚÷py{{{ÒÒÒÐ××ÇÎÎ;;;rrrXµj¥¥¥|ÿý÷DGGÓ¿æÌ™CPP—/_&11±B,MìííILLÔÔkggGhhh•îU×°aCtuuIMMÕìkaaA@@)))ާ§§Ç½{÷´êHJJªòy|ZÜœILLÔ:GW®\ùÞ9Bü9Ê€bu)¥¥eħå}-“€Q®ŒsoŠ{[kÍú­']ü|3;Ÿâ’®¦æàhQž}{4‰žÍð±%Þ® 15Ò#¯¨„2Êp°¨B.&ÝÑì{ùfŽæÿÌk¡£±7²5¯ÝÉ/æFæ=Ìks*á6!GèèXIƒš6©Fúºœ¸šYÝaâOõÜ’³–-[¢««Ëþ󉎎æ³Ï>cÈ!Ò½{w¢¢¢øæ›oHJJâÇ$44”aÆгgO6oÞÌÁƒILLdéÒ¥\¿~]³>­¦ÐÕÕ¥_¿~ò /hFSzô襥%sæÌA¥Rqúôi–,Y‚‡‡G…‘333¬­­ $..Žððp<¨U¦¨¨ˆ ˜˜Èž={ؾ};>>>@ùHTvv6‡ÒŒØEEE‘““CLL +V¬Ð\¸`ddD\\§N¢sçÎ8;;3cÆ ®\¹Â¥K—˜7ojµCCC222X²d §OŸ&55•Ý»wcjjŠ­­m…XÜ‹ŒŒ$--7ß|“;v°mÛ6nÞ¼Ipp0ß}÷fMדòꫯ²bÅ ¢¢¢HJJbÞ¼yœ?^³¿¡¡!111\½z•-Z““ÃW_}ÅÕ«WYºté3²š˜˜<1nݺuÃÂÂBsöíÛ§¹Bˆš$.5‡cW28v%ƒÈ ·˜þíi2s‹è×Ú Ãò+7£â2È)P£ÊbÅžK—<þ¦°ùE%¬?p•Ò²òäj[ô þÕ³|ý°i-=N'Ü&;¯Uú=f~W~cè"u)¦Fz jo˧?^äBòÎÝÈfÕÞËšzMôx©cCÿp3‰·‰OËevè¯X˜Ò­© X¹÷2»Î¤pëN‡.¦‘™[HsS„ø'Ñ3gΜçQ‘žžÝ»wçÀ¬]»–¨¨(ÜÝÝ™0aJ¥ š5kFHH_~ù%ñññŒ7Ns5g›6m $88˜ÒÒR>ùäÍ 5‰±±1aaaL˜0A“´( ºwïΡC‡XµjÄÝÝÉ“'£T*‰‹‹ã—_~aøðáèèèдiS~üñG6nÜHAA¯½öW®\ÁÛÛ›øøxbbbèܹ3óçÏçܹsL˜0wwwêׯϱcÇøî»ïN:pôèQ ´Ö»Ó£Gœœœèر#iii¬_¿žÍ›7cjjÊþó4h ‰qHHééé¼òÊ+˜šš²iÓ&¶mÛFãÆiÖ¬zzztéÒ…k×®…¯¯ïcÏ¡¹¹ùcãfooO=øé§ŸX½z5‰‰‰ 4ˆääd¼½½«û-(‘×8p›ÝgSØ}6…CÓ0Ð×eÚ‹­puª‡±¡s‚~¾Æ—¯q>éïömLÔÕ :8Ö£ž±>›Ž$àÓÍž:µÊGôãÓr¹v+ S}ž=gS3  ýÛXÐÜÆ”§’YýÓ]L£w‹(:èêèÐÙ¹>]›XpíV{.óóÅ4†»9rìJo÷uF©Ð¡“s}3î±ú§+„ýr{óÚ|âãBZzX™Qר€¯Ƴ!ò*ç“î0Þ³½[4¨îP ñ§Ò)“;{Ö8,Y²„}ûöUwS„Bñ'“K`„B!jI΄B!j™ÖB!„¨AdäL!„¢‘äL!„¢‘äL!„¢‘äL!„¢‘äL!„¢‘äL!„¢‘äL!„¢‘äL!„¢‘äL!„¢QVµ`|||u·U!„â/ËÉÉ©JåäÏ7 !„BÔ 2­)„BQƒHr&„BQƒHr&„BQƒHr&„BQƒHr&„BQƒHr&„BQƒHr&„BQƒHr&„BQƒHr&„BQƒHr&„BQƒHr&„BQƒ<×äìæÍ›Lž<™>}úàééɧŸ~JAAf{FFS¦LÁÍÍAƒñÍ7ßTwÿÿ4»víÂËËë¹Ô¥V«Ù¾}{•ËgggQÝ!øCÌŸ?Ÿ9sæTøÿÓÁßߟèèhرcuêÔÁÑÑ‘3gÎpîÜ9ÜÜܪ;)eeehù‚š“šÐñ×çdiL—Æõ5Ïõ• fl9K^Q µôuŸ©®a]=×rBˆß繜ÙÙÙ±aÃMb £££ù:yò$®®®ZÛ?úè#ÆŒSÝ1¨²éÓ§3sæL­×.\È´iÓHMMeêÔ©ôíÛ—~ýú±dÉ +ÔSÙÔWpp0£F ""‚7Þxƒ… òòË/3hÐ 6mÚ¤‰ãŒ3HMMÅÕÕ•[·n‘™™ÉÌ™3yá…èÚµ+>>>DGGðÅ_Æ?þˆ··7ééé|ôÑG¸¹¹1xð`V¬XAQQ‘æØ#GŽdíÚµôíÛ—¾}ûÄáÇñöö¦W¯^Ìž=›’’’Jcôꫯ²víZÈСCQ«ÕO=ÞãúzßöíÛy饗pssã­·Þ"66ö©çJ­V³råJ¼¼¼èÒ¥ ƒÖL£WÃG:uŠ7ß|“îÝ»ãåå¥Õ¦‚‚–/_ÎÀqssãÃ?$55õ™Ï³§§'/¾ø"»víʧ¿_ýuºvíÊ /¼Àüùó)..þ#ÞÎâoª¶ÐùíyAq)Ëw_fàâHÜæìãÃàR³+­½?]¹5úƒGòðïû~½‰û””–iMkªKËX¹ï ^KÒåã½ ^roŽ&Tw„øK{nÉ™±±1...šçeeelÞ¼WWW’““±¶¶& €þýûóÒK/±yóæêîÿ3ñððàÈ‘#š/ËÒÒR"##qww§°°Ñ£GSTTÄúõëY´hGeÙ²e¿ëXqqqlÛ¶õë׳iÓ&vîÜI‡˜;w.VVV=zKKKfÏžMVVëÖ­cóæÍ899±`ÁÞ~ûm¼½½8p ¡¡¡L™2…BÁÆY¸p!'Nœ @sìóçÏsïÞ=öîÝË”)SX¹r%AAAlذ   <ÈÁƒÛöÐÐPf̘ÁäÉ“Q*•O=Þãú hb8nÜ8¾ýö[úôéØ1c*M¨ÌîÝ»™?>aaa¼þúë,[¶ŒäääJcø°ÒÒR¦NÊÀÙ¶m~~~¬\¹’S§N°`Á:ÄüùóùꫯP«Õøùù=6a}œ;v°uëV<==ùõ×_™7o£F"<<œ¹sç²oß>öîÝû¿¾uÅßX‘º”¼¢î–p#3ÍÇÐÆ£ßFÍ„ŸãÐÅ[Ì­_½ß uI~Á1””>~ä¶k+2s‹8ŸtGóZDl*ý[[¡«ÐÑ*|XÅî3)ÌÖŽ°I½x½›=Ëv_"ù·ulBˆg÷‡]­@\\&L //;w’––ÆÒ¥KyóÍ7Y±bÅ_ê‹§{÷îœ8q(])((ÀÍÍÇ“••Å‚ hܸ1:ubÚ´i„……‘››ûÌÇ200`üøñ( lmmyíµ× C¡P T*5eºu놿¿?7ÆÁÁáÇ“’’BAAJ¥…B®®.zzzÄÄÄǼyópvv¦mÛ¶øûû³uëVMÒ©P(øàƒÐ××§oß¾¼ñƘ››ãääD‹-P©Tmûý¥nݺUéxë+À×_ÍðáÃ0`vvvŒ1‚víÚþÄø988ðñÇÓ¾}{lmm1búúú$$$TÇååå‘……ÖÖÖôïߟ5kÖààà@VV{öìaúô鸺ºÒ¤I,X@RRǦs¬§§€¾¾> …===üýý8p ÖÖÖôìÙâããŸÛ{Xüý,¥×ÜŸè=ï'^þü¿&ÞfT/'²r‹Øsö&Ó_j…«S=šX™°àõv$eÞãx\Æcë4«¥Ggçúì?_>"œ_T±+鸷µ®PÖÁ¢6¿Ò†öu±­kĈžŽèë*HHöÏ=!D¹ç¶æìa+V¬ 44”åË—Ó¨QùÚ]]]LLL˜3gJ¥®_¿Nxx8Õ‡*Ñ××§OŸ>DFFâææFDD½{÷ÆÀÀ•J…ƒƒÆÆÆšòmÛ¶¥´´”ëׯ?ó±lll´êrtt$$$¤Ò²>>>DFFJbb"çÏŸÊG€¥R©(,,¤ÿþZ¯«Õj’““011ÁÈÈx@4hÐ@SV©Tj¦%+Ó°aÃg:Þ“úªR©øõ×_ Öl/..ÆÔôÉ‹‘ûöíË™3gX½z5 \¾|™ÂÂÂJcò(cccFŒÁŒ3øïÿ‹››^^^Ô«WO3zÖºukMù:uêШQ#T*Mš4yæs}_‹-011!((ˆøøx®]»Æ•+WpvvþÝuŠ¿¿wú:Óµ‰99ùj¶Ÿ¼Á»ë£Ø4¶é9åË*ZÛ™iÊ×1Ò£‘ymTi¹4±2yl½m­Ùy•‰žÍ8r9:µôiרn…r}[Zr&ñ6«Š#!=—Ë)w)T—R…5!Äc<×䬬¬ŒE‹±{÷nèСƒf›……EEEš ggç¿ÜU„žžžÌœ98À¬Y³€ò$FGG{¸ÿþz»G‚G˦Ä],^XXˆBQq ³´´”ñãÇsãÆ <<<ðòòbذaøùùUÚþ’’¬¬¬X³fM…m–––\½z]ÝŠ‹ˆ+kóã>ÓñžÔ×’’ÆŽKïÞ½µÊÜOgÆ lܸ‘ÁƒÓ½{wÆŒÃÈ‘#«Ü‡ &àååEdd$‡fÛ¶m|òÉ'X[[WZ¾¬¬¬Â9¬Êy~Xtt4&L OŸ>¸¸¸ðÊ+¯ðÕW_U¹Í⟩‘ym\ì$MnÍ,²ô ;N%Ó«yƒJ÷)+ƒ’§\Ò§•%Ÿ|ž+7sˆ8w“m¬©ìc`Cä56Šgp{[º7µ`Ì€¦Œ\}¬ºÃ"Ä_ÚsÖ\»v-{÷îeõêÕZ‰@›6m¸zõ*jµZóšJ¥ÂÖÖ¶ºcðL:uꄎŽ!!!¨Õjºví ”ö¨T*­)ÌsçΡP(´F’àÁhÔÃe]KIIáîÝ»šç±±±4mÚÐþÒ'::š€€Æ‡»»;ùùåk=î'=—···'-- }}}ììì°³³#''‡U«VUiTéYUåxO꫽½=‰‰‰š}íìì åäÉ“O<î·ß~Ëĉñ÷÷ÇÛÛSSSòòò*É£RSSYºt)¼ýöÛáîîNDD5BGGG뢄;wîpãÆ žézŸ1SSS~üñG¸yó&±±±ÄÆÆÒ¢E LMMy饗X¼x1gΜ!>>žÙ³gcaaA·nÝžé<ßý‹ŒŒ$-- SSS®]»FRR|þùç¨Tª'N! —šÃ±+»’Aä…[Lÿö4™¹Eôkm…©‘/ulÈâ.p&ñ6ñi¹Ìý SCº5µxjÝžm­ùæXõ ha[ùRÓZzœN¸Mv^1ªô{Ìüî,P~¡‚â÷ynÉÙ4WhN˜0AëQXXˆ¡¡!«W¯&##ƒ7Þxƒ•+WjþšÀ_§§'ùùù¸»»k^S*•,_¾œ¢¢"|}}™:u*}úô©pë (1™={6ñññŒ1‚ýû÷óÁh•©[·.vvvøúú²jÕ*¦M›F¯^½hß¾=NNNøûû“ŸŸÏôéÓ fàÀ,]º”±cÇbddÄ¥K—4íÍÌÌdêÔ©( >ÿüsLLLxë­·;v,Í›7¯òõŸUUŽ÷¤¾öë×É“'³qãF^yåvîÜÉâÅ‹iÙ²å;{öl®]»Æ Aƒ˜8q"íÛ·§S§N\¾|¹B ´ö­U«Ÿþ9§NbèС|øá‡ôîÝ___&OžL‡øðÃ9r$zzz¬]»¶ÂÅO;ϵjÕ⥗^båÊ•8p€÷ßKKK|||>|8·o߯××WÓf!*|XÅ„'™°ñ$3·œåÖÝ>óí I¦&nAÇz|øu #×GOWÁÚ·;c |úÇ¿ÛoÓ¢5˜ýj®¥å2hq$7ž¤½C]:9×çrÊݧÖ/„¨œN™Ì™Ô8,Y²„}ûöUwS¤¯B!ÄŸLþð¹B!D "É™B!D "ÓšB!„5ˆŒœ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ’œ !„BÔ ÊªŒ¯î¶ !„Büe999U©œüù&!„BˆD¦5…B!jI΄B!jI΄B!jI΄B!jI΄B!jI΄B!jI΄B!jI΄B!jI΄B!jI΄B!jI΄B!j皜ݼy“É“'Ó§O<==ùôÓO)((à“O>ÁÕÕµÂÃÝÝ]³ÿÎ;ñööÆÍÍ)S¦••UÝñynvíÚ…——×s©K­V³}ûö*—ÏÎÎ&""¢ºCPANN®®®$$$ü©Ç}Öø QÓí9{×{øöXâ3ï—šÃÙëÙä¨q±‡„ô{ÕÝ¥ÿYða£Ö¯t[ÐÏñ¸ÎØ£ytýx/C—fÏÙ›ÿÓ1ï¸ÀÌïÎþ)}oÏ-9+))aÒ¤I”––ÈÂ… 9zô(Ë—/`øðá¬ZµJóX±bfffxxxpêÔ),XÀ˜1cØ´iùùùÌš5«ºãS#8p€ 6T¹üš5k8räHu7»}}}Þzë-ÌÌÌþÔã>kü„¨éöþšBÃzµØq*é™÷þí®g”'cúJoõqƬ¶~uwégnbÀªÿëĪÿëÄ’7ÛÓ¥±9‡žå|ÒênšÏ/9»rå qqqÌ™3gggÚ·oÏØ±cÙ»w/téÒEó8wîuêÔa„ „‡‡ãî‡Œ7ލ¨(nß¾]Ý1ªqÊÊÊþÐò>øàƒ?=9«©ñâ÷¸›_Ìñ¸ F÷kÌ•›9Ä¥æ<Óþÿ4(|0  fµôª»[8C=]×§KãúôjÞ€)^-hT¿6±©ÕÝ4!P>¯ŠìììØ°auêÔѼ¦££SéaZZÁÁÁ,\¸=½ò)S¦PXX¨)SZZªõoM0}úttuuY°`æµ… ’ÍâÅ‹IMMåóÏ?ç—_~A¡PàááÁĉ100ЪçÖ­[ <˜~ø‚ƒƒÙ¿?AAADDDHÛ¶m‰ŽŽ¦°°áÇãëëËÉ“'™1c®®®ìܹ¥RɲeË8vìyyy8880iÒ$:wîÌ_|AXXgΜaûöí¤§§³xñb¢¢¢¨S§îîîŒ3}}}"""¦[·nlÙ²€‘#GâììÌgŸ}FVV}ûöeÖ¬YèêêVˆÑ°aÃxñÅÙ³gñññ4nܘY³fÄÁƒ±°°`æÌ™tìØ‘œœúöíËÖ­[qpp`ذa 8ÈÈHâããqpp`òäÉ´oßþ©1S«Õ,]º”ˆˆ pqqÁßߟ† jµ¯²øYZZ²}ûv¾úê+233iÚ´)“&M¢uëÖÏܧììlú÷ïÏøñãÙ¹s'wïÞ¥{÷îL:CCCÍyX±bqqqÔ­[—#FðÚk¯åÓÿ¥¥¥\¾|™›7o²zõjêׯÿØó+ÄþØTôu hkÍúWÙq*™Iƒšk¶[q„.6Dž¿E|ZÆLÜ‚öuyoý 3î1wÛ9Î$ÜÆoPsúÎ`«_O,jŸ–Ë'ÛÏs!ùŽµÐÆš§’ÙöaOnÝ)`ð’ƒüðQolêåÓpûcS Ó €]gRØx(žÄŒ{ÔÒWÒ·¥%þ/µBOW§B?2s‹X¶ë"Ç®dW¤ÆÁ¼6“· ³sý§öàÚ­\~_ÞÖ6¦´¶3{æXÖ6P¢x¨iç“îðß½—‰½q‡²²2ÚÙ×åãWZcmfÄÞ_o²åx"6u8r)‰›WXÂÔoNsøR6ukñoïVtt¬€÷g?óv߯ é` À…ä;ŒX}œ£sÝ1P*žÚ‡g‰§øk{n#gÆÆÆ¸¸¸hž—••±yóf\]]+” ÂÑÑ‘^½zi^355Å€¤¤$V¬XA=¨_¿~uÇHÃÃÃ#GŽP\\ ”'Ž‘‘‘¸»»SXXÈèÑ£)**býúõ,Z´ˆ£G²lÙ²ßu¬¸¸8¶mÛÆúõëÙ´i;wî¤C‡Ì;+++Ž=Š¥¥%³gÏ&++‹uëÖ±yófœœœ4 äÛo¿··7$44(O„ 7ndáÂ…œ8q‚€€Í±ÏŸ?Ͻ{÷Ø»w/S¦LaåÊ•±aÃMBrðàÁǶ=88˜9sæðÓO?¡V«ù׿þE—.]ˆˆˆ K—.,Y²ä±û†„„0uêTöíÛGÓ¦Mùä“Oª¯mÛ¶qúôiV®\IHHH…$ú¾Êâwÿ<7Žo¿ý–>}ú0fÌnݺõ»û´ÿ~Ù¾};)))šíW¯^e̘1tîÜ™M›61zôhV¬X¡µ&ð‡~`ðàÁ̘1''§'ž_!öœ½I÷f(:ôniÉî3)¨Kµ)9¢bê‹-Ù÷ï~4µ6á“í±¬z«3ê×b†w+¦{·ÒÚ§H]Ê„'±23$d\|º;°þÀÕ*·ë×ëÙÌÛvŽQ½œŸÔ‹¹CÛ²ïÜMöþšRiùÙ¡gÉÊ-bÝ»]Ø<Á 'K„ÇV©…êR&n<‰u]#¾׃!²%êÉëïÊÊ ¯¨„¼¢îäq.•ø´¶+ÿå/¯¨„ñA'incÊæ n|ñN²r Y·ÿA bod£¯Th¦E]Jö^-¾ïFÿÖVŒûênÝ)xj¼žÖ‡g§øk{n#g  ..Žàà`­×óòòعs'S§N­t¿­[·²hÑ"”J%‹/®îøhéÞ½;'NœÀÍÍS§NQPP€››‡&++‹Œ˜6m~~~Œ7î™e``ÀøñãQ(ØÚÚòÚk¯ÆàÁƒQ*•š2ݺu£gÏž4jÔ(_ß7jÔ( 044D¡P ««‹žž111ÄÅűvíZÍHŽ¿¿?ï½÷'N@¡PðÁ ¯¯Oß¾}xã7077ÇÜÜœ-Z R©Ûv///7n @çÎ)..fÈ!¸»»þØ©E///­«~ø{÷ž¾8999###lmm111aæÌ™¤¦VœžP(â÷õ×_3|øp Àˆ#ˆŽŽ&<<œ÷ßÿwõiìØ±˜˜˜ðþûïóþûï3eʶnÝJ«V­3f P>ݯR©Ø¸q#ýû÷ÀÉÉ M]O;¿âŸ+ýn!§²˜ÿZ;^heIðaÇ®¤Ó«yM9¯¶´nX>«1¬«=?Ä$s¯°„Úºèèè ÔU Th¾¹œÎ½53_n¡ž.޵¹˜|‡W3«Ô6=]þ/µb Ky²cmf„‹}]âoåVZ¾[S z6o@£úµÞÝQkSP\Š¡žâ‰ýø%>“»ùÅüû¥Véëâ`Q›³‰·Q¥å>¶}É·óé5÷'­×†t°Å±Aùçw~Q ¾nŒèé„RW‡†õŒðümäî¾Ò2ðØS£ÓÀ-™èY>Š6f@]JcûÉ$F÷küÄxËxbž5žâ¯íIÎV¬XAhh(Ë—/×|¡ÜwèÐ!JJJèׯ_¥û<˜ÁƒÁÔ©S  K—.Õ' |{Ÿ>}ˆŒŒÄÍ͈ˆz÷î*• Mbжm[JKK¹~ýú3ËÆÆF«.GGGBBB*-ëããCdd$¡¡¡$&&rþüy ò)a•JEaa¡&¸O­V“œœ €‰‰ FFåÓ÷§4xðA¯T*)**zlÛ.«¯¯¥¥¥æ¹žž%%%”””Tº¯•••æÿ÷û#•O2tèP"""0`:t wïÞšäéiT*¿þú«Ö/ÅÅŘššþî>5mÚTëÜÝoBB‚&ù¼¯M›6|÷ÝwšçNÅ>Ëùÿ,{½‰®ŽnÍÊgZ74ÃÜÄ€1ÉZÉ™•™‘æÿ†zåËŠKJÝÇÖ—šƒScMy€¶êV99kakЉ‘’ Ÿã‰OËåÚ­®¤æàli\iyŸnöDž¿EhT"‰÷8£|a~éC¿ô<®ª´\™×ÆHÿA[[5¬óÄäÌÂÔ€…>.¨KÊP¥å²ê§+(Ë“ úÆú ëjÏö“7¸r3Uz.ç“îàÔàAûM •Z‰Pa*²¥mTéOO žÖ‡g§øk{®ÉYYY‹-b÷îÝСC‡ eŽ9B·nÝûÿý¤`È!ìÚµK3mTSxzz2sæLüýý9pà€æŠR===tt´ó¼?’òè—è£å€ ÉÊ£#K………(g¡KKK?~<7nÜÀÃÃ///† †ŸŸ_¥í/))ÁÊÊŠ5kÖTØfiiÉÕ«W+]KVY›§²ý«êþ¨VUŽÿpÌ5jDXXGŽáÈ‘#¬]»–~ø7>¶Î‡ë;v,½{÷Özýþ{ñ÷ôéáów-¥B¡¨ô}òh_þÙxÖó+þYöþZ>…ù‚Óâ¥epørÙyÅš…ýJÝg_Á¢«Ðá‘ÙQ­÷u¥ïã‡vˆ¾–É„'éÓÒ—Fuy¥³_Œ¯ôX¥e0>è$72óðhgW{[†uµÇïë­rÏÒ§•5P*p±¯«yîêT}=ÿ eÒàÜÍ/æ_«amf„[3 zµhÀ¥”»¼ð`äÌP¿â炞Rû¸º ô5mÑŽYIé“/Nz¸ÏOñ×÷\“³µkײwï^V¯^]atà¾_ý•áÇWx}üøñtìØ‘Q£Fi^ËÉÉѺÀ &èÔ©:::„„„ V«éÚµ+P>:¢R©ÈÍÍÕŒx;w…BAÆ µFÏîFåæ>ømêÑѵ””îÞ½«½‰ÕŒÆ<ü¡Ott4¡¡¡8::ðÓOåCõ÷?H.oooOZZšÖèÏ… øú믙3gNu‡÷±ž³ï¿ÿžºuëÒ¿ú÷ïObb"¯¾ú*‰‰‰8;;kÕõè—Š½½=‰‰‰ØÙÙi^ûüóÏiÙ²%žžž¿«½/^¤G@ù¹322¢aÆ8::rîÜ9­²çÎÃÞÞ¾Òzªr~Å?ÓõÌ<.&ßeÒ ætþm½@Êí<&ŸbïÙ^ïfÿÔz÷k—³¥1›Ž¨´¦/¦ÜÕl¿¿=·@ýP›,AØzâ:}ZZ²è·Ñ©ûms°¨]áXñi¹D_Ë$Ô¯'Ž¿mÿé\ù²„ª¼Í-ùòà=r Õ”­]¹y÷é;>¢X]JåÉâþØTÊÊ`Ã{]5 ¹”öôó’¡½ ãê­zþ6Š©§«£¯Œ¼*÷áYâ)þúžÛqqq|ùå—xyyq÷î]Ž;¦yܸwï)))ZS>÷uïÞ   Ž9Âõë×  !!á¹Ý¸õyÑÕÕ¥_¿~ò /hFezô襥%sæÌA¥Rqúôi–,Y‚‡‡G…[E˜™™ammM`` qqq„‡‡WX`_TTÄ‚ HLLdÏž=lß¾]³ÉÈȈììl:¤‰‰ŠŠ"''‡˜˜V¬X<˜422"..ŽS§Nѹsgœ™1cW®\áÒ¥KÌ›7µZ]£×/=-f,Y²„Ó§O“ššÊîÝ»155ÅÖÖ¶B]ǯ  €7ß|“;v°mÛ6nÞ¼Ipp0ß}÷]…)ùg±lÙ2Ο?ÏÙ³gY±b¯¼ò úúú 6ŒØØXÖ­[GRR»wïfË–-¼þúë•ÖcbbòÔó+þ™öžMÁØPÉ«]ÑØÒXóèÕ¼­ÖaÇ©ä*Õc¨¯KL|WY»Ô³yL õXø}, é÷Øsö&aÑ×5ÉœY-}¬ÍŒŒ¼J\já¿Üàà…É‹©‘×RsHÊÊ##§Ïw]B•~¢’ŠÓñ&†Jtt *.ƒœ51ª,Vì¹ÜŸ~}²nM,°01`AX,‰÷Ø÷ëͧö¿ ¸”cW28v%ƒ£WÒù.ê:k#âèÙ¬†z êÔÒçn~1gos7¿˜b’ù>&‰"õ“Ûó˵LŽ^I`çéâoåòr§ò_üZ54#,ú:çndsìJ_ý|­Ê}x–xŠ¿¾ç–œ8p@s…æ„ ´÷§u23Ë×*<¼^ç>FÅâÅ‹yóÍ79wîk×®ÅÁÁ¡ºcT§§'ùùùZÝ@©T²|ùrŠŠŠðõõeêÔ©ôéÓ‡™3gVØ_GG‡Ù³gψ#Ø¿?|ðV™ºuëbgg‡¯¯/«V­bÚ´iš«[Û·o““þþþäçç3}út‚ƒƒ8p K—.eìØ±qéÒ%M{333™:u* …‚Ï?ÿÞzë-ÆŽKóæÍkô¨YUb6räHz÷î¿¿?/¿ü2QQQ,_¾¼Ò„óáø%$$Я_?&OžÌÆyå•Wعs'‹/¦eË–¿»½îîî|øá‡øùùѧOÆ”¯'[¾|9dèС¬[·ŽÉ“'ãíí]i=–––O=¿âŸiïÙ›x¶³Á@Yñcüµ®ö\J¹[!áªÌÐ.Ø>•õûã´^W*tøü_HL¿‡Ï°éˆ ¯ Qþv<˜ýjâÓr±úûÏßâƒM4û¿ß¿ –fFøaøÊ£Ü¾W„¯›—S*ŽhYÖ1dúK­>ÏÀEXúãEÆhŠ‘¾.—Rž>¦§«CÀ¨NÜÉ/fø²ñP9…LØx’ Oâ÷u ‘Wé߯šÙCÛ0 /v°eRp /~ú3»Î$3Ù«%IYyÜ+,yl½ýZ[±åx"î °éˆŠå#;j¦—ßïßsSCÞ[‚{.á7°y•ûð,ñ}:e27RãDDD°dÉöíÛWÝMÏèþ}Îîß»Mˆ¿ªŒœBâÓr5÷XGlÒVŽrýjB<üás!„—”2>è$ßÇ$q3;Ÿ£WÒÙzâ:ý[[ýï• !žè»Ï™Bˆ¿.k3#æmˆȫ,úþ<æ&†ŒìåÄKþï• !žH¦5…B!j™ÖB!„¨A$9B!„¨A$9B!„¨A$9B!„¨A$9B!„¨A$9B!„¨A$9B!„¨A$9B!„¨A$9B!„¨Aªüç›âãã«»­B!„YNNNU*'¾I!„¢‘iM!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„BˆD’3!„Bˆ¤Êø¼*nÞ¼ÉÒ¥K‰‰‰ÁÐÐ~ýú1~üx µÊ%$$0räH~þùg­×}||¸zõªÖkk×®ÅÕÕµºãô?Ûµk«W¯æÇüŸëR«Õüøãx{{W©|vv6'Ož¤ÿþÕ†?ͳÆHˆ¿¢n³öR\òàÏ#›*éÖÔ‚)^-¨[[ÿ©ûgçÓÿ?ûÙê׋ÚÕÝßmê7§9pþÖc·¯ú¿N¼x‹ì{E,ôq©îæ ñTÏ-9+))aÒ¤IXYYÈÝ»w™;w.%%%øûûkÊ%''ãççGQQ‘Öþ¥¥¥\¿~éÓ§Ó°aCÍëM›6­îÕ8`Æ UN<Ö¬YCaaá?*9{Ö ñW5ª—œëSVVFzN!‘W™»íËGt¬î¦ýiÞ{¡1¯vn@ÔÕ ¶OdÙ¿ô¿¹­)/Þú½Õ ñ§{nÉÙ•+Wˆ‹‹cíÚµÔ©S€±cÇòÉ'Ÿh’³ˆˆ.\Hƒ *쟜œLQQWw\j´²²²?´üßÁ?±ÏâŸÉÉÒ˜.ëkžë+ÌØr–¼¢jéëVwóþ­LhüÛÿÓî ÐÑÑŠ‰5Ï-9³³³cÆ šÄ @GGGëKòøñãøùùÑ Aüüü´öW©TÔ¯_¿F'fÓ§OGWW— h^[¸p!ÙÙÙ,^¼˜ÔÔT>ÿüs~ùå Lœ8­znݺÅàÁƒùᇰ±± 88˜ýû÷DDD´mÛ–èèh >|8¾¾¾œûì3T*õêÕÃÇÇ__ßê~Ë ñÌj(ÑÑßžgæ±l×EŽ]É ¯Hƒym& nAgçÉKT\“7âfv>.öu™÷Z[ÌMÊ?·Î'Ýá¿{/{ãeee´³¯Ëǯ´ÆÚ̈ˆØT‚«èÖÄœ-ÇÙÛ ç&|¶ó"Y¹…ômeɬWÚ «Ðyj[N%Üæ³/ J¿G=c}|ºÙãëæøÜbSX\Êœmçˆ8—б¡’wú:3´Kùˆ[qI+÷^fçédJJËèä\Ÿ©CZjâ ÄŸé¹]`llŒ‹‹‹æyYY›7oÖZ/öñÇ3dÈJ÷W©T2aÂúô郯¯/QQQÕ-9r„ââb |*622www =z4EEE¬_¿žE‹qôèQ–-[ö»ŽÀ¶mÛX¿~=›6mbçÎtèйsçbeeÅÑ£G±´´döìÙdee±nÝ:6oÞŒ“““&|ûí·ñööfàÀ„††0eÊ 7ndáÂ…œ8q‚€€Í±ÏŸ?Ͻ{÷Ø»w/S¦LaåÊ•±a‚‚8xð ¬´ÝkÖ¬áøñã|úé§¢¯¯Ïĉ)--ÅÑÑ‘·Þz‹•+W’““CPPÙÙÙLš4é±qgýúõ„……‘™™I`` ¿þú+óæÍcÔ¨Q„‡‡3wî\öíÛÇÞ½{+Ñ“”––2uêTȶmÛðóócåÊ•œ:uªÞeB<›"u)yE%Ü+,áFf›'2 5F¿šÍ=KVnëÞíÂæ n8Yš° k~ºÂñ¸ >}³£»¢¯T0qãIJe^TƒçzAÀÈ‹‹#88¸JåU*wîÜaРA¼óÎ;ìÞ½???‚ƒƒiÒ¤IuÇ €îÝ»pâÄ ÜÜÜ8u긹¹qøða²²² ÑŒþM›6 ???Æ÷ÌÇ200`üøñ( lmmyíµ× cðàÁ(•JM€nݺѳgO5*ÿ pøðáŒ5Š‚‚ Q(èêꢧ§GLLŒfúùþ…þþþ¼÷Þ{Lœ8…BÁ|€¾¾>}ûöà7ÞÀÜÜsssZ´hJ¥ªÐ梢"6oÞL@@íÛ·`öìÙôë×S§NáêêÊÿýßÿñÓO?1þ|>̧Ÿ~Љ‰ÉcãðöÛok’«Aƒ±gÏôôôð÷÷gàÀX[[ãââB||< …¢BŒž$//ììl,,,°¶¶ÆÚÚšúõëcooÿ'½³„øý„Çj%[µôuÙð^WÍónM-èÙ¼ê×`xwF­=NAq©¦ÌÄÍèÚÄ€—;5b߯)ä•àëæÀˆžN(uuhXÏO"Z|¯Ð4E_© oËòŸÕ7º;`nb€¹‰-lë JË…V–Ol‹º¤”ì¼b,L ±63ÂÚ̈úÆØ›?¿ [™0ѳYùÿ-MØt$¸ÔÌM Ø|<‘€‘®´w¨ ÀìWÛÒoA§TY¸:Õ«îÓ,þaþälÅŠ„††²|ùrMÂð4þþþøùùaff@Û¶m¹xñ"áááL:µºã€¾¾>}úô!22777"""èÝ»7¨T*´¦eÛ¶m«¹ÐáYÙØØhÕåèèHHHH¥e}||ˆŒŒ$44”ÄÄDΟ?”=J¥RUzq€Z­&99ŒŒŒ€ò$ÐZ'¨T*+\Ð’’BQQQ…)ë‚‚quuEOO3fðÎ;ïпÜÜÜžkkkÍÿ 5£–-Z´ÀÄÄ„   âãã¹víW®\ÁÙÙù™cmll̈#˜1cÿýïqssÃËË‹zõäYÔ|ïôuÖ$V9ùj¶Ÿ¼Á»ë£Ø4¶võkáÓÍžÈó·J$1ãçoÜ ô¡%'ê?H€êÔÒ£ð·Ä­¾±>úڳýä ®ÜÌA•žËù¤;85xðÙdb¨§¥ÓS–OÆ4¨óà }¥®‚"uy}Oj‹±¡’=™±å,ÿÝ{·f ðjoK=ã§_uZUö¿%…::`b¤¤°¸„”Ûù©Kñûú$&„¡ ¸„ÄŒ{’œ‰?ÝsMÎÊÊÊX´h»wï& €:Ty_CCà ·Üptt$##£ºc¤ÅÓÓ“™3gâïïϘ5kPžÄèèèh•½¿ÞîÑ$éÑrP~µkeûÞWXXˆBQqº´´”ñãÇsãÆ <<<ðòòbذa¤‡ceeÅš5k*l³´´äêÕ«•®%«¬ÍR«Õ¬\¹’úõµãÞOº¡üâ…BÁùóçÉÏÏ×$‚•¹?öh\¢££5Sà...¼òÊ+|õÕW­çi1Ÿ0a^^^DFFrøða¶mÛÆ'Ÿ|€žÚo!ªS#óÚ¸Ø×Õ}X´h>>>¸¸¸’’RiŸ¡<É{8Þ©©©šQ¸ÔÔT–.]Šƒƒo¿ý6AAA¸»»ñ?ÅCˆêP«K)--#>-—èk™Œreœ{SÜÛZ“_TþKIU.hÞ›JYlx¯+oõqÆ­™w ~W»žÖ–Ô;,Ýy cÞîãLИn¸·±&âÜÍ?G_£Vœ>|8 .dذa-=;C† ¡C‡L:•ÌÌLvìØÁŒ39rd«^#hîE dáÂ…¤§§³bÅŠS&Ø×ÕÕ1mÚ4²²²ˆgåÊ•Œ;OOOJJJذaCK]bb"ååålÛ¶Ù³g´„OOOÒÓÓÙ¾};ƒ ¢[·n¼òÊ+¤¥¥‘’’Bll, §ô\ž+î¹çfÏžMbb"999ÄÆÆ²wïÞ–áíéÓ§ÊèÑ£™0aK–,!99ùœÛ²Ùl8p€œœ ˆ‹‹#33³e¸õäkTSSCXX‰‰‰¬_¿ž¤¤$bcc[Î×f³±jÕ*æÌ™Ã‘#GHJJ"))‰Þ½{øÈù÷¤ç•³9­€Íi¬Ûw”IKvPXQÇðð¼=šWn&¦P^ÓÀ¶Ì"fǧPßxæ7cm­n”U׳+«˜²êzþ±í0_lËi¦<gªÅæéʪ퇙ŸÂ‘’j’rJI:TBïNÍ;ü| Å?džs»gÃÃÕ™{1;>…ÄôrŠª‰]žÄÞœ’–ùq"F:oÚk×®mY¡¹téÒVßÛ°aVë¿~€?øàƒÔ××3sæL éÕ«ï¼óN˶ fÍgŸ}ƈ#Z޹¸¸0kÖ,f̘ALL V«•Q£Fñä“Ožòó‹…)S¦0}útÆGdd$O=õT«»øúúDLL >>>¼üòË\ýõôïߟ&NœÈ¢E‹˜4i ,`Þ¼yµì/—’’ÂàÁƒ‰ŽŽ&>>ž &@\\3fÌà‘GÁÝÝ¡C‡òÒK/—kóÜsÏðꫯRSSCDDsçÎÅÓÓ“ï¾ûŽM›6ñÉ'ŸŰaØ6m‹/>ípê?óÄO0uêTÆŽ‹Õj%**Š˜˜vïÞ}ÚkÍ®]»˜ƒGË©k°;¤àOíévEDäßçá៟Ž.EÄôÎ:œ­ßw”‰KvÒ`o,+8$$Äamddd8´G·¯Tƒ™Ú¿Xjºk<ßÊËËÉËË# @Mä ÎzXsÁºǃ@ÓÙþ˜ˆˆÞÞÞøûûSTTäèRDLï¬ÃYJn™£k‘‹˜——uuuŽ.CÄô´ @DD áää„Ýî˜ùÊ"…3Q81…3Q81…3Q81‘sº}ÓÂÝʼnOž”Ïv“t¨ÔГܖYÄ_ãSÙ´‚vÞîÄ\ו{£‚ ­á«¹¼»6‚ò:®éÑŽIw„áׯÍÐ~‘[\Íoælä­‡"âgX»eÕõ ›öÝ)Ç·¾mX ö&æ~“ʪí‡i´71<<€—F÷ÁÃÕ˜÷)Z¹—å?:å¸_7ÖLfØuøvOóÒ9ZZC¯@oÆéChG›aí›Ñº}GÿñŽVÇtõåïG]XÅ+’Ø›SBG_+/ŽêMTwG—}^”TÕ³5£›Ã]Šˆ`P8ópuâÏcûÓ¥—á'˜[\Íól#溮¼~ÿUì;\Jìç{ð±ºqs„1ODÛ3mŦÞÛ—^6f®ÚÇkËv1÷á« ¿o¬L¢º®Ñðv3ó+ñpuâ͘H‡œ7ÀÜoRIHÊcfÌ\œœ˜¼lóÒyáÖ^†´ÿÛ!]~Ò `ƒÝΔe»Ù7аk’[ÆkËv1ùîúûðÑÆƒ<÷ÁV¾xñ<Ýœ «Ãl2UÐ7؇? ïÑrÌæé 4‡ú>ØÊU]|™xGß%åñâGÛùü¿‡Ò¡íÅ+¢w¾M£¶Þ®p&b<œuðfÚýWa·;æ–Ok÷æÑÙßÊ77?áù[Ù‘YDüî#†…³?bDßÀ–àgFö"fÞfŠ+ëðõ2¶÷låÖ*kpq6þþ¨™Ç*èÒÎËa½ Uu|š˜MÜC‘ôëâ À7÷à‹­9†Õе½]ÛŸx“2?!¶V7ž‹5¬†Ó èhã¶~xvd/>û)›G+jkXf“q¬‚ð ŸÓ>>7¥æSTQÇÄ;Âpwqâ±›º±e+·æð‡áÝ]ú¬Iwä1• Î^éÇ)Çxoý6L¹Åð¼%"¨îíZ´X ‹ãG÷¦¶þÄ®ØöãÏ„FçÕü²Zæ­Icþ£ƒˆ™·ÉØÆ9ÎÚ·1¼Ý_ìÎ.ÁÍʼn¨n'^|Gžšv¬¬–ÈäÏôÃÕÀ°ÜÖêÊþ£åì;\JïŽmùjÇa¼Ü jguÈu0‹ƒù•ôïzúaþ=‡J j‹»Ë‰áï~]ýØs¨Ä°ú’òøð‡L†øñÅÖ, ÃÂ:0~L\œš?+·æðÞúVÔÒ3ÐÆoëMxP[2ó+y௙x{wìLCc1oo¦wG>ž-Cí;³ŠXùâ §´ýúò$ºþJ\œœX¸n?“ïŽÀÉø÷w"— Ζþ˜€³ƒþ’;´õh5ìPRUOü®ÜVCšÍÓ<›?Ï)ªföש éÙƒçœýù‹½Ü78˜n2ó+¨­·óàÜMäWìÄ1}èìgL(8\TE'_O¾Úy˜k÷SYÛÈð°¼p[oÃæœìýïpåm¸>ô CÛ½­_'6¦ä3îí[zPgHÛãCx—£¦¦æpösF!ïŸA]C#7öéÀó·†bus¦ ¼–öÞ­‡/ýÚ¸QPVch{sJéhãËñ7ràXO-ÜÂÕ!þÜÀ¦´|ÞZÌ«wEÚÑÆº}GyrÑ>{a(W¶÷â‘»1÷›T†‡ð?fQRYÇGõÆÓÍ™ü²jì¼vwÄiÛöaÚò$Þ}<Š£¥5|š˜ÅØkº8ìÿKäRwY­Ö¬©·3þãítôõäžAA†·ÿÙOÙÜùæ÷ìÊ.ænƒÛßu„œ¢*¾¡›áçý‹Ìc•”V×ólt(ÿûàjëí<µègjê™ÿVUÛÀá¢j–nÎbÒá¼rW8Só‰û*ÙðkQU×ÈW;óÀµ] o»¤ªŽ²êz&ßÎâ'¯åÎALY¶‹ü²ZÃk1‹¼Òê–ÇáŸÆ^Åø1}Ø”–Oìò=ÔÔ7âêÒúéÒÍʼnºãïù­¡xº9Þ¹-‘!'zïoÈä·C®ä–ˆ‚ü­Œz%Wû°âx¯ØÃ7tÃ×Ë×—ïaáºý¼zW8Þ.¸8Yp²Xp¶Xþiî˜(©ªãóŸñÌÈž¼½&¼cƒ©Èåİ՚ŽVYÛÈ‹m#¿¬†wŒ›‹ñ¹tTÿNŒê߉„¤<&|²ƒ9ÿ5ÐùWÅ•uÄ­NæÍ˜†ŸýÚÇÏ\‹‹³Öã“Î{ö'ú/ëØ”VÀð°¼}g' µ üå~'zëšš˜¸d'n?14d„ ÉÇZV‹mæ—ûˆöáŽÈμ<¦;ñù–ì–¹™—›@O¾~ù&ü½Ýq²@Dxº9óüÛ(¹½gêÄêì†/ °yºâå~¢MWgê›ëÊ̯`wv1þÙòýúF;6ks½«³…Wî 籿ÿÄÍá\׫ýY·ëìdaÜÐâV'³fÒ0‚|xûÛ4bïëkèù‹\..‹pVQÓÀ3ïýLEmï>>˜ö6w‡ÔñËù˜X½3—„¤Ù…ỦOå`~£#;Ò~zz·ú°ý¬´GSwúuõå­ÕÉ$*%%·ŒIKwÒ=À›A=ÉøxpKD¯-ÛMòá2vfóÖê³¡Ã½•µäWÓ3Ð1›¾ÞÌç[ñÍî#*¬bÞš42UrçÀΆÕWRÃÛߦSTQ4÷î¾wÒ˜_}¡õ öÅÓÍ™Øå{8XPIbz3¿ÜÇýƒ»àéæÌ5=Úaót%vù2ŽU°pýRs˸=Ò¸kv&^w%_nËáó-‡8RR͇?dòéY_…;ý{ íØ–Ñ:1aL–l>HòáæÞ@O7gÒóÊØ~°ø´¿{öש¼|{¥UõlL=Æ ··õ‹Èåè’ÖLØs„¦&xcåÞVÇC;Úøèék ©aì5]¨mhdú?öQRUGhGó¢«6åu¤ÿ¹·/³¾Ná…Å[©m°smvL»ÿª3¾k?Ÿ¦ÜÁÌUÉ<¹p Kó<Àç Üc  °¢yâ½£6/½s`gêìü-!üòZzxó·ÇZO^i5‹Ö`Dß@üÚ¸±ã`1K6äáBNùúBórwfîï®&nu2ãæmÆÝÕ™Ñ:ñôˆž@ópâ¬q‘Ä.ßÃs7Ñ¥qE:~&ÃÃ:P4ª7lÈ`æª}tiçÅôßö§O§¶|·÷(›RóùäÙ!Du÷gXxÓVìañS×}UGâwaÂÇÛIxeø)¿û½'·|¾jüŽ>U‘Kž¥©éì¶øJ¼£kŒ½ÕÏédddbÌ †ÛW ªÁLí_,5]Œ5êÜEç’Ö¹˜(œ‰ˆˆˆ˜ˆÂ™ˆˆˆˆ‰(œ‰ˆˆˆ˜ˆÂ™ˆˆˆˆ‰(œ‰ˆˆˆ˜ˆÂ™ˆˆˆˆ‰(œ‰ˆˆ!ìv;NNzÙ9“³þ+ íè˜[͈ˆÈ¥¡²²77cïc+r1:ëpöØMÝpv²Ð„0ð~;""rÑ+//§°°???G—"bzg}û&€õû޲`Ý2VPÛhwHÁŸ>ÚÓ!튈ȿÏÃÃ???<<sOY‘‹É9…3¹°43SDDDÄDÎDDDDLDáLDDDÄDÎDDDDLDáLDDDÄDÎDDDDLDáLDDDÄDÎDDDDLDáLDDDÄDþ¿Ý—}4@i%tEXtdate:create2018-04-12T17:59:42+02:00þ¶-ó%tEXtdate:modify2018-04-12T17:59:42+02:00ë•OIEND®B`‚django-tables2-2.7.5/docs/img/bootstrap4.png000066400000000000000000002146541473544236200206620ustar00rootroot00000000000000‰PNG  IHDRlN5&gAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿÿÿ ½§“ oFFs0ÞP±ctIMEâ ;.Ã6¬› vpAg4lqê€IDATxÚìÝuXTÙÀñ/`v¡b ¢ ØÝÝÝݹvwû³×Nìîn]×Ö]»[A%•þýq`‚P@×y?ÏÃÃĽwνsgæ¼ç¼ç\£„B!„%A|@!„BñóI „B!„’@@!„B$€B!„H!„B! B!„B „B!„0@!„Ba€$B!„™Äw„B!„‘óôôŒñ:É’%‹ÖrÒ# „B!„’@@!„B$€B!„H!„B! B!„B h·Þ‚ÑpíŸÉÈ4 Zoçñ½¿®ÔñÚsçç¿ö¢‹êµ½ý£^æÀ}µÌ­·?·l}öB–é?ÿ˜„W²LNž:ésrÎÇN|žó±1ã ØÍà¸}È< V]ýùûøé‹zOV^ùù¯-„"îìÚµë»ÖqÀàR°»lm}\TE²Æj þñ;×b3œ³u¸«¼ÿþøòÄÔ­·0ûÜÙV†©ªò,~>9ç£ïGžó?‹]j¨”ýØí†ÿÌ&6Q¯“Ó2¾÷X!Äï ,øž` Æ×p°¹µ÷S%¶Ûà¾;ä¶ŽïC=AÁ`ü’¢ºíÇ pîY|ï±ørÎGßñœ¯j«þâšqXR7¾÷V!Äï |å×®]ÔªU+ÆÛùîªEBõßG'ýä´Ú–ÀbŒj=½ë¦¿Þ×– KÉXw®¿Q·—^RÏyú©ÊFºÉ|”_—_ªç¸BÎYêvÝõªE´)23Ï‚õDµÀÁûPüO0 i'A›­ðá³¶ŒM6B¥ðÇA•Æb6ꬃ·Þß>.+¯ÀÍ·0¹Ò÷ßyTÙ_zœóêöwõÜ=7¨·RŽWÇ¢Ârõšáí¾yfC¢Qà0Σ’võ5”[æcT*Ì´p™3÷ÜÔû•b¤™¤Þǯ“àvX{‹1Ðt#|üq¹ 7À~.$Ùg¨”pé þ‚\³Ô{a?¶ÜÒþÀ}(ºP•=Ët}p˽œó‘ûQç|Xù“ŽUi4yç@’ÑPd\|¡¿Ü·öÔ1Í1SWÅÿTÇU÷s4ø XMÐ.ŸvŒ8½ö¨÷Êz¢:þþAÚe¼üÔó駨²ÙÏ…í¡=2Q}f¿ªÛó.h·¬ÎÑÌÓTùŠ,€ý÷´Ïßy¯Öq½5רýÌÇB!âNT=±éˆq ¬~Ð|ÔòÈ£à” Û¨ç?@é%pú Ì©ËëÁ O(ù'¼öŠÞ29-áfO¨‘ rY©Ûõóªu»ìT)ӫº†j[•VªJÖ¬j°¯•ZnNu8ÚN¿ìK/ÁäÊпüõª­†|©ao+˜[S•§óýu?Tû»§%¬m¨*Ñ-6ý}ü ƒÀ„Š`mþ}ov3{µÿiÌ¡EèíL)TÞ©%ðÁÖ4€ÍMUjCõÕª¼ºF%áÏ:깪«TÎpdí&2¡crHUÀ‚ÔñþøÖ5RïÑP}Ôû0ó,L< M Àê`ik®é/³â 4Ý%²À¶æjÙAUÙÃÌ8=÷@ƒ|j™²Ù ámååÖ[¨½ ¤Í¡›L8©*;ßCÎùŸ{·ñö‡5WÕ±:ÜVµ¨W]¥*á½ý9ûZn†ì©`U¨köûµ§Ÿ7_XRÚ9À‹°ì’öù¦aÇm˜Zö·†rÙ¡ÑUê3™öÛ`êièå¬>ÃYSB5*ÀÑÕq»ê™ZÛìÓ©ÀäŸw‘oÓÇ*¯w_XX†”V¯›×n¼Q}VG•ׯê;¦üräüù·ÒŶ7‡"é¡Ù&8üàÇœB!"ªU«V”1ãÔ f›ôï§³P?„F¡ùµË.Ã=wø§—6m¢|vÈ:]µ,Ϭ½eò¥ä‰U^m¾4Ú×;tÚæöê¾}:h»î»AñÌàZ Δ\U¨tíl¡ÍÏ}úIU *êä¿ôT-×Á!ÚÇòX« ¨r«VÓk¯¡`ºÈÑàƒ1¹úq¼çÆWAN«wâpïHª$êÏÄXU ÃŽ…_ ¬lEÓ«ÇR›C¡ùpý58eÔßIÕírÙ Û U©é_"byFS¯·£¹ jäVÇjúU±¾ë¦î/¬¥M§H™Xµ|zûk[ËÃÃÔSв Ì­¡«—WU².çŒgNÌ^ûïª7q[3@úþÈ86Þ„~ÅáÐpHB¿K*åPÈ}wõ¾!„øµÅ8˜XQµÄ‚úÑxB¥\êIÁчP0­~î´¥TËÇ©ûÑY&*.™Õn¦äª’š9iG´¤Kª½9x|Q•¼«¯Ô§_ |T•ɰ ¹™©þ6Â*¨7ÞD^)ú빪ôí½Á‡u×Á^îøç!Còo¯—4‘j‘Ž=TïEXêÂÇp­ý™ShogL…Ò©–ËÈy ZÜCÐV\JdVûäî«Z ÓZÀðêe¶bõ>èæÐëzö Þûj[·Ã¤µÐÞ¾õÞú¨@@WË‚ª'áìSUîO_TeEתÚY‘3€±´Þ ‹ªr+óícù-rΫÿ?ꜟuNµ"‡YÓZŒ|YݲdHùÓhÏÝèìÏ•Wª"o¢Ó÷™Æ‚o*˜Nè¤O¦¶¦dÕK¶á<ù¨>+ž~‘§¼EåèCõ_÷¼72RÇ¢ë.ýmÖ9îɃ¹©~yt]~öiõ%§Mª¿Lt^ûÔ0ªë|æ’˜ÂåîÚsÅ%“ê zæWçøÆ&Ñ?B!âWŒœ–PL§µ¹P:È>Ö_‡ÎŽðÞl’E\/­…ªÐAô–‰ÊúFª"6í ôØ£Ò@F—×¶XE×=7•/\+7,«6IUîð#__/i"0MyÎtP°Jãh]Hµ’k[ûƒC"°9»º*˜èTR@U<*.W­ö+©÷å¹TXñíu-Í"/Hˆj©_xQý…÷á³z³TÏA—ªòS!;L«ª*iá…½NÚ¯ì×ûÐ M¸ÊJXåÅý3˜‡ö4¤·ksm*ŠcUAžtR¥H™$Pi“*i{MbCÎù{η,¨ZÐÃdKý}H¥sîFgÞûDÿ3õ-ºãUz송©AEÒ«ŠyÑ…1ÛÞ{Õƒ–4‘þãaŸ•ß*Ÿ óÖ;bÅ?6¯ýá³ :ë/£Û°Ð×Emcá_0ù”Z¿§3 *ùs§ !„ø>1ÂË–JýP< W=ªJæk/°2‹þ2Q±H¨*¾+©îç)§T~úéŽ*M"ºÖ\ƒd‰TÎmXË_t*‹n¾*­!u$•‹÷áÚõ·<Ü|ݵשÐ'ôÏË©Ï>…oáNm:HtçAï£Ò?Â32R-°M ¨T”ð2†öTdK¥rýƒCTkð€Pv)<15(¬BÕ˜ÝãþÞWÿñ°üz+3•àæÃW•ɪþ>¨÷£û.•s°MìŽsdäœ×ŠÍ9ŸÆ"ö•ó×^*.ºû“.©êÉú‘üÕ˜–y5TÆ8†ÓZš©Þ¬Ïª¥]wÞãóò¥µPihßûÚ)«@? L#ߎ‘‘GÔ±ˆú^Yw]ÛÛ3¤ô=îB!~¼ïn³¹öZuÃçÐÉ•½ü Ô.ãñEUÂÒ+¢³ ¨4ÝV¯;ï!÷l8šJ‘ÓþWC¥±\}­]Ôc_óñ³úÓ].üŒ$ ~uË63Hd).™àï®ú[šªçfUƒÝ-cœ„+kXÚ€nÝ#²òƒþq~á¡R+ ÙD¾lÉ,jæÇ ª¼XFu;‡¥JX:‹‹—Ÿ*“s&•~ãþY¥H„—)¹4yäaÄã&_jUÞ|S™7T«~‰Ì?­ÊíÞnV“n» aèŒ8=vk-'1U-æ- jgØùQäœ×ŠËs>ì…¹ëÿ¾Óž»ÑÙŸ"éÕ…Ítƒä×±¨\ëòòW=º©CÜ#¦ä…ÿ̆W&ô}ßòþãoª^§°à7¦ŠfP稛Nä®lÑyíâ™Õq; 3x8(XÍ.4ó¬ºï¸P¦ zæú¸¨õ/¿ú¾c,„"¢«W¯Fû/ºbÜ#pí5X„v'?ù¨~2%‡¡9à‹ªÜÙ*+alHd “N©Ô%£¿ ¨ín¹¥R0Šf[+Uí´ÆUP-”k®ªÊb©,j´IUƒë µ½ê¹"ߪ¶0ÿ/5Ý`•œ°ç®vÚ;omì}w5cJ;•Š0è gŸ6â6S&Q]a-ä9REž:]™’«¼Þíÿª{%²¨Šq«-jí¿ïTî5€·Ÿþºuש‹b%2QïWÒ„ÐÞ!ò×S^ ¬³NåÙÁÿ·œï ¥³B÷Ýj:ʾÅ!0HÍÌ“!YĪ Ò•R9ÄÙRªõ<„µ×´©2¦Æêýì¶ R$QïÇ_/Ôvÿ(©måý£”JIJ–H'Ãâ¿ashNrÙl¡åÚ§ÒE^zªVãòÙcÜAÎùø:çA‡#˪VøáGB¯áP8úû3¨$[¬¦Þl]HõâŒ9ö}e²2Sû<ê¨ ˜¾ªAÉAúWñÿ™MîÛ¶P:hœ_õZ}ú¬Þëu×áôSíLP±ÑÎ&œPò¤JªŒºc2¢ûÚ.™ bv5»Ð¤Êjÿ­Îz¡iiÅ2ÂØãª'±@Z5ÀÕ×Ú÷H!į-Æ=Nª)üª®‚qÇÕì4':hsM-ªû…lTå¥õVÕU}®³vàbt–5CGÞ4Ðq‡šÂ0‘š­Å)£ªŒÖ\£¦ÐÛÑ\ýúAšXIÍòþÇOWõ\jf”“¡Ãvõ¾ºš…C·eÛ>­(Øt#ôÛ¯~ÐW7øùoÔØ ªu´íV5H0­ìh¡*!¶«)ÿ¶5S¯÷:é3æ¦0£ªšz³ÃvUÝßZåþFÆ1k§* \¡Å&UÙ kåÍœBMQé¤Zß[mQ£Cm£Nèë¢fqDMMzê t—zÔÅQåyﻫ¦0\u¦WñµË +S+«^‰j«U޶k#5Ô€äÕ Ô€ç*«Ô Æ:vjÚÔï!ç|üœó *”}÷AóÍê\Þ×Z{Ü£³?E3¨Ù–[`Û?ªÕ"ÎЛš¨’þûU>¢,4̧¦˜ þ3™Õ Ô{>é¤ zîº©Š¸îŒA1ef Ú¨´Ÿ²Ë ýv•êgîÛ>:¯½­¹š<`ðA55ïÇÏp²dI©žŸ^UÏig òJ5h|R%íìJB!~mF!!!ßÊ(0XM6ª.ÿKÝâ»$"®”,SñcGQºTÉïߨoàW9ç]T³×xŒ8î$¦<¾è¾#ŽÀìsà9B?½G!„øyzª‹·Ä$å§téè ÔúîÁÂBñ«ÚúêQVZéøû¥êPB‚!„BaÐNŸ8òý¿¬ºvjZÝ9çÕ@íLÉÕÔ«ý‹ÇwÉ„Bˆø'©AB!„Bü¢â25H.ù"„B!„’@@!„B$€B!„H!„B! B!„B „B!„0@!„Ba€$B!„Âýð@ nÃ&d³µ#›­'Oïý±pôø Í{˜ÍÖŽÀÀÀ­¿ïÀAͺ9rç‹ïÝ1Hå+WÓ¼ó,Šïâ!b rµZòùß$ç‰øL~æ‹;ž-Ûv-kþ\4Ÿ´iÒÄ÷þ óøÉS6nÚÂÉS§xõú ~_¾`ii‰½}~Ö¯OÙ2¥â»ˆѧÿ Ž?Aþ|yùsá<’%KßÅÒH< ü‚".‰ XšAþ4P37´*æ ã»Äâgzç¹gÁÇ/ê~ùlp¤]|—J!Dd~Z ðüù V¯]À­þeÃÆÍôéÕ#¾÷ÿ?kþÂÅP¥RErçÎßEúå-_¹š©Ófàzܼ~ó†×oÞpààaªW«ÊÌi“155·r^¾r•½ûöpñïKìÞ»æM›hžûö®7iî·o׆¤ñV^]Ÿá…§úÛ&ž„- °M|—,¢‰'Á?´³«^^(öÇmû•'üù·ö~¿,Q|ïñÏÑw¯6ˆ­»÷ðøñòåµ£Bùrñ½[¿ ùíBèúi@ÊT)Iš4)^^^dÉ’9¾÷ý?mÁ¢?ùüù3™3g’/óoؼuã'NÖÜ·07ÇÎ.¦¦¦\¿~oöîÛMº´ ùc`¼•5½ ¦¦¦šë,™õ?+oß½ãóhî7nÔ Þ É ½Ng…»/<÷Ðö¼ð„ª«àn_H‘8^Š¥‰'À'4&Ìaùƒ/s\{¿CÃ=€õ7¾;»vïáø‰S4jP_H~;„º~Z `anΖë8rô8Y³d¡JåŠñ½ïÂ@øøø2vüDÍýâ.ÎÌ™5T)Sðþ½m;tæßÛ·Xµz-={tÃÂÜ<^Ê›>½ ›\×röÜyòåµ£¸‹s|Â(uv„áeôóøC¿Ôýw>°ò ôq‰ïÒŠ¸ô9ºîŒïR!„ˆ‰Ÿ:F gŽäÌ‘#¾÷Y˜ýâãã @„ ™6e¢&°¶¶bÔˆ¡4nÖÿ€nÞ¼…s1§x+³}üØÈ¿.–’'†ÿU‡]·UÀ¹güîÆƒGÕí¤ ÁË?¾K$„â[bܹ{…‹ÿäü…‹xxxÚÚšreKÓ£[ׯ®çè\7wwFF«–Í#,ó÷¥ËlÞ²¿þþ›wïÞcddD¦Œ(]ºíÛ´&ujë딯\M“OºdÑ|òäÉÍÂEK8yú4oß¾ÃÌÌŒ‚öèÖ¥E‹8DZ¶àà`6mÞÊæ­Û¸ÿAAdÉ’™Z5ªÓ¶uKÊWªÆ«×¯Ø¿g'¹lsFØÆÓgÏX·~§ÏœååËW‡„!½ %K§m›VؤK÷]oÖÆM[2|d„ÇûLÿƒI îÜÒ{ÎÛÇW×>zŒGãéåEòdÉÈŸ?6 r¥ ß|Ý€€V®^˶;yüø MMɕ˖&R·N-ŒŒŒbµ?7oýÃÊUk¸rõ¯_¿ÆÌ܌ܹrQ«FuÔ¯‹‰Iä§§‡‡«Ö¬ãÈÑc<~ò??R¥JIüùiÔ ^„4‚{÷îknÛæÌé õð•nooïo–ÿþƒT®V ###®]º@Ò¤Iõ–©×°)×®_ÇÔÔ”ËÏEèe¨Z£6wCË7nÌHš7m‡)âT\û¾¯_CÑ"ôí?ˆ»÷D(GñRj]œ‹±vÕòHËúþ½ó-æè±ã¼{÷ s *D®±·/«÷ïkŒ@nkm àåõ²gžÂ¿àï—*­(‰ äI uí «ã×ÃÖ`ùe¸ýÞzCÊÄP$=´,òîé¹ôtÜq;-·¨?c#§ÿÜ]7˜sŽ>„gê± É Tèé u>Ö-6úë·Ÿqšú¯;p¶È¸üJÝþ³dN£ÂÕ×`øŽÖ®nÀÆ›pé%¸ùªã”7 4̧ŽS¢poH:V{ßclûGƒ›o! H¥D5ÎýK@âïhºùfœU·í¬¡XFX~%fÛxòô)å*Vðø¦-[Ù´e+ ý,èòõõeËÖíìØµ‡'OŸðùóÒ¦Iƒ³³Ú¶![¶¬zËûøø’¿PÍýó§OðÏ¿·YçêÊõë7ñòöÆÊÒ’R¥JЧgÒ¤IM@@kÖ¹²k÷>zD``™2f¤FõªtêÐŽD‰ôó¾jÕmÀ­þÔoRÒ¤IY²lW®^ÅÛÛ‡ÔÖÖ”)]ŠîÝ:ÇzÒŒçÏ_°Öu§NæÕ«×øùùaÚÇ¢EhÛºùòÚi–Ío¨ñS«×¬ãø‰“<ñã È”)åË•¡]›V$Ož{Fåjµð÷W‘eçŽíùc`½í-ús)S§ÏÔÜß¶Ù•‚ööšûÁÁÁìÞ»={÷sóÖ->~üDâD‰È™3ÕªV¡E³&$L¨ÿEó»œ'ÁÁÁ9zŒõ6qïÞ}>|ø@Š”)(\¨ Íš4¦Dqi¡1t1þêß»o?}ú"(H;mÈËW¯X³Î•‡cb»_“ÀÀ@†Å–mÛ#{Æ–­Û™2i½¶BĘñ Ñ[g¤)z”$I’àäX”Bí111áúõ¾puíܽ‡!ÃGj‚€¤I“R¶L)Jw!qb5BÒÏÏ~þàèñ±~³ìí 0dІ  7«MÍÕ2hƒ Ð<ööí;Z·ë  ’%KFÃúõhÓª%yí´_Ü»÷ìcɲ_}Ý‹_"a„8.„cÑ"z­{÷íçϥˉ‰Ysæê2¤§\ÙÒzƒÖ®\½Æ€ACôÖóöñ¡Cgm`™*­Z6§{×ÎzÁჇõZ‡òäÉMù²e(_¶L”é6ÖÜNښܹl£µ/º•÷K—õ›@Oœ8¥w®9zLïù«W¯ižÏ!=™3eúêkÕªYƒ!ƒЦUK½Ç{tëÂAhÖ¤Q¤ë8x˜?‘9S&\œ‹éõL0jìø½ß #ÀãÐ4# }‘ˆËõØ­dIuò€sFHú›ôôT\®þëò „J+ôƒ€BéÔúyt: ?†Zk´y§Œ0­ŠúK¨ó{Ü´€zljíc_¨Þƒ°uó§Á¥ qm`„jx ÍìÕvz‡‹«†—QwvŒü˜~¨ ÂŽ@Hˆ~`’êç…~Å¡šÎ)zó-´Þòõ÷åÄcÕãá˜ÊeÓ¼|ý tØN¬,ü .{a»N&áÄ“ªò ªB¿© ÔÖi˜üóoè²BPÁ¿ ·‹ªÌçý}}T[ɯf - ê—qÉßÚÊy±Œpº£ªˆŒ¯.‹U¨ž‹ÚyÔvªÙªŠûœóÚmu. ¾‘Eá’ z9CÖ”jà-À‘‡ú=‡ÛBL—ù Gh¶Øû*øÊš2òí粂½­´AŒ—4Ùûî©ûÛÿ…Ë/Á!}ôßÿWžj`8€•™~ SÉ’%£c•7uáâE>|¤Êmk«y\×è±4½\™2fdÅÒÅdÍšPßË»õàïK— bƬÿ±få²H_7Göì¬X¶XS9rÿðæ-ÛhzëüýýÉ–-+«W,ÕÑ/_½¢nýÆš†–ýQ·N佉'fñ‚¹”,¡ÒýBBB˜ý¿yÌ¿PÃׯY纑ŽíÛFë8é~º8cÕò%šï+oª×ªËóç/UÉ+TÐ>F¿~~~ü1d¸æ~Ù2¥˜=sºff²ûдyk>|ü¨R|Ö®§G·.ß,÷÷l7a„Œ5œ6í;ªWᯋ“8qb¶m׎PüÇÍo7ÀÙsçõzÖ¬\F1'm4¾zízF‡6„œ:}†çÏ_è›ÿåódëFMpajjÊâs5ÁO`` 'OÓüŽÌ·€fMé7ÂpD»GÀËÛ›£Ç´óáuíÜQ€:чkɉ.??úôêÁÀþ}Ø¿/-š7Õ{Þ.OòçÓ^¡ößÛw¢ÜVó¦5A¨Üíî];ë-óèÉÍmÝ=Q¢DÌ™9]ï‹$mš4Œ;:Ê×Û»¿fJTss3æÌœ®—'nmmÅÔÉÚkÞ½{Ïþ‡b|Œbʾ@~Íñ7z¤&;&h©W¯_ãááå¶æÌš® @¥×L›¢Ý'_Nž:­rmØ´YÓ£TÄ¡0}zõÐTŒªW«ª7£Ôž½û4·ÝÜ´)NéÒ¦‹0^¤]›VÔ«[›zukSµJ%½ô°¨ÓÐ`Þ½{¨–ùöm[Gû8ë¦Ô\¿qSó– hZº>~üÄå+W5ËëÞŽË<ÍÍšj‚€ г»þxž¡•®˜úëÌÿKû·ñ¦6H`kªÊmx‹uæØwÉ*jƒP-÷ãt†¯ì¸ Ï>©ÛÁ!*Ï=LÿâúA@§¢Ð@ç¢Öÿ;OŒ½õÑÞ.l£ @åÓ.­ ©¿‚ß7ü‡bád•¯ï˜J‡Vö­ÌaR%õ7»š~ÐÎAÿ¸]{õkL«¢Ÿ¢•4¬¨‰tbÞ7cVîž{À3tüÇôªêbr?ƒ›»;tzñÆ­ @µ4­Í‰?{î<>~Œt[ ì§×Bj™*ÂU¶Æ©×“–ÞÆF¯B÷ðQÔŸŸæMk*w ¾ûöî©—RvÝè¨Y½šæ»ýýõ-,ÌÍ©^UÝþÊoeTŽ8©éÙ·07gú”ÉzÓçÌ‘ƒ^=ºiîë~GÇåvK•,¡—Z9vÂ$ÆŒÓþ-â@ýºuôÖI™2¥æX6X/hÔ ž^JÌ×êÿµódÝzmg§íôz@LLLòÇM½ÀËÛ›“§£÷.~?Ñî¸uë½” š5ªGXÆÈÈ‹XÌgž2e ºvîáñ/_¾ðùóBÁÂB›üúµœVVVKž<9fffšn5//íúW®^ÓÜ.Y¢¸^…9L’¯´Þ=vBs»l™Ò¤L™"Â2¹lsââ\Œsç/púÌÙ([~”ŠÊS±By½Ç‚‚‚ðññ!0((B‹···O”¿"˛̑=;9²gçÁÇܼu+ZSÂ>¢M©Vµr¤Ë-âÀƒêGþÆMmkŽ]žÜšÛ>dÒ”i´mÓJÓ…š5k¦O™£ã4~âŽ? ¨ ûìÓbÔ*beiIîܹ¸sç.~~~Üúç_ ´çâß—4×&èܱ½æòïG h¹v]022Â¥X±•;&R¥ŠØDœ"Er’$I¢é¢ f¤àh¹Yõ̪¦?hwÎïm«‚‘¯ßÞÐÎõø¡zìòK5O˜ÖQ êå¬rßA'?D>V!*ÓÂîÐr®¼¢z5çWh€ZyÔßP>›~ ¦Pºˆ½)!!ê˜øª$&Úcäù•AÙI"¹F^js¨c§ ÂCøš]·a›çHé,* úYŽ?App0 Îåâ.??¹ls’,Y2MŠÉ­[ÿPªd‰Ë%Jñû=,ÿ\³­HRÓ¥ÓöÂéþ¦„UïoÓ& 1Jæ¾}ç.Ѻadyÿþøúø¬÷z^јô ¼#:ßÑ..Αþ¦éؾÿà!_¾|ùf/÷ØîˆáC8yú ¾¾¾zAޱ±1cGŒçž×.^*,¨–v_ßÏøûûB‰'Ö|~­nñ_:Ož=®7þL78 cjjJA{{9 ÀÍ›·ô-a8¢¼~­ÍýOhjJ–Ì™¢»j´yzz²výŽ=ÎÝ{÷" & >Ç?:ŒuZÑYÿΘ†èæ…ëzùò•æv.Û¨×Ïž-«&xòôÙ7·û£œ¿ð›6oåÒå+¼|õ*ÊåbsL³dɬ Þ¾{÷Íåyóö­æþØñ“;þëw÷Ú^çbT­\‰ýUÊ’e+X²l9²gÇɱ(%Š»P¶L©h¥Ì[°H/Åf¸Ñ.T0ÆÇ¡dqîܹ ¨tŸBí5Á…±±1íÛ¶fÏÞýy=×·Qº»øæ2a¢3p2Qä¿)Ñ•5KÍ퀀>~üé¬x‘ bç®=ìÚ³W3 N¤bq>¼x©=¶‡!›­ÝW— áÃÇßœïGl7mš4ôîÙ]oŒ¨ÞàÈfò óøÉSÖ®såÌÙsÄï%Ú©AŸ¿h+åfæf?|„ùµë×)W±*ÓgÎæÚõëQ?ÚçÏ_4·csuÖ:ÝÍ_[_·§âóçèø‰­    FóVmÙ¹{ÏWƒ€Ø2ÓàããóÍå?~üã€C÷ý˜=sû÷Õë½xðð!ë\7еG/œK–aåê5ß|µë]™9ûšûû÷¥A½oYF&²ÃÇO¨@ ˆCa’'ON…òe•ÿäéS._‰Ùø€ÿŠF*G}T9è¨3@xÊ)måÕ-Ü©òµ+î¦ÑùHù„ÎKï¦óñ±H¨ßÓ ËÊL Ž ¿~t¥Jg;«^ˆ°ÅÁpþ¹£Pr ØÍQyüqiñEÈ;fŸSAQTA@lYëÌ4äÍc4ü°šêÔŒI¹£WýaÜ?ļҢûö+H•J?*õ‰æ@P7wwê5l€?†pêô™¨ƒ€X ?Ë\t| ÷=—Û­_¯Ž^:)¨«OGeý†T®V“«VsÿÁƒ(ƒ€_UlΓØTêµÏ‡øy¢Ý# ›.áééEP$é%±åíãCçn=59œ¹sç¢OÏî´·ÇÒ2•jUíÔ%ÂÔ]?‚¹NÊQT9¤_caa¡ù‚ûÚ“·NEÙò+S”ý(ËW®ÖÌÂdddDçŽí©S»66é°07çÆÍ[Ô©ßè»^ãóí—´Y’o§Ó$J¤ßRß´I#råÌùÕu’˜é@655¥k玴oÛš'OqîüÎ_¸ÈýªôãÇOŒ?‰7oÞ1xPÿH·¹{Ï>FÑΔӵsÇHSÓ¢«hššâÀåËW4•}€òåTP¡|9Í€õ#GkÒ‚à÷ tUÉ© àþž|TóÖ‡¯ø¿ÿJ ©{ýÔ¡AîúŸ¾¨Šqdi5¾jVŸðëÇ„•,­«rôwßQƒœ?Ö¶œß~•WÂÎæP#wÌ·ÿ-_@·ÝÚAËõìÔŒA¹­µùøcô{XbJ·òoδÛïÕàè0ãO¨¿¨}F¡cDÿîªzT¾W¢„Ú“ ]Ú´tîØþ›ëqpøæ2Q‰Mé·„ŸÆ,“- :\36EŠäôíÝ‹reJcmmE„ Y²t9“¦Nu¹tgºq.æDåŠß¾ÖŒ•Õ·Ó~ÔvgÍž«I 3}ÖlÎû_„e¯_¿ÁˆQc5ï_åJhß¶ Ù³eÓ¤&åµwøa ¿Êyþ·vÈIôžrÝ16°D;È^ûíÌ«W¯£]SÇŽŸÐÌ“ÐÔ”õ«Wþ´T‰ô66š\ðÊ[LdÊ”Q³Þ×Ö¿{÷žæ¶nÎ`\Ù°q“æv‡öm4 ßwl-rOžh÷7:]ÚÉ’%ÃÜÜL3ˆ7]Ú´‘^T.:&LH¥Š¨úcòüù ÆMœ¬™¢sÙŠ•tìÐVo3¨™{ú¬ùÂnѬ)û÷ý®ã8qbŠqàÜù |øø‘%˵3g…õq(LÊ”)4ó÷‡+‰'¦p៘\ý%—Æê7¦±P-ùaÐûî*Õ&¼€ ¸ó^{?cèW‚nzOP<ú¶‡öp3ÜLÆ¿ã+ÅÚ\ ÌmZ—<÷ Úoƒ;nª’>üHÜË.iƒ§ °¥iÔ= ±u[çÛ$ýöòï}ô¬ø ûúáãGš4iDÂhä×ÿJÂÒ*A ÞL™*å7×yûö&í`ÆÔ)”-Sê›ëÅ„Mº´šTG##£XGÇÅv¯ß¸Éú 5÷ŒŒ áà¡#;~2Â5‚6nÙªù®··/À‚¹sþsóåÇæG£ë16tsÂ?ÉÛ·sÝ¿Öçâ줹}ðБH»?ß¼}Ëù iî—-Sšï¥÷eÉò³Ðéã@m˜/_¢<߽ᱰ)'Èb~þðŠ9iׯÍ[¢œÙçáÃGÎ¥FM[P¾r5ÊW®¦7ƒ@ÆŒ˜Ó8º*o˜Å#_Çmð ŒT¨é-“ëô ,ŠbýÕ×´·ó¦VWîÕõµÐã{¶öïm¸1~.™Ô¢aþ ÷µ¾ªÛwH÷¢lÒF ü£_)ÿI¯·?콫½ï¶ ÉÔ é¯ýéki̵G÷‚eºçxdç·‹³vp°ŸŸ6nŽr[aséïùŒùùE>‚{Ï^í 0vyrGë{àÙóçz÷#ûnÿüïöoýv8ëL\pñïKü{ûv¤ÛñòöÖû ø–ïÝnPPÃGŽÖ¼åÊ–¦E³&šçGáwM·n‘;—m„ïNÿš*ô«œ'vyrk®¥°rÕš(—½výú/û"~ŽhfffTª¤íÊ[¾r•Þô‡L˜4%Bå+:tó½Ÿ=®wu½   .^Â…¿.Æx»ÑQ«fuM¾a@@ ®7ÓÂë7o:|T”ë7¬__3¾¿¿?ƒ‡×KúôɃ>ýj¦•´¶¶¢|Ù2ß]nÝîÁȾ4“ë| ìܵGïŠÇ=ÖÌB½ûÐ p>~üÄÀÁC5÷-ÌÍ)S*z—Vm­Ó ôòå+ú ü#ÂŒ5'O¦i‹Ö4iÑZïu“%KÊãÇOxüø /Ñ\r>Ì¿ÿêÿ°èv+ß¾}‡öºj~(*V(ÏÔÉ~Xën öÖ¦bùr€þElÒ‚Âw ?xÇIê1¢ Ï:«}¬`Zý|ÿž:SŠ^y­ëþÝz ½÷hï×Ê£]ß$tÑ™páEØO¿ oªk „éT4b9Íu~OÃWäÓ'S-ßwÝÔß´Ó׿¢3ì&|ÚQø›¿=–>R©t2îößÓH<¾@óÍêJÌÑ1ø¼Ð™%Ø?:ïP×jÓÔþÛÛÉ– 64þú_…ìÚåó¥Ñ>ž-šƒµ“è¤>xø0BE%¯]tzÒ¦L›!uÔÓÓ“ ¡~£¦¬Õ™Jñg[¿aS„+Æ®^³Ž3gÏiîG6 _dÂ7’…o;qòK–}ýßúí¨_·ææêøÒ³Oÿ¿ë=¦eëv4lÒ"ÚÖ÷nwͺõüúobb°Áзw/MýáÅ‹—šÙÙ4ÇK§nqòäi½+Ò{yyѧÿÀ¿#ñåGž'ÆÆÆzAÒþƒ‡˜·`‘^ÐȲ«hи9ýÖ4 ã ŠõíÝ‹C‡âïï/ ›4Ǿ@~’&MÊ;wc5 b…rLœ<???‚ƒƒiÖ² NŽE±°°àúš9ÞÃx{{`jtÙ¤KGÛÖ-5WÖ;uú %J•ÃÞ¾~~~\½vý«”)S0bè`†ŽPÁ±ã')Y¦E‹8ÌÅ¿/é“'Œ×Ë•Œ­\¹lq;§&G_ôçRο@``Ö¯ÆÂÜœ5ª±zÍ:@ý8”«X•|ùìpwÿÀå+W#ü°~mÚ´ËW®R¢ty ÚÀÔÔ”«×®ëå-vïÖ%ZUñmÔ >›¶làð‘£/UŽ"EH”(!÷îÝçqhÊ‘›»;'NžÒ âíÑ­ §NŸ%00+W¯Q®bU*T(GÊ)xúô™f6!Pyûa­ñoÞ¾¥u»Žz‡‰±ñ7¯$9bøR¥üv7,¨ŠIXêO˜ á’%Š“(Q"½–ŸØ3e$qâÄš ¦K÷^äµËCjkkæÏãí}e—àˆN]âK jÅÖÐkL 7+]•œÐ¬¬*1ê¨  ¥w_8ýT;(6eb˜[Cýáeհ׬¶ZµfgN¡fºª3Ÿ¾sFèîDùÓÂÛÐjÊ)8úP½æÉjŠÐae hcÜŒ³pꉚÇ?‘‰šfóà}í¶Ú‡ëuÏ– ÌLÕ8€zëÕ¾¥K ››mMòk§@}á ¹fA©¬*mêô“ˆc¾6}è?ï ÇLu‚¤‰ÔÕ€ug j˜/âT¥ñ%—mNÍœéW¯]§Båj$Ož‚~}zi®_3qüê5l‚/Ÿ?¦}§.äÎeK¶¬YùäáÁ¥ËW4•¼«ÖШAýhÏ(ö#ùúúÒ¤y+ ÚÛc“.-=âî=íÉ“>½M”W/gŽäÎeËÐTÓ³æ°ÿÀ!2eÊÀÇ#Tì#›>ô[¿)R$g쨑ô4€ÇŸP©ZM ÂÊÒ’—¯_s]gŒÓú )TðÛä÷l÷Ý»÷Ìœ¥ЦU MN{ŸžÝ3^]O`ɲÔ­]Ks=£Õ«±ïÀA@ý”¯T Ç¢E äïK—#Œ ðŽÅt«?Êa äÏÇüÿÍþ¾ à;R'rç²åÞý\»~k×õ[¹“'O΢ùs£Ý0kÆ4šµl­itø÷öm½4ÝïvOOOüüüôŸ¾õÛP·N-ü5z,þEúý_·v-&Œí²Çv»ã&NÖô´§J™R͛5aëF<|H@@ÃGÁu­jØ«R¹"Íš4ÖŒ+ðòöÖ|ÿ‚jÈK`”@3ÑGØXÅXû…Î333V¯XJ·ž}4³Ù½yû–7‡Þê-gmmÅÌiS$0`ÑN S¹RvïØJÍÕ°²´ÄÄÄ„tiÓÒ¤qCöíÞëPêÖ®ÅÖM®T«R+KKŒ±²´¤ZÕ*lß²‘Ý´ü›·þ‰Õ…R¢’0ôòÛS&§PA{ÌÌÌHœ81ùòÚ1fÔ¦Nš ÐDL'éЮ ÷î¢Y“ÆdÉœ™D‰‘Ô‚ܹsÑ­K'Ø£wu×ïU¾lþ7kyòäÆÔÔ”$I’'OnLLTl—Ô‚M®k4 yòä&a„$J”ˆÇ¤S‡öäÎ ss&LH† é©[»ë׬dÆ´É1°bºÝ3gÏé]Qw@¿>$MªÕnbbˆaƒ5÷ÿºø7ÛwìÒÜ?vóæÌ¤˜“#ææ˜šš’1cÚ´jɾ];4c·Íµ~âÃ>O@M[¾qýæÍ™I…òå°¶¶ÂÔÔ””)SPÐÞžÁƒúsxÿŠ»8ÇxÛâ÷aòG‰8:—Ô´ÌŽ1ì‡ÍF׎?AÇÎÚKŸ_8s2Ú~‰ÊËW¯X°èÏ­Ó¬IãX}! ·?$Õ t¸­~;µê6àÖ?ê²ËÝ»v¦ßÞñ]$ñ ’óDDWØä)W¯^ö:¥KGobš¥}¯\{ÓŸúÒß´aãf5¬áB%Çu¦kK™2ÖÖV1Ùt¤ÜÝ?àºaSŒÖ)îâ,€B!„ø!bœ[ÿü{픩­ò¥(¿bÏÞý 1Š^}è•`çî=¸êÌÉ_­J•ÿÜ<ÄB!„B„çÍòþ‹/5WBu1¨ï¹Êã´™³Ø·ÿÇŽŸÀ¹˜#æææÜ»wŸ{÷µ#ë,S¥¢G·.?äu äÏÇ£{ÿÆ÷î !„BçÀÃGõ¦5 äϺrð·1oÎ,zõéÏÓgÏøòåK„ù¨A]xÑ‚¹¤I“:¾‹,„B!Äw‹ó@ kæÌ±7þŒ8ùs–IÀmØ·· ¦#Õíãí¡LV(ñ'œ}£ÊÂèòñ½—Bˆø´ý_˜qn¼ P:Pjå‰ï’ !„ø¯Š—#ÀØÞú¨·’K`啸>?†±‘þŸ‰ô¹!¾ÓÔÓPo½jðòo8ýj¯ƒ¹çã§LuÖÑpè±;¾ŽBˆØŠ³¯içK듪%}íuèº \2­U|’ØËž ô‹ïR!~'wÝ`øau»¯ .­S††?/Á¡©=X™ÅwI# –Æ!„ø•ÅëWt–”°ªäH_aÁ_ñ}8âÆµ×ªåÌh8¸ùªÇ¸k{ò1vÛmàªÖzÚn…dcÁf2Œ:ª¿Üî;à¼ÌFCú)Ða»¶O>j˱ûäû$EÀí÷0ÿdž¦¶]s ¼õÖßö‚¿ ÏlµNÖé0ñ$‡Ä÷â÷±æ*C–0½*¤6ks˜SLÀç@8x_-¢Ò‡rÍÒ~&ÇWrˆÞwÑ­·êv‘°÷.ØÍQß.‹Õw€ÕØy[Ýžÿ—Z>0²LW·G…œ3ÕýûÕc¹gk÷i÷õ˜ñxíßGX! W¼·Õ$0‚:vêöáñ]šÿ¦ig`õ5L½ö†±ÇµÇrß=¨½.<‡ÜÖàåË.C#׈۩»~¿ ¸ü Ê,…{T…ÁËöÜU­aF…î»áŽ$4†'Ÿ`Øa˜|*¾ˆ¿¿_ªÿ.™Ô÷e˜Ä&ð¨?<î5s«Çzï…àž;'PŸÉ‘G¡ï¾˜¿n@°JÿyüQ矫F€ÒYÁ:´"C2(ŸM¿lcÃ걦ÔcwÝÔ¶„.¥²@º¤ñ}„…ÂpÅ{ ê‡à¥g|—äû<ú Zátÿ\oÄýëæOï†ÀÇáÇZ=vª± òÅÛaBA< ïé'õß&Y|—äûD5XøÚëŸW†° *,Oßã <›oÁ§/úËFUÁH`ôíí†UüƒTªQxï¼µ­€BˆØK•Dýïñ¹ŸÕgÒ"¡öùð©6é’ª4çÚ€>¦t¿b3¨©=Œ;¡z"wü««]5`!„ˆ?ñkÊe‹ïÒÄ ÝŸ<¶ÃvØò8e€Þ.Äê®ÿþí&O¬þ›™Â‡a(ÞÏ$!~O…mTúü3õýö}ò9ÒNR¹ükj†wá†W¡½x©Ìâï»(5H7Þª©PAõV!„ˆ_ñšôÜZoUÝÄ ¡›S|ޏ‘V'·÷â Õ¢6ËG\;šÐÙQ ÚËœòÇl·tußtPtþAjJÃÍ·~ξ aZR-ò>ªÁøn¾ª²ßs «Öõ2YÕò/=aýuu{Û?j€²Ù~ìw‘iè¯GØìCßÒÔ^ýÿðC]»ø>²B!â¥wåXsMUAU(çÖ»Ôñ}8âF mkXÍ5ÄTU †"éaÿ}è¸&ÔÏÝ÷ Œýv3¥PyÀSNÃÿÎÃÒKêqß°³†yŸ±Bħiahi˜p&Rº&TTß1i, ƒ,½ Í7«k³xú©ešÐŽúQßEùÒ¨ÞÆ7a×m¸ÿk¨4ÉC©ÛÕr©ÎB!âW¼ô…¨ ÀÚLM{w¼=t*߇"n¹6†™U*M†d0§úÏyÝåõ¡a>HšHM/:±¢6wÿ®Û÷m{reX\[ÍZ¬ö­eA5SˆBü8ã+ÂÊúà`£¦ 57Uß'[›ª€<Ì¢Ú0®dM©R‡2%‡eÔºa~ÔwQo¨‘K•ÅÚL¿¾|–”jà2ÈlABñ«0 ùÙYëB! ÍáPi¥ Þ UˆBˆoóôTó½_½z5Úë”.]:ZËÉO!„qÊe±º @‡"!įB!„q*¡±ê ¨c§Æ4!„ø5H „"Nèß%B™_âÊÂB!„BˆŸK!„B! B!„B „B!„0@!„Ba€$B!„ÂI „B!„’@@!„B$€B!„H!„B! B!„B „B!„0@!„Ba€$B!„™DwÁwîŸâ»¬B!„q"µeŠø.‚?ô!„Ba€¢Ý# ‘²B!„¿éB!„ÂI „B!„’@@!„B$€B!„H!„B! B!„B „B!„0@!„Ba€$B!„ÂI „B!„’@@!„B$€B!„H!„B! B!„B „B!„0@!„Ba€$B!„ÂÅy àééÉê5ë¨V«.7nÞŠÕ2·oß¡u»Žvt¦ˆSqº÷ìÃË—¯âûØýPS¦Í °£s|ã·áççG6[;V¯YßEù¡:véÎa#â»â'ºõŒ†kÿLF@¦iÐz <÷øþí?pWÛÝs'òç}ü!ó4Xu5n÷sÞýýÔýk³5n_[! •I\mØÃÃ1ã&²ïÀA ŽÕ2ÏŸ¿ ~ãfq(Ì”‰ãñýü™é3fÓª]öíÚN¢D‰âûþÒ6nÚÂá#¹véÉ’%‹öz}ûâîýûìÛµ=¾wá—òäéSÊU¬Ê¢ùÿ£RÅ q¾^xE‹&y²äñ}D<\ Šg‚ xø¦œ‚k¯áJw0ŽÃ&Ä&P)ä´ü9û¹®!$ ÷µž1ÅÏym!„04q¼}÷ž—¯^1aìhR¥JEûN]bµÌòU«I–,)KÍ×Tú-S¥¢UÛ\¹z çbNñ} …øi:uhßEñÄÁjäÖÞO•ÚnƒûîÛ:vÛ þö2Æ `IÝŸ·Ÿ•r‚•ÙÏ{=!„0dqÖŽd›3ׯ¡~½:$I’8ÖËØ¤MK»6­ôZþ³eË €§§W|;Ü?| gžülÜ´Eïñn={S·aÍý›·þ¡Eëväµw ˆSq†ƒ—·w”ÛíÔµ ›4×{láâ%äÎk¯¹ïè\’ “¦Ð½gœ\(TÔ™ “¦@aGg†  @Á"ÅèÛ!!!,_¹š Uª“;¯=%ËV`Þ‚E„„„p÷Þ}²ÙÚ±s÷îܹK6[;;ÙvdƬ9zejÝ®#­ÛuÔ+Ó”i3è?p0 ž°##GÃ? €©Ógâà䂽ƒ=zõåãÇO‘îû˜ñq)Y–Ícaå:yê4nîîô8˜BEÉkï@ûN]xôèq”ÇsùÊÕd³µ#00PóØÕk×ÉfkÇÕk×èÙ§?­Ú¶gòÔ”,S<ù Ñ©kÜÜÜ4ïi¹ŠUèÒ½Î%Ëh¶uêô4nFžü…pt.É€?†ðé“Ç7×ûðñ#ýü½ƒöN :ß(÷£nÃ&tëÙ€‡‘ÍÖŽ5ë\iؤ9ù ¡dÙ l٦ߋóøñÚwê‚}aGŠ+Aÿƒ5ûàíãÃèq(V¢4¹ó¤ZÍ:í$•Þóáó÷íó=7¨·Rއäã Âr¸ùV™÷¡èB0Y¦Ãè£úÇ$0X=–y$EÀþ{ñ÷> !D|úå wìÐ.B+è¥ËW011Á¡pÁø.–©Rá\̉ƒ‡µ'N>KõªU¸wÿš¶ÀÈȈÙ3¦Ò·OOöî?@ÇÎÝ"M‡Š‰5ë\)^Ü×µ«éÖ¥+W¯eáâ%lv]GŸ^=زq=ƒö`é²L6ƒÆ °jÅR:uhϼ‹X³Î•¬Y2³ÏNÊ•-MÖ¬YØ¿g'U*WŒQ™–­XEš4©™6eµkÕdízWªV¯Åí;w7f=ºuáèñLŸ5;ÒõkV¯Æ›·oõÆ‹9zŒäÉ“ãâ\Œ/_¾Ð¤y+.^ºÄÈáC˜:i<¯ß¼¥QÓ¼{÷þ»Žç™³çñó÷céŸ ™5}*W¯^£ï€?>t0Ë—,`äð!¬[µ€kׯӶCglms²|É"FÆß—.3lĨ¯®çïïO³­¹zí:ÇføÐ?8rô £2ÿ¹dÝ»vfãú5¸+Æ ÁêõhÕ¶žÌš1¡ƒqæÜ9ºöè­Y¿wßþ>|”Áƒ°rÙŸ8+FÞ}¹}ûNŒÊ!âV@°ª”ûÀÅ0ò(8e€Â61ÛÎ_/à­7,«UrF|¾Ó8óö´„,)£ÞNÇíª—bmC°OÝvÁ?ïB_ã9T[ ùRÃÞV0·&œ~wÄ~ÿ½ý¡Ôøà kÀæ¦Àª¯VÇÔxŠÚk¡@ØÑº9Á„“ªâ¦ý6˜zz9«mdM 5Ö¨ÀE! Mœ¥Å¦MŸEÓÆ ±²²ŠïâP£zUFŽ‹·ææœ;___ªU­ ÀìÿÍÅÊÒ’åKajj @æL™hÕ¶ÇŽŸ Bùr±~í†õëѬIc@õ°<}úŒå+WѵsG²gÏFšÔ©È‘=›fŒ€‹‹3E‹:PÐ^õ.89åô™³;~‚V-š‘Ë6'I-’’(Q"rÙæŒq™êԪɠ*è¨\©ç.\ÀÓÓ‹Å æ’0¡jÆ|øðçÏÿéú… Úc“.‡Á¾@~@•+VÀÔÔ× ›xüø ‡öí&{öljŸœ)U®.]Æð¡ƒc}<³gÏÆ¨áCÈe›“ÀÀ@zõíÏ¿·oc—'þþªùÕ&]:MÏ”µ•5+–.¦Dq$P±õ›·ï˜>s6ÁÁÁؤKéz[·íàþƒ‡ìß³Ûœ9011¡ÿÀÁ¼|ùŠôé£WÃ<¨?eJ—`òÄq\½v¥ËWR´ˆ?áå«WŒ3R³L²äÉX¹j >>¾˜››Ñ®MklllÈš%3Ŝٲm;'OŸ!OžÜÑ*ƒˆ{Í6éßOgû[ƒ‘Q̶“)9ìj¡WðÀ]ûÜÈ#°ú*ìl鿾A¥`dYu»|vH1N=†¼©!mRØ× *æP•u€—ž0ì0‡h‹Œ¿—&˜™BH¬lEÓƒehêPjs(4®¿§Œpâ1øÁ¼šÄT½~@<û¤–¿õV_ƒÅµ¡SQõX\Pr 9•cþu'„ÿiÿ©@@UÊ(q"†V4•+V`øÈ1œ8qŠÕ«røè1Meàì¹ 4jPO”(î‚•¥%çÎÿõ]@ø”ª²eJ±~ÃFÞ¼}Kz›È+’yíòpäè1Z¶iÏãÇOðòòâó—/äÍk÷Cއ¥e*½ûémlH›&H¤M›÷"]ßÈȈêÕªpððöïË»wï¹~ã&ýúôRÇóüìòäÖ)S¦ LéÒœ»ypíã™Xÿx–)£*ÏwîÜÃ.OžH×IŸÞ///z÷È¿ÿþ‹›ûüýýñóó# ÊígÏ'GöìdΔ??? àÆ­[Ñ'I¢wìJ—*¡é¡Ê˜!=VVV̘5Jw¡|Ù2”/[F³NÑ¢EX»Î•~{÷ñòÅKüüüðòöÆÃãLI#~˜‰¡lè)ÿÒ&ž€J+àR7Hƒ9¬Í#\¼ñ&¬½3ªBõ\ßÞNátÚÛɃ¹)¼óQ÷3§/*x¹úJ=îŸUE=ñW~y²L׿ŸË îôQûXØÆŸ€cÕ1ðWY|ü¢þ;fc#h½:…™aXí¶Ž>Tÿ›Ð>fd- B×]ðñ3¤L‚BŒ_>5Hר±ã¹qóKÎÇÂÜ<¾‹£‘}úáepssr™»÷Ð¥{/œ‹²hÁ\öîÚN©’ÅþÓ^õjUyôè1>äȱc¤L™B30üǤIñxZ[Yñ)Šq±eanމ‰ nînQ.óøñ4iŽq‚Lž8žÝ;¶Ð½kçonÛÍÝû'!Í_™òªÉãSì+áÉ“'×¼ÿffflÙ¸Ûœ96r NÅiÝ®#wîj“¢GÇÂEÒ¢iÖ®^ÁÞ]ÛI™2Å=Žâûå´„bÕ_ý¼*­åž;¬¿þc¶¿ö:˜$P)<±ö‘¾ç¦Æ©¤ËÝô+ä_³§%œî¨ý[ßH=þ%*.‡³Oab%8×¶ë£Â1i§‚ê«Ár‚JurvóÞG­8¥µPÿ¿w ƒBü×ügzý¹”­Ûv°nõ ²fÍßʼn FµjŒ3–¿/]æÝ»÷T­¢*tÆÆÆX˜›ó!’Öï÷ïÞãìù¬GFFFzƒe£ëÝ{•#oeõ\ëÖo ZÕÊô覥)I3àã×7Ë2ÅFüùÈ”1#áÒå+T®Tuº¦H‘7÷ˆÎ»÷ïI™*ò¤æ°ô‰˜–ÿÃÇb™*êã¹}ç.,,Ì™5c*F¡/Š´¹¹yíò0vôÈÏe̘!ÖÇîýû÷z½2™2fdÆ´Ésíú &NžJ³–­9}ü(¦¦&lÙº1£†S¿^Í:Æ ŒcýúâçÈ– óИ1,Û&¶Ñ6…TjLÓM°ì2´wˆ}ÙÖ\SS€®m¨ýìYFs& §Œ‘Ïtö)Üx«zr…f…G²¯e²ª¿Ïjàp÷]ðôl£Êàí¯žK¢í åµWÌÊ(„¿‹ÿDÀÞ}û™1ksfNפNüj*T(G@@ ã&L¢ˆCaÒ¦I£yÎÉÉ‘C‡hfó¸ð×EÜ?|À¹˜c¤Û³¶²âÍýé0Â*ùº¼¼ôgN:tè)S¦ mZõúF¡É¸º`OM…Ô Õ;wïêm'A‚jÖÖV¼~óFï±÷nQ·”¯êÕª°k÷^Ο¿ x *‡ýÖ­xþü…Þq8uê4ÎNQO5¿¢î1l`±··Þ±::{Ž]h®|ØÝCãáá!p»~ýfÄãn½¢EŠðäéS2¤OO¡‚öš¿ŒÒ5 O÷ äè±äµSi^Û¶ï¤RÕšxûø A *H÷®]øôɃ/_âíãCPPFhχ'OŸâáégï«ø1®½V©19BO•´IÕÿ:oÝëL¬V?/4)­ BŸ½êZ±õñ³ t¿A.¾ˆõæ4ÛÐ^~›=vCýõêvS¨k§Ò~.¿T• M­ÚòþzoB¡t"òÉë„â·õË÷\¹zþƒ†P«FuR¥J©™  ¡©)öö¾cë?NR J—*Éá#G5ƒMÃôí݃z ›Ò±KwZ4kŠûw¦N›IÑ"”+[&Òí•(îÂú 3~"ÎNŽ\¾z5k×cb¬ßR»eÛlllp(\ˆ“§Ï°kÏ^ôëƒqèr6éTžùêµë)QÜ…Bí)Sº$ËW®&{¶l¤·±aízW>|DÎ94Ûµ±IǾÙ¹{òç'k–Ì”(îÂÆM[ÈŸ//©S§fç.5Ũu‰¸´]£ZU.^‚eªTÓ©à7iÔµë\iÓ¾#}{÷"aBS.^‚iBÓ(çÙ/ZÄ„ 2lähš5i̫ׯ™·`a„åž<}JßþƒhØ Ÿ|D6[;Ö¬s¥a“æä+X„’e+°e›~oÅãÇOhß© ö…)Z¬ýÆÍÍMó¼·£ÇM X‰ÒäÎWj5ëpðБ(Ëöº7m¡i‹ÖäÎW—’eY纺tïE^{Š•(Í´³ Ñ+K—î½°wp¢@¡¢´hÝŽ;wïémÿæ­¨Û° ¹óÚS¶Böí?€]šã½Îu9rçãâß—¨Y§¾*s­ºü}é²f’Rf¸,ªÿ>þÚÇÞù@«-`9,Æ@Õp7ô#0ï‚J-zé sΫÛÜÕs÷ÜÔ²)ÆAšIjoC¿ž†›Éú¯]nT^©ÿXê‰0ü°ºíå½ö@ú)d4ØÏ…íÿj—½ó^½¾ë ¨¹ÌFCæi°è¢þ6Þ‡âªçÓN‚6[áÃçø>òBñûˆ³@ iÒ¤¤H‘œ   >þÌ¿·o3yêt ÚÛS @~Íroß½ãö;88¹¿PK²mûN½mEg™øb™*ÎÅœ8xX[¡ô÷÷çÔé³T¯ZPiRš¶ÀÈȈÙ3¦Ò·OOöî?@ÇÎݾ;·{Í:WŠwÆuíjºuéÄÊÕkY¸x ›]×ѧW¶l\Ï ýXºlS§Í qìZ±”NÚ3oÁ"Ö¬s%k–Ììß³“reK“5köïÙI•ÊcT¦e+V‘&Mj¦M™DíZ5Y»Þ•ªÕkqûÎ]ÆEn]8züÓgÍŽÑvÿ\²Œî];³qý\ŠcÐàašJq@@­ÚvÀÃÓY3¦1tð Μ;G×½5ë÷î۟Ç2xÐV.ûçbÅèÑ»/·oßùêëN›1‹ª•+1sú²eËʈQc©Û  éml˜1u2U*Udáâ%ìÙ»PÁjãf-ùôé3§OaþÜÙ$H`DûŽ]ðóóTPÒº]>}üÄØ1#éÞ­33çÌåË—/z¯LÿƒiÒ¸!S'OÀßߟ^}ûð]çøï †/à_ÀÈ£à” Û¨ç?@é%pú Ì©ËëÁ O(ù'¼ö‚föp³'¤1‡¡·3¥€€ ¨´>~u`zU8òê¯WÛ­”^{«žO?8óN>ïÐ äÎ{xï«–hºv܆©U`k(—m€ëoô÷©ãvÕÓ±¶!اƒn»àŸw깿žCµÕ/5ìmskª}ë¼#¾ß !„ø}ÄYjP˜å+V1iêt2gÊÄæë022Ò}ò`ë¦ šò.X U"&nϘ6Ç¢EH’81»õäé³gäÈž=Öçøïi¶Iÿ~: UÉû:]vî¹Ã?½´©Bå³CÖé0í4̬¦Ò‰LŒÁÒ ò¥QËÜz O?ÁÂZPÕV=–2±ê5ðöWã,ªŠNK8üÒ'S­þ‡@];8õ’&çŒjý¾ÅU‘ÓRÝ/“V^÷À>­v•‚‘eµeM1N=†¼©!mRØ× *怡ûøÒ††àícB!b/ÎÚµjR¸p!ž>}ÆœyóéÒ­'×¯Ñ ^¿f%!!!šà l™Ò”)_‰+W3sú”h/Ÿ*W¬Àð‘c8qâ5ªWåðÑc*hMh:ÓÙshÔ ž&(QÜ+KKÎÿë»°t«0eË”bý†¼yû–ô66‘®“×.GŽ£e›ö<~ü///>ùBÞ¼v?äxXZ¦Ò»ŸÞƆ´i‚4A@Ú´ipÿð!FÛMœ$‰æ¶‘‘¥K•ÐôÄdÌ+++fÌšƒ%Š»P¾lÊ—-£Y§hÑ"¬]çJ¿½ûxùâ%~~~xy{ãáñõ©Wt÷ÇÊÊŠ„¦¦ä w¬Ò¤I͇°07'o^;æ-XÄù xóö­¦ßÃÀþù—ܹl5A€Ú¶eï—Îë¨Ú›»û  ÌÄŠP6›ºýÒ&ž€J+àR7HšŽ>„‚iõÇ XšAµ\pìQÔÛÍšÒZ¨´/?Uñ®‘[ÕHêÔÖ8.D½ºµ™3sW®^ãÄ©ÓzËèö˜››áääÈÃGb¼L|Iž<9%K¸pàÐaBBB8zì85ªW ((///R‡¶Ìë²²¶âÓ§O?¼,nnîQ.³s÷ºtï…“cQ-˜ËÞ]Û)U²øÏ?p:9õ±Ý×°ý433cËÆuØæÌɰ‘cpp*NëvõòòGÇÂEÒ¢iÖ®^ÁÞ]Û5-ö?fwÔþøùùѪM{._¹Â€~}ٺѕEóçê-ëþáC”ÿ˜¼–09-¡XFõW?/lnªzÖ_WÏ¿÷›H&Kkî_ZežÎv‚¼i ËN°žUVÂM™ž+å„“OÔGvÿ=¨ž jæ†}wÕc§ž@åÚå{ì†I'¡«m×z¨ $:ÂNí{nj|€±,«—»Á°2ñý.!Äï%Î'Oqêô½ÇrçÊÀëׯpswg÷ž}|þqôW‚Ѓè,ó+¨Q­'OâïK—y÷î=U«¨´ ccc,ÌÍùIë÷ûwïI™2e¤Û322ŠUeïÝû÷XYF]É\·~ÕªV¦G·.äËkG† éI’$¿Ò±,S\yÿþ½^k}¦Œ™1m2W.žc“ëZ|||hÖ²5>>¾øûû³eëvúöéIýzu°Í™ƒ ÒcœàÇŸC—._áÎÝ{Ìœ6E3Ö"mhK~˜Ô©­¿:XZˆoÉ– óÐ-K3 „÷Ú ¬Ì¾½­Õ Àmœî^þPv©v @Åêš›o©–ý2Y¡rNpóUƒ€ŸyhÇøŠ+0¶<´.¤Ò|²¤Tú˜Xs ’%RãJdVeŒn0!„"zâ,8xøÃFŽ&00PóØÝÐÖÙ,™³¨BBèÝoÇOžÒ,À•+W±Í‘#úËü*T(G@@ ã&L¢ˆCa½ŠŸ““#‡ÑÌæpᯋ¸ø€s1ÇH·gmeÅ›7ú_ «äëòòòÒ»èÐR¦LAÚ´êõBiu+ðžz=,þþþܹ{Wo; $ˆÐbommÅë7ú£ýÞëÌÊ×t÷500£ÇNhÒf¶mßI¥ª5ñöñ!A‚.Tî]»ðé“/^¾ÄÛLJ   ŒÐî÷“§Oñðôüáå Û¦î1¾~ã¦Þ2ùóåãö»¼xñRóXdï¯Q¹öZ¥ÈäÐÉÿü Ô.ãñÜצÊ­×ýd¯¾ y稴 FàœIµ¼»Vi=y¬!cr~Dåò'6Q•ôRYTÎ~–Úñ^þ¬»jv¢_ˆ‘ŸÕ6tËzñE|u!„ø½ÄY–eËæMÙºm=ûô£iãFxyy1sÎ\ äϧ©üZYYQ»f F€¯¯/©­­Y»~ïÝÜèÔ¡]´—ù$µ° t©’>r”QÇê=×·wê5lJÇ.ÝiѬ)îÜ™:m&E‹8P®l™H·W¢¸ ë7ldÌø‰8;9rùê5Ö¬]I¸^-Ûv`ccƒCáBœ<}†]{ö2 _Í ›tjœÀêµë)QÜ…Bí)Sº$ËW®&{¶l¤·±aízW>|DNÀÊÆ&ûdçî=ÈŸŸ¬Y2S¢¸ 7m!¾¼¤Nš»Ô£Ö%¬~Ê1ž0q X[Y±fÝz^½~Íì™Ó5àyä˜qtëÑ›öm[Äü‹I›& Ù²fÁÔÔ”üùò2ëó!???/YF@@¾¾?vJÚ¢˜››ÑoÐ`ZµhƃY¶b>¡¯Õ ^æÍ_HëöéÚYM¿:ÁâŸrÅÓµ×`‘HÝ~òQ]P,Sruq0€ŽEU^~••0¶$2†I§T¯ÁÀ’ÚídJ®ÆlÿÊeƒÒY¡ûnhàªùÁ„“!äÒùhWÌ˯@,š¹¡Ï>èTDû˜•I£Žª¶„/0õ´šÈÛŸh«j óÿRS†VÉ {îª1 ¶#c„âûÅY€]ž<¬[½‚?Ñ£W_ÆOšBQ–/]¬©¤L7šºµk2{Î<:véΛ7oX¾dÙ³g‹Ñ2¿‚Õªbdd¤I Ò=ë׬ÄÛÛ›n=z1mú,ªU­Ìò%‹£¼"låJèÑ­ ûö w¿Ü¹s—¶­[FX®Vêܾs‡Ž]º³qÓÚ·mMçŽÚ‹q¹8;Q­j.^Âæ­ÛèÓ«Ögåê5Œ›8™‚íé×§ šjµ6oÖÛœ92l$W¯^ o¯”/W–Y³ç2tø(,-SýÔ‹Y5mÒˆõ®›èÔµwïÞgêä q( @úô6¬[µœ€€ºöèMÿƒI•*«W.Õ Òž7gvyr3aÒT–¯XMÏî]©Vµ Ÿ?ǰ©ò¬­­X¼`~~~ :‚S§Ï°hþÿÈe›S“"fffÆÊå’®ê¹Ô4¨'C‡íªò¿ºJ5zò1úÛB5£_)é[Ĉ£sIêÔ®ÉÐÁƒâ»(qêáÃGT¬Zƒ%‹èÍô_çéé©™Òàì¹ó´lÓž[7Q ¾ø.žB!~ž¡)ÇW¯^ö:¥K—ŽÖrÒ¹*D88¹P¨¨3&MÑ ®-ìèÌá#(X¤}ûTåvùÊÕT¨RÜyí)Y¶ó,"$$„»÷î“ÍÖŽ»÷pçÎ]²ÙÚiö;°#3fÍÑ+SëviÝ®£^™¦L›Aÿƒ)P¨(ö…9zþL>'ìœèÑ«/?~Štß§L›AõÚõhÖ²^Æ©ÓghиyòÂѹ$þ§OúùçÎ_ ~£¦Ø(L©r™;!šç?|üH¿`ïà„½ƒƒ‡ŽÀÇÇWoJ“æ­°µ+Àí;w¢µ^x?¡K÷^Ø;8Q PQZ´nÇ»÷ô–yþü»õľ°#E‹•`ÀCpÿðAo™5ë\)_©*yò¢ZÍ:ì;p0Fç À²«(S¾2v SµFmvïÙ§÷ü·Žklß×èœ3^ÞÞŒ5G—R(T”æ­ÚróÖ?ˆ¯³H¨þûø«ÿ‹.ªÔ¡™gÁz¢6}çà}(þ'˜†´“ ÍVøðY»“ú©Gaó.¨ç« ñ½·B!âBœ.ÎN¤OoÃÔi3yóö-¯ß¼aòÔédʘ‘bNŽÑ^æWg™*ÎÅœ8xøˆæ1N>KõªU¸wÿš¶ÀÈȈÙ3¦Ò·OOöî?@ÇÎÝþ¾~ý5ë\)^Ü×µ«éÖ¥+W¯eáâ%lv]GŸ^=زq=ƒö`é²L6ƒÆ °jÅR:uhϼ‹X³Î•¬Y2³ÏNÊ•-MÖ¬YØ¿g'U*WŒQ™–­XEš4©™6eµkÕdízWªV¯Åí;w7f=ºuáèñLŸ5;ÒõÛ·mÍü¹ê¹)“ƳÏN,,,¸vý:m;tÆÖ6'Ë—,bôÈaü}é2ÃFŒÒ¬{íúuZ·ëHÚ4i˜;g&mZµdÑŸK™9ûš÷¦Y‹Ö\½v‰ãF3|è9zŒ?† Ó+ÃÖm;ÈkgÇìSIocíõÂøøøÒ¸YK>}úÄÌéS˜?w6 ѾcMÚ›——›µäù‹L?†‘ÇréÒ:wí¡9/–,[Áè±ã©Z¥2‹æÿbNª²}âä©h¿®61iÊ4š7mÌâsÉŸ/½û àÌÙsšcö­ãú#Þר 9šCGŽ0tð@f͜Ɨ/_hÕ¶nîî1ÚÎï. ¾‚o\|#‚S(l£¿ÜÒK0¹2ô/=‡j«!_jØÛ æÖ„ÓO óíòGÛÁñöÚ¿²YÁÌ*æˆï=B×â,5(qâĬXº˜º ãR²,I-,رm3 &Œö2ÿ5ªWe䨱xûø`anιóðõõ¥ZÕÊÌþß\¬,-Y¾d¦¦¦dΔ‰Vm;pìø *”/ë×nX¿Íš4À6gž>}Æò•«èÚ¹#Ù³g#MêÔäÈždÉ’àââLÑ¢´W­ÆNŽE9}æ,ÇŽŸ U‹fä²ÍIR‹¤$J”ˆ\¶9c\¦:µj2h€ :*WªÀ¹ ðôôbñ‚¹š÷õáÃGœ?ÿW¤ë[YY‘9SF2fÈ )ƒµ•5+–.¦Dq$P1ì›·ï˜>s6ÁÁÁ$H€ÿÍ[HΜ94@@@ .fЀ~lݶƒû²ÏNlsªšŽ‰‰ ýæåËW¤O¯jUµjTgİÁšm¸nØ­õ„´))?¿¦—ËÒ25j×çö;´·Çuã&>|øÀÎm›±¶¶ SÆ ÔmØ„oß!—mNæ-XD«–ÍЯ¥K•äñ“'¬[¿2¥KEëý8}ö,ùóå¥c‡v”,QßÏŸyüä)%Š»Dë¸þˆ÷5Êò9K£ ¨S«&yrçbÐàa}ú `ãú5$Nœ8ZËüT®Xá#ÇpâÄ)jT¯Êá£Ç(TЛté8{îÔÓ%Š»`eiɹó}W $‰þ1*[¦ë7läÍÛ·¤·±‰t¼vy8rô-Û´çñã'xyyñùËòæµû!ÇÃÒ2•Þýô66¤M¤Ü¥M›&B Ì·¤Ooƒ——½û äßÿÅÍýþþþøùù@¢D‰¸ø÷%Ú´j¡·^ËæM©P®,ÁÁÁœ=wžÙ³“9SFM˼CáBܸuKS¡OZ?é:ºë…±07'o^;æ-XÄù xóö­&=ÉÃC¥9]üûùòåÕ äçðþ=¤I›†;wïáååEù²eô¶=}ê$|¿’’^áB…˜|x:ÓfÌ¢ZÕ*ØåÉͼ93ct\ãò}u(Tˆ­Û¶cc“ŽòeË>½ kW-Ñ6 ÁÄŠP6›ºýÒ&ž€J+àR7HšH»\º¤ÚÛ™S€ÇD\}ï|À/>‚$Öùp÷…6[ š-tùotÈ !„øNqlÙ¶ƒröÔ1M«^üù)Q¦<Ûwì¢i“FÑZæ¿ yòä”,áÂC‡©^­ G§sÇö€º–——©C[æuYY[ñéÓ§^77÷(»÷Ðà`úôêÁû“"yrFû‡?÷À…ĮǟРIs*”+Ëä‰ãI“&5»vïÕ¤ýàëë¡ÂjffFöìªåæîÎýÈ“¿P„í{|Šz.Ƙ®çççG«6íI˜0!úõ%k–̼zýš–mÚk–ùôÉËp-ÞFFFš²zx¨íZ¦ÒßËT©"<ö5íÛ¶ÆÂÜœµë7°pñ¬­­hݲ;¶ÇØØø›Ç5®ß×Ù3§³`Ñb–,]Îè±ãÉË–Þ={P¹R…ؽþo*§%˨½_(dŸ ë¯Cç(*î÷ÜÔø€Z¹aY=°I ®7`ø‘ˆËvÚA!°¼~|ï©BˆŸ%Îû÷6m½®ýÔ©­I›6 wïßö2ÿ5ªUc䘱ü}é2ïÞ½§j•dllŒ…¹9"i%}ÿî=ÎNN‘nÏÈÈH3cML¼{ÿà«)ëÖo ZÕÊôèÖEóX’$fÀ7X–éGÙ¾sæÌš1U3‹OXÚ €©©)I’$‰r2€¹¹yíò0vôÈÏe̘ᇭwéòîܽǑ{É–Må^„’,Y2>~Œú˜'Mªšv?|eáz6ˆ€IDAT™èœ'FFF4iÜ&âþá;wíaÒ”itëÒé›Çõ»D£|ææf ìß—ýûòøÉSÿ¹”®=z±Éu-E ÿ˜rü†²¥‚„Æðì+×Xs ’%‚µ µ)D–f—[~¶ý«¦'Mmß{&„âg‰³ÁÂ2¤çíÛwzó×úäÁ»·ïȘ!C´—ù¯¨P¡Œ›0‰"…I›&æ9''G>¢™ÍàÂ_qÿðçb‘7åY[YñæÍ[½ÇÂ*ùº¼¼¼ôî:t„”)S6­z}£ÐÄ`Ýʘ‡‡§Þt˜þþþܹ{Wo; $ˆÐ²kmmÅë7oô{ïæöÓŽ±‡‡G„Šïõë7õ–q(\0Â@Úí;vQª\E(Z¤Ož>%Cúô*h¯ù˘!ýWƒ§˜®çzNëçë7—µ7nÞÒûäéSœK–áÊÕkäÎe‹¹¹Y„ý1j,Ý{öQïI4Γ:õ±èÏ¥€êMhצyíòp럢}\cë[çÌÇ(_¹ç/¨qY³dfÔˆ¡üûïíR†ßÕµ×*½'ÇW†Q|ü¬ÝOòÅúË<ú}öªt ÝéI…Büþâ¬G ^Ú,ús)ºöPƒCBX¼dI“&¥NíšÑ^æ¿"©…¥K•ä𑣌>Tï¹¾½{P¯aS:véN‹fMqÿàÎÔi3)ZÄreËDº½Å]X¿a#cÆOÄÙÉ‘ËW¯±fízLŒõ–Û²m6668.ÄÉÓgصg/úõÁ8t9›t*=hõÚõ”(îB¡‚ö”)]’å+W“=[6ÒÛØ°v½+>"gí4!66éØwà ;wï¡@þüdÍ’™Å]ظi ùóå%uêÔìÜ¥¦µ.ñsF–.UŠ5ë\øÇPJ•*Á±ã'4Ó`úøú’(Q"ztëJ³–mèÓo õêÖæÕ«×L™>“:µkbjjJ³&Xçºæ­ÚÒ£{¬­¬8tä([¶nãÄÑCQ¦ÜÄt½¢˜››ÑoÐ`ZµhƃY¶b•¦¬j›Yµfí;v¡[—ÎÁìÿÍÃ2•%öòcllL玘3w>q(Ì_ÿfý†ÌÿßìhŸ' Úó¿y H’81¹sçâêµëüóïmÔ¯íã[ß:g²fÍB¢D‰:|ýúôÂÒ2ÛwîÂØØX®#ε×`úV<ùSNA¦äÐ oÔëTµ…ù©)C«ä„=waà õœ·¿êQh±Y ®kgžj×µ6—ÃBñ»‹³@ÀÚÚŠM®k˜„¥ËV²ïÀAJ¸83cÚd-^Ê‹/I•2%ŽE‹°|É"¦ÏœMÇ.ݱL•Š6­Zнkg@¥ãlr]ËÄÉÓ1r ÈŸ•Ë–|õœ‹ézÖÖV,^0ñ“¦0dèræÌÁ¢ùÿcÊ´š4±”)S°qý&LšL¿`bbBÅ åòÇ@M ×£[’$IÂêµëX°èO²eÍÂÿfM×LéódèàAX˜›óçÒ帻»cccàþ}iѬi´kl}ëœI +—þɤ)Ó9f~~~ØæÌÁâóÈ;×O9¯þ+&œNªÛ6I¡d˜TI pxÕsÁœê0ã l¹rÀê0ù” &LÀùçjÙÊ+õ×mœ64Žï½B—ŒBâ3é[|Gç’Ô©]“¡ƒÅwQ„B!DK¡¿zõj´×)]ºt´–‹³1B!„Bˆ_—B!„B 8# âÞÅó§ã»B!„â?Jz„B!„0@!„Ba€$B!„ÂI „B!„’@@!„B$€B!„H!„B! B!„B „B!„0@!„Ba€$B!„ÂI „B!„’@@!„B$€B!„È$º ¾sÿßeB!„ˆ©-SÄw„øé¢ÈD!„Bˆß‡¤ !„Ba€$B!„ÂI „B!„’@@!„B$€B!„H!„B! B!„B „B!„0@!„Ba€$B!„ÂI „B!„’@@!„B$€B!„H!„B! B!„B „B!„0@!„Ba€â4¸}û­Ûu¤°£3EœŠÓ½g^¾|¥·ÌÇhÕ¶yí(Z¬S¦Í 00Pó¼sÉ2d³µ‹ô¯NýFñ}ü~˜)ÓfPØÑ9¾‹ñU»tgȰšûŽÎ%™8yj|Ë Œ?‰’e*Ähÿ¹%¾íìS¨²Ró1Pj yߥBñ_gÀóç/¨ß¸!!!L™8žÇpãæ-Zµë€ŸŸžžž4mÙ†?2mòÚ´nÉÒå+™1kŽf;‹æÏeãú5zÖ­&eÊ”,Q<¾ß/oã¦-d³µÃÓÓ3Fëõí?ˆjµêê=V´Ha ÚÛÇ÷.ýbû¾ôû”]FF0¿üYÌL¡òJ8õ$¾K'„â¿Ê$®6¼|Õj’%KÊ’EóI”(–©RѪm®\½†s1'6oÝŽ§§'{wnÃÚÚ ???–¯\M—NHž<9öòGØöñ§ðôô¢YÓÆñ}ü J§í㻿´ŒŒŒâ»(?\PPÆÆÆñ] ƒäw@¹l°¯• š€b‹ çn¸Þ3¾K©/ô£ÀoøQBˆßJœõؤMK»6­4A@¶lYðôôàúØåÉ£ êÖ©¯¯/7oýå¶W¬ZEÅ åH—6m|?Ü?| gžülÜ´Eïñn={S·aÍý›·þ¡Eëväµw ˆSq†ƒ—·w”ÛíÔµ ›4×{láâ%äΫm‘wt.É„ISèÞ³N.*êÌ„IS  °£3C† `‘bôí?PÖå+WS¡Jurçµ§dÙ Ì[°ˆîÞ»O6[;vîÞÃ;wÉfk§Ù·º ›Ð­go½2yûøÐÐ`ò*B¡¢ÎŒ?I/µË¾°£^@ëviÝ®£æþãÇOhß© ö…)Z¬ýÆÍÍ-ÊcĤ©ÓqprÁ®@aºtïõöî:¬Šì àø—V °°QAE@@PL °kµ;°cÕUìÂî.{íîv×µu×^uW×A¥Œ\¸z ÄýÝ÷ó<÷;qæÌ™ œwæ=FŽ«–2£Éq_GD0rÌ8Ê–¯ˆ]‰ÒøÖ©Ï¾ýÕö yù’~~ÂÑÅGwNDD¤Ú58x(Í~l­})®ß¸ñÁöýÐuy_|| ÎB¦Ê2û™Ê÷¥f+iG—ƒë|e¿bÓaýUõzÄÅèÃ7²Œ„êËávpÒú\ Ý/Pq Ž€ËO”åÏ" õ&°&£ v Ü|ïÇûcÛÜx:Ã`í¨³J9~ÉÊù!„ø|étêØ>Åäsç/ ¯¯‹siÞ¾ÂÐÐ@m›¹íz÷íχ?yÁÚ¦JðQm9DÆ$m³ò"8[CP(`ob âb8qfÖ‚e áá+ðZ•ûAm“¨Ó¥V7ÇÜÐ};üñ,£¯Büw¥[jÐû˜øÍëˆLŒ9}æW"##ñõ©ÀŒY³±²´dÙâ(OüùiÝ®#‡Å»j•Ï>v“F iÑLI‘²-Z„þfÙŠ•të҉…mÈ™#E Û`ff€§§®®.ª|w7WNœ<Åá#GiݲÅl‹bjbŠ‘‘Ål‹~ðø5«WcÜè‘Ê÷5ªÉÊUAôèÖEu®ò×½û<ú÷_ÆŒò§RÅ ˜e5cÅÊUDDDblœEmû˜˜–._I£†õ:X¹“^Á«<ÿþû˜³¿Ÿû¤¶kß¶ ÖÖÖ*X€²înlÚ¼…c'NR¼¸¿lÞÊí;wÙ³s¶E‹ ¯¯Oÿƒyôè_UÐZ·v-†ÿr4OŸ>ãòå+tîÚcã儆†±eëvZ·úQãc 5ªyŸÀÑ£Ç8pè0N¥±Î€S§¥fjjãòå<±²´äô™ß¾èØ™3gR{_¹RBCÃxòôišû8ØçÅ‹`Zµí@ùŠUqtvãè±ã„}ÆÀÕÄÎp¢jU«Æã'O4Ú?_Þ~LÈË—)žÆd·JJ£»uë6/‚ƒ©[»–Ú6 êÕ%&&†óç/¤(÷Sö±°È¦6.àÔ™_ð®ZEÕVV–äË›—+W•<''~Ù¼…ÀÕkxôè_¬sçfõÊe”qqþäë#>WAØuS;k˜°·ðò½û*9L’¾/`®|uNö£œ7«òõy„òõлىꇷ±Ê+§ Ê¿?JÚ/·©úqÝ…Ò¹’:ø–YÀ·þKóm9çNú>k&06PÒŠ„B|žoŒ=–+W¯±xþ\LŒ“:löÅ‹3jÄ0vîÚƒ‡W%š¶hEÃõTèäÖ¬[¡¡¡ênã÷"kÖ¬x•÷dïþ$$$pèðj×ò”;°áááäÈ‘ò–•Uv+BCC¿z]^¼H;cÛŽtíÑ w7W̛ͮí[¨àõuf`Ê–íãÇO.K–,lZ„mÑ¢üì? ÷r´i߉7o¥º}ppˆÒv_áîòˆQc˜¿`-›7cuàrvmßB¶læªõ/‚ƒ¹}çÅK:©^•ª*OyÂBÃÒ,÷k´obû%?“¢-B^¤øl%îó2•:~Î>Im¯ÔÉÓ«²Z›üýÏ?„¾ÛoÆ´)üФ‹—,ë²wªã.ħɚ £ÕSpÒâ·&SîÚê—ü”NõçJ5òìÝp¦¼Ê‚Ä×݉L{ÿç`ò¹L”'šnóÁ:&||!„©K÷Ô ‹–ðËæ­.§P¡‚)Ö·lÑœzuëðÏ?ÿ7O.\¼„¾¾>%ìÕ¶‹euÐZš4j@–,_ð—-ÔöõÅÔh~?wžgÏžãSSé0êééablLHHHŠ}ž?{އ»{ªåéèè|tÀgjž=|¸£´f¾>5ðëÞUµ,sæ,ÀË/n‡Äަ¥¥Eâ‰|ô<òçËÇÔɉçÒå+ŒŸ@‹Vm8qäPЧVVÊy…¼ ýpE>rÜèèh6ý²…Q#†Ñ¨a}Õr=ݤ;àÆÆYp°/Îè‘þ)öÏ—/oº¶oâÿÄõ©I ZÞÿl%~,’5_²ª=²dAOOµ«W¦˜A(±\cã, ìß—ýûrïþ.ZB7¿^lX»Zž |¦RïRz.þ å ¨¯ ¼¨äûŸí¦L'ºüÌ© mœ’¶Ñû 3÷˜¾.ízïÝ>²úÀ¯cË,ð4•9‡'í§É6B!ÒGº>ص{S§Ïdæ´)ª”ŠÔ˜š˜`_¼8†††Ìœ3êÕ¼SäMïÞ»'OŸÒòÇæÝf©òö®BLL,cÆM Œ‹3¹ræT­swwcÿƒªÙ|~ýí,Á!!x”uKµ¼ìVVÃÖí;T è×§ÃGŒÆÌÌ”Š¼¸tù sç/¤kçŽjiN‰>gŸD¥©æ]•¾Ñ»gwŠÛÙqýÆ ¦NŸÅÜYÓñ*_###†A¿>½°´´`˶íèééáæZæ‹Æ´Uf˜WWÐÛx­2 ÷MŒ2½ç™¿áÈ»ÉÙ¬²@™<0â$¼…€§Ì ô%ÜóAýâÐrŒ¨¢ÌØsù1 ;›+ÁJj:¹Â¼ß”ÿˆ<ÚŒô`Âq0Ôƒ^šo#„"}¤[ °wß~¢££Ù²m;[¶©OyheiÉÙ3'e~¿ÞýÈ+Õ«yÓ­k§w[/^ºÌåËWXºHói)3Bm_:¬J Jd_¼8kV­`bÀºûõÂÌÌ _Ÿ Ð_¹óžŠÕ½ñëÞ•õ7±nýFÜ\ËЮM+V­^£¶]ÝÚµ¸~ã -ÁÀÀ€íÚÐ¥SÒ´­žîøúÔdþÂÅ<~ò§ÒŽôéåÇëˆV®">>õëRµJeöí?@LL üØ¢ÇOœdÈÏþŒ5"Í@ [—N<|øˆ? FOOŸöm[Ó­KÒ\ý}{ùñâE0ÓgÌFGW_ŸTªXAõt$Ok‚V.câä©tóë¡¡!e\œ˜86ÍY‡:wlOXXkÖ­gͺõT(__Ÿ\¼xYããÌ™9á#G3nBٲѳG7Îüz–7o”[¨ffflX»šñ'3Ü1±±”*Y‚K§µoj×å}Úµ!,,L5­gYw7š6iÌÑcÇUÛüؼ†††,\¼”ÀÕk°Î›¡ƒÒ®Më4ë÷9û$š=c*SgÌbá⥼|Jüùñ6„Š”ÛŠ%‹˜0i2þ£Æ…mÑ",œ7;»bšþ‰TüPÌ2)i@-7‚™‘2¸÷tpK–¥¶¡™2¥fÿ=`e Ã+Ñ¿4_ð1ëšÂðƒJpñ"ŠX(3 ÕüÀÄb&†p´£RŸÎ[•e• )Ó&,Öd!„éC'ásÑÅwÁÍËúõꨦÑÔf#Fáð‘cœ8*S…BñÿãÕ»´å‹/j¼OÅŠ5Úî›Ì$„B!„ø¾H „B!„’Ô !„B!¾S’$„B!„øª$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´¾¦> Íèº !„B¤‹–æ]!¾9ùB!„âÿ‡¤ !„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…Ò5¸xé2-Zµ¥”“+nžèÓo Ož>UÛfËÖíØØÚ§xM>ó“Êù¯›4y*În]êÔµC~®zïæáÅø‰Z¨¨(llí \”ê{!þë®=aI/ýá2´Ùÿ„¥ÿñ§žû™Ÿ ¼/»¯ÍèVBñ5è§WÁ·nß¡E«¶Tð*ÏÌéSyýú53fÏ¡S—îlýezzz<}ö 333†  ¶¿½}ñO*G¤ný†M æÏ¥s¿bff¦ñ~}ûâæíÛìÞ¾EµÌµŒ3YͲfô)}7õø¯ûÜφȃ+@¹ü—wC`Òq¸ô.ô½t¼¥cŸª]Œn!„_[º¿lÙŠ©©)sgMG__9Œ‰‰ :wåî_÷°-ZPùóÑô‡Æ_TŽH;vÈè*|WõÈqqqßUð› Ü&ÖÑ‘^bzs±†ÚvIï-2C»Íp;ì²§Ü>.þë>¶ÊK!ÄÿŸt»ä×­ ûwïPuÞ2gÎÀ›ÈHÕ²çÏž“;wn"""?»œŒBÑâ%Y¿a“Úòî={Ó I3Õû«×þ e›ö88ºPƽÃüGþúušåvîæG“f?ª-›¿p1vŽª÷n^Œ›0‰=ûàâ«ã&L"..g7† ó t™²ôí?P:oËVâ]³vŽxUöfμ$$$póÖmllíÙ¶c'7nÜÄÆÖ^un š4£{ÏÞjuzAÿAƒ)éT'WF@ll¬j½£³›Zš@›öhÓ¾“êý½{÷éй+ŽÎn¸–-OÿƒyñâEšm“Z=>$66–³æP¾bUì©Û 1Gÿà>÷îݧk^8º¸SÊÉ•–mÚsãæ-µmŽ?A½†M°/åŒW%ofÌš£:÷»wÿÂÆÖžUAkiÒìGJ”.ƒWeo6mÞ¢VF\\3gÏÅëÅK:Ѻ]îÝ Zß³O4iÆ Á?S¼¤+W´vEìJpö÷sÔ©ß»¥ñ­Û€ßÏWí÷¡kü¡Ï†¦Ÿ»ƒ‡ÒìÇÖØÚ—âú_ ñõ˜*_#¢•¯ Î*©CÓNAöñIé;ûnC¹Ee$äšm7IåèWO=J|ÍùUY?xXËè³B‘Ò-055ÅÜ<+qqq¼yó†?¯_gbÀJ;:RªTIÕvOŸ=ãú¸¸{RÒ© n^lÞ²í“ËÉ(–x”ug߃ªeÑÑÑ?qŠZ>5%½é‡æ-ÑÑÑaÆÔúöéÉ®={éÔ¥;ñññ_tüUAk)W΃µ«éÞµ3+W3áb6® ¢O/?6­_àýX²t9“§Ò´IcV._B玘3o«‚ÖR¨`öìÜF•Ê)T¨ {vn£fjiý†MdÊ”‰É'иa}V®bμ×?&&†Öí:öŠéS'3tð Nž>M7?Í;úóÓÐa,\¼”6­[2wö òåÍK‡ÎÝ8~âdªÛGDDÒ´E+BCC™6esgÏ@WW‡ºÀÍ[·éÜÍ»bÅX4-lÎÜù ™9{®ZY‹/¥G·.¬_³ ϲe4øgµû°£X´d];udÖô)„††ÑºmÞ¼Iê©]¾|…ÁÁLš0–Š^刧ÿÀÁ4kÚ„€‰ãˆŽŽ¦WßþÄÄÄ|ô賡©_6oÅÁÞžSÈcmýÕ®•H[L<¼…È8ûü{^p~¯ù—œƒ‰5 yøíð „9`Wk˜]N܇.[“¶?ÔŽtHzU.Y  šH-ÀÀÀã,YèÓÓ–¯ dÀOC(Z´%K8h\NFª]Ëÿ£y‰±1§ÏüJdd$¾>5˜1k6V––,[¼Õ9´nבÃGŽâ]µÊg»I£†´hÖÛ¢Exðào–­XI·.(\؆œ9rP¤°*ÜÓÓWWJ;*wyÝÝ\9qò‡¥u˳-Š©‰)FFF³-úÁã׬^q£G*ßרFDd$+WÑ£[Õ¹~È_÷îóèß3ÊŸJ+`–ÕŒ+W‰±q–/º67oÝfËÖíŒ=’æÍ~ JåJüм%S¦Sá]Ç:¹˜c!„é-ݧ­W·×1eÒââéÚ½§*u`ͪìÚ¾…õëR»–Ë—biaÁòŸTNFªQÍ›ø„ŽUÒM:ŒSiG¬ß¥<:ý+5kTSë—/牕¥%§ÏüöEÇNL“JT¹RBCÃ>8«’ƒ}q^¼¦UÛ”¯XGg7Ž;NØ«WŸ|ü5•´ ===LŒ I±ÏógÏñpwOµ<U§îS<{þ+KË4· Z³_ŸøuïªZ–9sàå·CpˆR†¥¥Eâ‰|ô<òçËÇÔɉçÒå+ŒŸ@‹Vm8qäÐ?077'22’·oß’)SÒÓ“ÄvÊfnžbŸsç/pãæ-îÝ…’7‘ÚX޲în”uwãíÛ·;~ÿQ£yôè_—/I³>ÏŸ?Wµq–,èéé±võÊwüS’>×ç^ãÏý܉oÏÆ õàïÄŒ«.™¬n’”Bd™ÊÔ²ó°ùOØÑr#„BK¤Û}ò³ÿHµdn¾»Ë[°@AeAB½û àH²\bbb¸pá"¶EŠh^ÎwÀÛ» 11±Œ72.Îäz—ªàîîÆþÕR™~ýí,Á!!x”Mý|v++žøÍ«W¯(T¨ Û¶ïä÷sçY¾daªû¸º¸`lœ…~ƒÓºe îܹËÒå+ˆx7emÙ²îôèÙ‡1ã&â]µ2Ož>eËÖíxz¨ÿwèqã'Kv++V­áßÇ™1m2¥©æ]•¾Ñ»gwŠÛÙqýÆ ¦NŸÅÜYÓ©XÁë³Ï[£kœÊgCÓÏøö.=#åûû/•(–?+4vH{[˜û›2ehÍ¢°ó&¬»¢¬{­¿^}161¦¢—ƒöSKƒ7f$3gÏeÆÌ9<ñ‚b¶EY¶x… Û|R9߃ھ>54 ¿rç=5ª{ã×½+ë7nbÝú¸¹–¡]›V¬Z½Fm»ºµkqýÆ ,Z‚Úµ¡K§¤¸åéᎯOMæ/\Ìã'Op*íHŸ^~¼Žˆ`Eà*âãhP¿.U«TfßþÄÄÄ```À-šqüÄI†üìÏØQ#Ò ºuéÄÇðÓ`ôôôiß¶5ݺ$ý€¾½üxñ"˜é3f££«‹¯O *U¬ z:’'5A+—1qòTºùõÆÐÐ2.NL«Ñ¬Cš˜:y"Ó¦ÏbÞÂE„…½¢¸]1–/Y˜êŒA <ÅX8oc'LbÈÐá-Z„sg1iòTUŠ—OêL ˜ÈÂÅKX½f-ææY©î]•ŸÞ›†³y³X³vüù'9²g'`â8ʸ8«ÖÏž1•©3f±pñR^¾ ¥@þüøòEA Ñ5Ní³¡éçN|{ãŽÇ”ï­MÁ« L¨¦FiïS«̬SO¦kà]ÃÄãJ0¡¯ gþQ¶­±B}ߦ%a]ÓŒ>k!„éI'A‚ÿ³Ü<¼¨_¯CÊ誈÷ܽûÕ|j³xá<ªV®”ÑÕB!ÄÔ«w)Ë/^ÔxŸŠ+j´]ºÏ$„B!„øþH „B!„’Ô !„B!¾S’$„B!„øª$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´¾¦> Íèº !„B¤‹–æ]!¾9ùB!„âÿ‡¤ !„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…Ò=xõê«‚ð­Û€+W¯¥Xýú ڴ›eÜËÑ£g=úWµÞë6¶ö©¾ê7ú!£Ûï«™4y*În]êÔµC~®zïæáÅø‰]-F€W%ï4ë*ÄÕµ§ 3,é¥?òO†6›àŸ°o_Ÿˆh(0V^Ìè–I]é9Ðö—´×[¿©¯«¤ì¯-¦žû™ŸÑ5Bdýô*8,,ŒQcƳ{ï>bcc‰O±Í?ÿ<¤QÓ”qqfÒø±D¾yÔ©3hݾ#»·oÁÈȈsg­¶_BBÝüzáU¾\F·ßwoý†M æÏ¥s¿bff¦ñ~}ûâæíÛìÞ¾EµÌµŒ3YͲfô)iäSëzÿÁªTóaÁÜYT¯æ­ñ~B|+ƒ+@¹ü—wC`Òq¸ô.ô½oøl7“>T/E-3ºEÄ—²Ï¡\K]Œ®‰"£¤[ ðôÙsýû/ãFÄ‚»¦ØfÙÊ@ÌÌLY¼`.FFFXZXк]G.\¼„GYwK•L±ß‘£Çyõ*œÍ›ftûi•Î;dtþ/ëú!qqqèééet5ÄwÀÅjÛ%½·È í6Ãí`°Ëþíê¡§ ‹dtk|Ÿââ¿mPö¥|l•—B{¥Û¯,Û¢EX¿fÖ'sæL©nc+íÛ¶V66…xõ*<Ͳ—¯\I5ï*äΕ+£ÚM%8$„¢ÅK²~Ã&µåÝ{ö¦A“fª÷W¯ýAË6íqpt¡Œ{9†ù"üõë4ËíÜÍ&Í~T[6ábìUïÝ<¼7a=zöÁÅÝ'WÆM˜D\\În æ@é2eéÛ mÚw¢MûNª÷÷îݧCç®8:»áZ¶<ýæÅ‹i¶M||ÕµªRÍ€®=zááUIµÏñ'iÜ´ÅK:áæáÅ€Ÿ†šz>ƾý±±µçþƒjuu-[žI“§ª–íÚ½‡šµêbçàˆOíz=v\µ.hí:llíY²l.îžtïÙ€ØØXfÌšCùŠU±sp¤nƒÆjû ícb¨|HöÀôY´Þ–ãÀdÔ„›ïý(í½ ®óÁxœ#Aì»¶7ž+éGk¯@Ue¤’´àlÒþoc•mæüš´lßm(·HÙ>×%5'äMÚuOH€§¡ø È4 M±G•å‰rM€á¡×Nå|²‡îÛ!:.i›7Ðtr.9'€ÿAõ2¾†‹¡ÊRåù'ÃäIëœUÚbÚ)¥~×ÂÉê©\É_מ&í»á*”œ¥œ©Ù°ç–úqÏ?‚² ”õE§ÁÆkJ–œÓìz?õíA©cÙÊ÷ƒ÷)©RŸÒæáQÊú<“ óHpœ [þüºm.„øv2ôÞE§ŽíSܹ=wþúúú¸8—NuŸÛwîpòÔÚ´j™‘UW±´°À£¬;ûT-‹ŽŽæø‰SÔò© À­Ûwø¡yKttt˜15€¾}z²kÏ^:uéžjÊÔ§X´–råu2CâäéÓtóëæ>K—­`Þ‚EÔ©åËÔ€‰d37gËÖí<Îâ¥Ë9z,>5k°`î,ʺ»ã׫/GgØÐÁ,[¬ÔÙØ‚V.àÒåË´ëØ[Û¢,[¼€‘þ?óû¹óü<|DªÇ¨\©&ÆÆìOöY¸xé2Á!!ÔòUm;vÒ³OÊyz2îl슣s7?®_¿¡Þ®71h@:¶o ÀOC‡±pñRÚ´nÉÜÙ3È—7/:wãø‰“_ôùÿ1ñJ'<2Î>ÿCàžœ­•õob âb8qfÖ‚e áá+ðZßÝW¹öê­†R9aëÐÝÆS‚ä:mQž@¬n޹•ÎàÏR¯×oÿ€o ”È»ZÃì:JºlMû\¦žR:¡ËÀ¾¶0Ð Æ…y¿©o7å$¼ˆ„Åõ¡½ Ì? K“ul[o„]7ad˜[~ý®<å£bâàutÊWÜ{¿Žo½€ ‹ÁHÖ7…Ne`Èþ”õ\r&Ö€þå¡dN8Ò!鵿-ä4‡Pä]:ÕšËÐl=xÍ?B©\Êu¹üDY5V@p$Ì« ?WR‚œÈ˜¤c~ìzg7†*6êô¨X%hû!åƒvÛ¼ùzØzjž6P¥0ü°.©îBˆÿ–tK úaaaLž2æM›`ee•ê6ËW®Â¶hÜÝ\3ºº*µkùà?b4¯#"016æô™_‰ŒŒÄ×§3fÍÆÊÒ’e‹```@üùiÝ®#‡Å»j•Ï>v“F iÑLI‘²-Z„þfÙŠ•të҉…mÈ™#E Û¨ÆxzzàêêBiGåé‚»›+'Nžâð‘£´nÙ‚b¶E151ÅÈȈb¶E?xüšÕ«1nôHåûÕˆˆŒdåª ztë¢:×ùëÞ}ýû/cFùS©b̲š±bå*"""16΢¶}ll, /¥A½ºŒô¦:nÈËP.]ºœê1bbb˜3o­[ýÈ€~}¨XÁ‹{÷ï´f‹ÎSC±Î[õT*»Uv–/YHùržèê*1ó“§Ï˜2mñññªe‰ ©æ]•}ûªÜ“?_>J8Ø“ÀÄ€)øúÔdøÏƒßÕ£å}ÅB°ÿì¼ =Ê&mW.?5Qα¡ì¿­œC7w¥ã¹ë,m tXê‡ÂÓ>Þ–‹Î)¯Ô8&{Ð<ò°’‚µõG%¨mB•Îrw÷¤í¶µT3Q©PÒ÷C÷ÃË·°¯2¾"!íƒ&%`º¯²MÍ¢ðûC˜yZéÐ/9§<í8Ó5©\ü`7#©\M®wÓ’J¦Fpø/å R“i·Í‡Ú o9ÈožT¯J…`ÅØ{K½í„ÿ ßM K¯¾0ÊdÄÀýRÝ&44Œ-[·«:Qߋռæ?Š£GS»–Æ©´#Ö¹spêô¯üи¡ZǸ|9O¬,-9}æ·/ ÞO»ª\©kÖ­çÉÓ§ä±¶NuûⓈˆÊ—ó¤jåJT­\)Õíÿ}ü˜—/S<¥ÈžFàpãæ-ÂÃÃS”9%`‘‘<·ððpz÷ÈŸþɋࢣ£‰ŠŠ"&&F-¥-Q-_:v鯳gÏÉ‘#;¢–¯òdèÎÝ»<}úŒÕ¼‰ŠŠRíãâìÌ•«WÕÊÉ‘=)éûô™3Ô©SKµLGG‡úõê0|ÄhÂÂÂÈšõ¿1ˆ[|¾ñÕ ²òý£W0þ(T_çº+¼Cw¡t.õñ–YÀ·˜ÒpË z:Ðæèâ å (w›ßçœ;éû¬™ÀØ@ICIMs{«*ÿU¶‹Š…7±JБ)•¿2N¹aûu¨¶\¹ëöV¹ÛíüÞ¯¬Ò¹“€QnÓÔsòL:“j$u’¯?W®_û¤2ÊP‚€ ÿ*O’9MÔËÖäz7°‡®Û`÷-%(ØvÊæƒ|øuñ¡6ð*¨<Ywî¿TÎáU¼ü@*˜âûõÝ#FåÊÕklÞ°cãT·Y³n=†††Ô¯['£««&kÖ¬x•÷dïþÔò­É¡ÃGèÒI¹#Gxx89r¤¼•f•ÝŠÐÐЯ^€/‚Ó ¶íØIÿƒéÓËŸöÇ~¶lIÇ×$È’% ›Ö1sÖ\~öÅëׯ)çéÁŸbW,å(¶/‚ÈžÝê£e' Srú--,Ô–[ZX¤X–ܽ{÷iÜìG¼«Tfâø±äÌ™ƒí;v1mƬ4÷ñ*™ûijlYîÝ»OíwiAÁÁ!ôî7 Å~9r¤=Ú3$ä%Y²dIñs‘ü„J  ŠZ*¸DN¹•»ßk.C7%'Ü:•‰Ár™À©wÃVÜòÂÁö0áÔ }]háª+ÈI+÷þÖ e|@];XÚ¬M•1æ]ÖšËJnûȪJ'Ù"3ôÜ©œÃÇ$Öãék%ÈÈšéãû¼/·©Ò¡ŸE–¤@ !AI‘™Vy½/ä#ßWQÐj#T,ý“‰êæRîcý. x‘²ãÿ>M®w¶Ìʬ@›ÿ€JÀŽ0ÈëÓÛ+ùµ÷Û¡5¡L%Htÿée !¾ßE °`Ñ~Ù¼• Àå*T0ÕmbccY´–&%K–O;À7PÛ×ÿQ£ùýÜyž={ŽOM%-HOOccBBBRìóüÙs<ÜÝS-OGG‡„Ïõöìùs¬,ÓžÛ/hÍ:|}jà×=i&§Ì™³_$––‰'òÑóÈŸ/S'O$>>žK—¯0~b-ZµáÄ‘C)Rƒ;¿Lþ>SSå¯kÈËO;¿-Û¶cbbÌô©è¼»E–-›ù÷100 F5oö8HDD$…  xqeª—ÄÏíè‘Ãq°·O±_ZÌÍ͉ŒŒäíÛ·dÊ”ÔëI¼ÖÙÌ?\'ñÿÉÆ õàïwc×-³(ã÷=«d?F• )¯71ÊÀáÛ•t—}m?¯«.™‘2ž ñNòÇ‚Šùg•ô”a•’–j$ÊeªÜŽŠUÒv¾6ÈbÍKA‡2)×çûHìí·BßÂÊÆêwØyÏ«NïÝ«1|7AXnS¸ò‘œ{M¯wÓRÊ5>ñ@Y׸Ÿ-*–_€9µ¡SÒr=™~Tˆÿ¬ Ÿèl×î=L>“™Ó¦àâì”æv»÷îãÉÓ§´ü±yFW9UÞÞUˆ‰‰e̸ ”qq&WΜªuîînì?pP5›À¯¿%8$²n©–—ÝÊŠ'OÔG½%vü’ WïïßlÙÌÉ•K9¾Î»Ýäñ°°WªŽ-(ƒ›oܼ©VŽ®®®FÓoüóð¡ÚûÃGŽ’5kVUZTöìV<~¢þíy²6oÙFuŸ:¼Žˆ@WWg§ÒôèÖ•ÐÐ0>z”âxÖÖ¹±²´äÔé3jË_`&»b¶gI1ËÎð£éñnVžÄ|ÿä§–" »|ù*S»–¿þö;[·mW N¬‡©‰ ÿ<|„SiGÕ«°M!l‹I³¼²îÊgdÏÞýjËwîÚƒ}ñâŸôÿ!ÄÿK•Ô›"ÉrµÏÿ ÷’Å»ao•Î~bJ‘ßh´Fù>³’:Ò²tRšÍçxùFéè&ÿmqö¡fû$ŠŠýxÇ÷}®y”¯ï&-KHPò`‚põ©ò$¥l>åå–WióL>6^S¤ùuS %sBV#å:%–Y6Ÿ’â“8£Le Äýd×òñ{÷>4¹Þ Œ›ˆ‰‡¾»”ô£<_ðë"|Ä€Ÿ£§§Oû¶­éÖ%éôíåÇ‹ÁLŸ1]]|}jP©bÕÓ‘Â"[¶T¥¯¯OÍÕùíìï)Æ9´kÓccc/]ΊÀÕXYZR¿^z÷ìñÁÏÖÔÉ™6}ó.",ìÅ튱|ÉB™1H‹Œ;S¾·6U:»ª+…AI99ÚQé`vÞª,«THIÙIÌÚÈCÀq¥i™êÛÃÄêŸ_¯ZÅ”é+§ž„M×”)1ÃÄãJÀb•JšÐ¨ªÊûY§•Îp+'%0Ùü‡Ò©4Ððè­h¤ †ýa’*Ó¬¤2ëÍ×â–·W¦ m¼2ëC¢ÊXˆ´œ}¨Ü!?ûP "’›_ººAoOåºM9©/9M”'3#ÞÍal{Û*AN—mÊS…¾žà·3)°Òäz'jZ¶ßø²´ Dš)3õßVÆ0¼2ùK}jS!ćNÂç$¢‹ï‚›‡õëÕaèàA]!„_Qè[0O6úÐ]ð^¿wSîÊ !´Ç«W¯¸xñ¢ÆûT¬XQ£í¾‹ÁÂB!„Püû fAoåÉÏßaÊ?}«PPùGoBñµH „B|G¬Í`mS{DÉÁ·È¾¶Êø™¡GñIjB!„ß©ôL ÊðéC…B!„ßžB!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„Ò×tÃgÁ¡]W!„Bˆt‘ÃÒ<£« Ä7§q ? B!„BüÿÔ !„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´Pº/]¦E«¶”rrÅͳ}ú äÉÓ§Ÿ¼Í–­Û±±µOñš:}fF·ßW3iòTœÝ<2º"ƒ;¯JÞ] ñ¹öt†%½ô‡CþÉÐfüöåå/8«”û:úë×}ëŸJÙ÷_~ûvBñqúéUð­ÛwhѪ-¼Ê3súT^¿~ÍŒÙsèÔ¥;[Ù€žžžFÛ<}ö 333†  v {ûâÝ~ß½õ61d˜?—ÎýŠ™™™Æûõí?ˆ›·o³{û–t­ß·:Î÷vl!>Õà P.?Ä%Àݘt.=† =@Oží !„ø éü²e+¦¦¦Ì5}}å0&&&tèÜ•»Ýöh¶%(?MhœÑí%„š¸¸8UÀ*Dzr±†ÚvIï-2C»Íp;ì²^™qñ}VB!2RºÝGòëÖ…ý»w¨:ø™3gàMd¤ÆÛ<öœÜ¹sùÑcKÁ!!-^’õ6©-ïÞ³7 š4S½¿zíZ¶iƒ£ eÜË1Ìá¯_§Ynçn~4iö£Ú²ù cçà¨zïæáŸ “èѳ.îž8¹z0nÂ$âââpvó`È0J—)KßþƒHHH`ÙŠ@¼kÖÂÎÁ¯ÊÞÌ™·€„„nÞº­=ÛvìäÆ›ØØÚ«Û®Ý{¨Y«.vŽøÔ®ÇÑcÇÕêxìø ê5l‚})g¼*y3cÖbccSœß—'hí:ŠØ•àìïç¨S¿v%Jã[·¿Ÿ;¯vŒ?þ¼N‹Vm±/åL¹ UX´déGíèì–"å¬MûN´iß €»wÿÂÆÖži3fQ¥š.îåR½~jãDñññL™6wO]èÕ·?a¯¾B®‡Ð &†Ê׈d)=ûnC¹Ee$äšm7Ië›­‡²  ýfe›Ù¿&­ÛqŠÏ£à2Îü­~¼• °þ*™™F@ùEpE=Ë€óÀ{˜Œ‚ã¡Ûvx¥¾ÍÞÛà:ŒGAÁ)0òÄJÐ"„_]º¦¦¦˜›g%..Ž7oÞðçõëL ˜BiGGJ•*©ñ6 <¸~ã.îž”t*ƒ›‡›·lËè¶ÀÒ²îì;pPµ,::šã'NQ˧& ¤Iýм%:::̘@ß>=Ùµg/ºt'>þËþº­ ZK¹r¬]H÷®Y¸šù °qm}zù°iý ìÀ’¥Ë ˜<•¦M³rù:wìÀœy X´–B °gç6ªT®H¡BÙ³s5kT`ÛŽôìÓŸržžÌŸ;»bÅèÜÍë×oJ»s7?ìŠcÑü9´ü±9sç/dæì¹)êý%Ç¥Ýà`š5mBÀÄqDGGÓ«obbb¸wï>M[´ÂÐÐÙ3§)ÛM™þÑsÔÔÊÀÕ4oÚ„IãǦºþCmœhé²Ì[°ˆ:µ|™0‘læælÙºý«~>Åÿ˜xx ‘1pö!ø÷¼àl­¬ÿíð „9`Wk˜]N܇.[ÕËùí!<} KBÍ¢Iˇ„^°¨¾rŸ•úVó²ûZl€"°²1Ô+“O¨ûgàµtt èí ®BUÿ.F¾öê­†R9aëÐÝÆS‚!„_Wº¥%Z¶|%¦P ~6®BGGç“¶100À8KúôôÃÀÀ€å+ðÓŠ-BÉ݆ԮåƒÿˆÑ¼ŽˆÀÄØ˜Óg~%22_Ÿ̘5+KK–-^€ê<[·ëÈá#Gñ®Zå³Ý¤QCZ4k €mÑ"óÛ;1•*QåJ K1óRröÅyñ"˜Vm;P¾bUÝ8zì8a¯^¥¹Ï»wyúô5ªyETT111¸8;såêUK•DWW—ƒ†pêô¢££ñëÞ•ñcFi|>š'é<ìUßçÌ™€àà¥ÍO¡r¥Š$$$¨Ê)SÆ™‡ñòeè—]p {Ž'e¬ÿ}ü˜—/S<‰ÈneõÅuÿŸÆWƒ3]”צæÛª/‡ðwi5Ì•e-6@±ém, ݯ5•´ ===LŒ I±ÏógÏñpwOµ<µ¥šzöü9V––in´f¾>5ðëÞUµ,sæ,@Ú@–,Ê_àÑ#‡«Ý‰Ôžt”uw£¬»oß¾åØñ“øÍ£Gÿ¸|‰Fõ×ô8¢££CæÌ™©[Û—š¤œiÊ:w®íüYíþ©mœxçÿÕ«ð/>–ÐN6`¨¿_¾ê˜Áê&IéB_Òq~ųk^v.“¤1©ÑÓUÊx‘rÝãp¨l“ô¾R!åõ&F8Üc;<…}m3®½…âÿQº¥í;pŸýGªÍsóæ- (¨ñ6$$лߎ$›5&&&† .b[¤HF·ŸŠ·wbbb3ne\œÉõ.UÀÝÝýª¥;ýúÛY‚CBð(ë–jyÙ­¬xòD=½'±“Ÿ\x¸zGrÿþƒdËfN®\ÊñuÞ%ô&ï܆…½R{ÂÍ›7ÕÊÑÕÕ…dûسÅÔÄ„>©´£êUئjš×£ÆÐͯ™2e¢Fuoê׫˵?þH³Ý>ç8šp-ãÂ[·q,URU†c©’(##£T ÊúÇOž¨-{þâÅ'>ÖÆÖÖ¹±²´äÔé3jû½þÀLRB$wé±’–Sä]ÌÿòÒIOþ‰>ûPóòî%»ð0LI r²Ö¼l×¼pàŽú²÷gªT¶ü©>méÑ{ð,*RÞûí€Fk”ï3@{hYZ™mH!Ä×¥7räÈ‘éQ°uî\,]¶’7obfjʵ?þ`äØñäÏ——þ}{£«««Ñ6Y²dáþý¬ ZK¶læ¼xÌÄ€©üñçu¦O ÀÂ"[F·!F††\½ö¿þv–NÚQÚ±”j]‘Â6,Y¶‚K—¯`jjʹ 6|$%ìØ¿/:::œ:}†?þü“.:ú  #:*Š ¿lfÅÊUèêêâ×£‹—.çÂÅKèèèÇêµëX»n~Ý»âîæ ÀË—alÙ¶,Y²`hhHî\¹xøð!6màáÃGøÍõÊ5hõcs®ýñ‡#_¾¼aeiI¦LFÌœ=—ׯ_£««ËåËWéÝw/^¼ |9OÞFE1kÎ<^½ ÇÐЀsç/°xérʺ»«N¿ïsŽsõÚ5Ž=F÷®14Tž<}öŒu6Ò¨a}òæÍC¡‚˜·`W¯^ÃÄÄ„ûþfÔ˜qlúe‹êÿQ¼ìlææüuï>[·íÀÄĘ?aÊ´™œ=û;òç§A½º¼|ù’UAk¨[§6… ¦ùyøXëêꢣ«Ëü‹0ÏfNæÌ™Ù±s‹—.ÇÄØ˜öm[gôGZ|'žEÀü³`k Ññp'Þ…ž;•;ìsꀑ¾ÒI_ø»Ò¡ŽÃêKJ}ž`žYyÂ0íüú˜g‚SC×mÊÌGã«+ÁÆ›XuÂÞ‚‘œzSN*O š”Èè«!„ß^T”rWåÉ{7*?¤`Á‚m—n@öìÙ)ëîÆá#ÇX¸š_ϞųlY&Çø]ú‡&ÛTð*GXX«V¯a݆Md22"`Â8J—vüÜê¥Ø·ÿÇÅØØX­-<=ÊrøÈQ–-_Éùóñõ©AÀÄñ)“¿.lC||<[·mgÛöá]¥2W®\U ª{Wåñ“'Ìž»€ë7nÒ²E3úôòSîvyóæáÎݿظi3ñññT­R™2.·„°é—Í;~’Ê•*Rµr%îß¿O“F ÑÓÓÃÆ¦gÎüʆM›±·³£xq;œJ;’+WN~Ù²•«‚¸pá"ujûÒ§—úúú-R˜üùò±eÛ6V®æÜù T÷®ÊˆáCUwáß÷9ÇÑ$È•+'eÝ9rôËV¬äÐá#³-ÊÄñc011ùà±ïÝÀú ›8|䎥JbeeEBBÂ'š´±SiG¢££Y´d)›~Ù‚ŽŽ®eÊpÿÁ „Jb pâ]V^ÿÏJªNÎwyù¶VÊ?[{Ö]¼YaX%%¿¿R!°6K;8ú—2wÌXyñÝ€¦PØRó²­Í”l¶à,Ì:£,í]”§‰@.S¨j;oÂôSJ'¿I XÞH fìs(3 ­º3Ï(ÛÔ·‡™µ” D!´Mz: _#!Zd7/ê׫ÃÐÁƒ2º*B!„"¼z7ãàÅ‹5Þ§bÅŠm—îÓ‡ !„B!¾?!„B¡…$ãò?ìì™]!„Bñ%O„B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH_Ó Ÿ‡ft]…B!ÒEK󌮂ߜƀü€!„BñÿCRƒ„B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐBé\¿~ƒ6í;áìæA÷rôèÙ‡GþM±]DD$ÓfÌ¢šOmìJ”æáÃGÝ.ßܤÉSqvóÈèj A“ftïÙ€¨¨(llí \”ÑÕúh]Åÿ§kOAgXÒK8äŸ m6Á?aŸVÖÞÛJמ*ïO>ë‰ðç3Íö¯·:mÉèBñµ¤[ ðÏ?iÔ´ L?–áÆpåê5Z·ïHTT”j»W¯^ѤY –¯ ¤ZÕ*L™4++ËŒn—ÿë7lÂÆÖžW¯^}Ò~}û·nƒŒ®þÒý°±µgÿƒ]•Tɵýo\v´„_Z@O¥S_;ââ?¿LkS¨^¬Œ5Û¾|pÏ—Ñ-!„âkÑO¯‚—­ ÄÌ̔Šæbdd€¥…­ÛuäÂÅKx”u`Ê´™u°+Qšº sùòµòC^¾¤ß€ŸptqÇÑÅÁC‡©ñ5¿wï>]{ôÂÑÅRN®´lÓž7o©®{•j>tíÑ ¯Ji–sìø ê5l‚})g¼*y3cÖbccUëccc™1kå+VÅÎÁ‘º «ïûþ¼~[{Nž:£¶ÜÎÁ‘ù ðÚ~èœÄ÷ÉÄPù´ìY´Þ–ãÀd”òÄàæ‹´ËHž*´åOåû;ÁIëã ç¼Oy_v4^›´>< zí„<“ óHpœ­”#„â¿!ÝNÛÓ¹cµeçÎ_@__çÒ\º|…·oßòüùsÊW¬J»x׬ũÓg>ãˆÃÒ²îìK–Íñ§¨åS€[·ïðCó–èèè0cj}ûôdמ½têÒý‹ƒŸUAk)W΃µ«éÞµ3+W3áb6® ¢O/?6­_àýX²t9“§Ò´IcV._B玘3o«‚ÖR¨`öìÜF•Ê)T¨ {vn£fjlÛ±“ž}úSÎÓ“ùsgcW¬»ùqýú¤s‰aöÜùôèÖ…qcFI·½èÜÕ:µk1mòD¬­­é?hpªãE’[¿qƒô§cû¶?22’-Û¶0q,«V,EOO¶»ð:"BumZ´lÃÅK—?f$ÆþÄÁC‡ùiÈϵwDD$M[´"44”iS&1wö tuuèЩ+QQQ :˜e‹à?lA+—§ZÎÍ[·éÜÍ»bÅX4-lÎÜù ™9{®j›Ÿ†cá⥴iÝ’¹³g/o^:tîÆñ'?ë³’ÖµýØ9‰ïCL<¼…È8ûü{^p~÷ õM T\ 'îÃÌZ°¬!<|^‹àqøÇË÷µ3#õŽü¯ÿ(ÁÅ%Sß§ùzØzjž6P¥0ü°.?ÉèÖB¡‰tK z_XX“§L§yÓ&XYYðàÁ߬[¿‘þ}{“={v,ZLçn~=¸ìÙ­2º}4R»–þ#Fó:"ccNŸù•ÈÈH|}j0cÖl¬,-Y¶xÈŸŸÖí:røÈQ¼«Vùìc7iÔÍš`[´üͲ+éÖ¥… Û3GжÁÌÌ OO\]](í¨<]pwsåÄÉS>r”Ö-[P̶(¦&¦Q̶( ˜¶hþ\ ,ðÉÇŸ1m2V–Ê8“ysfR±Ju6nú…vmZóËæ­Ü¾s—=;·a[´úúúô¨&KOK É“ÆSªdI²e3ÀÒÒ‚ÚõqýÆ J;:­Ü¢µÎ[õì}¿ýv–˜˜FF¦L™(_Γ˜˜þ}üP…-[·3nôHš7û€*•+ñCó–L™N¯òŸüY144LõÚ¾Žˆøè9‰Œ×bƒúûÜ&Jç[GGy¿ô<Ü †?z%¥ U- …¦Àä0Í÷ÃåéC½âJ ˜´í:¶H 6Þ×·ä7‡¢ï†uU*+.ÀÞ[à˜+£[L!ÄÇ|“éCcccéÕwF™Œ8 Ÿjùëw©1Kͧé©R¹" çÍ`íú Ÿu¬ŒP£š7ñ =ª¤m8t§ÒŽXçÎ À©Ó¿R³F5UP¾œ'V––œ>óÛ;sæLjï+Wª@hhOž>Msûâ¼xL«¶(_±*ŽÎn=vœ° (¾s÷.OŸ>£F5o¢¢¢ˆŠŠ"&&gg®\½ª¶mb'Pu¬ìíUËrçRz!!!<·Ù“Ÿ?åø™3eV;V1Û¢\¿~óݵ8C‘Â…)?Ÿªg'®\»öÑö616ÆÁÁž9óà[§>În4m¡8aašÈv,U]]] ©ÓgˆŽŽÆ¯{WÆÀé3ÊS±:uj©öÑÑÑ¡~½:üyý:aaŸ8]Ì78'‘¾ÆWƒ3]”צæÛª/WÒsÝ…Ò¹ÔÇ Xfßbpø/ÍŽÑ´¤ò ñ ¶ëðC‰´·÷*»n*)C¹&€ù{ /ßdtk !„ÐÄ7y"0bôX®\½Ææ k11VŸžBGG‡IÄlÙ̱/nÇí;w3ºm4–5kV¼Ê{²wÿjùÖäÐá#t餤EÅÅÅNŽww擳ÊnEhhèW¯ À‹Áª»òïÛ¶c'ý¦O/?~Øó¬Y9f,Á!/Ó,78Xé´÷î7 ź9>o¤bB‚æÛ~ÉñÍÍÍy¬$>¿æö;/é”b»°Ðw®£¢¢hݶ††† è×—B ðïãÇ´jÛá£û&çèXŠÕ+—1oÁ":tꊞ¾>uëÔbPÿ~dËfNHÈK²dÉ’âç%û»§i¡aaªký¥¾Ö9‰ôUÔÊ&›±Ç)7žk.C7xÖf)÷Ëe§hvŒêEÀ<lýSIó¹ùš–J{{¿J°PÊäcpŸÑ-%„BSé,X´„_6o%(p9… T[—=Gvxòô©êî9(i †ÉîžÿÔöõÅÔh~?wžgÏžãSSI ÒÓÓÃÄØ8Õ»ßÏŸ=ÇÃÝ=ÕòtttHø”žò;Ïž?ÔïÊ¿/hÍ:|}jà×½«jYæÌY€´,Y²0zäpµ»û€Ú“Žôò%Çöì9¥J)·5³à`_œÑ#ýSl—/_ÞÖãÜù ܸy‹ƒ{w©Ò~>wœGYw7ʺ»ñöí[Ž?‰ÿ¨Ñ‰×7›¹yŠòtx—#§}n¾æ9‰oÇÆ õàïwñ«exšÊüÃÁ*‹feèA{%=(<l-ÓNñ‰Š…å`Nmh“,®ÖÓÑìXB!2^º¦íÚ½‡©Óg2sÚUúEreÝÝÐÕÕeÇÎݪe¡¡a\¿q“bÅŠftÛ|oï*ÄÄÄ2fÜʸ8“+gNÕ:ww7ö8¨šÍà×ß΂GY·TËËneÅ“'êé=‰ÀäÂÃÕGîßlÙÌÉ•K9¾Ž®òW9yPö ¤¿ÖÑÑÑܸyS­]]]µ[övÅl151ះp*í¨z¶)¤ÊµOOŸrüð×Imò×_÷¸}çöÅp-S†û7OµròåÍóÁàIÕvïÒ§’·ßå+ê©IºººïÚ<írFŒC7¿^dÊ”‰Õ½©_¯.×þøP~6öìݯ¶ßÎ]{°/^\5Þ#¹Ä15Ÿ$Ô  !6ÙçNU¿äŸ ÎI|.=†è8(’,?ÿü¿p/Y<öV™¨²æå6-GïÁêKi%PˆO£ÊŒC/ßftË!„ÐTº=¸pñý ¡níZXXdã÷sçUë pt,E®œ9iÕ²ÓfÌ"!!‚ °xé22e2¢yÓ2ºm>‰©‰ +xqàà!F ª¶®oo?6iN§®=hÙ¢9Á!ÁLž†kªT®”jyåËy²fÝzF‡»ç/^bÕê5è¿7Ÿþ¦Í[±¶¶ÆÅÙ‰c'N²}ç.ô룚wß:·’¸z åËyâTÚ‘J½X¶"Â66ä±¶fõšµÜ½ûE‹$u¨­­s³{ï>¶íØI©’%)T°}zû1nB ññT¬àEHÈK¦Í˜EÍÕøi`ÿtm_CCCߥ›½üºÍÔé317ÏJãF hÑì‚Ö®ãÇÖíðëÑ•ìVVì?xˆM¿læè¡ýXZX|°®..g¡ß Á´nÙ‚;wî²tùJ""•)H³[Y¡¯¯ÏŽ»000 JåŠ)Ê)[Ö=û0fÜD¼«VæÉÓ§lÙºOå¿K;ا–¯þ£FóêÕ+ *ȶí;ùýÜy–/Y˜jݬ¬¬°+fËì¹ó122"&:†¥ËW¤¸»ÿþµÕäœDÆ»ôLÞÍÆ|ÿ%L:ù³BceY'W˜÷Ô\£½ÁH&Wž|ÊüÿUl k&¸ú‚>ðkØ*‹’4âW¾…€¯£5?žBˆŒ“nÀÞ}û‰ŽŽf˶ílÙ¶]m•¥%gÏœ`ØŸ0Î’…eËWòêÕ+J—vdõŠe˜ššftÛ|²Ú¾>54 ¿êîñûjT÷Ư{WÖoÜĺõqs-C»6­XµzÚvuk×âú,X´:´k£ŸàéᎯOMæ/\Ìã'Op*íHŸ^~¼Žˆ`Eà*âãhP¿.U«TfßþÄÄÄ```À-šqüÄI†üìÏØQ#(T°íÚ´ÆØØ˜ÅK—³"p5V––Ô¯W‡Þ={|“öÕôø5d̸‰<~òÛ¢E˜0Q•koffƆµ«?q2ÃýGK©’%X±tñGƒPîº/œ7‡±&1dèpŠ-‚¹³˜4yª*ý+sæÌ ìׇ9ópóöíTŸÕ™0‘…‹—°zÍZÌͳRÝ»*? LL?uòD¦MŸÅ¼…‹ {Eq»b,_²ðƒ3MŸ:™Ÿ‡à§!ð´° }»6Ì™§ž´ýþµmØ ÞGÏId¼qÇ€cÊ÷Ö¦Ê@Ý ÕÁô]p`bG;Bÿ=Ðy«²¬R!XÝDX¬)}]häÇîAÉœÞvC3è¾]9¦•1 ¯ GþR¦8BñýÓIøœDtñ]póð¢~½: <(£«ò]Z»Žá#Fsõâ9Œ5LŠB!„øŽ½z—Â{ñâE÷©X±¢FÛ}“éC…B!„ß „B!„ÐB’$„B!ÄwJRƒ„B!„_•B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„Ò×tÃgÁ¡]W!„Bˆt‘ÃÒ<£« Ä7§q ? B!„BüÿÔ !„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B é$$$$dt%„B!„)½zõê“÷133Óh;y" „B!„’@@!„B-$€B!„Z(]ë×oЦ}'œÝ<(ã^Ž=ûðèÑ¿ªõ^•°±µOõU¿Ñªí.^ºL‹Vm)å䊛gúôÈ“§O3ºí¾ªI“§âìæ‘ÑÕøOûýÜyÜËUàö;]!ÒŃPÐý÷|Ýrë­†N[2úìÒWé9Ðö—´×[aI¯œ úr8ý÷×9~® 0à+_7MÏM!Ò’nÀ?ÿ<¤QÓ$$$0iüX†•«×hݾ#QQQ,˜;›õkV©½Ö’-›9^åËpëöZ´j‹©©)3§OeøÐÁ\ýã:uéN\\\F·ßwoý†MØØÚò@“¾ýá[·AFWÿ“äÌ™¯òåÈ–-[†ÖãþƒØØÚ³ÿÀÁŒnñfõ%HÖ\†¸øÏ+£åF¥ã˜\ùàž/£Ï.ãÕ²…-•ׄêð"j¬€û/3ºfB‘>ôÓ«àe+133eñ‚¹`iaAëv¹pñeÝq,U2Å~GŽçÕ«pZ4o À/[¶bjjÊÜYÓÑ×WªkbbB‡Î]¹û×=l‹Éè6߉üùò1eÒ„Œ®Æÿ¸¸8ôôô2º"™Õ—”Îê®[pð.Ô(úuÊè•Ñgö}(˜ jÛ%½¯b…¦ÂÞÛÐÕíóÊŒ‹=IÂB|§Òíדu®\´oÛZØØàÕ«ð4÷[¾r%Õ¼«;W.üºuaÿîª sæL¼‰ŒÌØÖ‚CB(Z¼$ë7lR[Þ½go4i¦zõÚ´lÓGʸ—c˜ÿ(Â_¿N³ÜÎÝühÒìGµeó.ÆÎÁQõÞÍËq&Ñ£g\Ü=qrõ`Ü„Iª'%În æ@é2eéÛ ,[ˆwÍZØ98âUÙ›9óÀÍ[·±±µgێܸq[{µsÛµ{5kÕÅÎÁŸÚõ8zì¸j]ÐÚuØ98²oÿAªùÔVmsîüþ¼~z ›`W¢4U«û°sWÚÏÈ?·œcÇO`ckÏÍ[·Uå±+ÁÙßÏQ§~#ìJ”Æ·n~?wþ“Ú9üõk†›gJ9¹òcëv\½öGªuïÞ³7UªùеG/<¼*©Ö…¼|I¿?áè⎣‹;ƒ‡'""é3ìæáŤÉSé?p0¥œ\qtvÃä¢cb˜2 wO]ÜñëÕ——/C5þ$Ú±s7¾uêcçàHŪÕY¼t9ÉgîÙ§? š4cÐàŸ)^Ò‰•«‚>øYßÖïáÆ TœrêK)·É5†„^;ÁrdÝ·Ct\{ª¤¼]†ËO”ï—œSö+»¯M*'.ïSö7ÍÖÃý7@Yé±²ÿÁ»êÇÏ4&&ýJàÖ h¸²…¬cÀ{\ý@VgBÌ8 Åg(ešc*Ë59ÇD!o é:0¥¤øøT/CS&ïþ|EDk~N Î*m3í”R·äím6Ùh¥î}vAl²';æc`Øõ:Ô\¡¼>åÜ>µÝ…Ú+ÝNÛÓ¹cµeçÎ_@__çÒ©îsûÎNž:C›V-UËLMM17ÏJ\\oÞ¼áÏë×™0…ÒŽŽ”Jå‰Â·fiaGYwö%K‰ŽŽæø‰SÔò© (éM?4o‰ŽŽ3¦зOOvíÙK§.݉ÿÌçûï¬ ZK¹r¬]H÷®Y¸šù °qm}zù°iý ìÀ’¥Ë ˜<•¦M³rù:wìÀœy X´–B °gç6ªT®H¡BÙ³s5kT`ÛŽôìÓŸržžÌŸ;»bÅèÜÍë×o${L ³çΧG·.Œ3ŠˆÈHºõèEç®~Ô©]‹i“'bmmMÿAƒÕÆ‹¼ïk•OÿƒiÖ´ ÇM¯¾ý‰‰‰Ñ¸‡ùdÿÁƒ <éÓ&óöí[Z·ëÈ‹àà”Û̲Šð6„ •ËUŸ‰-ÛpñÒeÆɰ¡?qðÐa~ò³ÚþK—¯$gÎLž4zuë°zÍZ|jÕåú›Œ5¿î]9tä(S¦ÏÐøs°ñ—Íôî7€2..ÌŸ;›:µ|™0…i3f©•sùò^3iÂX*z•ÿàgE|[«/CNc%§q Øò'¼ŽN¹Ý”“JJËâúÐÞ柅¥ç ¨%\í µ‹A1+åûF©kÒ åÕ´$6†ìÆ0ùħÕ÷u4TX !‘°ª1llº:P+ÞÆ¦¾ÏÔSJÒ± ìk«<©wæý¦Ù9&j½vÝ„‘U`nøõ¸¢AG8.A©ÛÛX¸·‚Ef¥>õœ–œƒ‰5 ùdËÎCfXÑÚ:ì30öȧµëÇÎísÚ]¡½Ò-5è}aaaLž2æM›`ee•ê6ËW®Â¶hÜÝ\S¬[¶|%¦P ~6®BGG'ƒšM]íZ>øÍëˆLŒ9}æW"##ñõ©ÀŒY³±²´dÙâ¨Î¡u»Ž>rïªU>ûØM5¤E3%ʶh<ø›e+VÒ­K' ¶!gŽ)l£úçžž¸ººPÚQ¹ëíîæÊ‰“§8|ä(­[¶ ˜mQLML122¢˜­’{ÀÄ€)øúÔdøÏƒ¨X¡ûeóVn߹˞ÛTélúúúô¨2‰õ¯_·ƒ([êÞœþõW^½ gá¼Ùp÷î_œ9£Þ3úÐç`ò”éÔ©íËè‘è\©‹–,£m›VXZX¨ê¼xÁ\UJÐÛ¨¨~VÄ·ë®@C¥Sר~>›ÿ€ÖNêÛ–ËAM@GGÙ~ÿm8zº¹C‰œ5dÒW¾OMLL; -aŽò‘§¡¼ˆ€4¯sB¬h ®yÀ2‹²,‡18Í…ËS“Pµ0x%¯P±ì¿;oB²šãå'JêÔÒJP¯8žöñ:/8«¼éëÂÖ!oÖO?§m-•à+¹†ö° ^R›¾Ž‚Ù¿ÂÏ•À@ƒ,ɹ89ñËæ-®^ãGÿb;7«W.£Œ‹³Æeœ:}†"… S >Uû¹8+=¸+×®©¶³´´PÛ/µ5¶E‹¨‚€\¹rüÞ9èspëÖm^S·v-µmÔ«KLL çÏ_P-³°È¦6.às>+âëÛwžE$ÝÁ·µ‚9RO*[é 'Êc¦ì«©¿Ã øÒQM.§É§ÕÙÔœ­•ÔÇÙʬ<Þ=¤zù6õ}œrÃÓ×Pm9˜¬¤Êì¾ /ßh~Žç)_“?í0ÐSîìLc8ÓEyíi£¼oº.>þôsÊmš²üæêïëÙ+©>ÿ„iÖ¦šœÛç´»B{}“'#FåÊÕklÞ°cãT·Y³n=†††ª»®ïË‘#;9rdÇÅÙ ›‚4lÒœ£ÇOPµr¥Œj;•¬Y³âUÞ“½ûPË·&‡¡K'%-*..Žððpr¼»3ŸœUv+BCC¿z]^¼VÝMß¶;é?p0}zùñÓÀþ˜gÍÊÈ1c I{jŒÄŽsï~R¬Ë‘#ûgÕõk¥™N9Ÿ’ã>cÚæ-XÈâ%Ë9z,vÅléÝÓÕ½5.ãEp0·ïÜ¡xI§ëÂB5ì|ÂI'ÿ„‡+cQÞÿ fÏ®<™{ùãÎgE|}«/y&(“')¨Ž’¾óè•ÒþOùyþ®Cý©ÿ÷½…jËÀHÆWWîŽÿÞËÓÞgÍeh½ FV…I5”nÏIuÒ䟾VžxdÍôñ}Þ—ÓÊ&»c^­0ÇamÓÏ;§±ÌœTg‹o¯É¹}í: !þ¿¥{ °`Ñ~Ù¼• Àå*T0ÕmbccY´–&%KµuGGWW— ^I‰–vÅŠðøñã l:uµ}}ñ5šßÏçÙ³çøÔTÒ‚ôôô016Nõ®õógÏñpwOµ<Ïùìùs€)+É­Y‡¯O üºwU-Ëœ9 vç.ñºŒ9\í; ö¤ã¿F“v66ÎÂÀþ}Ø¿/÷î?`á¢%tóëņµ«5~*`lœûâŒéŸb]¾|y¿úy%ÿ$´ÿ3˜¸E6ó4ËùœÏŠøºÂ£`Ûux« þ|ßšË_wÖŸÄ;ÙÁ˜‹!ñnü‡~tN=Pr×oôQÆ$ÄäWÚü³Ð¤ «”´ÌØP³@ Q.S¥3«t†¿„ž.±Pž’|î9}Hâyå0Ijקɹ}í: !þ¿¥kjЮÝ{˜:}&3§MQ¥A¤f÷Þ}Þ“}Är½ë¸>L–!öüpÝ^¾QOù‰Š…+O>íü\ó(_“Ïh” TŸêMŒ2ÛR‹Ï?§äî½?I<õÈŸ5©]¾÷€îI²Éå49·/­£B»¤Û /ÑÐêÖ®……E6µ) pt,¥z¿|å**Vð¢`)Êiõcs~Ù¼•ž}úѼ鄇‡3mælJ•,‘f':#˜š˜P±‚bİ¡jëúöö£a“ætêÚƒ–-šLÀäi¸–q¡JåJ©–W¾œ'kÖ­gÔØñx¸»qþâ%V­^ƒþ{óºoÚ¼kkk\œ8vâ$Ûwîb@¿>ªÔ.»’O?ï7¸ñìs ±9•´©nÛa~]¥œYgௗàUðÃûþ¦üÏPfÝYô;<„^ŸNÉm¿з¿nIDAT¡ÌDäc«üÇâE¿Ã¨ªI…«QfrÉÖ¦IS½æ*¢ù¹}i…Ú%ݽûöÍ–mÛÙ²m»Ú:+KKΞQ梻xé2—/_a颩–c_¼8AË™:}&~½úblbLE// ì÷Ýý³£Ú¾>54 ¿êîñûjT÷Ư{WÖoÜĺõqs-C»6­XµzÚvuk×âú,X´:´k£ŸàéᎯOMæ/\Ìã'Op*íHŸ^~¼Žˆ`Eà*âãhP¿.U«TfßþÄÄÄ```À-šqüÄI†üìÏØQ#(T°íÚ´ÆØØ˜ÅK—³"p5V––Ô¯W‡Þ={dtÓ¶µ³®®.+–,bÂ¤ÉøCTT¶E‹°pÞì슥ZfæÌ™دsæ-àæíÛT©\3336¬]Íø‰“î?Š˜ØXJ•,ÁŠ¥‹¿8€~lÞ CCC.^Jàê5XçÎÍÐÁiצõËÕä³"ÒÏ£WÊl8«§¾¾y)è·/ÂTÍÊìæûî@§­Jg²h*¤ú–S±.< ‹ÏA­bʱ~Kvg9è%é°Y™^´o9õ©0s™ÀÖ–Jý:n‡°¹ü´/íTŸQU•»Û³N+é,­œ ®2;RLœf3ë€2=g×mðÃ:eæœf%•™†>fÇ å-”ÊÛ)óÏ=§ä†TPž ´Ù¤ÌHÔdžTT?ÿ§¯•ÿ  «£¤IùÚªÿéÛ—ÖQ¡]tä?ýg¹yxQ¿^†”ÑUH>âk‹ˆC=õŽw…Å`f;[~¹B!>ݫϘ©/qÊøùfÿG@!ÄÃàýpñ_%%Æ,“rGþÄ8Ô>£k&„âk’@@!„š±Þàúî†Ð·JŽþö–PÅ&£k&„âk’Ô !„B!¾Sé™ôMþ³°B!„âû"€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…ô5ÝðYphF×U!„"]ä°4Ïè*ñÍiȈB!„ÿ?$5H!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-$€B!„ZH!„B!´B!„Bh! „B!„ÐB!„B¡…$B!„B I „B!„’@@!„B-¤Ÿž…_¿~ƒ‰“§rõÚ5tutqwseèàAäÉc­¶Ý«W¯Øºmë6nbâ¸1”*YBmý–­Ûé?hpŠò{tëBÿ¾½3º ¿ŠI“§²~ã&.œ=“ÑUB|G®=…’³“Þë逵T.c«A¾¬š—Õl=Ü †sÝ3ú¬¾¾"ÓànHÚë³Aèðÿï6BˆO•nÀ?ÿ<¤QÓ”qqfÒø±D¾yÔ©3hݾ#»·oÁÈȈ°°0FÏî½ûˆ%>>>Õ²ž>{†™™C P[no_<£Ûï»·~Ã&† óçÒ¹_133Óx¿¾ýqóömvoߒѧð]ñðª„Oø’ÑUZfp(—â”ï¤ãpé1\èzòl—Åõ!"Zùþæ °FUçÜÊ2½Œ®¡B|Ò-X¶233S/˜‹‘‘–´nב /áQÖ§Ïžóèß7z$tèÜ5Õ²ž>{FüùhúCãŒn/!„È.ÖPÛ.é½Efh·nƒ]öŒ®]Æ«l“ô½Õ?ÊW·¼P³hF×L!¾_évÉ:W.Ú·m­ ll ðêU8¶E‹°~Í*5¬OæÌ™Ò,ëù³çäέÜÖ‰ˆˆÌè6SBÑâ%Y¿a“Úòî={Ó I3Õû«×þ e›ö88ºPƽÃüGþúušåvîæG“f?ª-›¿p1vŽª÷n^Œ›0‰=ûàâ«ã&L"..g7† ó t™²ôí?€„„–­Ä»f-ìñªìÍœy HHHàæ­ÛØØÚ³mÇNnܸ‰­½Ú¹íÚ½‡šµêbçàˆOíz=v\µ.hí:ìÙ·ÿ Õ|j«¶9wþ^¿N½†M°+QšªÕ}عkÏÛ5äåKú ø Gw]Ü}Æü…‹iÝêGJ–pH³­-YÆË—/™0v4M5"hí:6lT‚œˆˆHš¶hEhh(Ó¦LbîìèêêСSW¢¢¢¨[§6{vnÃÊÒ’úuë°gç6¬­­ÑÄÍ[·éÜÍ»bÅX4-lÎÜù ™9{n:}*Åÿ£˜xx ‘1pö!ø÷¼àüîcøÛ?à%rÀ®Ö0»œ¸]¶ª—sõ),=«Ã„êpêôÛ´¾ùzØzjž6P¥0ü°.?Q/gè~hä«C1+è¾Ü@sl `ÂqXUÙþu4TX !‘Ê>›ƒ®Ô TÎ ”ñõVC©œ°õGèîãŽ)ÁÀ×ô±6XsYKà]6ÿ¥r)õz¿ „â¿.] 'Æä)ÓiÞ´ VVVŸ´¯ÆY²Ð§§,_È€Ÿ†P´h‘vü¾•Úµ|ð1š×˜sú̯DFFâëS€³fceiɲŠ000 @þü´nבÃGŽâ]µÊg»I£†´hÖPž°5þ³2p»b…ò\¹z•å+W0qœªN çÏ&Ï»N²‘‘½úöÇØÚ¶n€“SiÊU¨Â¹ R øeóVn߹˞۰-Zþ×ÞÇUYåÿ° rÙ„ "hî¦"¢ ¨¥‰ ¸fÖ¨™hN¿œ$7—TÜEܲÔ17H ÍÌ­M͵Òs&qÉ™ ¸(¿?\îET@ G¾ï׋—ϽÏsÏóÜÈùžç{¾077'|‚ ÜÝݘ>u A!¬Z½–Ÿ¾†í#Ž·õiÃ{‹bbbB î>z”ã'¾cÈàAä“ÏÂóhÕ²%µj9àääH¯¾/óÓ¹s´öòÂÁÁ3s3ôýR'N|‹N§cfÔT¬¬¬èÔÑNÇ/))ûƒ(žjƒ·?®c£ê&&걫-|5º7QlP³Üïî…{ùEÏÙ[Âîa µV/f¨o¡qáhê¤wiq§`÷?Á˵è¸EÁ0Ô[mwn®Ñð\mX¢žëï©^³ÿ l¥î Ä €vîàTpn x¯€¿¥€_=8prïÂòÞPÓB½Ý]ø÷íŠíˇõA~>DîWž+z/=šÂw?òdX߿ʾåBQéª$ÈËËcô¸,­,™1¾Ì¯OØG~~>&ñºvy.݉ÛÀ’E ª¶ÇJÔ=€©ÓgràÀ!zõ fï¾ýx·ö­ éhòq^Ð_têèÖɉäc'+(žRÕµËó$lþ„ë©©úÁxqž-HÚ·Ÿ×‡àòå+deeñ{v6žž<Ï…‹IM½AP÷rrrôÏû´iÙ4:Vëä¤ß.è{zµ]ÇU&22J.ñq4ùM7¦þ3õôçòi£FgΞÅÝÝ g"Æe^t ¹:+>xæ¡}åÑ¢¹þgÀµ¶ éééØh4xzz°|å*Ž?ÎõÔTtiC™™wx^­ZbjjJDäd |•vm}{{äcµ)ªŸyÝ‹òà¯Ýy 0VU¿±µT3ñ™Ù*`øá¸ñ+ääÁïyjpmUð¿ý3E`w;ul¡Î `å Ø|®ÜR³õwrTÚ!›¢íÚ6`iVtwÂ°í´‚¶m-Õþ9`ÿEõrU#·²Õ¿¾uUU¤ÐÏà­vЩ>¼Û¥âûòa}ðSšº¶—<ŠîTt¬¯‚!„xšTI 5kg~<˶-›9X{ÜFcŸŸ//]ªš^z{{{:wògwâ^z†ô`ßþoxëM•u÷î]²²²p)˜™7¤uÖrûöí ¿€›7Óìüü Â'Lbìè0&NÇÁÞž³çžqëí¦§«Aû˜ñ÷ísq)ßJEÃÜaC7ÓÓù×… ´hé}ß¾ÌÛ™úí?½:€˜ÅKqtt¤G`÷r^ƒºˆœœ†A5ˆ?ކ êóKJ ¯Q®v yyµ"þ£õ¬\µšoŽÄÌÜœ>½{>^÷AˆGiêíë=ö®—¨™ì·|Uþ}ÇÕЧ9¬ën¶°é LM*ÛyÂ>‡©AmÝAc¡röË«ð×<;º¯Ks˜¨ÞÏ2! ¶èXߺôÌ?¨R†ÌMa°—Jßq².×éˬ0 ´åþ}n¶Us BQU*=Xµz-ŸmÛÁÇbiذA™_3=cÇNЭ+5kÖ4ÚgjöäÔƒëÂô™³øîû“ܸ‘Fp•dff†FSâìwÚ4:øù•Øž‰‰‰~Z7ÒÒãYùâ>NØLHpѬtÍšÖÀƒkkõWxÖŒiF³û€ÑŽŠ ÑXãéÑ‚Y3¦ß·¯^½ºúí5ëb151!=#ƒ-[·ñ§W^.÷9¿?yŠsçÿIÒî/õ‹ÚK³~ÄÂõáß«ö~¾´÷ó%;;›ƒ‡Ž0}æ,®]û… ±k+´ïDõÑÈj˜Á¿ bã§ÁÎâ_)J*ëà9'bOÁò^j‡›™”­’½ gRáÜXµ¦TÊRq]ª¯ßujáð¨]põ6ìV5ýZ¸{eoð.6—RãÉù“#„¢R«OùÕ×,^ºŒeKéS;Ê,?Ÿ1ã#øÆ :N§ãÔ©hÖ¤I•vÖüˆN—Çì¹óiëÓ×Úµõûüü|IÜ›¤¯æpüÄ·¤gdС½o‰í9kµ\¿n\N£po(++Ëèqbbµj9àêªÎoRlTdfÞ1ºÃ’››Ë¹óçÚ1555š²oþl3lmløÏÏ×ðní¥ÿjܨ¡>¿¢´kÛ–+W¯R×ÝÝè\õêºëœ+W¯²|å*F¿3ŠaC‡³ˆôŒŒrŸ3óŽJÿ1ì—¿ùñ¾ãLMLÉ7ô;;«MÊõ¢U„éä|¯£fÎæ/a£°²²"(0€~}ûpöï%W8¢4N§¨Ôš&1ÿ­ßU`8¶þ¶Œ©,Y¹ªBÁ¯Ò‹RwGaj‘aLQüúÂ>‡—ÔvM •ž3¤5œ¼VZ emµ†àò-u¦ð«¹3xº<~ûBñ$©´;§~8Mxädúôꉣc-¾ûþ¤~_ ¼¼Z•ª­VKßÞ½˜1k.¿ýö.ÎÎÄ'l&íæMþïÏoüÑý§gkcà ÏwfoÒ>¢¦N1Ú7nLý_Ä›#G1dð Ò3Ò‰Y¸„vm}x±k—ÛëÔÑŸ„ÍŸ0sÎ<:øùrò‡ÓlŒOÀ¼Ø]­Ûvàææ†Oo>®/¾$büXÌ Žs«£¦´6Ä'Щ£?Þ­½èòBgÖÇm q£F¸»¹Ÿ°‰‹/ÑÔ °rs«ÃW»÷°óó/hÕ²% Ôgì˜0æÎ!ÿÞ=^x¾3·XòÞûôêÎÄ áÖ—ƒ¾ÊÇ›6óÚÐᄉ³VKbÒ>¶~¶ûqrtdZÔ,ÜÝÝ}ý5²srؾcsç-(÷š‘v>>h4ÖŒœÄÐ!ƒ¹pá"ëb?à×ߊJÖº¹Õ!9ù8{“ðïà‡V«¥ù³Íø`Å_±´´D—«c]lœÑÝ„öíýõÎXfÏ& [W®§¦²}Ç.ü;t¨°>O¿Ó)`SPùÊ-õbÏØÃ€‚z ÁÍ`Å U2´GSøâ¼ÊóU±ÇªÿÛk­U:PÔ>51‡Õ‚Ýÿæ>úõÓ©ØÖ€¡[!¬=üã,M.¸¾‚eG]Á€M0î+•ât펺ÓÑ­±Úà²*YúÓ˜ÊûìKsõAdá_«;ÁÍÔ:‡iIªJRtPåœW!þ•ìÞ“Hnn.ÛwîbûÎ]Fû´NN|{ìp©Ûš;{Ë>XÁ{Ë–“vó&Ï6kÊú5«hܸQ©Û¨ ½B‚IÚ·_ŸTÈ£E 6Ƴˆ·ÃFcggGHp‘ájæ½A„½=’O>ÝÊæO>Å·][†‡¾ÎÆø£ãúôêÉOçαjõZ,,,1À¿ƒ!Á=øë‡kH¹~ïÖ^ŒÆý•¸ ¹w/Ÿ—úõ¡Û‹]Ù“¸N‡……¯ È¡ÃG˜üîtæÌŒ¢aƒú ŠF£aͺXâ6Ä«Rš}{3æQÚvvvlÙϼè…L›>]^­Z>Gܺ589:²}ç.Ž&cÝjU…É‚ˆð±Lš2þ/õ£SDz°µ|¸r9sæ/`ò”i4mÚ„U+ÞgÁÂÅFi]ãÆ¼CDäd"'MaǶO±µµeéâ…¼;-Љ“§âäèÈÃCY¾²(©:8(Å1Ñ|¸f-ñ ›pp°'0 '”}Ἠ¾æªm7[µ¨w~ Z„ ÐóYXÖ­gUéË  ú ´¥LÚ2P• ÿ´˜Ö¾¹¤Ê–>WØ1D•éüóv5»¾m0LÜS´ øeOuÍ1‡Ô‚e§šÐÏC•ù•. ©Q¹}=Æ_õë¢#ðþ1µzHkˆ*]!„x"™ä—']<|;t¦_ßÞL™ùG_ŠBTºéIêÁ¥pãô%!„xšÝ¹Söê……%ã¥R×!„åËóªâBQ1ªìÅ„BˆÇ‘ü–ÊáBQ1$5H!„Bˆ'TñÔ ÐÐÐûÑGªÐ‰¤ !„Bñ”)ì—öù‡‘@@!„Bˆÿ!Åýå @!„B!þçþË€¬B!„â‰%åC…B!„J!„B!ª! „B!„¨†$B!„¢’@@!„BˆjHª !„BQ É!„B!ª! „B!„¨†$B!„¢’@@!„BˆjH!„B!ª! „B!„¨†$B!„¢’@@!„BˆjH!„B!ª¡ÿAŽ;öcä¸%tEXtdate:create2018-04-12T17:59:45+02:00;}%tEXtdate:modify2018-04-12T17:59:45+02:00JL«ÁIEND®B`‚django-tables2-2.7.5/docs/img/example.png000066400000000000000000001223171473544236200202060ustar00rootroot00000000000000‰PNG  IHDRâgz”JÈgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿÿÿ ½§“ oFFs3ú°5tIMEâ ;*Ä[h‚ vpAg4lqê€IDATxÚìÝwTÇÛÀñï½¢ €(UEE¤ˆ"‚A¥Ø±—ØKÔD5j,±›X¢&¦˜{ï%Ä{WÀÞ±¢€]¥Íû/ûAoˆó9Ç#ìÎÎ<3;»³;»÷¢B$I’$IÒµ¦$I’¤O™ˆ%I’$Iƒä@,I’$I$bI’$IÒ 9K’$I’ÉX’$I’4HÄ’$I’¤Ar –$I’$ ’±$I’$iPžâ%K–`hhˆJ¥¢D‰ÖíÝ»###š7oŽüò.8tè™¶ÕëâããéÛ·/*• •JÅ´iÓò-†³gÏR¿~}Š/Nùòå™:u*©©©¤¾#FŒ@__ŸÙ³gg›.>>žV­Z)õ]²dI¾”?uêT%ÏîÝ»:¦{ñâcÆŒQÊkÑ¢E®óØ¿?¾¾¾|ùå—2tèÐ|‹ïCö©‹›7oâçç§Ô1$$DÓ!ýg} q÷îݼó¯rþüyñý÷ßçz»/¾ø"Ó¶zSTT”ÒvS§NÍ—˜¯]»& ”|Óÿ;öƒ´‘‘‘‘„««ë[ë:vì(’’’”ßCBB”x/^ü^åÍ;WìÞ½;Ã2ˆnݺ}:¾©téÒÍ›7ÏÕvÇ… –––âÞ½{ÚÚÚâùóçù[v}êùóç¢G9Êç}ûþǰråJ¥ŽÁÁÁ¹Þ>7íð©I?wikkk:”ÿ¤>>ßËüú믹|ù2Ÿ}ö™²,00Þ½{çk9W¯^¥aƤËýû÷”Ç•+WÆÚÚúƒ—+„`äÈ‘9zPPû~~·ƒ$}¹ˆ-Z„ ¥J•âóÏ?ÏôxâÄ ”ç5/_¾TÖ=zôˆ¡C‡Ò¾}{*V¬H¹rå˜:uªòùúõëT«VMÙvË–-øûûS´hQ\]]¹qãF†²¶oߎ··7õë×ÇÇÇGÙN¥Rñý÷ß’’Âĉ±··§I“&¸¹¹½uWö.OŸ>¥AƒìÝ»H»“466füøñ9ª×›Ö¯_O¥J•ÐÓÓ£yóæÄÄļ3†¸¸8úõëG@@¾¾¾|õÕW<þ\Y¿oß>œœœ¨U«ººº¨T*âââP«Õ4jÔ(C^®®®”-[ƒLËû믿(V¬*• µZÍ–-[8zô(æææJÿñÇ >cccöìÙÃüùóQ«Õ¨T*6l¤½Oйsg^½z@©R¥066æÅ‹ÊLLLä믿ÆÀÀ+++vïÞe{œ9s†ºuë @ëÖ­166f×®]o¥]°`åÊ•£dÉ’Ìœ9ó­õ³fÍÂ××—æÍ›Ó¸qc²Ý‰‰‰|óÍ7˜ššbmmÍôéÓ³L{ýúuš6mJ»vípwwç×_UúEÕªUù믿¸}û6ÆÆÆ„‡‡ç¨O]¼x[[[e_ÄÆÆ²{÷nJ–,‰J¥¢B… ÙÖ¡OŸ>Ê…ò±cÇ066¦nݺo¥{WßÏ®ý’’’èÚµ«ã”)SèÝ»7%K–¤D‰Œ1‚/^0tèPLMM)Q¢'NTò={v†gï³fÍ¢téÒ3jÔ¨½ã°mÛ6Ú·oO£F000Àßß?ÃÅ»Úa×®]Ô®]›.]ºàææ¦´Cv²Ú&"""Ãyªzõê\¼x‘V­Zahh˜áÆåÒ¥KtïÞ¶mÛbbbB•*Uؼy³²>""%¯7Ò¨Q#Š-JùòåY³f ´iÓ]]]Ê—/¯lG=”m§NJ¯^½Ð××§bÅŠ9>?¾OÛH™ÈÍ<ö¢E‹ ¬¬¬Ä³gÏDHHˆÐÓÓËô¹ç¨Q£”ç5 B!^½z%*W®,*V¬(RSSEll¬ÐÖÖ€˜;w®²í´iÓ”m;tè îܹ#*W®,ѤI%ÝÚµk Å«W¯Ä«W¯„‘‘‘P«ÕbÇŽ"99Y!Dß¾} fÏž-„bäÈ‘?þøc®æñ”¸F­,Ïi½z÷î-Q¨P!ñÃ?ˆ°°0ѰaCˆÊ•+‹W¯^ !„xðàÁ[Ïó…———(]º´HHHQQQB¥R‰víÚ !„xúô©(Y²¤hܸ±"í™°‰‰‰ˆŽŽÎ´.éÏöÇ—mG­Ärùòe!„¿ýö›²l×®]Bˆ´çœS¦LQ¶³´´€hР²lĈÊv¯?#>s挲ÜÚÚZ,^¼XÔ­[WÂØØXÙ™Ù¾}»²í›ÏˆÓŸ‰›˜˜ˆ‰'Š!C†(iÏž=«¤>|¸²,%%EØÛÛ KKKŸe¹]»v€h×®HII³gÏVò~ýñ­[·„‰‰‰hÑ¢…BˆU«V @Ì™3GIÓ®]; ‹\÷©×³'Ož!„hÔ¨Ñ[ùeÖ§„ÂÝÝ]ÂËËë½ú~NÚïÒ¥Kʶ–––âÒ¥KbáÂ…Ê2OOOqþüy±téReÙ™3g”üÓŸ½—,YRüòË/bÿþýÂØØXâÛo¿UÒ­_¿þ­gÄ+V¬€X¶lY†>X®\¹ }0«vزe‹(T¨øù矅B|ùå—BKKKœ?>˶z×6±±±ÂÔÔT¢bÅŠ"))I 4H)y\½zUèêê*ϬÓûy¡B…Ä¥K—”t¿ÿþ»RçF‰ˆnݺ @èèèˆvíÚ‰û÷ï‹=z(çÊ””¥Œ×÷Ë?ÿü£ì•J%vìØ¡”Ó¯_¿·ž¿OÛH™Ëñ±‚±cÇжm[ôõõqwwÏò ÑÌž‡^¹r…K—.qóæM8€r7vìØ1%ÝëÏö¾ûî;ÌÍÍ©V­öæoº?ÿükkkŠ)B‘"E°±±!55•õë×S¨P!BCC™3gîîîxxxiÏ1cccó|1“Óz¥ÓÓÓcøðáXZZ2dÈ íêwË–-Y–±bÅ Ž=Ч§':::”)ScccÖ®]Ë­[· åÉ“'ìÙ³‡%K–`ccÃ÷ߟáŽ9]RR»wïÆÂÂâ­g¸oêÛ·¯²/W®\ ¤]‰-Z€Õ«W°iÓ&:wî¬l÷¾Ï”ú÷ïO÷îÝéС>Tîxß———ãÆ£_¿~ʲӧOioÛΜ9CCCœœœP«Õ8::κuë2ÍïÚµk,[¶ €îÝ»£V«éׯeË–}+íØ±cyðà~~~@Ú#€~ø!Û˜sÚ§2kçù>žÏ?ÿœ;w2wî\Š/þV^+V¬ ..ŽmÛ¶¡§§—m|æææøùù±gÏV¬XÁ¸q㈋‹£Y³f¬]»–M›6ñÇpçÎ ýâ}¥6¯Ÿ$2»˜È¯|ø''§,/ÒöìÙ£üü®c!}¿­^½šS§N‘˜˜ˆ“““òÈ&«ƒä¥O}L¹m¿7ç×—.\XYöæ# C_¶³³Ò^ ;qâuêÔÉ4¾Ñ£GS¬X1ÚµkÇ£G8tè²î]íxùòe"""?~<Å‹çñãÇÊ€“—m¾øâ ¦OŸÎíÛ·™5k–òž@ºŽ;réÒ%¬¬¬P©TlݺõqçgÛž9s†   RRR2í×ïÓ6RÖr<ß»wïiåx³ Ôj5'N$88˜FÑ´iStuuyñâEŽ?kœ’’¢ü³;…7€]¯×ïþ³{{9½¦¦¦Y¾T²`ÁºuëFRRkÖ¬áâÅ‹>|8Ãç–ï߿ϨQ£Xºt)nnn9ª_§NسgŒ3†zõêQ´hQÖ®]ËÓ§O=zt†«ùüöú>Ïï<ÓÛõåË—üñÇ+VìÛæôXHMMåÁƒÔ®];WŸßÍcåcxŸöˉw=û}ýòÑ£GY¦sqqaÞ¼yüðÃLŸ>=Ã]÷»ÚñõsÇ—_~©¼ïÛhkk3fÌz÷î‚•+W2hÐ e½±±1sçÎeùòåxzzf˜mÊëþÏiÛ&''óôéS ó¥m¤¬åxjÚÈÈHù9..î½ œ•ÓÄý›Ú/?¼~ g™îÖ­[T«V… ò÷ßS©R¥\× r¾ÿr³MíÚµ•}:}úô 炤¤$š4iB÷îÝ™={ö[/[~Hém«¥¥•勜ïÓ6RÖr|WªTI™ ¾xñ¢²<}Š/'Wi§OŸf̘1!”éà¼\Ý%&&Ò±cGÆŒêU«X°`cÆŒQâP~~üø1ö(€®®n®î䲚†Ïm½^¿ÃK¿»*R¤-[¶Ìr›ôç>ÏŸ?ç—_~Q–ïÙ³‡+W®pïÞ=~þùg\]]9uê”ò îõ+×iÓ¦qûöm˜9s&Ó§O§sçÎögfôõõiÚ´)­ZµBGG---Ú·o€¯¯o†3·í—yÍ3½] o$ß½{Wy¾˜Ý6ém—’’¢|¾öõ}Ÿžvÿþý+ËÿüóÏlïôsÚ§^ÿ¬qú…fn>žÓöË*Ýû´_~Ho@GGGyþ™™rýúuêÔ©ƒ¡¡a–Çefõ³±±Q.¤~ÿýw弑’’’á÷}¶I›~,GGG3þ|eýìÙ³Ù¶m¥J•ÂÓÓó£Î‚¤·mÍš5³ÜïïÓ6RÖr<ëèè(S'ëׯg÷îÝÌŸ?ŸiÄë/Xdvõ~åÊåçöíÛÓ®];å*ðÖ­[>|Èx"I¿:KÿÿõuëׯçðáÃDGGgÙQœœèÑ£ö|PÊ5jT†»›w)\¸0¥J•ÒNÀ{öìaË–-9®WºçÏŸ+ŸH¿4i¥K—ÎP××îÕ«—r:fÌúôéÃØ±cÙºu+vvvèéé1yòdBBB(^¼¸2H6kÖ €Ý»w3vìXΟ?Ï7ß|Ã7ß|È#X¹r¥ò"\vÒ§Æ^Ÿ"Ka&ýŪ׽¹ßÊ—/¯ü|èÐ!åc*¯ïÓôçK™µAf^ÏóÔ©Süúë¯ÊË(éùf—g5¨W¯ö‚LãÆ™:u*}úôÉp÷:777|||˜9s&gΜaèСÊÝñ•+W”éÒ‘#G*w­Œ;–Þ½{£«««œäÒä×Û!§}*ýå/€£G²víZöïߟmf¶O"""8wî¿þúk¦uΪïç¤ý^¯Wz2;Æß¼#|ÓþýûÙ³g/^¼P^8p 2žY/_¾ ¤½LØ©S'/^¬¤Ùµk—òh*³vÐÑÑQ¾jôþýûÔ¬Y“)S¦Ð¦MåsøoÊé6?ÿü3Mš4᫯¾ÂÊÊ H›IïCéqGGGÓ¬Y3†®l{áÂå\›_m»páB¸zõ*AAA@Ú×Ô¾™Orr2Bˆ÷j)¹yÅ:99YŒ9R‹%Jˆï¿ÿ^ôíÛWxxxˆ~ýú‰%K–(iÇÿÖÇ—ž={&jÕª%Š+&Ú´i#ÂÃÃÅôéÓE±bÅD•*UÄ¥K—Ä7DõêÕ•m¿úê+qêÔ)åu@üúë¯B!~úé§·¾²±H‘"¢B… bäÈ‘ÊkúIIIbìØ±ÂÞÞ^ˆÊ•+‹ß~ûí½^3ßµk—°´´Å‹ß|óHMMÍQ½„bàÀâûï¿6lõë×­[·NNNbáÂ…Jþ/^¼P¾NU«V§NB¤} ¤^½z¢X±bÂÌÌL :T$&& !Ò>î---Q«V-agg§´Sdd¤044|«­Òÿåä+H…“““Ò¦éªU«&bcc3,ûã?„Z­€(^¼¸Ø´i“c§ND±bÅ„“““8s挈íÛ·Wb©]»¶¸xñ¢¨Q£†²¬cÇŽÙ~”hÆŒ¢dÉ’ÂÜÜ\¬Zµê­¾ann.NŸ>­|¬...âÆB!ž%DÚLJtuuEÙ²eÅŒ3DóæÍ•ü/^œmŸŠˆˆ¢hÑ¢" @ùTNûþ»Ú/11Qùè ÿÿ•§W¯^;v̰ìâÅ‹–¹»»‹°°0!„ÊG‡ "ŒŒŒ„‰‰‰9r¤òѶ7nˆÚµk+Û·iÓFÄÄĈ¥K— Q±bE1wî\#„®®®5j”R·¬Ú!55ULžgÎèÖ­[†ÏcZ[[³dÉ’¿É›“Ͻ©Õj¿¡*IŸ¢ôãóC|ŒíSöúyO¶­fåû@œ<ÄÆÆÆxxx(±‡‡… ΦvíÚèééQ·n]&L˜À¥K—¸téŽŽŽ¹.oéÒ¥X[[+?çW§pqq¡X±bèéé0bÄbcc™5kÅŠcúôéܼy“qãÆ)ir*ýŠßÛÛ› .˜˜HݺuiÒ¤ B¦L™ÂÇyõêúúú8;;Ó¶m[ *ÄŠ+8|ø0ÎÎÎDGGóôéS\]]±¶¶f÷îÝ<|øš5kÒ¾}{nÞ¼I`` ·oßF­VcggGÇŽsó‡loIs2ëëéý³Q£F;v 777^¼xÁáÇéÕ«¥K—&11‘mÛ¶qâÄ ž>}б±1¸¹¹)3BVVV¨Õj"##133ãË/¿ÄÔÔ”„„Ö®]ËÙ³g)Z´(©©©YÆ—~'Þ­[7jÖ¬Itt4'N¤B… Œ5*×õÍÏ>IPPZZZŒ9SSSš6mʃ¸|ù2ÿý7ÑÑÑ-Z”*UªÐªU+Š+¦Ô­oß¾T«VÀÀ@¶oßNûöíñõõÍö<Hhh(S¦Là?þ`âĉö]58qâ?æûï¿ÇÄÄ„°zõjêׯOëÖ­5Ú†ÒÛrõŒX‘å¿ÌÒ¼¹,½Sܸq#Ûü2û·téR<==ÑÓÓCOOOOO–.]úÎí²‹=ÝÔ©S;v,cÇŽeß¾}™ÆŸÛß3+G­VãïïOrr2[¶láÉ“'!°¶¶¦}ûöôèÑCCCöïßϱcÇ2l›@­ZµÐÒÒâÈ‘#œ8q‚š5kR¨P!öïßOTT÷ïßççŸF­VóÕW_ѤIN:ņ >Z{Ëšû—×¾~âÄ J”(AÉ’%ßêÏëׯgÇŽ0dÈŠ)ÂâÅ‹¹ÿ¾’G\\ÎÎÎØØØpûömvî܉‚uëÖŒ……5BWW7Ëc§V­Z9r!ÁÁÁÔ©SGãçŒô/6*W®&&&Êr]]]*T¨@LL ³gÏæáÇ´oß'''>ÌòåËsuÉì<áåå¥LGwìØ‘¯¿þ:ìbú¾344ÄËË €£G*m¨R©¨]»¶<|Äc1§>úƒÚ÷Ž^¶lõêÕP®æÕj5õêÕcÙ²etíÚ5OquíÚmmmÌÍÍ?Xý000àüùóܸqƒ'OžP¢D lll8uê?æÑ£GýñTúïÉÉÉèèèdz×ùfùµjÕbݺu,]º”ØØX4h@¡B…4~ÎHün߾ͽ{÷”©éøøxîÝ»—i;ªTª·ê™~Ny×yäõóDNϳŋÇÉɉ3gÎ(ÏvÓï†sJž>ž<Ä>äÊ•+Êï!!!ØÙÙe¸ò:pàBNœ8Á“'OpttÄÞÞ>ÇÂÜÜœÀÀÀw¦y×ø®º½™¦dÉ’ÄÆÆ²mÛ6LMM•í]ÓJ9ˆ_?¥ß—,Y—“Ù2GGG¶lÙ‰'044ÄØØ˜Û·oóàÁz÷îýÑÚ[Òœ÷éëÙ­K_V½zu‚‚‚X¿~=žžž$$$púôiš5k–í`.„ bÅŠ„††²iÓ&¬­­3Ü g¶­»»;DDD R©ðööÎUûPç åìŒ3puuEÁ©S§ð÷÷ÇÉɉÀÀ@‚ƒƒ)Uª$''ãììŒZ­V¦ü>LBB‚òÌ7«cýÍvL¿+=vìñññØÚÚf¹ï¼½½9sæ ˜ššæêœ›_m(åLžâððpV­Z¥ü¾jÕ*>ÿüó ocnÛ¶ ---LMMiÞ¼9~~~Yæ—Ÿ÷Ž/'ëÒ׿™¦iÓ¦¬\¹’*V¬ˆŽŽÏŸ?Ïv€Ìéñë¼¼¼¸|ù2çÏŸçÞ½{.brr÷ðú簾¦ôïߟíÛ·³ÿ~^¾|I‰%ru"Ëö–4'¿â7û]Û¶m100àÔ©S¬\¹’Â… S®\9 ”)è¬òÏ>ûŒeË–qäÈ”¯ÌªOëèèP½zuBBB¨R¥ †††¹êoòœÑ¹sgÊ”)ÃñãÇ9räEŠÁÊÊ [[[ÌÌÌèÓ§[·neíÚµ-Z///Z´h¡Üé_¹r…°°0”Go–—U;úùùqçÎ<ȉ'˜:uj–ÛØÚÚbddÄ£G¨]»vŽúF~·¡”3*‘ÃV £\¹ršŽ÷½Ü¾}»ÀÆ.I¹ñ_êëÓ§O'22’þýûcgg÷QËþ/´ãóçÏ;v,*•ŠÉ“'S´hQM‡ôI¹}ûö‡ùëKùʧ Ç.I¹ñ_èë7oÞ$22SSS*Uª¤‘:ôvYr –¤ODHL0cƒG“J*jÔ¤’ʥǙwñO¦Öü·RîšQ’>Iù65ýâÅ 9{ö¬¦ë”g8::É‚ 4R®U«V 6iÜûÆ9ÚîÍ´ƒ¦ÿþL˜0 &|xcccQ©T<|øHû®éÃöõŸ¥÷þ4œÑÁ£”A8w™ZôwȨ D< ×t˜Brr2Ó§OÇÊÊ 333*T¨@ß¾}¹ÿ¾¦C“ ¨|ˆ—-[†§§'×®]Ót}òEóæÍ9wîPpâS§NѺuk oñO?ýį¿þúÁã-Q¢IIIþ¤”Žß; Ü §’Jµ6Ã]Fboh@ÈÝM‡Y ôë×={öĽ{÷8yò$„‡Ë éýäË@ܵkWΟ?_ྉ&99™¾}ûR©R%¬¬¬ðóó#11‘ƒR§N Äùóçqtt¤qãÆ™æ£R©èÓ§uêÔÁÞÞž5kÖ(ëöïßO­ZµpqqÁÃÃcÇÒNˆ/^¼PÊ._¾<:u ""‚fÍšáêêJÕªUY¼x1öâ 4ÀÎÎKKK† À¡C‡pqqÁÎÎŽJ•*±råJüýýÙ»w/«W¯&$$„>}úàèèÈÖ­[ ÄÂÂkkk*Uª¤ ´™¥4iãÆSêsîÜ9jÕª…‘‘Í›7çÙ³g 6Œ™3g*é† Æ´iÓ˜3gŽŸ³³3çÏŸÏ´ .œíïóçÏÑÓÓË𻎎N†ýðå—_R§Nøõ×_éСµjÕ¢jÕªÊÅÕ§èpô!ª™:+˦{Ï@­RómÐ(ŽDÖt˜ÿzׯ_gÕªU¬ZµŠR¥J`llÌÈ‘#qww'22,--©X±"½zõ"%%Hûû¾íÛ·Ç××—Ê•+3lØ0†Н¯/¶¶¶¬X±B)'«óÆ„ hß¾=õêÕCWW—àà`®\¹‚vvv¸¸¸pàÀ%Ÿ-[¶àää„ 6äÖ­[J>þþþÔ­[êׯÏóçÏ5ݼŸ.‘C7oÞ|g[[[qæÌ™œfùÑd{`` ðòòR~?|ø°HLLû÷ïW–>|X¸»»g›? Ž?.„"44T”.]Z!DLLŒpqq=BqæÌáàà „bðàÁ¢G"99Y¤¤¤ˆ7 !„ðôô§OŸBñìÙ3aii)¢££ÅàÁƒÅ·ß~+„"99Y`?ÑeGGñ<ñ¹øëÆ&1)d‚HJIœ›-ü6Ö~ëˆûûi:ü¬ÚqíÚµÂÕÕ5ËíâããŽ{÷„iÇiíڵŖ-[„iÇäÌ™3…iǶ¶¶¶ BqõêUann.„Èþ¼1~üxáîî..\¸ RRRDRR’¨\¹²Ø´i“BˆóçÏ 333ñøñc)J•*%„iÇ@:u”|Zµj%„B|öÙgbîܹšnöÿ”œŒ™é>é—µììì¸rå }ûöÅÝÝFQ¸pá÷Ê«FØÚÚCJJ ûöí#::šV­Z)é^¾|Ijj*[·neÓ¦M*T€V­ZÍÙ³gùú믕ôÚÚÚܺu Fމ‚5jаaC\]]9r$§NÂÃßÅ;zôhNœ8AJJ /_¾$&&†âÅ‹¿s;OOOÊ–- @—.]˜;wn¶éõôô°´´¤sçÎÔ­[???>Ð,--Ñ××§bÅŠÊï«W¯þ`åþÛy–®É‚KóX{m =*÷¤q…¢_D±áÆz%WoM‡Y ˆlþNNáÂ…Y²d »wï&..Žððp¢¢¢”õéýS__###ªU«¤õÏôtÙ76l¨¼ÃŽ{÷hÙ²%UªTÁÎÎŽcÇŽñàÁ¼½½±´L{#¾gÏžôïߟ„„%múŒ’­­-·oßÖtÓ~²>éØÖÖ–ÐÐPvîÜIPPÆ {kÚT¥Rå*Oµ:m¶_Ajj*nnnfšöͼSSSÑÖÖÎ0µ”ÎÃÃöïßÏÂ… ™:u*ÇgÞ¼y9r„    Dƒ ˜>}z¶å4iÒ„&Mš°gÏttt°³³SN.¹©orr²r «T*åDñ:---N:Å®]»8vìþþþÌŸ?Ÿ&MšäzeUFVi_?a¾ùû§Æ£”'‹.-`õÕ•XXâQÊ“ !iÒŸ»Ë·¦ß©J•*„††òàÁLLLÞZ?zôh®^½ÊªU«011¡OŸ>Yö»×ûäëÇÝ»Îïò®c8³xÔjµ|!Rƒ>é/ô8uê±±±téÒ…Ù³gcddDLLL†4†††DGG“”””빇RÖW¯^±nÝ:êÕ«ÇìÙ³IMMEÁîÝ»)[¶,L:H;`víÚŃرc¥J•⫯¾bΜ9\¹r!6lÀÓÓ“#F0lØ0._¾üV†††DDD(y>xð€fÍš¡££CTT±±±Y¦}Sú3á—/_²`Á(S¦ŒRv||¼òóýû÷ ¢yóæL™2…ÆsõêÕ÷Ú_ºººÊ ÀÉ“'ó¿SüGYX2¹fZ¿šrü{šmàVÜ-eý”šÓ¨P\~–ø]ìííiÔ¨íÛ·Wî`ŸM‰`tð(eNç]¦ý2*hÏR#4]íÁÆÂë28Ú–ÃÖÒŒ/{tâÅ‹ïÜÎH_Mrr2Occ1ÒWóèÑCMWEú—È—¸Ž?Á'C¹|3†ÆMš3¨ÿš®—F|?õG¦Íø%Wé‚âàþ½9Ê?7i º7ëÚ8 9‡‚Ïj:¬&¿ë{üÞ1åN8•TЍµî2{C{Bî†hºÚÆÖ‡¸xõ6ç¯DrõJ(;þÙœãm J”àÞ“DŒŒŒ5] é_"_⻣§§@M¯ÚÜ»£ézåØÁ{ñ¯]ƒêU*âåV…C÷±sûV:´mª¤Ù¹}+m[6àÈá¸:Ùеckêצn7e`Æ´ï˜úýxÔï ì+–Æ£º=Ç)ù¥§ »yƒùÎfŲ…x¹UaÚä ܹIj•pv´Â¥ª5ƒú}AJJJ¦iÌM]w|¼ªÓ¾Mîß¿—i=¿›0š*våqs¶¥NMgÚ¯¬›7çW<\¨VÙ’€úµ‰‰‰æÅ‹ ünζTµ· wÏÎø×®Á™Ó'•mýk×àäñ´ø¢sð¨n›³->^Õ¹tñ<ÍûáäPÕ*Ñ"ÀŸ˜è(~˜2‘–MêÒ²i=êûyÒºyž?ži]9HÓ†>ÊþÈnÿ¸9ÛÒ¥C+Õ󯝖++—-¢mËF4ð¯I‹âââ2m£ˆð0ÚµÀÕÉgG+æÌžÀ޶PÛ³ÕíiÛ²·#o)eùz»0૞X[óˬøaÊDš5ò¥M‹†Ôóõ ãgÍxöô)×®]¡Y#_<ªÛãWË•#‡(eé«™õãTœ*ðY«ÆêE› ñ¨n³£cF Íu??}€j¦ÎʲéÞ3P«Ô|4*­>чóãú¤<Grr2¶vÄÇÇ3|Hêú¸ãí^•Ã’ššúÖvf%‹œœLxØMêÔtVþ™›ã×YÓœÛRÁ—ïoM/Y4†›iº^9EŸž™ýçbN_¸Éš [ÑÓÓçvZZ…ùù·ylÛuˆþƒ†1|Hÿ·ÒÌœö/^<çÜå[9~{Ç·ÒXU´æ‹>ýéܵ'G_`äè ›ðÏî#œ¹Æñ3W »y=»¶gšv׎m bÇž£8zߺ̜ö]¦1÷ü¢/g/…süÌU&MžÁ„1ÃøgÛ߬Z±„{ŽröR8ã'MC¥R1å»±$&&|2”³—ÂiÚ¼U¶mò46–±£†²çÐ ŽŸ¹ÊÒ•Ñ×/Àosq.4‚g¯Qý&s~ÿYÙ®¸A V¯ß®}Á”(Q’ëWeZ×ÜB0óç?ؾû¾þ X³j‹–­cçÞ LÍJ¸im¦Ûuï܆-ÛròÜu‡œ§’­=Qwn3d`o–¯þ‹Ó—iÒ¬%ýzwÏЇZ¶úŒ+7ïòUÿ!”.cÎêõ[ؽ?sórü1{B>ïÜ–Þ_ $äôefÿ¹˜/ºw öµï3.Q¢$G_dåÚ¿3Ä5ûשæìJÈéËœ€›‡—2H–+oA¹òìܾ5ÛíLMÍ042ÀÏ¿ýzwG‘!ÍŽí[˜ýçbŠ)  JïR¸paV¯XÂý{xþ<ŽÈˆpbb¢2M»}Ûf.^8G«¦õx•øŠrå,2MûàÁ}&7–ë×®œ”ÄݘhvmßF§.ŸSâÿ¿ÕÍ£fÚò[Yºr#…  I³ìb]==Ê[XÒ·WêøúS«ŽŸr—pæô ÆŒJLL±OSÍÙUÙΡrttt°¶±%êÎ<ï×Ò¥Í13+€E… Ü»ƒ¾~ÚVy KåŽüu1ÑQܹI‡ÎÝÐÓÓÿ^CÖ®^Ž»§7,èܵ'#† !!€J¶öøÕm!/«Š6.\ߺõY¼àO¢£îðàþ=š¶TêmSÉŽS'á_¯!Ÿ÷ì“i}œªUgÒøQ!¨îâŠ_݆¹nÏÒ5Ypik¯­¡Gåž4®@ô‹(6ÜX¯¤ñ*ãç¶ÿT¬ÿkV­B°lÉ|>kÕˆSço°ãŸÍ³90mð}öìi¦ýíMáa71lÿÞE±bÅrulK_¾ Ä3¦Mbïžlü{—2M]¨TªL—e6”™„— hë輕ÏË— èêêæºüÉÇpãúUæ-Z‰±± CõUù7Ó¦¦¦Òíó/èÓop¶e~uY³jqÏžpéâyîÝ»‹o=ÌýÔÔT„Ø·€R¥Êpíêe€´»¼÷•¶;~,ˆÆÍ;a2uë7æÆõ«<|ø€J¶ʬÃÕ+—s´oÞ¬ëëÞµÞGé2曘²aÝ*^½zÅÑ#©éU›cÁG”çÂ+–-ĵ†ÅŠË2¯¸¸gÊ~_²p. 7¡ŒyÙ´ÇÛÒ.BB/]àê•Pª»¼û#C{wïÀÔ´=¿øŠ™³þàúµ+9¾HgP¨“kN`Êñïi¶9€[q·”õSjN£¸ºBžÛñS#„`õŠÅØ;8RÜÀ€z ˜>e¢2crõJh†w*23tP4jJƒFÿ;äôØ–þò|G|;òkV.¥Té2¸9Û*Ëÿ\°ïZ>š®_¶Ê•·àçÙóéÙ­‰¯^Q¸H&ÿ0‹Úuüð®å‹WuJ”(‰‘±I†íîDÞ¿v =zˆsõü<{Þ[y›8•¾_teóß)^Ü@99¿©Y‹6¬Z±gG+Ú|Ö‰ƒ¿¡o¯.lù{:E‹òôil–iGûŽ˜˜hÚ´hHrr… iÑoà·Ê([®<»õÂ×ÛCC#ʘ—UÖµhõ7o\£®OÚ€`llÊ‚¥k;a #¿ˆ‡‹=©©©¸¸ºããW!ÃG3¨_/–-Y€‰©™rW§R©˜5c _ø==}líèÖ£7jµš¿ÿZO}?Ot´uÐÕÓCW÷Ý3&oֵޝ¿²®ª“s¶ûç}-Y±žáCú3cÚ¤´gº=ûàå]‡?ÿAÇÏš‘”˜ˆy¹òü>wI¶ùìÞ¹=»¶óâÅsZ·í@ÇΟ£R©X²b=ÃŤ±#)Z¬s­ÌÑ…kDD£†BK«0:::Ì™¿,ÇwÓ¯³ÖscžÿBBî“öò`-óÚÔ0«!á\jÖØ---’©\ʼn¥+Ó¦¢ÇŒŸÌw¾¥¶§%˜6ã×,ó9y<„ukVPÉÖž­›7вõg 16GǶôß 9¼´ £„©¥¦ã}/±÷Ãó-ö#‡0}ÊD6oߟ缤ÿž¦L`Ä·ã5R~~öõO™lG)¯bï‡cee•£´ò5II’$IÒ ù—¹ä]Ëïí>šCú—ÒÔ°$I—¼#–$I’$ ’±$I’$iˆ%I’$Iƒä@,I’$I”«—µTI±šŽ÷½äØ%)7d_ϲ¥¼(ùÿ_œ¹ˆs“ñ¿É“'O lì’”²¯çÙŽR^………å¸É©iI’$IÒ 9K’$I’ÉX’$I’4HÄ’$I’¤Ar –$I’$ ’ß5-IŸð§á„Ü æhÌÔ¨ñ*ã›™;–ò/ I’¦ÈX’>!1ÁŒ M*©¨Q“J*—_dÞÅ?™ZóÜJ¹k:DIú$åËÔô×_••¥K—ÆÎÎŽÀÀ@M×KʇbÏž=šCÊáOÃñÇñ{Ç”;áTR)¢Öf¸ËHì í¹¢é0 ŒåË—Ó±cG¶lÙB\\\޶yóxjÞ¼9çÎÓtUòUË–-¹sçÑÑÑLš4‰áÇk:¤!_â–-[¢¯¯‚Û·oãââ¢éz嘯¯/ØØØàççGTT/^¼ oß¾TªT‰òåË+W¾aaa4nÜkkk,--™5kÏŸ?GOOOÉóùóçèè蜜¬äcee…ŸŸ‰‰‰DFFbccƒ¥¥%+V¤W¯^¤¤¤iWƒíÛ·Ç××—Ê•+3lØ0†Н¯/¶¶¶¬X±B)'·å¾iëÖ­4iÒ$Ãï 6äÆüöÛo,\¸GGGÆûïì8p€J•*ѪU+jÕª…³³3.\PÖÿý÷ØÛÛcggGŸ>}xùò%&LÀßߟºuëâááAýúõyþü¹¦»ÂÚáèCT3uV–M÷žZ¥æÛ Q‰>¬é0 Œ+V0xð`\]]Ù´i“²<«¾ÙñtðàAêÔ©£lûóÏ?S¡B*V¬HõêÕ•;ϬŽQ ÛóÈ›²:×½ë8þé§Ÿ(W®*Tà÷ßÏqݹs‡5j¼³ü &о}{êÕ«‡®®.ÁÁÁ¬[·ŽÊ•+cllÌÈ‘#ìÏy{öì¡råʘ››Óµk×Õÿß ßÞš ÁÐÐ_ý•?þøCÓõʱŋsëÖ-®_¿NÍš5™5kcÆŒ!11‘Ë—/AëÖ­hݺ5Ÿ}ö7nÜàÂ… ØÛÛg›ÿ¶mÛ¸pá×®]#,,ŒI“&¡R©011áèÑ£„‡‡síÚ5®_¿ÎöíÛ•íjÔ¨Áþýû aöìÙÔ®]›ýû÷³eË¥S¾O¹9emmÍ€èÙ³'/^dâĉ™¦B0{öl>Ì—_~ÉÔ©SX¿~=Û¶mãÔ©S„††òâÅ ¦M›¦lW¢D ¶nÝJHH%K–dÕªUêŸ5jÌuÍ™à>‰Nƒð1÷ÅÞÐ%¡‹xú*VI#½[PPvŒ¶oßžåË—gXŸYß~×ñ´sçN.\ÈÉ“'¹yó¦r¾y—wG^—Õ¹²>ŽwîÜÉüùó9yò$´oß>Ûx¶lÙ‚åÊ•ãÛo¿¥F!ÞY~DD³fÍ"..777Š/NHH¡¡¡¬[·îSÕ÷ïß§S§N,^¼˜¨¨¨ ù¿«|MË·#ÏÃÃ'Ož0sæL|||HNNÖtÝräĉ´jÕ wwwÖ®]«\%mݺ•ÁƒS¨P!Ôj5­Zµ"**ŠÈÈHºwžžreš;;;®\¹Bß¾}Y²d 666.\˜Â… ³dÉêÕ«‡——W®\Ép…–>« ¯¯‘‘ÕªUÀÒÒ2GWrY•›ßÌÍÍ)S¦ ¶¶¶Ü¾}€;vйsgŠ+†Z­¦gÏžìØ±CÙ®J•*Ê•ìëÛI†géšD½ˆbíµ5´¨Ø’®£ˆ~ņë•4^e¼5f°|ùrÚµk¤]˜9r„;wî(ëß§ooÙ²…Ï?ÿcccŒŒŒrË»Î#¯Ëê\YÇ[¶l¡gÏž˜™™å(®¦M›råÊnß¾Í;wX¸p!K—.}gù 6ÄÑѵZM¡B…”eúúú˜ššÒ´iSBB²trèÐ!jÔ¨¡ äoÆš]ùš–ï—ÀÜ¿ÿ_Uɬlß¾1cÆ0iÒ$Ž;Æwß}§\½™ÞAfµ,555Ó2lmm ¥fÍš;vŒÊ•+ÍèÑ£ bÕªU„„„вeË e¿™úº×ËŸrs{nî Ôju¶uxŸí¤üáQÊ5jV_]Éþ;ûH)LüïNØ]¾5ýN‰‰‰¬[·Ž%K–P¡B\\\P«ÕYÎè¼Þ·³;âããÑÖÖÎt]vÇhNÏ#ï:×esBBºººïÕV&&&Ô¯_ŸsçÎåªüÌ$''£££“m[dk^ËÿÐò<ÇÆÆ²zõj^½z¤ÝþS®\9M×ív;¦¦¦‡)))tîÜ™æÍ›¿óå(éß+7}H~³–$I’†½zõ WWW^¾|I±bÅhÚ´©òB˜ôß'bI’$ +Z´h®ž¿Jÿ-ÿþ¹’$I’ô&bI’$IÒ 9K’$I’ÉX’$I’4(W/k=yòDÓñ¾·‚»$å†ìëùC¶£”%K–ÌqÚ\ Ä¢p M×í==)À±KRnȾž?d;Jy{?<ǃ±œš–$I’$ ’±$I’$iˆ%I’$Iƒä@,I’$I$bI’$IÒ ù]Ó’ô yšAÈÝ`ŽÆA¯2Þ¸™¹cP¨‚¦C“¤O–ˆ%éq-.„±Á£I%5jRIåÒã‹Ì»ø'Skþ€µž›¦C”¤OR¾NM‡^º€EéâìݽCÓõÊw‹þÉ ~_ð툯>tAGqpÿž^~³F¾ï]Λ1nÛˆ—[•Ðjùãil,Fúj=z¨éP ¬§)Œ¥ Âé¼ËÔ¢¿Ó@FàYj„¦Ã,l,Lp°.ƒ£m9l-Íø²G'^¼x‘ïåh²ß_»vW'›L×]½ˆ£m¹ Ë"oE`Y6ç_`ñoòúù\SòíŽøÑ£‡ôý¢+FÆ&­ÐÇðýÔ•ŸƒâÅóÔñ­«é°²ôfŒšÓ°QSM‡•c%JpïI"ZZrç}¿w @¹(¢Öf¸ËHn? änõËTÐt¨ÂÖ‡°ªhÍ«W¯¨ïëÁŽ6Óºm‡|-Cöûãõó¹¦äËqRR=º|Ƙñ“)[¶¼¦ë”+;þÙBmÏjxT·§mËFÜŽ¼¥¬ûã·Ÿ¨bWžj•-Y8ïeùŒiß1õûñ„ݼÁü?g³bÙB¼Üª0mò„·ò?rønζtéЊFõ¼ñ«åÊÊe‹hÛ² ükÒ"ÀŸ¸¸8âž=£ï]ñ¨n§keæÿù[¦1GÞŠ S»æø×®A-'V-_ @LtmZ4Ä£º=ÎŽVŒ54Ó9HÓ†>ܹIj•pv´Â¥ª5ƒú}AJJ v'íWËêö¸9Û²~íÊLã1ÒW3¨ß´ð§f G7­SÖý8ý{<\ð¨nÏÐA}yùòe¶y/Z0GYæãUKÏ`V²ÉÉÉJy_èMÓ†>xºVfÞœ_ùâóŽÔ¯M-'.^8ÀÎí[éÐö;·o¥mËFY¶ÕÙáèCT3uV–M÷žZ¥æÛ Q‰>¬é0 œçÏãHNNÆÖÎÈþΪoé«:¨/MúàáâÀ¦ k”m^ï÷ÍûáäPÕ*Ñ"ÀŸ˜è¨LcÚ¶5'‡ ¸:Ùàæl˼9¿*ë²+ëàþ=Ô¬áHåJeùêËnyj—ÇöP¿6~µ\©ïçÉ©i‚?L™H¯îhÕ¬>åÌôøû¯ Ô©é¬ü37)Ư³¦g›GBB_è£m9<]+ãRÕšukViwòÍùâQÝ¿Z®9|€Y?NeàW½”ø<¸O¥ ¦ÄÇÇ+çóôøZ6©K˦õ¨ïçIëæ xþüù;Û5¯òåRkøþÔkИz óÛÏ3ò-¸-êÎm† ìÍö=G±¨`ÉÒÅóè×»;›·ïgßž,[²€½‡N`jjÆO3&s+""ÃöV­ù¢O^<Á¸IS³,GÁÌŸÿÀ̬ßMÍšUËXµ~ úúú|Ù£›ÖÒ¥[/¾›ð-%Jrú2OcciTÏ;GjÕöÍß—=;1ýÇÙTur&..Ž:5«áW¯!³ý‘jήlÜAJJ ÇBŽfczç026áŸÝG011%%%…þìÙµšðíðÁLøî|üê÷ì7ofý‡Ë¿ð5¶vܹ‰_-Wüë6dßÞìÚùûŸDGG‡~½»óËO?0âÛñ™æý46–±£†r5ü>zzzÜŠG¥ReZ^·_RÍÙ…ý{wñeNìÚ‚¥UE–.žÇì_fòç‚åÙîûÌÚê¿Ls]s&¸Obwä..<<½¡ó/Îåé«X%”3m[6DKK‹»1Ñ88VEG§(@–ÇpÕªÎÙöíN]{PÝ¥×®^¦e“º´jÓþ­2›³ˆò˜ßº{x|2”bÅŠñøÑ#\«ÙСSwô‹ϲ¬îÓ»ggV­ÛLuW7Ž¥_ŸîYÖÿÞÝì+–V~O¿x¸wï.Æ gCàNJráüYz÷ìLЉ‹DFFðËìùØÚ9 „ yË6ìڱɓÆÐ«wÿló˜9í;^¾Làì¥p:~Ö H;Ï~Þ¹-ߎD@Ó–„^º@ëfõ >J§.=ðtu`ò³Ð××gíªe´lÓžbÅŠ½U·â%˜»p:::ôìÖžëWÑíó/ßÙ®y‘çxŲ…$&%Òа<ó±9|wOo,*XйkOF @BB;¶o¥sטššPÒÐè­8§J—6Ç̬*pï^ úúú”·°T®l÷îÙÁʵiÓRÍ[¶eÿž]☘h.ž?˘‘C”eEŠhs'òNÕª3iü(„TwqůnÃwÆV¸paV¯XÂý{xþ<ŽÈˆpbbÒâ©æìʤñ£8{ö®5<ð®å“e>éweË•§ŒyY®^ eïî´m×Iéìºö`ÒøQŒøv|¦y'''SÞÂ’¾½ºPÇןZuü”|ßTÍÙEi?=}},­*¦µ¯…%›Ö¯á]Þ§­ 2ÏÒ5Ypik¯­¡Gåž4®@ô‹(6ÜX¯¤ñ*ã­é0 ŒõíÀª¢5B–-™Ïg­qêü,aÏšµ²íÛÕ]j`mcËÝ»1¤¤¤P¨P¡ ež9}‚1£†Eì“ÇTsvÍ2¾É“ÆpæÔ RRSxõò%wïÆ(Ffe=„³K ª»¦½°WÒÈ(Ûú›•*ÍÅ«·•ß#oEPÇ+m¶åðÁ}܉¦[§ÖÊúW¯^’ššöHÄ¿nCì3äv“ðñï]+VŒ¶f™ÇŽí[øcÞReʾxq¢£îðàþ=š¶À¡rl*Ùqêä1üë5¤Vm?å¦gåòÅ,^¾ŽÌ8T®‚ŽŽŽÒFQwîä¨]ó"Ï—À7¯_çìé“ÊÔÂÙ3'>´¿2]Z !x™@1]Ýw¦ÍêŽ-»ôBˆ7~Ï*m&±¥¦RD[›ÍÛ÷+ÿBN…âêæÁgí;³ió.ʘ›³bÙ"š6òygŒ“'Žáı`æ-ZÉ®}Á4m©Ä7ë·¹Lþa*•*ívìˆÕñeBÚÿß‘³j«ÌòÖÒÒbßá“tèÒ˜˜Z6©ËÎí[ߣ=…òsúÁÿ¦¬Úê¿Ê£”'jÔ¬¾º’ýwö‘"R˜2øß°{)wM‡Yà¨T*Z·íÈ­ˆpâž=Ëd}Úÿ9íÛjuÚ¾oœöìÚΔïÆ2jÌDvïaÔ˜Io¥IסmSJ–,ɦ-»Ù¹7ˆrå,ˆlËJHH X±wŸïr"55•ê.nÎQ§ÎßPÊ{S||<ŸwiË´¿bUÑúy$$Ä£­­ãý“®{/Y¾t!ÇC‚044Âξò;·W«ÕJ;ç´]ßGžâñßMãèñ  :ÃÁ 3Tsveú³éØåó| ðCªéU›cÁG”çÂ+–-ĵ†ÅŠ£†»ûvïTvBØÍ™æQ¢¤!‘‘Y9åëWŸËioLþµquü2¾VƼ,åÊY0ëÇ©J™û÷îâáÃìݽSÓRôüâ+fÎúƒë×® „È6Ƈà]ÛccˆˆSÖmÜ@ 7O}=‚~‡ríêå,cO? íܾ•¤ä$ìì+ãë_ëWOjj*+–.Äׯ~–y?xpŸãÇ‚hМ±&S·~cn\¿úÞíYªtÂn^W¦ÍΟ;­¬Ëª­þ« U`rÍ´>3åø÷4ÛÀ­¸ÿ½1¥æ4Š«+h:ÌGÁꋱwp¤¸A–Çp^ûöǨdë ÜI^½’õ±øèá6n†ŽŽ1ÑQ<}ûÎü]\Ý9qëÄèqß½w<ã&Nå›!ýp¯n‡J¥¦{/©ããÿVº¥+72fä\lB`ieÍüE«ˆˆcÔðAhiFGG‡9ó—¡R©ÞбŽïÿò0øúöê–¿7¡S´h†Îµw÷N&މ®®%K2óç?²Œ½}›&DEÝÆØÄ”ÅË×S¸paZ¶nÇõkWðóvA ð¬Y›ÁCGf™·J¥bÖŒ)|=àKôôô±µs [Ü0éª:9ã]˯ê”(Q2ÃýYµÕ™µžóür7˜à˜ j™×¦†Y 9çR³Æ¾hii‘”˜Hå*N,]¹Èú~øðAžúv³møû¯õÔ÷óDG[]==tuõ2M;iò z÷쌑‘1Æ&¦9ÊßÚ¦ý ¡Q]¯\m—™2æeY¼|=“ÆâÉãG!ðóo@‹VŸ½•öäñÖ­YA%[{¶nÞ@ËÖŸ1lÄØ,ó?i}zuÁΪÖ6¶¨Õjôõ‹£R©X²b=ÃŤ±#)Z¬s­¤¤¡!vwÜ¥{/fÿ<“f-Úäº^ïÓ®9¥9¼  £„©e¾þ±ÄÞ/°±FújÅ¥æ=#)Ïd_ϲÿ½^¼xAáÂ…)R¤Ïž>¥a]/Ömú‡²åþ]ŸØ‰½Ž••UŽÒ~ÒwÄ’$IRÁrñüY¾èѵZ®®ƒ‡Žü× Â¹%b)Ïäݰ$I‹»§ç/ßÊ{Fÿ"òƒƒ’$I’¤Ar –$I’$ ’±$I’$iˆ%I’$Iƒrõ²–*)VÓñ¾·‚»$å†ìëùC¶£”%KæüÏBæj ÎMÆÿ&Ož<)°±KRnȾž?d;Jy–ã>$§¦%I’$Iƒä@,I’$I$bI’$IÒ 9K’$I’ÉX’$I’4H~×´$}BŸ†r7˜£1GP£Æ«Œ7nfîXÈ¿4$Iš"bIúD„Ä36x4©¤¢FM*©\z|‘yÿdjÍp+å®é%é“”/SÓíÛ·G__cccåß74]·wŠE¥RñðáC íG'''¿õó¿YûöíY±bÅGk£Ìâè蘯å–*UŠ;wîP­Z56lØðÁêø)ÎèàQÊ œÎ»L-ú; dTÐ"ž…k:ÌÁØØ˜Ò¥KS¶lYLMM騱#/^¼øhågw¼Íž=]]]%6OOO–-[¦é&ËÖ„ 3fŒ¦ÃШ|{F|¨ü³¶¶ÖtÝÞ©D‰$%%all¬éPþµrÒFÍ›7çܹs,†S§NѺukM7EvüÞ1åN8•TЍµî2{C{Bî†h:ÌãðáÃܹs‡Û·oÊæÍ›?ZÙï:ÞZ¶lÉ;w¸ÿ>³fÍbÖ¬YŒ?^ÓM&eã“Y«páÂÙÞù>þ==½ ¿ëèè(¿«T*¾üòKêÔ©ƒƒƒ¿þú+:t V­ZT­Z5ËfذaÌœ93ÃïÓ¦MÒ®ýýý©[·.Ô¯_ŸçÏŸ@¯^½(Uªvvv)yDDDЬY3\]]©Zµ*‹/δìP½zuzôè‘‘?üðñññôë×777ªT©Â€HMM}«öîÝ‹««+VVV8::²oß>>>T©R…!C†(Û„‡‡ãïï™™5jÔàÙ³gÊ:öîÝ Àœ9s°³³£R¥J8;;sþüyMw¡ápô!ª™:+˦{Ï@­RómÐ(ŽDÖt˜N\\ÉÉÉ888iÇnûöí©W¯ºººãêêÊÉ“'•m\]] I»èÉî˜Èêx~óxËŽ‡‡ÿý7Ó§OWfµ|}}±°°ÀÆÆ???¢¢¢€ÌÏ ÙÅžÙ±˜œœLß¾}©T©VVVøùù‘˜˜Hdd$666XZZR±bEzõêEJJÊ[ñbaaµµ5•*Uâ×_Uֽﹶ È·xĈ”+WŽZµjqàÀM×ë£êÝ»7äçŸfÒ¤I|ÿý÷>|˜þýû3cÆŒ÷ʳD‰lݺ•J–,ɪU«˜4iÏŸ?'22’‹/f˜¢êر#'NääÉ“=z”ï¾ûŽ˜˜˜LóŠŠ¢]»vÜ»w!C†0nÜ8ìíí9~ü8çÎ#**Š7¾µM§NX²d aaalÛ¶ }}ý i¶mÛÆÑ£G âÌ™3Ô«WI“&)ë]\\8pà§OŸfýúõ\»vM‰½yóæÜ»w½{÷R¬X±·bŽeÈ!œ 6äæÍ›4lØ0WïLdvLèééey<çÕ‰'2dQQQ<~üX)Þ>/d%«cñÕ«W\¹r…¾}ûâîîN£F(\¸0*•Š%K–°{÷nâââëü’nôèÑœ8q‚””^¾|ILLŒržûçÚƒ|ˆÓ§nU*-[¶D[[›ðððÿÄIR¥R)S´9I+„Èò÷÷ÍW­V+Sà èê꾕&55mmí÷žHMMå?þ Zµjï¬ã»òùòË/>^ùù]<==•)øøxåÍâ²eËbaaÁÔ©SB°k×.gÏž}+ŸÙ³g+wð>Ìt êuæææèèèpúôiîܹ£Äñºû÷ïDóæÍ™2e 7Vb‘²gi`Éäši}cÊñïi¶9€[qÿ»»šRsŠËÏç–‚Å‹ãèèˆA¦i^?ÖÓ_¢z—¼ÏéNœ8AË–-5j%K–äÁƒ888(wì¡¡¡ÙnŸUìY‹§N"66–.]º0{ölŒŒŒˆ‰‰áÁƒøúúbbbBBBaaa™–÷àÁš5k†ŽŽQQQÄÆÆ~¤=©YùrG¼|ùrŒ––*T`Ë–-ÊÔéÁÌ™3iРfffùvÑ­[7Z¶l‰§§'ºººÊ…Ì»L›6.]º°aà 2\…oÚ´‰¯¿þZ™2³¶¶ÎñtÍ”)S5jU«VÒžQÿöÛoÒXXX°`Á>ûì3^½zE‘"EøùçŸQ«ÿw=ײeK¢££iРIIIhii1tèÐlËV«Õ,[¶Œž={¢¯¯¹¹y¦éT*“'Oæ‹/¾@__z÷î/ûãSàVÊyþ ¹LpLÚK~µÌkS솄sÉÇÇ---qrrbÓ¦MY¦3f ={ödþüù˜™™½sæ'ÝûÏýõeË–%11kkkHçÎhÓ¦ ëÖ­ÃÃÃôôô2¼ŒšÓس:Ïœ9C¯^½xñâ:::têÔ †NçÎÙ¸q#E‹Ír€9s&:uÂØØSSSMíÞN%rx?†•••¦ã}/9vIÊ Ù×ó‡lG)¯rÓ‡äk’’$I’¤Ar –$I’$ ’±$I’$iˆ%I’$Iƒä@,I’$I$bI’$IÒ \}ŽøÉ“'šŽ÷½äØ%)7d_ϲ¥¼(Y²dŽÓæj …KhºnïéIŽ]’rCöõü!ÛQÊ›Øûá9ŒåÔ´$I’$iˆ%I’$Iƒä@,I’$I$bI’$IÒ 9K’$I’åËŸA”$©`xšAÈÝ`ŽÆA¯2Þ¸™¹cP¨‚¦C“¤O–ˆ%éq-.„±Á£I%5jRIåÒã‹Ì»ø'Skþ€µž›¦C”¤OR¾MM¯Yµ *U0eøþš®×µmk ^nU2]·x០ê÷…¦C|‹‘¾šäää÷ÞþÎíH–/]ïi¥ãiJ£ƒG)ƒp:ï2µèï4QA#x–¡é0  ¬Ëàh[[K3¾ìщ/^|°òÞçØíÕ½ëÖ¬x¯òŽ>@³F¾¬>ÒÛòe þuÖtÎÿƒµ·q-â>'ÏÐt½>¨ÆÍ9|VÓa|TwîD²|éÂ|O+}ÇïPî„SI¥ˆZ›á.#±7´ änˆ¦Ã,0¶î<ÄÅ«·9%’«WBÙñÏfM‡$`yˆ™õãTþœ¿‹ –-ZTÓõʱûvãí^'‡ 4ªç«“ ;·o¥CÛ¦JºÛ·Ò¶e#Ž9HÓ†>ʺ?~û‰*vå©VÙ’…óþÈÿ‚¹³©ëãŽWuÚ·iÂýû÷hÖÈ—^Ý;м±5k82nô7ŒývÍûá^Ý.Ë«Ù6-²võrå÷­›7ѪY}®]»B³F¾xT·Ç¯–+GxkûçÏŸS¾”~†ß˧í¯#‡àêdC׎­ ¨_›ºuܸz€o‡&ôây¼ÜªÐ®uÍûáäPÕ*Ñ"ÀŸ˜è¨,Ó>´Ÿ€úµñ«åJ}?ONH~˜2‘–MêÒ²i=ük× S»æÌÿó7š7ö£–‡#† Ìrßé«:¨/MúàáâÀ¦ k”uY•wäðÜœméÒ¡êyãWË••ËѶe#ø×¤E€?qqq9nÏ‚âpô!ª™:+˦{Ï@­RómШ´¶‰>¬é0 œçÏãHNNÆÖÎH›-sr¨€«“ nζ̛󫒶Y#_¾ø¼#Íùâí^•1£†*ëØK-'ªÚ[d8½)«c.!!Aý¾À¾bi<ªÛsüX²M||<Çô§®;ÞîU1l ©©©$''3lðW¸9ÛR½JEZø“˜˜ÀÓ§±|Õ»;vV¥ðt­Lè¥ šnêÿ´<Ä‘·"HMIaò¤1x»W¥¶g5Ö¯]©ézåȃ÷éûEWæ-ZɹÐ&~7=×yìÛ³“eK°÷Ð Î^ §U›vʺ];¶q,$ˆ{Žràèi||ë2sÚwÊzçê®üýÏ>vî fÁÜÙxzÕâïö±ríf&•iyÝ{|ÉŠ×î6W._Ìç={#„àóÎméýÕ@BN_föŸ‹ù¢{bsù}¹ZZ…ùù·ylÛuˆþƒ†)¦LÿǪ=~µ·ðÛœEœ àÄÙkÔp¯ÉœßÎ4í½{w™0f8+Ö²ïðI~üy¾ê©”iP¢$k6leï¡<~üˆÈÈ[üµuûœbÇ?› »y#Ëx;uíÁ–X¶j#cÿÿÄö®ò„Ìüù¶ï>‚¯Ö¬ZÆ¢eëØ¹7S³RnZ›oíùo¡F¹®9Ü'1Ài>æ¾Ø:°$tO_Å*i¤œiÛ²!îÕí¨îhEqttÒ.fÝ=¼>ÊÉs×Ù±'ˆiS&÷왲SµêlÞ¾ŸýGNñ÷_ë¹qýܧOÏÎü>w ç/ßb¤²,7«cnæ´ïxñâ9ç.ßâÈñ Ø;8*ÛL›<žJ¶vì9pŒCÁg‰‰ŽbËßÙµs¡—.püÌUN_¸ÉÈ1Q©TŸµjÄ©óiŒ“'áÌ©¤¤¦ðêåKîÞA¿xqªUO;F .Œ……%QQ· ½tžê®nTuJ›­(ùÿÇ_f²:ævlßÂì?S¤Hôõ‹+Ûìøg3†FÆlÜÀ³gO‰‰ŽÂ¿^#®_»Â°Á_áâê†ýF.\++kÜ=½p÷ôæàþ½šnòÿ´<ÄFFƤ¤¤(Q¥JvTqræê•Ðý@œ€Öÿw¼7©T*åÄ— ÓÕÍt]jj*Ý>ÿ‚>ý¿3•J¥ xéW¥™ÑÒÒ¢CçÏY¹lúÅõéЩ;ZZZYæù¾õHx™€¶Ž*• óÚ³k;S¾Ë’°wpdÓ†5ü³õï´2ÞH›ššJu7–¯ù+WížWV¯S«Óîæ„y+O¥"«â²Û/ÿv¥Ä={F‡¶MiÐ0€M[v£££ƒGu{™w(µZ BO‘"Úï,+»cîåËt³9ÍøéwªT­öÖº “—Ø·g''Ž3nô7 9÷VšB… åè”Þ_žç¢*ÙÚ£§«§<Ÿ»s;’«—/eywôoâZà#¹s;’¤¤$íߣ¬+Uº a7¯“’’Àùs§3Í£†»ûvïT:êëÓ¨õ6fÁ¼ß‰ºsH›îÙ³k{žãîÚ½k×,gͪetíÞ €2æe126áŸmifè¥ \½Ju—ŒIÑÕÕ¥ˆ¶6·#opöÌÉ ëãâ•74çÌžEƒ†M(ihÈݘh’’’Bððá*Ù:([W¯\Vòx3­w-‚‚)ÏX_½zEà¦ul¿æGy9mς P&ל À”ãßÓls·ân)ë§ÔœFquM‡Yà!X½b1öŽ70àÑÃ4lÜ b¢£xú4öyTwu#$è°2£tä`¦é²;æj¸y*ç–øøx¢£î(ëê5`ú”‰$$$üÿv¡œ9}’³gNñ46–vºðÿahdĽ»1šnÒORžïˆU*Ë×2lp_¦O„ŽŽ?þò',­4]·wªhmÃðQã¨çãN1]]üë5RÖUurÆ»–/>^Õ)Q¢$FÆ&™æÑ®CWŽ…áW˃ÒÒ¢lÙò4mILL4mZ4$99‰B…´è7pHžã.oQÊŽUÑÒÒ¢l¹òÊ~X²b=ÃŤ±#)Z¬s­¤d&Óí“&Ï m‹†˜˜šai•qÊ÷Nä-ük×àÑ£‡8W¯Áϳçi\^Þupv´ÂÖÎå«ÿâï¿ÖSßÏmtõôÐÕÕË4íÆ¿w²xùz&Å“ÇBàç߀­>û ûµŒyÙ<——›ö,(¬õܘ翻ÁǤ½ÌS˼65ÌjÈA8—š5öEKK‹¤ÄD*WqbéÊ´ißI“gлggŒŒŒ161ÍQ^•*Ù1ðëáøy» S´(ž^µ3L-+e¶h“å17nâTú~Ñ•Ío¤xqââþ÷\zÌøÉ|7á[j{:``P‚i3~%)9‰Áý¿ >þÚÚ:´ù¬#Õœ] ôK‰•JäpÎ!,,Œ¦–šŽ÷½ÄÞÏQì×®]¡cÛ¦œe"›·ï×t(Ò{Êi_—²÷±Û1öÉJüÿß®]8ÿB/^àÇ_æhº¤<ˆ½Ž•UÎnHå7kI’$iØA}8|]==ììùé×?5’ôÉ;bIú‘}=Èv”ò*7wÄòƒƒ’$I’¤Ar –$I’$ ’±$I’$iˆ%I’$IƒrõÖ´*)VÓñ¾·‚»$å†ìëùC¶£”%ÿÿãh9‘«87ÿ›Rþ ÎèàQÊ œÎ»L-ú; dTÐ"ž…k:Ì!99™éÓ§cee…™™*T o߾ܿ_Ó¡ITžb;;;>|¨ü‹ŒŒÄÌÌ {{{M×Mã4=p•(Q‚¤¤$Œ9ÊŽß; Ü §’Jµ6Ã]Fbo˜v¬†Ü Ñt˜B¿~ýسgAAAÜ»w“'ObaaAx¸¼‘ÞO¾¿¬5kÖ,6lHÅŠ5]·ñõõÅÂÂüüüˆŠŠ`„ øûûS·n]\]]iÖ¬¿ýö¾¾¾T­Z•(yDDDЬY3\]]©Zµ*‹/`РAœ?GGG7nœ«ò8@õêÕéÑ£FFFüðÄ……Ѹqc¬­­±´´dÖ¬YJ>“&MÂÒÒ333Ö¬Y£,/\¸0ÉÉÉ 2„””\\\pttäÞ½{ÄÇÇÓ¯_?ÜÜܨR¥  55€½{÷âêꊕ•ŽŽŽìÛ·/ÓúlÙ²'''ìììhذ!·nÝz«Ž eX7pà@&Ož Àþýû©U«...xxxpìØ1e´oßžzõê¡««Kpp0W®\ÁÇÇ;;;\\\8pà€¦»Pp8úÕL•eÓ½g V©ù6hG¢k:̽ëׯ³jÕ*V­ZE©R¥066fäÈ‘¸»»‰ –––T¬X‘^½z‘’’€íÛ·Ç××—Ê•+3lØ0†Н¯/¶¶¶¬X±B)'¿Ž‰¬ŽÏ×ÏoÔ¯_ŸçÏŸkºy?]"‡nÞ¼ùÎ4<fffâÎ;9Íö£È.öððpåçÑ£G‹¡C‡ !„?~¼hݺµxùò¥Bˆš5kŠ!C†ˆ””‘””$Ê—//®_¿.„ÂÓÓSœ>}Z!ijgÏ„¥¥¥ˆŽŽ‡îîîÙÆ–Uùû÷簾¦bÇŽ"))I$&&ŠjÕª‰Å‹ !„ˆ‹‹Û·oBˆ•+WŠW¯^‰ˆÒ¥K+y"))I!D¡B…DBB‚²nèСâ·ß~B‘’’"Z¶l)Ö­['îܹ#ÌÌÌÄ… „BDDDˆãÇ¿UŸÈÈHQªT)&„bîܹ¢N:™ÖsÀ€bܸqB!^¾|)ÌÌÌDLLŒˆ‰‰...âÑ£GB!Μ9#”}àîî..\¸ ´{åʕŦM›„Bœ?^˜™™‰Ç´¾ôo—U_°¿Ÿè²££xžø\üuc“˜2A$¥$‰?ÎÍ~ë¿uÄ€ýý4þ¿FVí¸víZáêêšåvñññâÞ½{B!’““EíڵŖ-[„BÔ©SGÌœ9S‘vžÐÖÖB!®^½*ÌÍÍ…"ߎ‰ìŽÏñãÇ‹V­Z)çƒÏ>ûLÌ;WÓÍþŸ’“13]¾¾¬õÝwßѵkWÌÍÍ5}}‘c'Nœ`È!DEEñøñc\]]•uŽŽŽhkkP¡BªV­ŠZ­F­VS¶lY¢¢¢(V¬gÏžå믿V¶ÓÖÖÎôÎ0·åÛÛÛÓ A¢¢¢ˆŒŒ¤{÷îèééѰaC%mÇŽðöö&&&†”” *”mÙ›7oÆØØ˜ 6ðôéS¢¢¢8pà^^^8::`aa……GŽɰýðööÆÒ2íÛž={Ò¿(Z´h†´½{÷¦qãÆŒ?žÀÀ@jÕªE©R¥XµjÑÑÑ´jÕJIûòåKåμaÆJQQQÜ»w–-[P¥Jììì8vìX†¶ÞæYº& .Ícíµ5ô¨Ü“ƈ~ņë•4^e¼5f „Èr]áÂ…Y²d »wï&..Žððpe– ÀÅÅ}}}ŒŒŒ¨V­–––Jº}ûöåË1ñàÁƒ,Ïô´:::ØÚÚrûömM7í'+ßâ°°0V¯^Í•+W4]§Û¾};cÆŒaãÆ8::²fÍ3M«R©2€é¿§¦¦¢­­ééÑ£Gó­üô2ß%}ðÍìdñæö©©©üñÇÊÉ ÝÊ•+3-+'ågUvåÊ•)[¶,»víbáÂ…Œ1B‰ÁÍÍ-Ûzg'§1}ê|8Ë—/§víÚ´hÑ‚ØØØ\çŸ_ÇDnOIsT"»¯ ÃÊÊJÓñ¾—‚»$å†ìëùC¶£”W¹éCò»¦%I’$Iƒä@,I’$I$bI’$IÒ 9K’$I’ÉX’$I’4HÄ’$I’¤A¹úf­'Ožh:Þ÷Vc—¤Ü}=Èv”ò¢dÉ’9N›«X.¡éº½§'8vIÊ Ù×ó‡lG)obï‡çx0–SÓ’$I’¤Ar –$I’$ ’±$I’$iˆ%I’$Iƒä@,I’$I”«·¦%I*Øž¦Dr7˜£1GP£Æ«Œ7nféÐ$é“%bIúD\‹ alðhRIEšTR¹ôø"ó.þÉÔš?`­ç¦é%é“”/SÓûöì¤a]/êצ®;;þÙ¢éz}²êÔtfsà†÷Úöil,Fúj=zøÖºk×®àêd“§ØîÜŽdùÒšn¢OÒÓ”FRátÞejÑßi £‚Fð,5BÓa6&8X—ÁѶ¶–f|Ù£/^¼È·ü¿ñ5Çà‡)™ÌDÉ;‘,_º.Ýzi²‰>IÇïPî„Ѝµî2’ÛÏ#¹Bý24j°uç!¬*ZóêÕ+êûz°ãŸÍ´nÛ!_òþ~êš®žô‘åùŽ8))‰¤¤$ÂÂn _¼8Z… kº^9f¤¯æë½iÚÐO×ÊÌ›ó+_|Þ‘€úµ©åáÄŠ瀴»¹Õ*áìh…KUkõû‚””nE„Óºy*W*K=_,Êu€f|ùâóŽ4kä‹·{UÆŒª”ݼ±N¨Q­-ü‰‰ŽàÈá¸9ÛÒ¥C+Õ󯝖++—-¢mËF4ð¯I‹âââ2­OË&u9t`ï;ËŽ£]ë\lpv´bÎìY˜•,Brr2÷ï¡f G*W*ËW_vS¶}þü9åKégø½ŒqÑ ±wíØš€úµññªNè¥ |;|0¡ÏãåV…v­ÞŠ=99™aƒ¿ÂÍÙ–êU*Ò"ÀŸÄÄÄlÛ>òVÚ5Ç¿v jy8±jùâ ûvP¿/hàOÍŽnZ§¬[0w6u}ÜññªNû6M¸ÿž¦»âu8úÕL•eÓ½g V©ù6hG¢k:Ìçùó8’““±µsÒî`{uï@«fõ)g¦Ç‰cÁYçãÇ §NMgêÔtÆÙÑŠ²¦ºÌ˜öS¿¯éªIQžb}}}æ-ZI»VéøY3zvmǂū ÔÝp·_²eǦü0‹Ó¾ã۱߱m×!zõîÇì_f`dlÂ?»pæbÇÏ\%ìæuöìÚÀ—=;Ñ´y+.]»Ã¦¿wQø §jÕÙ¼}?ûœâï¿Ösãú5~›³ˆs¡œ8{î5™óûÏÊ6BfþüÛwÁ׿kV-cѲuìÜ„©Y)7­ÍQݲ*»{ç6´hÙ–“ç®s8ä<•lí3l÷àÁ}z÷ìÌì9‹¸tí“§þ”ãöBðÿ±m×!º~þ?ÿ8 €)ÓÆÁ±*G_`íÆmom·kç6B/]àø™«œ¾p“‘c&¢R©ÞÙö#¾ÀÞC'øg÷fNÿž»wc”<¿ð5Ûö²nÓ? ÒŸ¸gÏØµcÇB‚رç(ŽžÆÇ·.3§}÷ñ;ÞG¤F¹®9Ü'1Ài>æ¾Ø:°$tO_Å*i¤œiÛ²!îÕí¨îhEqttþw¾‹ŒŒ`ò´Ÿ¸óŒê®nY翟ÎÁ 3ì9xœÒ¥Íùñ—?5]-ICòejúçŸ~`Åš@ôõ‹3ïÏßøaêDjz×)0ƒq5gÊ[X¢§¯¥UE,,,Ù´~ … fõŠ%Ø¿‡çÏ㈌'&&ŠÇqåò%º÷è ¤ÍÖÊ8W«îªäaaaITÔm¬m*qæô ÆŒJLL±OSÍÙUÙ¦tisÌÌJ¥ÅQ¡÷îÅ ¯¯¯Ä™~UýκeR¶®®.wîDÒ¡swôôôð¯×0ÃvÁGáìRƒê®i/ð”42Êq{–.mNéÒe°¶±eãºÕ9ÚÎÆÆŽë×®0lðW¸¸ºá_¿… F¥ReÚö11Ñ\<–1#‡(y)¢ÍÈ[”*U@¹S)[®mVпnCì•´ÙçcF ¥Š“3í:tÑtµ$ Éó@|èÀ^ *¤œÈÝÿ‚>ýç¾ (Rž,º´€ÕWWbi`‰G)O&„Œþ÷ÜØ½”»¦Ã,pT*­ÛvdÈÀ>Ä={öÖúwçë×®äü¹Ólþ'g}XúoÊó\”¥•57o\#òV÷îÝåÅóçoMut=À»¶ÆÆ&$$$@é2æÑÖæàÿ?—=}ò8/^<w~PÉÖA¹r¾zåòG«Ké2曘²aÝ*^½zÅÑ#3¤qquçÄñ`åYôÍÿŸÒÐÕÕ¥ˆ¶6·#opöÌÉ•[Òл1Ñ$%%e¸°HwöÌ)žÆÆÒ®C~øñ7 Œ¸w7&˶/c^–rå,˜õãT mJ|ÿÞ]<|ø@É3ýä¸sûV’’“°³¯Lý†Y0ïw¢îÜàÑ£‡ÊT÷•A¡ L®™ÖNSŽO³ÍÜŠ»¥¬ŸRsÅÕ4f#„`õŠÅØ;8RÜÀà­õÙç¡—.0mòx-[÷Öã,éÓ’ç;b›J¶ü0ó7ºwnƒŽNQRE*8¯ï#IDAT?ý:— –Vš®[¾0øúöê–¿7¡S´(OŸÆP¨P!æÌ_ƽ‰Ç»–…´´ÐÓÓÏ6¿f-Úð÷_ë©ï牎¶ºzzèêê}´ú,Y±žáCú3cÚ$„|Þ³^Þu”õÖ6•è7`êzaddŒ±‰i†í'MžAÛ 115ÃÒÊ:GeV²µÇ˻ΎVØÚ9°ñïÖ'$Ä3¸ÿi³ Ú:´ù¬#Õœ]²l{€¥+72fä\lB`ieÍüE«”õíÛ4!*ê6Æ&¦,^¾žÂ… д%11Ñ´iÑää$ Ò¢ßÀ!9ªCAf­çÆ<ÿ…„Ü &8&€Z浩aVC¹Ԭ±/ZZZ$%&R¹ŠKWnÌ<]6Çùø1Ãyöô)mš7PÒoÙ~@ÓU“4@%DÎ&9ÃÂÂ(aj©éxßKìýðûÓØXŠ R©½t^Ý;t⢦«ýÉ3ÒWó(.gSèÿº¯*d;Jy{?+«œÝÊoÖÊ«W.á·Ÿg S´(¦f¥ø}îM‡$I’$r Î}ú þ¤^ü)(>µ»aI’ &ùÁAI’$IÒ 9K’$I’ÉX’$I’4HÄ’$I’¤A¹zYK•«éxß[AŽ]’rCöõü!ÛQÊ‹’%Kæ8m®âÜdüoòäÉ“»$å†ìëùC¶£”Waaa9îCrjZ’$I’4HÄ’$I’¤Ar –$I’$ ’±$I’$iˆ%I’$IƒäwMKÒ'$üi8!wƒ9s5j¼Êxãf接üKC’¤)r –¤ODHL0cƒG“J*jÔ¤’ʥǙwñO¦Öü·RîšQ’>Iù25½e˪U«F¥J•ð÷÷çÊ•+š®×Guåʬ­­?JY*•Šäää|ËoÙ²eܸq#ßÓJÿ.áOÃ}pttdëÖ­<{öŒ.]º`gg‡ƒƒ¿ýö[–i±°°ÀÚÚšJ•*ñ믿*yûøøÐ¾}{|}}©\¹2Æ cèСøúúbkkËŠ+4½û?)‡£PÍÔYY6Ý{j•šoƒFp$ú°¦Ãü×»~ý:«V­bÕªU”*U cccFމûÿµwçQ”}Ç¿»È%ˆÜ‡x‚ x+ÞšWij¦–™ie½ÙÛe–G©•foÙe™¦f™ZyhÞ‚šG *‡ r_;ïä–‰º+èjý>9;Ïüžgž™ßÌ3ÏbpõÐ~^^'NÄÅÅ???bccصk=zô`çÎx{{3räHºuëF‡tånt^‰(EOgÏž­ñóØØXÅÁÁA‰WEQ222”fÍš)»wïÖ7ômw½¶*VVVW-›››ë–åàÁƒŠ¢(J||¼âææ¦(Š¢dff*ÎÎÎJLLŒ¢(вwï^¥E‹Š¢(ʶmÛ”1cÆ(Š¢(ÊÒ¥K•éÓ§×X¿¿¿¿²råJ]ÝááázµéJl@‰‹‹SEQRSSGGG%??_)))Q233EQ”ÊÊJ¥{÷îÊÖ­[klCXX˜®[ž>}º2cÆ EQ%77WñóóS~ýõ×Ë^¼xQ)..VEQ²³³[[[%??_QEéÑ£‡²xñbEQ¥  @177W~øáEQ%!!Aqww¿ãÇÁ¿ÁõŽõ§£žTާ].R¾?ó2/zŽRQU¡|t|™Ò{S¥÷¦ÊÓQO»ùwëõãúõë•ÀÀÀën¥¸»»+{÷îU´Z­2gÎeìØ±ºu]ºtÑýÛËËKIKKSEQ>úè#]¹WâÞq½c¨&µž¬Õ¦MÞÿ}F¢(øùù‘““ƒ›››±ï1êDPP>>>h4ªªªØ½{7AAAtêÔ ]ùÍ›7süøqúôé@yy9Íš5»&nZZçÎcÒ¤IX[[3`ÀŠŠŠ jŸŸŸM›6¥qãÆÄÇÇȪU«øùçŸ),,$99™´´4½âEDD°eËlmm5jÛ·o§W¯^5–Ÿ5k‡¢ªªŠ²²24 666РAð÷÷ÀÃÃCïöˆºêÖ™Ïã–³>ñm=™AÍ“^œÆÆ3teº4êjìfÞE¹áz///ºté@×®]‰ŒŒ¬±œ»»;5ª¯/ëÖ­Ó­»Ñy%þyêdÖô¸qã7ngÏžeçÎ5&Ÿ»J¥Òû]¶Z]=Н( ¥¥¥XYYÕXN«Õòøãóì³ÏêUmÚôw¥¥¥XXX0kÖ,X·nNNN<ñÄ×½xÔÔ†ë­ÿ{Ù!C†0dÈ"##±°° U«V7¬çʺ›Õ)ê^ˆk(_Ä}Î× _áÑЃ×PæD¿üùÞ8XfMßTÛ¶m‰'++ ''§›–711¹iâ†êëË•r†œW⟡NfM—••Õï&'NœÈìÙ³1555ö¾Ý”••æææ¤¦¦pøða½¶ æÀ˜˜¨[7xð`–-[ÆùóçÈÎÎ&<<üšîîî8;;ëî‚ËËËÙµk—Ám*((`Û¶mTTTкuk²²²èÕ«NNN”––’””tÝííííIIIªo2úõëÇŠ+€êw]ëׯ§oß¾5–ÍÊÊbèСXXX––F^^žq¾HqS =x³óB|ƒ¡[“Z˜ª[¿ ó"šÛÈo‰oÆ××—2fÌݨNnn.ï¾û.111uR‡œWÿ>uòDüâ‹/²nÝ:™5kãÇ7ö~émñâÅôïß½‚äííÍÌ™3éܹ3ŽŽŽ8;;ëÖ1‚ôôtú÷ïOEEõêÕcæÌ™5ÆÙ¸q#O>ù$sçÎEQ¦M›F= jÓàÁƒ9þ<ÎÎÎlܸSSS^xáÆϦM›°´´¼á‰OIN"3Sð£6bIIgHO»pGvN ÔÌ|f÷ èIH€ßmü€¢¢"šº6Е+**¢‘£åUÛýçé©Ü7 '¡­YþñûLydƒûu§[H{ÝÝeII 3¦?FŸ&„¶& —î)ôµYÿeÙÿëb¾6뿼÷î"~Üöíýšؾ%:ø°üã÷ ®ûïJKKyæÉ)ø¶p#¤£/cöëÖ0mÊB:úØšÏ>ù Æ‰‰§:°!}éÝ-½{vÕw彺ðôôÉx5säKߢ¤¤„ž{Š>=ƒéÜŽŸŸV«­Õ>ìݳ“N|˜0î~÷ëNÏ.‰‹àEóYøÆëº²ùùyLŸ:‰Vž®„¶¾n¹ƒ=é»ðwî ûìí®ï V©yeÿËìMßcìfÞõΞ9ͦ ëXþÅW¸¸¸àààȳϽD@P0ŸºŒ>=ƒéÙ¥#cÂÅ‹™@õÈÒ”GÆ1t`/º·ãÕ—g°iÃ×üv(š™ÏN£K§¶l߯[ æòؤ±ŒÚ&.ÖŠ9À°A½iïל o†C“ž¦W›·mùŽ N¡<:eÖ¯Õý?æó_…Ì £G⡇UïK5K—,¤½_sFtÕ¹R\\ÌóÏN§SÚù6cêäñÕqæÌ¢m«¦têàCÎس;JW÷õ®¯7Ó»ïÒ.œ§¢¢€s©)<ôà0ºÑ-¤=ëÖ¬ª¯í[ê®}ztâdü àÆ×W€±Çܯ;^Íÿàp ®i‡>û_në¬éôô 899£R©ª+S«qttÒûºSšð([#v²zÝ&fÿqrèc⣳5b' ÞZÊ;‹æóÊìùü¸c7M}Rw¼³pUÚ*ŽÇ§°'ú8^-}ôŠÒ…‡ã9|ü4‘ûY´`ÎUŠ>uÿÝâEó)..âøÉTöŒÅׯnÝü9¯`kgOô‘“DDîcåŠO¯:¡Eá‘ñ£˜:}ÑGN²ì“•L™4–¼?þ&¯&=#GsêlÓŸzŽEo¾Ž·O+"wưûÀ14éilݼ©Vûp¥o-ù€wìfÂ#SxoÉ¢Ëåäd3qÒNžÕ0âþÑ×-÷o F»•;s‚çñtûgèéÞ _{?VÅA~yž®Œ¸±ØßÒÒ»Ž5®ßñ#1Ñû‰ˆÜÇÎ}GèÙ«‹Í×­oïß‘-áQDíýÍßoàÌéDî5–€ –¼÷1ûÆÒàÎKáÍEï’ª) c`'>øø ŽÇ§pèX"AÁùøÃ÷ôjó·ß¬eÄýÚ¹ÄDï`öÜXZZ¹¯ÖoÖ•·µµcßÁW}°`þl._¾ÌÃñ‹Kæ¾a#˜Z¶ô†å¯·ÿuá¶OÖR«M®ùìrÅåÛ]­A:àÕÒ‡Œ UUUzmçß!€¦Í<°nÐÏ4kæÁwªïüvlÿ‘?[‰Iu?4h`£w»Þœ÷*G;D•¶Šò²22244°±Ñ»î¿‹ßʲOVbffvM[~‰ŒÐx mm6bQ‘;èÖýÏ÷¬éiȺ˜ÉàûFà׺--½[ñÛáÌ-,ðöñ¥wŸþÖ÷ÓìÙòCuò-(È¿ê&ìVöÀÍÍ7·FºïlÓ·_×XÎÓÓ‹àÐ.‡veWÔ/ÿ¡nù®ºY>z䯾<&¼ÜKøw¼i[324ŠÙϪµQ©T 1Šo¿^KHèõ¿ëG&?Qãç;"¶ñåW›tײ!C«QVÖEÞœ?›Ó‰§¨¬¨ C“~Õv5]_¯Äø«¬‹™„tô%ób¾¾møzÃV4štNü~ŒW_zNWÖÌÌœ çRpvvÁÞÁ€Þaýyrê¤~GWu ¥‘{c;žU_,¿aùëí]¸­‰ØÕµ9ÙY(Š‚J¥BQ²³³pumt;«½ejuõÁ•ö^F½™+ûVÓrYY©î®®¦í®WÇØQ÷ÑÀ`¾Ûú3„tôEA1¨î¿+++ÅÊÊJÏ}Ò¯ÏT7(¨ÕjyçÝiÛÎÿ–ûïfÔjµ^eMLLôŽùOâÊqŸóuÂWx4ô Ä5”9ѯ¾7v 6v3ïz~­Û’p*žìì,®Y¯Õj™øÈžxòÙ›ÆR«ÕðÇ1©ºÉ ¹#œóg³jíF|ýÚðÝÆoøiÛæ›Ö±qýW(ŠB·vT\¾LiY) ßùæææïÿßÛ™{é#îë矯%¬ï²².Ò³s‡·ýëõµ&NÎ.D9É©“qŒÒ‡¸¸ßéÜ¥;ŠV‹™¹9[£®ÙæÊ«±+JËJ1·°@¥Rt ¯¬¬ÄÂÜÂàý¯+·u,ʳ…vööü´í ú]…““³îÉçnfee…™¹9çÿ¸ë:vôð-Å éÂ7ëV£( ©)ÉœN<¥[çêêFâË%%%$&œÔ­ËÉÎbÀ ¡XXX IO#??¯ÖûÔ)”Èáºúþú®¾Wï~¬]ýùyy|¿é[zôîsÕöÜãàèÄO?V_âãbI8OÇ€šöÒ·ÿ`Þ^0—ÒÒRNÅsôÈ­õ£¨†&Íy³óB|ƒ¡[“Z˜ª[¿ ó"lÔÍÝÌ»ž·/a}2eÒXÝèN^n.}ð.¿С߀A|¾üCÒ.TO:ÊÉÉÖs7bggϹs)@͉*;; o?Ýr©“7 ÕÃÒ«ÖnäX\2Çâ’‰;FãÆMÙ±Íà}ïÙ«/Ÿú!Z­EQØùëÏafjF¯°~¨ÕjNŒ«u·òmÍ'+ÖòèãI8O#÷Æ4iÒŒ¥Kêú'ê—dggPRZBee%/[JÿÕCû7º¾V¿ê+++cÍ—+è;`Áû_Wêä‰øË•ËY½ò3ΟOeêäñøµnË竪‡ W¯ûŽgžšÂìWžÇÉÙ…/¿ÚTËÚîœyo¾Ã¨áprvÁÃóÖf3Ξ»'&ÇÇÃ…vþ±¬__·n̸‰L7’þa©_ߊËååWÕ=uòxq¼Å™˜÷ÚÜ…L›2-›7acÓPw ^Y÷ßçž$¸c+T*5“}œ=îÚ^¥R±jížv:óf¿„eýú|úÅWØÙÛ×Xß«¯¿Éü9¯Ð=´= Ú²è÷Æáe݉åa+ˆÎ8ÀMõD½nîÝ r ’$l€O>_ÃÂ7^cø0 P©Õôé;QcÆŒF“ÎÃPYY‰I=žœñÜMc>þÄÓLŸ:‘>x—yo^;?bèðØüýúõÅÂÜ+kk¬¬¬o3>.–œœlzöî{ÕçãÆObýº5Ü7ì~ƒö{öœ¼ôß„ø¢Õj  æÓk?ñ1zu ÀÞÞA7Ô[[=z†ñú¼EŒ9ˆˆÈ}|ùÕ&^}é9Û·DQ<<½øì‹u\8—JX÷ rr²éÐ1ˆ÷–U1ßèú °+*’n!íÉͽÄÈÆ0~Âdƒ÷ÿï}{«TŠžãuIIIØ:{ÔI¥wZÞÅ仦íMK¿ƒ=¦îfÜ qÅÝt¬ßˤï {÷ìäísk¶6¶¼‹ÉxzzêUV¦I !„F$âò»2d/„¢vºvëI×ðžÆnF­É±BaD’ˆ…B#’D,„B‘$b!„ˆ š¬¥ªÈ3v{oÙ½Üv! !ÇzÝ~µagg§wYƒ~G,„Býèû;bƒžˆõ *„Bü›òð*B#’D,„B‘$b!„ˆ$ !„F$‰X!„0"IÄB!„I"B!ŒH±BaD’ˆ…B#’D,„B‘$b!„ˆ$ !„F$‰X!„0"IÄB!„I"B!ŒH±BaDõnWàÊ*-sKxg] Y¹%,}& 'ÛúÇX¶ñ7’5ùä•cceFhwF‡ùb¢Vé'ãR1_lûô¬B K/Ó ¾íZ83~@ê›Þ ðî×9š˜Is·†¼ñxwƒ¶_q‚í1É×|~+}””žÇ»I=»ˆ>ŒªqÝûÿ鋽…Þ±¶Ç$y8…ì¼,ÌêáçáÈCý[cß@ÿ·ËáS¼·þÐ5Ÿ?>ÌŸîþMP€ðg‰ˆN"¿¨G[KÆömM`+Wc7€3r™³b/þ-y~\°±›#„¨C·-¯ÝGä¡”ZŨ¬R8‘”E ¯ ­Ì‰:rŽ÷ŸÅ¶C<õŽ“WBv^ |\±²0åÐI »ÇÎÆ‚Q½ZÜ®-{Ns41³Ö}èëJs׆ºe+K3ƒ¶?“–Ë+÷SY¥Å«±ÎvõqhhiP +3èå£[Î-,ã—é¨Õ*ÌLMôŽsð¤†5'pw²fò}í9–˜It\:å—+ïªÄÑÌÕ† _7Ýrs·êþß÷ûÖíˆÇÅÞŠá=¼ ?Ä{ë±pZOš870v³…ÿ`·-îÜ‚®í³ä냖\¾¥æ¦&üï?}±²0ÀÌÔ„ïw%rábAq|›;òîŒ0JÊ+))« ¯¨ŒÌKÅ?}Ä%g³1êúµdóžÓµê£VÍèîßô–žÊ6E%PY¥åámðjl‡Z­Ò%}Y[š1¼»·nùãïÐ'°9Ö–¦zÇÉ/*ÀÜ´úIØÖÚœè¸tƒ’ùàê`MX`s¬ë›ñ×1•]GÏð@/BÛ¸SY©eóžÓìûý— ¿ž$E“‰ZEkO'& jKfn1óWîÃÜ´‹¦õ¤¸¬‚×>Û¹Y=æ=Ö9+öpìôEÆÏÝʰn-ÕÛð›H!ÄÝç¶%b'Ûú8ÙÖ§žÉ­¿†V©Ð%a€„s—ðnboPœ+ÃØkÂcÙsüÛºÓÿ©Aq.–ñá¦ßèäAH÷Z'âµq¬ˆ£A}3Fôð¦_'ƒ¶OÑä°1ê¥å•´lbÇËB1«gx<“–˾ß/Pߢ#{z´m·öM8’AìÙ,žYú3–æ¦x6²å±¡þµê£º—NL\:¦õÔtmטIƒÛa¢V‘W €“]õÍ™ã7iY|n(µŠ!-ØuŠïv&гcS*+µ¼µæ^Mìx~\0©ù¬?™© S‡ùóð€6¬ú1–¿ûâÒ *«´<ÿ@Nvõ™4¨-«~ŠÅ«±†ùâhkØÈ‡âîuÛq][÷s<ñÉÙ´ñt¢» ôŠA[Ðɯ¿ü–ÊþØ4l­-×O¿§E6ÆÚÒŒ¡ž\Ì- J«WTŽ­µ¹Þí lN;/g´Z…s™|¿+‘Õá'hÙÄ=Ÿh ¨´z¤áµG»RÏDÍÒorú|.{_ w@3ƒúFÖ„Ÿ`xwo¬ &ÏÎ/!+¯ÏF¶4sµáÀ‰4’ÒóØøë)& ls+_WònbÇ þ£­¥lÙsš¨#çps´fPh ê™ü}ÎR«úFôðÁÖÚœ£ $œ»Ä¥‚RÎ\È¥¼¢ŠS©9¼½6ZW6)-¨…8{!Ww³8¶¯­=?‡Ð­-Mñmî`ìîBÔ¡»>_®¬bÅÖßÙ÷ûZ¹2}dGTúÏÓªŸdm­ÍiâlCgJÊ+8–˜ÉÙ?.€ú(¯¨âôùêòÿùß/ºÏÏg0{ùn>x®¯Þ±l­Íiäh @oԢɧ ¸\ï*À¡¡%Ùy¥˜›šàlWŸ–MìIÏ.¢ìr¥Áý¼÷øyΦåálWßà's€v%’‘SÌÔáþtkß„¡-xáÃ(~9œÂÃÛ ïW¦(Õ“ôÔj&jUZ­VÁÄD…Zuí²¾ê™¨iïå¬[ÎÈ)"ü@ÅÕ73 ë£É)&+·/w;Ý–ƒMížÄŽƒÉì=~ö-ÉÎ+eóžÓ´hlGGoÝD¾”Œ|Ž$dàîlƒ‹ás„wŸÛ–ˆw=wÕ¬éÕá'ðnbGkýZ£É.â\fõĬœüR6E%èÖÝ×µ¥Þ?ajánËÑÄLöǦQQY…] FöôaX·–z·E­RÑÁÛE·|þb!–æ¦Ý\´máÌáSŽÎÄÚÒŒÞ.ŒêÝŠúúOŽÓÇÓzjöǦŸœMc§ÜßËw'ÃfùnÞ“H^Q9ÞMíéô—ņÑíVKLœ†[ÇÊÒ”ÎmÝÓWÿ‰N·“w{NŸ¿DäáLMÔ4u±á¾®-uó º´kL^aQGRùaO"6–<6´=>M ›p#nV¼ôpßïJ$":‰ÒòJìl, hF¹K|µ=Ûæ<1¼yEe¼öÙ>ùþóaÍØ›Æû~cTïV îÜÂØÝ*„¨*EQôz–””„§§þ?B!þ­ É™ò—µ„B#2hhZχg!„BèIžˆ…B#’'b!„ˆ$ !„F$‰X!„0"IÄB!„I"B!ŒH±BaD’ˆ…B#’D,„B‘$b!„ˆ JÄZ­¶–Õ)`Њ'1$†ÄCbü³cÜáD|¥‘CbH ‰!1$†ÄšB!ŒÊOÄB!„¸Â Dœ››kìö !„ÿ(*EÆ›…B£‘ÿX!„0"IÄB!„I"B!ŒH±BaD’ˆ…B#’D,„B‘$b!„ˆ$ !„F$‰X!„0¢ÿC/g/U%tEXtdate:create2018-04-12T17:59:40+02:00i)<Ú%tEXtdate:modify2018-04-12T17:59:40+02:00t„fIEND®B`‚django-tables2-2.7.5/docs/img/semantic.png000066400000000000000000001553041473544236200203600ustar00rootroot00000000000000‰PNG  IHDRçI ™VgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿÿÿ ½§“ oFFs'¤ïFDtIMEâ ;099‘ø vpAg4lqê€IDATxÚìÝy\OÙÿÀñW{’%Ed«l¡ÈR™!bÈ2™‘%Ûh†aÛ,|™ÅûŒ™±›!;a˜Aƒ¡ ÊZÖ"YJ«¨hýtôëóÕ·"KúïçãÑã¡{Ï9÷œsïçöþœ{Î¥¥(Š‚B!„¢Ìi—u„B!„y$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!^88=z4ÖÖÖÌš5 •JÅСCqtt$$$¤¬ÛWææÍ›‡µµ5#GŽ|bº«W¯bmmµµ5)))/½÷îÝãÛo¿¥S§N4nÜWWWÖ­[GnnîK?ÖO?ýDóæÍY»vm‰Ò÷èÑkkk6nÜøLÇQ©TüöÛo¸ººÒ¨Q#Zµj…··7/½Mšàe|¶vïÞ­¾ÎîÝ»WdšÆcmmͯ¿þªÞ6a¬­­éÝ»wYwà kÕªÖÖÖüý÷ße]•%é_!„x1/}ä<**Š£G’˜˜ÈŸþYÖí{)F­­mYWã¹=xð€¾}û²víZ177'**Šo¾ù†¯¿þú¥oÓ¦M¤¦¦ ¶_f?æææ2bÄfÍšÅíÛ·©Y³&YYYàííÍÅ‹_Iß–†ÜÜ\ðöö.°]Ó?[ÅÕûßäu¿!„Ð|/=8¯[·.íÚµÃÔÔ”^½z•uû^гgÏ–u^Èœ9s¸yó&;vŒþùG=*ºiÓ&ÂÂÂ^êñú÷ïOùòå8p`í/³÷îÝË‘#G033Ãßߟ€€BBB˜2e ï½÷vvv¥ß±¥äÖ­[$%%ڮ韭âêýoòºß „Bh>Ý—^ ®.ëׯ/ëv½4< ..##£²®Ês;xð Ÿþ9ÆÆÆtêÔ‰š5kMpp0666/íx'Ndâĉ¶½ì~ ÀÑёڵk ¯¯Ï‡~XêýYÚ®^½ZävMÿlWï‹ý@!„æ{¦‘ó¤¤$&Mš„½½=Íš5ã?ÿù=*”®¨9‡‘‘‘|þùç8;;Ó¨Q#:wîÌ äëÑ£M›6åúõëL›6V­ZÑ´iS&NœHjjª:]NN¿þú+nnnêù±ÿüñÇdddðóÏ?ãêêŠ mÚ´áË/¿äîÝ»%jï?ü@ÇŽxô葺üíÛ·y¬,X@çÎiܸ1mÚ´añâÅÅÎãàwÞ¡qãÆtïÞcÇŽ=µéééÌŸ?Ÿ:`ccƒ««+>>>Òܹs‡±cÇâääDÓ¦Méß¿¿:x8tè´jÕª@¾ììlttt lOMM¥aÆ4nܘK—.©·_»vÆÓºuk>|¨Þþ×_Q¿~}Þ{ï=à¿ë¾ûî»õc¾ÌÌLf̘Qìy\NN§NâÆOìÃèèh&NœHëÖ­iܸ19r¤Èk/44”>úˆ¦M›Ò­[7.^¼HRRãÇÇÞÞ'''¦OŸ^àºW©TlÚ´ ìììhÖ¬“&M*°vàï¿ÿÆÚÚšï¿ÿž€€zöìIãÆéÔ©S9òƒV± P÷Õ;w€¢?[>dîܹ¸¸¸`ccCûöí™3gNsTÚžVo€­[·âîîNãÆqttä›o¾!--­PÍœ9“­[·Ò¡Cììì˜1cŠ¢°k×.ºvíJÓ¦MéÝ»wÏÏãýNÿþýiÒ¤ íÛ·/ÑZ†§}–Kr 3xð`õ50jÔ(¢¢¢žûÚ¼uëMš4¡~ýúlÛ¶M'99™6mÚ`mm¿¿‰¯Áï¾ûŽzõêáççÇÌ™3iÑ¢ŽŽŽìرƒœœ~üñGœ±··çƒ>àöíÛ/­KrB‘§Ä#çÙÙÙ 4ˆððp´µµ©Y³&[·nE¥R=5oTTîîîddd`bb‚¹¹9‘‘‘Œ9’ 6àìì¬N›žžNß¾}ÉÌÌÄ‚[·nñÇ••ÅâÅ‹˜={6>>>èééQ»vmÔAœ¾¾¾zþñ!CV×7>>ž­[·Àü……Åë]¾|y*UªÄýû÷ÑÖÖ¦Q£FT®\™ÜÜ\úöíKdd$FFFêºþøãdgg3a„e:u j×®¶¶6aaaŒ1‚={öP¯^½"Ÿ••ÅСC FOO îܹÃwß}GRR“'OæÑ£G 4ˆÛ·oS¹reªU«ÆéÓ§3f 'NœÀÐÐcccõˆy¾Ë—/ä-|\… hÕª'OžäÈ‘#4mÚ€™™Iff&ǧsçÎêí¹¹¹êàåYúñqóçÏ(ö¼?ÎÎÎŽíÛ·“˜˜È;ï¼C=èÛ·/mÛ¶EKKKîöíÛôíÛ—ÄÄDŒ©V­çÏŸgĈüú민ýöÛ®=OOO ¨X±"áááŒ?ž*UªpáÂu½6n܈¢(Ìœ9€iÓ¦áëë‹®®.µjÕ">>ž;wÍ–-[ Ô{ëÖ­¬^½šêÕ«cddDTT}ô~~~4jÔˆ *P¾|yÒÓÓ©P¡µjÕ@OO¯ØÏå!CÔÓ-j×®Íýû÷Y¹r%¹¹¹L:õ©ŸÏ—áiõž3g+W®DKK‹š5krÿþ}Ö­[Ç•+WØ´iS/ˆ›7o&##KKK²³³Y³f ©©©üþûïT­Z}}}.^¼È|€¿¿?5kÖTçݽ{7¾¾¾”/_žòåËÃôéÓ©\¹2=zô(²î%ù,?í>xð £G&''333´´´øûï¿ aÏž=˜››:nI®ÍqãÆ1oÞ<æÎK×®]©X±" , !!îÝ»ãêêúL× ¢(|þùçäææR½zun޼ɗ_~ÉÑ£GùóÏ?©]»6™™™üóÏ?x{{³wï^´µµ_¨KrBñ¥„6nܨXYY)Í›7W®\¹¢(ТįÆ*o¿ý¶bee¥|ÿý÷ê´-[¶T¬¬¬”ýû÷«·ýôÓOŠŸŸŸ¢R©EQ”ï¾ûN±²²R>øàušîÝ»+VVVJïÞ½•»wï*Š¢(ûöíS¬¬¬kkkåÁƒJnn®bcc£XYY)¡¡¡Š¢(JjjªÒ¾}{ÅÊÊJ SEQ|||+++¥E‹êm÷ïßWÜÝÝ+++e̘1%jw`` bee¥4mڴо?ÿüSYµj•òðáCEQåÀŠ•••bkk«äææ*Š¢(sçÎU¬¬¬”&Mš(ÇŽSEQÅÊÊJ™:uª¢(ЦXYY)VVVʃEQ”U«V)VVVŠ“““rûömEQåöíÛJëÖ­•† *qqqÊáÇ+++¥C‡JFF†¢(ŠrãÆ %88¸Ø6åææ*ƒ R¬¬¬”=z™fÅŠŠ•••2tèPõ¶~ýú) 6T¬¬¬”éÓ§«··mÛV±²²R.]º¤(Š¢|üñÇŠ•••2cÆŒõcIÎûÿÊÌÌTŸËǺvíª\¸pAîÃ?T¬¬¬”Áƒ«ÏS@@€bee¥tëÖ­P† ¦¤§§+*•J2dˆbee¥Ô¯__]æ–-[+++ÅÆÆF}-_¸pA™7ožºîwïÞUlmm+++åüùóŠ¢(ÊþýûÕemذAQEÉÈÈPz÷î­XYY)ß}÷º. ,P¬¬¬”#Fj÷ÿ~¶Ö­[§XYY)vvvʹsçÔ}³eË%99¹Pþ]»v©û*))©ÈsŸÿùZ¹r¥zÛøñã+++¥W¯^Å^WÅÕûâŋЕ••R¯^=åŸþQEQ=z¤ôïß_±²²RöîÝ[ 6l¨>|XQE9}ú´º¾ü±’““£<|øPquuU¬¬¬”%K–Èkee¥øøø(Š¢(ÙÙÙʰaÃ+++¥{÷îÅö¡¢”ì³\Ü5œ‘‘¡888(VVVÊ‚ ÔégÏž­XYY)_ýu‘ýU’k3++KéÖ­›úótñâE¥^½zJóæÍ•øøxuY%¹g̘¡XYY)íÛ·Wnݺ¥(Š¢,Z´HÝo«W¯VEQ"""” (VVVêûÈ‹ôoIîcB!þ«ÄÓZvîÜ @ïÞ½Õó“ÍÍÍ ºçÓO?¥{÷îêQ˜·Þz €ˆˆˆBißÿ}ªW¯äÍÖÒÒBQnݺ…–––z4NQ”Ç¿d¨Cûùùàáá¡åªT©£GÀßߟÌÌÌúbÓ«W/>øàõÔ:yÓ bbb ¤mß¾=mÛ¶ÀÔÔT=ääɓŖŸß†¡C‡ªG"kÕªE÷îÝÉÎÎ&((HÝ÷îÝ#88KKËBSX÷Ã?pìØ1ôôôÔ¯Àü_ù£à!!!äääššÊÙ³gqqq¡B… =z€7n‹¹¹9Mš4y¡þ|Òyÿ_úúúøúúòÑGgðàÁÄÅÅñðáCþùç o|þyêØ±# 6äêÕ«…ÎSçÎ144D[[[}¾¬­­Õ LóG*333‰òFñ?ûì3uÝ«W¯®¾æþ÷ú611aРA¨¯™›7o>WŸ=~7oÞ\Ý7žžž…žL”•½{÷àââ¢þÜ—+WŽ!C†yÓ®W©R%u¿´nÝCCCÜÝÝÑÑÑÁÈÈH}nþ·ßÌÌÌ6l7Gÿƒ>àÊ•+O|Eé³|–ÿ×É“'Õ£ßãÆS?¹ùè£Ô×àãJzmêééñý÷ߣ¥¥ÅúõëùòË/ÕODªV­ª.ïY®Á-Z¨×i´oß¾@Ô¯_Ÿºuë¾´þ-É}L!Ä•xZKþÜÉüþ³ÊÍÍåŸþáŸþ!22RØ|8‹-¢oß¾Ô©S‡øøxRSSiÑ¢…ú‹C~}4hP ¼úõëyÁULL VVV/Ô/^dß¾}\¹r…û÷ï«·?>—¶(–––$$$›&Îç¼yó˜7o^¡ý±±±ôìÙNŸ>ÍàÁƒ©[·.|ðÁ”/_¾Pž7²téR¾ùæš5kVä±4h@­Zµ¸sç—.]"&&†œœ:v숾¾>ûöíãæÍ›?~ Ø)-Ï«¨óþ¿ŒŒŒøòË/™4iGeÅŠœ>}š””öìÙÃ[o½¥ž›îááQd±±±ÅNo200P‡øæKOOWÿ;&&†Ý»wsþüyÔ‹#Ÿv}ç—W\Ÿ&¾}I¿$?>=æñ/·Ëÿ‚[ÜTšg•ÿåêðáÃX[[ÚŸ/xReddy?EÉÿœÄÇÇS±bÅbÓ>ïg9ÿsš––Väý±¨öݽ{·Ä×fË–-éׯ¾¾¾\¾|™æÍ›óþûïJÿ<×àã×siõoIîcB!þ«ÄÁyþÜòçySARRÞÞÞœ?þ¥TzäÈ‘øùùÇ;w033£OŸ>|úé§æåñù“OKû$Š¢ðå—_X¨õ,òÿèýïbÌÇåGµk×.4gòþ0êêê²aömÛÆüAHH .äÀüþûï¬={ö¨ßk>zôh ðÄ:¾ýöÛlذS§NqíÚ5 /×ÓÓcß¾}=z”S§N©Ó–===:uêD‡èÕ«aaaܺu«@Ûmll ommmÌÌÌ^øø›7ofÆŒ/ü$æE”ô?“ªV­šúß111˜ššØŸ ^(üxÚ‘LMM‹,3MCix|á®®nÑ·»ý,ç·ÏÐаÈ/ûUªT)6<ýÚTEýùƒ¼Åõ ú²¬®Á’ôoIîcB!þ«ÄÁ¹¹¹9÷ïß/°‚¿¤~úé'Ο?“&M¢yóæ\ºt //¯çªô?þHdd$Ë—/ÇÍÍ­È4VVV$%%ø£ÿ}Äk``@5ž»ãüüüضm¦¦¦L:•¶mÛbffFÆ K”?¿OúÃdmmÍÝ»w6lÇ/6žždàÀ„……Ñ·o_BCC¹zõªú?L9rä“&MR/~+É"¬Ž;²aÃÎ;Ç™3g°±±Á‚·ß~---Ž=Ê… Ð××§]»vÏÝ—Ï#''‡ÜÜÜ#ºººT«V°°0ÌḬ̀°° \¹r¤§§³`Á‚žvS”ØØX¾úê+rss™øü·èèèàààðR꘿à¹eË–¬X±¢ÔûäqùŸ3}}ýbŸ¼èg9ÿi€¡¡!»wï.0Pœg¹6·nÝJHH 6¤J•*œ8q‚Ù³g³páB l¯Á’ôoIïcB!ò”xι££#¿ÿþ;ÉÉÉ@ÞHJò:Àü€¾{÷îtèÐ##£"ça–Ôƒ;v,]ºt¡OŸ>Œ1‚Å‹«Gç¿9`ÇŽê? ))),[¶ È›[œ?máIòÓddd@bb"€úqvvv¼ûî»T¯^]Øåúõëêÿ.=""Bý ¶âiêÿ*ý×_åúõëêígÏžU¿¾ìÑ£G,]ºT=/¶Fê:WªT €3gÎ0zôh²³³yë­·˜={v‰ú¹mÛ¶rôèQâââÔSWªU«FãÆ9~ü8qqq8::9…¦$ýø¼¦L™BÇŽ ÌW>sæŒ:éܹ3zzztïÞÈ{ÃOþu“““Ãþýû ¼Âðyݽ{•JEÅŠ1bÖÖÖܸq㩯Ð{’üé111¨Tª'öÕ;ï¼ä­ Éÿ_Q>|Èüùó ¼3Ÿ¾¾>C‡à·ß~S¿ò>Ï ,ò®ËÇç4¿H½{ô莎^ͯ¿þZìë2ŸGZZšú?ÕzðàË—/W×áñ/r+ég¹¸k¸E‹Ô©S‡û÷ï³`Áõt•üÿ%7<<¼PY%½6“““ÕSA¦NÊþó´µµùóÏ?Õ×zi\ƒ/³KrBñ_%9>|8[·nåÖ­[tèÐjÕªE5ž¸Ð  yóæ9r„¹sç²eË’““©\¹r‰F˜ŠÒ¹sg~ÿýw Üì:ÄÁƒÙ¹s'ÄÏÏàà`ÞyçjÕªE\\™™™˜™™1eÊ”«Aƒê®·Þz‹¬¬,Ö­[§ž«ýÏ?ÿàää„¶¶6<ÀÜÜœ¸¸¸BåDGGãììL5ˆŽŽ&''+++úõëWì±=<<سgGŽ¡k×®Ô®]›ôôtâããÑ××§U«V?~œ ðÓO?©_+™––FûöíÕ¯ÕûðÃÕŸ³³³éÛ·¯:€ÐÒÒ¢ÿþE>Å044ÄÙÙY(<>¯¼cÇŽ,Y²¤ÐögíÇÇ_£YRœ8q‚»wï2bÄ*W®LÅŠ¹sç¹¹¹|ðÁê9ØŸþ9Ç'(('''jÖ¬Ibb")))ÔªU‹öíÛT”Dýúõ166æÁƒ8::bffFTT”zAÝóÈ¿¶Â°··GWW—€€LLL ¥8p ;vì 44”>}úP»vmîÝ»GZZIIIÌ™3§PžO>ù„óçÏsôèQ>úè#ªT©‚ŽŽŽzýCóæÍ™6mÚK«·¥¥%ãÇç‡~`Ô¨Q˜››c``ÀíÛ·Q¥ÈÿMöyeddУGjÕªEBB”/_ž±cÇ>µÞOû,?éž5k#FŒ`ùòålÚ´ 333¢££ÉÌ̤gÏžüüóÏ…Ž[’ksΜ9$''ãææ¦^¤êááÁöíÛùúë¯Ù³gO©\ƒ/³Kr{™ÿ šB¼îJ[ZZ²zõjlllÈÎÎF[[›ùóçóŸÿü§Ø¹ÛùÛGEß¾}©X±"ÉÉÉtéÒ…ßÿý¹³‹/2nÜ8:uêĹs爌ŒäÊ•+lذ.\¸@bb"zzz¬[·ŽO>ù„Úµks÷î]*T¨@ß¾}KôŽó|•*UbþüùÔªU ]]]066ÆÙÙ™/¾øsssRRR°¶¶fëÖ­ÅNÕùâ‹/8p ÉÉÉ”+Wwww6oÞüÄÀP[[›•+W2aÂêÔ©£^”éææÆ¶mÛ°±±aРA|öÙgêýUªTaذaêwƒ_½zUý¤àرc\¼x‘+W®påÊ._¾¬~óJQòïêÕ«Ó¢E õv777õ—«’çÅõãó044䯿þâóÏ?§yóæäääG“&M˜3gNÀ²jÕªìܹOOO*V¬ÈíÛ·©P¡^^^lß¾ý…sÈ{¿÷/¿üBƒ ÈÊÊB__ŸŸþ™Y³f=÷‚J>þøcªT©‚A…­ÿK__Ÿ 60hÐ ªT©Bll,Õ«Wç³Ï>cÆŒÅæY½z5ß}÷Í›7'##ƒ´´4š4i”)Sðõõ¥B… /µÞcÆŒá矦Y³fÜ¿Ÿ¤¤$Z·nÍ/¿üòÒsÈ{›ÈgŸ}¦þ½M›6lÚ´é‰ÓÇJúY~Ò5ìììÌÖ­[騱#Š¢¥¥%Ÿ}ö™úýýÿëi׿ŋÙ¾};•+W.pMOš4 SSSÂÃÃY³fM©\ƒ/³KrBñ_ZJq¯lxNéééØÛÛ“ÍÖ­[iݺõK­ðÆ™>}:ݺuãçŸVÿñ9yò$ÄÄÄ„'N»8Iñïó÷ß3jÔ(ÌÌÌÔ‹”ÅË#ý+„¯ÎK`ùå¶mÛFvv6fff¥ò†:P±bEöíÛ‡ƒƒæææ¤¥¥q÷î]´´´˜>}z‰ó‹/òå—_¹¯víÚêù”B!„B¼ /58¿téIII4oÞœ¯¾úŠråʽô ×®]›­[·²lÙ2‚‚‚ˆŒŒÄØØ˜Ž;2räHõÂÕ’xøða±o¹(Ë×â !„Bˆ7ÓKŸÖ"„B!„x>Ï÷º!„B!ÄK'Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCè>KâGqÿþ}²³³ÉÍÍ-ëº !„B¡‘´µµÑÓÓ£råÊ•8_‰ƒó‡’””„©™zzúeÝÞ×N||ÕªU-ëj!Ä!..sóje] !Ä.;;‹ÄÄDLMM)_¾|‰ò”xZ˃0«ZUs!„B!J@OO³ªUyðàA‰ó”88ÏÊÊ’À\!„Bˆg ««GVVV‰Ó—88WEQʺ}B!„B¼Vž%†–·µ!„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„ni|þV _l¾BÇ&f|ѳÝç$Wùošrú:Ô77âûjtjbVÖ}¡Q6E³.ðN¡í&åõØüI˧æï¹à «—篦\‰IcÂúKôw¶`˜Kí²nšBh$ðM`ßù®Ç?"G•‹yeC:66å]‡êéë”u…o€R ΋c¤¯Ã”ÞõHÍÈaï¹xæí¾Î™È&¹[£UÖ=¢az¶¨†c}õïz:ò°C!^¶\æìºÆ‘°$ìjWà7Kôt´8õ€-Ç£±·¬DӚƥ^ÍÇ¢ùçò=VxÛ•u—!ÊÈ+Îuu´p°®¬þýíÆfÌü#œƒ—°«cL×fÕʺO4J-S£ý%„âåÛu&Ž#aIthdÊ—½ë£ýÿ#ElLùÈÕC½W30r):µ¬»BQÆ^ypþ¿´µ`ˆK-Ž…'ãw6^‚óÈŸ¦2¨}M·«À7;Â9‘Ì®Ièë–üÈÈß.R­²>Õ+êp9 }]m(šK·SÑÒ‚¶õ«0Æ-ïÕ’Q »GŸVÕÙ|=m¾z·áÉì=@×fUñvfªXsøÿ\¹@+«J|ÜÙ’JFe~ !Þp{ÎÆðÁÛµÕy¾Çóè{¬:|‹KwRÑB‹¦µ+0â­ÚX˜â{<Ÿ#·™; 1ÍëT ïOÁÔ®bÈOCl9vYF0Òµ.û/$œA£å™ðŽ5&†|ðëybîeÐmîIZYUâû~6Œüí"æ&úXU5bWHlLIHÉ$ôN*Ë?°ÃÂÄ€Õ‡o³õD +FØQ×̨¬»Tñœ4bŽDS#t´µ¸‘𨬫¢qT*…ô,•úçñ9û/Ëéë÷ÉÌQß-ïĪ÷ˆˆ{À©kÐÓÑflW+Þw´à@h[ND«óÞ”ÍÕ¸4>q³¤r9]¾ØÆékëf…MMc¶¼Ë©ë÷˜õgû.$п­£:×åJL þº^Ö],„xÃåä*ÜIJ§¼Õ+›.5#‡Ï6_æjÌC>x»C;ÔâÒíT¦ø†‘ž¥*ѱ`ÇéXÜ[š3â­Ú„ÞNeå¡[Œïf±¡æ• øîýF ëPKïÔµûD'gðeÏzx:Õà½65ÈÊÉU©ÈÊÉå¯sq´¶®,¹¯9²Ôp+Ýdå¡›êßjûÒaR^‰Ý­¼'—î¤r5æ! ÌË3¨}Mu:U®ÂŽà»„Þ*øØõ—ÚÔ6+ǃtKþ¾Á»Žæ¸ØT¡Ry=N]»ÏµØ‡Ô¨d@ÈtmVîöyOGeªXr Šä‡Ù˜”×+ë®B¼¡”ÿôÐzÊ¢§ÃWîq/-› Ý­éjW€l•ÂÒQ¿–\âãõs² Çÿß÷žOàjLÍjW@OGC]BÓËèðeÏúèþÿ°¾…‰!õª•gÿ…†¸Ô&ðji*Þs¬QÖÝ)„xAœßNz„*W¡^õòe]ó®CuÚ5¬¢þ½vC¢ÓKíxº:y7þìœ\¢±)0†ó·ðàQÕ*=B”?›&ѪÞÿ—••›ËÝ™ì¿Ïþ ñòÅ>È”à\Qfôt´¨^Ù€Øû™Ä=ÈļRÑ£çqò¦œÔ¨d¨ÞfQ9ï߱əèh?û+ ôtòF½ŸF_W[˜ç{Ï©:sw]ç`h"—¨o^ûº˺;…/¨ÌƒsX˜7M¢G ™oþ¿ªW2ĶV…ÛôuónÐ9ªR˜ãò˜Œì\&m¸L• ú «6U+0oϵçlGÞ»ÎM«Ò;ªz»Pß\¾” !ÊV—¦UYt‡õG£™ìn]`_þÓ½êùøƒ š‘w_¾{?/`¯nbÀƒ‡y¥}oÎ÷–kßaÛÉâdª_[,„x½½òà ©Y¾œÄù[)tlb†›,-‘š&å(§¯Ã?—“¨W͈›Ié„üŸ¾L©é9<ÌTѨ†>• õ½“JjzÕ*è?sYuÌÊÑ®QŽEÜÃÚÜ‹ÊÜLL':9ƒ†5JÿõdBñ$ýÚZpöæ^Jà~z6o7®B9=NE>à`h?x5¥ƒM6ÝaÝѼÿƒ"WQØt,óJ´­oµ¸¼uS;NßåQ¦ŠS×ïó0³dsÑó•7Ô%.%ƒýhjaL-ÓrŦÕÖÖÕYæ“j è`cZÖÝ(„x ^ù‚ÐGY*¦o»ÊômWYé“U.“{XóyÏzòŽó2ÔÓæ³õÐÒ‚~‘\{¤žÇý2U­¨O?' ®Ä¤ñãÞH2³UôjUýé3‹1©»5nͪòGp,ßýAÀ¥$×4VOBˆ²¢§£ÅìþÞ¡6 ²øyß æû]çFüCFu¶¤aõòT0Ôe^ÿÆÔ3/Ïo‡n±úŸÛ4²0f¶§ åôu°«]íjûŸ÷Gb\Nç™§™ i_“rú:üp‹€+IOMoW'o¿O«êÏ5­F¡y´E)Ñó·7nP³–üï’Ï+>>jÕª¾xAB!ž*..sóÿÓØy»¯sòz2ëG·ÿÁT }ç6VVV%J«¯RB!ij¹“”Î?WéݺºæBü‹”ù‚P!„B<»Z¦åøëó6e] !ÄK&#çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B ¡û,‰ããʺ¾¯5é?!„xuâââ˺ BñÌž)8¯U³FY×÷µyµje] !„x#È=W¡InܸQâ´2­E!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCè–ö²²²˜þøcfÍšÅ÷ß_ÖýöÆHHH`ñâÅÏœïܹ“Fitpþ²=k›O:ž}ûž+8·°°/¨B”’Íc¨d¤KfN.›‚îðåæKøŒj‰y%ƒR9žyeÊè”j[„¯—R›Ö’››Ë Aƒ¨Y³&ß|óÍ3ï¿qã:::|ùå—”+WSSS €¿¿Y÷Ù%::ú•äyÝ=k›cbbžûXŸ}ö3gÎ,ë& ñ¯f «ÍÐuPå*œ‰zPjÇñîX—‰Ý_üéâߣԂsmmm–.]ÊÂ… Ñ×׿ýÖÖÖìÚµ«À¾¸¸¸7bÄðêÕ«4nܘ[·n©·åääàââÂþýûQ…µk×Ò¥KìííéÓ§‡.²,___:wî\`ÛàÁƒùù矼é®®®:t777š7oÎØ±cIIIaÏž= >œäädlmmqttòžxL:GGGìíí7niii¼ûî»2kÖ,lmmY³f QQQX[[“ ®ÃâÅ‹0`€º;wfýúõ888о}{Ξ=˲eËpttÄÁÁ•+WjÛèÑ£ùòË/ µ74:((ìííéܹ3«V­"77·È~rvvf÷îÝê߃ƒƒ±¶¶VÿnccÃñãÇéß¿?Íš5ã½÷Þ#<<¼Ø6ìß¿Ÿ>}ú`kkK§N8xð 3gÎä—_~áÀØÚÚ2fÌÒÒÒøê«¯hÑ¢mÚ´aÆŒdffªë´iÓ7n\‰ê–ššÊرciÙ²%...Ì›7•JENNË—/çí·ßÆÎÎŽ÷Þ{ëׯ¿š \ˆ×„*WAQPOùþp~ü«àç¤ó÷Aœ»™¼OÞÊï§b˜ê{™žóO°óôÝBe>ÌTñáÊs¬;r€ÿºÎŒWÕûÝfãÔõdÆø\ ûÜãŒ]sè{êúl ºÃ ÅÁtŸ{œO|.p+1ý™Û•’žÃ~×ðøñ=çŸ`ÆŽ«<ÊRÿˆqk.Ðsþ †/?ÃÁмû¶ì8}—¡ËÎÐsþ >^užSדËú ñ¯Tª B---_hÿãî߿Ϻuëxï½÷^E¿”©FѬY3|}}ÕÛP©T¸ºº²aÃ~ûí7-ZDHHcÇŽeÔ¨Q\¼xñ¹ŽwóæMNŸ>ÍîÝ»ñ÷÷'22’ àîîÎìÙ³111!44T½&àÎ;X[[ãïïO`` ·oßfõêÕ@ÞôŽúõë3uêTBCC6lX‰êIvv6AAAŒ3†?þ˜›7oÈ’%K˜?¾:àÌçååÅ®]»HIIQo[¿~=ƒ âÊ•+x{{3räHBBBXºt)k×®eíÚµÏÕG*•Š+V°dÉNŸ>••Ÿ~ú)Š¢Ûæˆˆ¾þúk.^¼È¤I“˜8q">dÚ´i¼ÿþûtéÒ…ÐÐP–,YÀäÉ“‰ŠŠÂßßbccY´hÑ ÕmåÊ•$$$pôèQöîÝ‹££#:::>OOOôõõqttÄÜܜ˗/HïììLÍš5ùã?8wî·nÝ¢wïÞlÞ¼™öíÛÓµkWttthذ!ÞÞÞlذá¹ë÷á‡bjjŠŸ|ò W¯^åæÍ›Å¦ÿä“OhÑ¢ZZZtíÚ•´´4"##‹L›˜˜ÈßÿÍ”)S¨R¥ ÆÆÆ|úé§ìÚµë…êfddDbb"ׯ_ÇØØ˜·ß~€Š+2yòdjÕª…¶¶6nnnœ?þ…Ï¡¯»‹NÓ}îq<~<Éáˉôw®…žŽV‰ó»ÚV¥qÍ …¶+ŠÂ¼]èêh1±Gý'–áÙ¶&fôÑÓÑ¢c3"bó`cCFt¬KõÊhkAûFU‹I}j[ò"by ]û9Õ¤¼Fú:´²®Ì•ÿ/ÇP_‡èätR2©VÑÛZymù#ø.}-¨W­<ÚZàܰ N ª°çLlYŸ2!þu4~¥HVV#GŽTòéêj|•_ŠnݺñÝwßáïï­­-ÇgΜ9@ÞÈuÍš5 ¤¯U«W¯^}žCRµjU’’’ŠÝŸ™™ÉêÕ«ùûï¿‰ŠŠâÑ£Gê)//Cþâ`ƒÛŠšâ1hÐ ¶lÙÂ!Cؼy3}úôÁØØ˜;wîP§NB}ô²æÃW­Z€¤¤¤bŸ;wŽÕ«WsöìY’““Õ}W”¸¸8ÞÿýÛŸçz¼n#FŒ@KK‹Ï>ûŒôôtÆŒƒ§§'»víbûöí„……‘ššªþ"(Ä›,e®áwÓøjÛb’3Ô®V‰òê=æ5cÇUT¹ K>hŽŽvɃ}]m²rþ;ÏÿRûÎÅÿ´ŒT¹ÊSÛò¿²rrÙv2† «Iܹ—NzV®ú4_öjÈúÀÛŒ^}žê• ÕÅ »Ú‰½ŸYhQlõÊ܈ôêNŽo9ÏÊÊâ£>">>žµk×R¡B…/ô5¡§§G¿~ýðõõÅ××—N:annä™ÿ» °¨€@__ÿ™ƒ®[·naaaQìþiӦȌ38qâ£G~byùë²³³_z?õíÛ—›7o„ŸŸƒ zæ>zž~º};o¾hqýÅÀi×®;wî|ê“…ü/„††ªÎ;÷Ì}òxÝôõõ5jû÷ïgþüùüç?ÿ!**ŠíÛ·3oÞ>>/ýÜñ:ÓÖ cœê›pîÿ„êëh¡Rå>Wy_÷µ¡ÛšÌø=Œä‡Ïw/Üw>ž•þQ¼ïdÁºÑ­˜;°és•óã_× ‰¼Ïøwê±}¼#ƒÚÿ÷‹Gc=>ífͶñŽ874å»ÿŸ_½²q)Š Ø…/NcƒóüÀüîÝ»¬_¿žÊ•+—u•^¹rüøqvî܉———zû Aƒðññ!,, •J…¿¿?‡¢ÿþ…ÊhÔ¨ÑÑÑœ>}š¬¬,üüüŠœ›¾gÏrrrˆˆˆ`ÕªUêÑU###ÒÒÒˆ‰‰!""€k×®áàà€111(ËÈȈk×®‘””Dbb"æææ˜˜˜°k×.T*.\(ñt§©P¡½zõbÚ´i4mÚ”FàééÉÑ£GÙ¿?*•Šððp~ûí7Xd95bïÞ½dffï¿þZ(ÍþýûyôèÉÉÉÌ™3‡·Þz‹5jÙæ¨¨( pss£bÅŠlÙ²míÿ~ÜÊ—/ÏíÛ·III!**Š *лwo¾ýö[îÝ»@XX~~~%ê‡âê@pp0Š¢```€®®.*TàúõëXZZÒ®];T*UŰBˆ¼áwÓ8qVÖ•°ªVž3QHJËâa¦Š5Gnñ„ëB>tµ¤yJüÇ÷2™9ÏäßJ|D­*åheUU®B@hÂ3—‘_N³:iXظ™„DÞWïÛ}&–»÷3ÑÖ-*–ËyïÕª¿ŸŒázüCr8~÷èÑ¢ú«:%B¼1J58ÿõ×_±··gòäÉ$%%aooO×®]K´ßÇLJ#GŽ””DÇŽ±µµÅÖÖ–æÍ›«Gÿí,,,pqqÁÀÀgggõöÁƒ3lØ0ÆŒC‹-øé§ŸX²d Íš5+TF“&M?~øàÜÝÝ144|)çCˆ×YþïAW“ø²wCú´®A_G ¾ÙFÏù'Xsøß¼gƒEÉîQBˆ’ÓR¥D÷‡7n`eeUÖõ}mÅÅÇc^­ZYW£àà`úõëGxxø3Ÿÿy4hЀ5kÖЮ]»²®ÊkU7!ÊŠ¦Þs…o¦g‰£5vZ‹B!„o*}õnݺØ×û‰ÿÊŸo¯‰4¹nB!„x62r.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„î³$Ž‹‹/ëú¾Ö¤ÿ„âÕ‘{®âuôLÁ¹¹yµ²®ïk+..^úO!^¹ç !4É7JœV¦µ!„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„BhˆR ÎÏ;‡——-Z´ÀÉɉ¯¾úŠŒŒ õþE‹accƒ­­-¶¶¶ØÛÛsâÄ õþÓ§OÓ Aõ~;;;–-[VÖ}¦ñ¦M›Æ¸qã^JYŠ¢àããóLy‚ƒƒ9þ|©·3++‹Ž;²xñâR?–¦Ø´imÛ¶%99¹¬«"ĿʩëÉtšD÷¹ÇÕ?ã×]ä­”—¡ÛOÆ”uS„¯¹R Î:t(îîîóçŸræÌ~úé'ušëׯ3cÆ BCC åܹs899©÷_»vž={ª÷_¼x‘?þ¸¬ûì’ðÌÁïÎ;_Ip®­­¥¥%ÕªU+«îyåLMM±´´D__¿¬«"Ä¿Òæ±üõE[vNjCóº•øró%âd–(ï½´,6Þ.ë&!^s¥œòÉ'ôïßÌÍÍquu%<<\&""++«b˸víÚ÷‹ÒýJò<]]]|||èׯ߫î–2ÓµkW6oÞLùòå˺*Bü«èj3´CT¹ g¢”(OIƒx!„x’R Λ4i‡~@vv6.\`Ïž=¼ûî»äæærãÆ ,--‹-ãÚµkOÜÿouõêU7nÌ­[·ÔÛrrrpqqaÿþý(ŠÂÚµkéÒ¥ öööôéӇÇY–¯¯/;w.°mðàÁüüóÏ@ÞWWW:„››Í›7gìØ±¤¤¤°gφNrr2¶¶¶8::œœÌÔ©SqttÄÞÞžqãÆ‘––À»ï¾K`` ³fÍÂÖÖ–5kÖ…µµ5 ê:,^¼˜¨ëйsgÖ¯_ƒƒíÛ·çìÙ³,[¶ GGGX¹re‘íëܹ37nT—9uêT–/_NÛ¶mqttäÇDQöíÛ‡««+öööxzzrñâEÂÂÂ1bööö8::ªûæÙØØ°{÷n\]]iÞ¼97näСCêß'L˜@VVÖSû(;;›¯¿þGGG6lˆµµ5ÖÖÖŒ3F}>öîÝ«.÷ÓO?%==€Ý»w«Ïƒ¢t©r õòþT*ÀŽÓwºì =çŸàãUç9u=oŠÙ¡Ë‰|±é)é9tŸ{œ¾ Oÿˆqk.Ðsþ †/?ÃÁÐ2sré>÷8Á‘÷ÕÇZáÅËϪÏÌÉ¥çü\º“Êõø‡LÙr™žóOÐwá)Öùï߆õGo3Ï5Ö¹Eß…§ðøñTÑû'åBh¦W² ´wïÞôéÓ'''ÞyçîܹCFFØÛÛãîîÎÎ; ä»~ý:3gÎÄÞÞžN:ñÃ?¨ƒŸ³FѬY3|}}ÕÛP©T¸ºº²aÃ~ûí7-ZDHHcÇŽeÔ¨Qê`óYݼy“Ó§O³{÷nüýý‰ŒŒdÁ‚¸»»3{ölLLL åÔ©¼?6wîÜÁÚÚ¹}û6«W¯ò¦´Ô¯_Ÿ©S§ʰaÃJT‡ÈÈH²³³ b̘1|üñÇܼy“ÀÀ@–,YÂüùó òõõ%88˜påÊ>úè#œœœ˜3gQQQœ?ž={öpàÀΞ=˦M›^Å¥'„ø)é9,Ú‰U5#Ú72àÏà»l=ÍW}ñçd'»ÔfúÖ+„ßM£c3&»7 b9]þú¢-¿OÈûýÓ_×hR«"NvbîÀ¦T¯lˆ®6ŽõM8–äý—HLËäFÂ#B"ïSN_‡&µ*ÿˆ®Íª±sR~ÚŒm'b8ÿØ\øC—°01dÛxGf¼oÚ÷¿›7ð´¼BÍóJ‚ó¿þú ???Î;ÇŒ3¨Y³&~~~ìÛ·S§N1vìX¦NÊ‘#GÔùÖ­[Çž={8sæ ¿üò ~~~ÅŽjþÛxyy±}ûvrrrؼy3žžžèêê²~ýz† † :::¸ººÒ±cG¶lÙò\ÇÊÍÍeâĉ`ffÆÐ¡C9xð`±éíììðöö¦B… Ó¾}{.\¸ðÂm2dúúú¸¹¹§§'úúú8::bnnÎåË—ŸZFãÆéÕ«ZZZØÙÙÑ¥K8€®®.„††’ššŠuëÖ W¯^ôèÑ===,--±±±)vÎü{ï½G¥J•hÔ¨ææætèÐ*UªP­Z5œœœ }jéëë£( ™™™äää““ƒ±±1*TòáNž<™råÊQ­Z5Ú·o¯.WQº,:M÷¹Çñøñ$‡/'Òß¹z:Zü|—¾ŽÔ«Vm-pnX§UØs&¶Øò õuˆNN'!%“j °­•÷9«±Ç#îp%:•Іz¸6­Ê‘+y{ÐÕ$:ؘ¢¸ÚVåí&fèjkQ³Š!õªª>F}óò¸ÙUC[ lkW¤†‰!±¡y…šç•½J±qãÆŒ;–={ö ££CãÆ)_¾<úúútíÚ•Ž;ràÀukkkªV­Š¶¶6¶¶¶ :”¿ÿþ»¬ûì•èÖ­Š¢àïïOtt4ǧÿþ@Þ¨lÍš5 ¤¯U«wîÜy)Ç®Zµ*IIIÅîÏÌÌdÙ²e¼ûî»´hÑ‚•+W’™ùòæZêéé```P`Ûó#¿-úúúlذ«W¯âââÂèÑ£¹{÷.wïÞåÛo¿¥k×®ØÙÙqæÌ™+?à/ªŽOê£~ýúabb‚‡‡ÎÎÎDEEñõ×_?±?^fÿ !Š—¿ ôï©í˜;°)ËÜ`cPÞ½5ö~&æ• ¤¯^Ù€Ø'Ì5ÿ²WCÌ*0zõyƬ>ÏÅÛy£ÖmT!%=‡ˆØ‡¾œÈÛMÌx«‰GÃÉUàxD2›’Å¢ý‘ _~†îsséN*Y9¹ÅSO[[½ÿYó !Ê^©ç?þø#sçÎ-°-==ý‰o™HMMÅÐÐð‰ûË•+WÝôêéééѯ_?|}}ñõõ¥S§N˜››yxLLÁ×u°CÞ(mþè{Iݺu ‹b÷O›6ÀÀ@f̘Á‰'=zôËË?çÙÙÙ¯¼oß¾­n‹K—.%((ˆœœæÍ›‡¢(ôïß---V®\ÉÙ³gqppxáã>©BBBˆŠŠÂÏÏ~ûí·"Ï¢ìhk…1NõM8÷ÿ B«W6 .¥` ^TÀþ¸*Æz|ÚÍšmãqnhÊw;®yóØêUæXø=Ž„%ñv3šÕ©DòÃl^ŒGëÿGÁàÓuо÷lÂîÏœhV§b‰Úð"y…e§Ô‚ó:°víZ8€J¥",,ŒE‹1dÈ‚‚‚ضmééédee±eË.^¼È Aƒ€¼`sùòå$$$››Ë©S§X»v-£F*ë>{eÈñãÇÙ¹s'^^^êíƒ ÂÇLJ°°0T*þþþ:tH=²þ¸FÍéÓ§ÉÊÊÂÏϯȹé{öì!''‡ˆˆV­Z…§§'FFF¤¥¥CDD·P×ÁÁ;;;bbb ,P–‘‘×®]#))‰ÄÄDÌÍÍ111a×®]¨T*.\¸À®]»J¥ÏnܸÁ… ÔóÃýýýñôôäáǬ]»–ÔÔTôôôÐÑÑÁÄÄ„ôôtîܹC—.]¨S§§N"22ò…ëñ¤>²¶¶&++‹·Þz‹&Mš`ggÇ!Cˆ/•>B<;¿›Æ±ˆ{´²® @¯V5øýd ×ã’«À±ð{œˆ¸GÕ(§¯ÍÃLq2‰JÌ›;¾ûL,wïg¢­::ZT,§«>Æ[Íøë\,ËéR³Š!ÚZÐÁÆ”Uÿܤƒ)ÚZ™KìýLÚ52¥†‰!ço¦p+)½Dmx‘¼Bˆ²£ûâE­uëÖ,\¸Ÿ~ú‰I“&aff†§§§ú .øùù1sæLôôô°³³cÓ¦Mê·³hkksýúuzôèAff&–––|÷ÝwtëÖ­¬û앱°°ÀÅÅ…ÈÈHœÕÛLNNcÆŒ!!!ºuë²dÉš5kV¨Œ&Mš0~üxÆŒCnn.ýû÷ÇÝݽPºË—/3{öl²²²ðòòRŸ§6mÚкukÜÜÜhÓ¦ Ë—/gܸqL›65kÖàììÌ Aƒøý÷ßÕe1‚éÓ§sðàA¾ýö[ºté‚ ˜1cK–,ÁÅÅ…¡C‡âçç÷ÒûÌÔÔ”eË–qüøq*T¨ÀÂ… iÑ¢©©©„‡‡ãææFVVNNN|úé§1~üxF…¡¡!xxx¼p=žÔG_}õ£GføðáÜ»w>ø€Ý»wÓ¼yó—Þ'Bˆ’°è4ZZZT­¨Ï{mjâÙ6ïÉVŸÖ5P©¾Ùƽ´ljšòÍ{6ØXмn%ìjWdøò34¯[‰ýó0SÅç›BINËÆªš_ön¨>VÛU˜¿;‚÷Ûü÷É™«]5þ ‰UOi1ÔÓfØ[u˜¾õ zÚtmV®ÍJö;¼H^!DÙÑRòß3÷7nÜwŽ¿€¸¸xÌÍ5ï¦L¿~ýGW·Ô¾«½‹/æÈ‘#ê7¦hª^½zñÖ[oñÉ'Ÿ```À¥K—9r$¿üò -[¶,ëê ñ¯ ©÷\!Ä›éYâè×;â5´`ÁfÏž³³3¹¹¹XYY1cÆ Ì…B!#篊Œâ!Ä«#÷\!„&y–8ú•½JQ!„Bñdœ !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†Ð}–Äqqñe]ßךôŸB¼:rÏB¼Žž)877¯VÖõ}mÅÅÅKÿ !Ä+"÷\!„&¹qãF‰ÓÊ´!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B QªÁùîÝ»éÞ½;Íš5ÃÕÕ•7>Ó~ñ|¦M›Æ¸qã^JYŠ¢àããóLy‚ƒƒ9þü+ikJJ #GޤyóæüôÓO¯ä˜BˆŸSדé43ˆîs«Ư»È…[)¥r¼Ýgbyÿ§Ó¤¤ç0tÙv…Ä–u7!4€ni|æÌ¦L™ÂªU«pttäòåË 2„jժѥK—§îš!!!Å‹3|øðçÙ¹s'5¢yóæ¥^¿+V˜˜È±cÇ(_¾|Yv•â_`óX*é’™“˦ ;|¹ù>£Zb^É१²‘µª¢§£UÖMBh˜R9?þ<666´iÓ---š6mŠƒƒgÏž-Ñ~¡¢££_Ižç“““æBˆ—Ê@W›¡ê ÊU8õॗïbcÊÂ!v”Ó×)ë¦ !4L©çÎÎÎ\»v#GŽ••Źsçæí·ß.Ñþ7ÙÕ«Wiܸ1·nÝRoËÉÉÁÅÅ…ýû÷£( k×®¥K—.ØÛÛÓ§O>\dY¾¾¾tîܹÀ¶ÁƒóóÏ?ySP\]]9tènnn4oÞœ±cÇ’’’ž={>|8ÉÉÉØÚÚâèè@rr2S§NÅÑÑ{{{ÆGZZï¾û.Ìš5 [[[Ö¬YCTTÖÖÖ$$$¨ë°xñb  ®CçÎY¿~=´oßž³gϲlÙ2qpp`åÊ•…Ú6xð`<Èo¿ý†­­-sçÎ}bÝŽ=J÷îݱµµÅÍÍßÿ€´´4¾úê+Z´hA›6m˜1c™™™êº~ñÅÌž=›Ö­[«§ =)ÏãÂÂÂ1bööö8::ªû>¿ŒéÓ§Ó¢E 3f ñññDEE1lØ0lmmqqqaΜ9dee1a¦M›Và 4àĉìÛ·WWWìííñôôäâÅ‹O­ÇâÅ‹ùüóÏ™:u*vvvxxxÄ hÖ¬:u"00P]¯'ÏÔÔTÆŽKË–-qqqaÞ¼y¨TªWúâePå*( êåý©\ô6óvG°ü`}~8ÉŒW8–ĨUçé>÷8^KB8~È›*óø4™.³ŽÑs~Þç4àR"}ž*ë& !4P©ç5âóÏ?gøðá4nܬðž¶ÿMÖ¨Q#š5k†¯¯¯z[@@*• WWW6lØÀo¿ýÆ¢E‹ aìØ±Œ5J„=«›7orúôivïÞ¿¿?‘‘‘,X°wwwfÏž‰‰ ¡¡¡œ:•÷‡äÎ;X[[ãïïO`` ·oßfõêÕ@Þ”–úõë3uêTBCC6lX‰êIvv6AAAŒ3†?þ˜›7oÈ’%K˜?>áááò¬_¿žN:áååEhh(_|ñÅë–œŽ5гgϲzõj*W® ÀäÉ“‰ŠŠÂßßbccY´h‘úX»víÂÒÒ’Ó§OóË/¿”(O¾ððp<<<8}ú4[·neÕªUê¾üüóÏ gïÞ½;vŒž={¢££CFF^^^X[[sòäIþúë/lllÐÕ}òL´G1~üx¦Nʹsç˜6m+V|j=þþûoºwïÎÙ³g±´´ÄÃÃvíÚqîÜ9úöíËĉQå©çråÊ•$$$pôèQöîÝ‹££#::2:(^/)é9,Ú‰U5#Ú72Uo÷M VCvNjÃWˆJxĸnÖø}ÑïŽuùþpÒ³T8Ö3á¯/Úò×mù}‚#5*àݱnY7M¡áJ-8ÿ믿X°`7näêÕ«üùçŸøúú²nݺíÓyyy±}ûvrrò mÞ¼OOOtuuY¿~=Æ ÃÆÆ\]]騱#[¶ly®cåææ2qâD 033cèСýôSvíÚ¥.·^½z 0-­¼ù¡%É“¯W¯^ôèÑ===,--±±±áüùó$''³oß>¦L™BõêÕÑÓÓ£[·n˜ššòÏ?ÿššÊ—_~Iùòå©P¡}úôA[ûÉY]]]  %55;;;êÖ­ûÄzäkذ!íÛ·GWW—Î;“’’Bß¾}ÑÖÖ¦gÏž$&&ûôkFFF$&&rýúuŒåi˜x­ XtšîsãñãI_N¤¿s­óÂë˜áÞ²:ÏìR›&5+ EÞt•ô,·’Ò ”ûƒß5T7¦wëeÝD!„†+µ¡ëׯ§oß¾899yAÓ°aÃX³f C† yêþ7]·nÝøî»ïð÷÷ÇÖÖ–ãÇ3gÎ oäºfÍšÒתU‹«W¯¾”cW­Z•¤¤¤b÷gff²zõjþþûo¢¢¢xôèÑK}â¡§§€AmEMy–ºEGGS§NByâââxÿý÷ l|”ÚÈÈè™óä»{÷.+W®äرcÄÄÄžžN‡Ôsó‹ªStt45jÔ@__ÿ™úN__Ÿ 6°lÙ2\\\pvvfúôéÔ¨Q£ØzEWW}}}õ—‘üsR’s0bÄ´´´øì³ÏHOOg̘1xzz>S;„(+ù Bs¿›ÆWÛ®“œÁ vµ(§Wø ò•èT¶ŒáJt*e•“«Þ¿+$–ˆØ‡,Qú‹ä…¯¿R9×ÖÖ.¨èêêªo?mÿ›NOO~ýúáë닯¯/:uÂÜÜÈ Äcbb ¤/*`‡¼`-ô½¤nݺ………E±û§M›F`` 3fÌàĉŒ=ú‰åå˜ÙÙÙ¥ÞoOª[Íš5¹sçN¡<ùÁq@@¡¡¡êŸsçÎ{œ’æQ…þýû£¥¥ÅÊ•+9{ö,êúEÖ©fÍšÄÆÆÙgúúúOìK;;;–.]JPP999Ì›7ï‰õxVO;ŸúúúŒ5Šýû÷3þ|þóŸÿõBçUˆWM[ l,Œqªo¹',½s/ƒ ëCieU™¥4gÏçm ìˆ}ȪC7ùæ=Yü)„(‘R ÎÝÝÝñõõ%$$EQ¸rå >>>ôêÕ«Dû 8ãdzsçN¼¼¼ÔÛ „aaa¨T*üýý9tèýû÷/TF£FˆŽŽæôéÓdeeáççWäÜô={ö““CDD«V­Rt‘––FLL \»v ìì숉‰Q/Ìgddĵk×HJJ"11sssLLLصk*•Š .9ýãexRÝúöíK`` ~~~¨T*n޼Ɋ+¨P¡½{÷æÛo¿åÞ½¼…\aaaøùù{œ’æIOOçÎ;téÒ…:uêpêÔ)"##011¡K—.Ì™3‡ÄÄD²³³Ùºu+—.]ÂÅÅ###æÍ›GFF))),[¶ŒÔÔTlll8vìñññ¤¦¦òóÏ?“››7J÷ðáCÖ®]Kjj*zzzèèè`bbòÄz<«§Ï€€‚ƒƒQtuu©P¡B©œo!J‹BÞÈù±ˆ{´²®\lºè{éèëjábcб¡.~gcÕS^fªøö÷0ƸYaUÕ¨$‡BˆÒ›Ö2`ÀT*_~ù%±±±T­Z•òÑG•h¿ \\\ˆŒŒÄÙÙY½}ðàÁäää0f̨[·.K–,¡Y³f…ÊhÒ¤ ãÇg̘1äææÒ¿ÜÝÝ ¥»|ù2³gÏ&++ ///>üðCÚ´iCëÖ­qss£M›6,_¾œqãÆ1mÚ4Ö¬Yƒ³³3ƒ R¿õò¦5LŸ>ƒòí·ßÒ¥K,XÀŒ3X²d ... :ô‰ÁïózRÝìììX´h .ä?ÿùµkׯÛÛ€™3g²`ÁzöìIJJ 4àÓO?}â±J’ÇÈȈñãÇ3jÔ( ñððÀÃÃC½îܹ̜9“®]»R®\9Z·n««+åË—ÇÇLJ3fàää„©©)ݺuÃÈȈþýûsöìY:wîL•*U˜4i 4òÖ„‡‡ãææFVVNNN|úé§O­Ç³ÐÑÑyâùÔÖÖfæÌ™Ü¸q333,X€©©ésKˆWmÀ¢ÓhiiQµ¢>ﵩ‰gۚŦoiU™æu+Ñÿ—ÓT1ÖgH‡Ú4­•÷eôà»Ä$gðÓÞëü´÷:uÍŒX&Ó[„O ¥”äõ À7°²²*ëú¾¶âââ17¯VÖÕ($88˜~ýúþÔ7!ÄëBSï¹Bˆ7Ó³ÄÑ¥6­E!„Bñld¨ô ׺uëçž{,„B!^.9B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B÷YÇÅÅ—u}_kÒBñêÈ=Wñ:z¦àÜܼZY×÷µ/ý'„¯ˆÜs…šäÆ%N+ÓZ„B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4D©çÁÁÁxzzbooÏ[o½ÅêÕ« 슊bäÈ‘´nÝæÎKff&>>>ØÚÚú±¶¶fß¾}eÝomÚ´iŒ7¥( >>>Ï”'88˜óçÏ«ÏÊÊ¢cÇŽ,^¼È;·=zô(ënÒþù'III%JŒµµu‰Ëîܹ37n,ë& ñÚ8~IBé9ÿÝçgè²3l ºCfNnYWMñ†(µà<::oooHHH+V¬`éÒ¥ìÚµ €ÔÔTLóæÍ bëÖ­„„„°`Á†NhhhŸ%K–`llŒ““SY÷Û#!!AT—ÔÎ; çÚÚÚXZZR­Zµ²nŽFúå—_HLL,ëjñÆ[éŬ?Âqµ­Êö Žø}Ñ–)½rñV 7â•uõ„oˆR ÎOžœäädlmmqttòÎÑÔ©SqttÄÞÞžqãÆ‘––À»ï¾K`` ³fÍÂÖÖ–5kÖEO¯X½z5íÚµ£U«V|÷ÝwêóîììÌîÝ»Õéþw*ÇÂ… qrrÂÑÑ‘±cÇ9%äÚµk¼÷Þ{ØÙÙÑ·o_.\ÈÀKÔ'Oj_þ4ŸöíÛcoo··7wïÞU—cccÃßÿM=hÑ¢—.]",,Œ#F`oo£££ú8·nÝÂÙÙ™ÌÌLÞ}÷]lmm¹téRz¥¦¦2aš6mŠ««+{öì)°?--o¾ù†öíÛÓºukFŒÁ7мžvÝ <¼½½±··gݺu@Þô³áÇckkKÇŽÙ´i“:Ͼ}ûpuuÅÞÞOOO.^¼øR? B¼*~gci^·-­*=1ÝѰ$F­:O÷¹ÇñZ±ð{ê}“6„²õD4Ö]¤ûÜãÌÝAô½ >ñ¹@÷¹Ç·æ±÷3Õéƒ#ïã½ò,ÝçgÔoç¸x;E½ÏmÖ1¯ÞãÕçèµà±¹qŸ1«ÏÓsþ †.;ö1ä*yy`Çé» ]v†žóOðñªóœºž\ ~ßå»WqŸw¯%!œ¼–ŒB³¼²¡gÏž%((ˆ¾}ûжm[âããñññ!33“7n0wî\7nŒ®®n¡ü‹-bðàÁT©R¥¬û¬Ô5jÔˆfÍšáëë«Þ€J¥ÂÕÕ• 6ðÛo¿±hÑ"BBB;v,£FzîÀèæÍ›œ>}šÝ»wãïïOdd$ ,ÀÝÝÙ³gcbbBhh(§NàÎ;X[[ãïïO`` ·oßV¯'عs'õë×gêÔ©„††2lذ"yýúuŒ9zô(7ndÇŽìØ±ã©u býúõüþûï;vŒ÷ߟråÊHóèÑ#¼¼¼ppp 88˜E‹qâÄ E)Q<©}kÖ¬aÕªU¬ZµŠàà`ùôÓOÕyU*+V¬ÀÇLJ³gÏÒ´iSÂÃÃñððàôéÓlݺ•U«VqêÔ)êÔ©£¶wîÜIhh(M›6-P—‰'’˜˜È‘#Gصk:::öñÅDFFò矄­­-ƒ âáÇ…ÚU’ëfÙ²e|òÉ'œ;wŽ!C†¨û²qãÆœ9s†uëÖ±jÕ*yôèãÇgêÔ©œ;wŽiÓ¦Q±bÅ纅(kQ hX½|‰ÒëfßmñîX—ïÿ'=K¥Þ¿ÿ|<“ݰõSG®Ä¤2eË%&t¯Ç“ÚPÞP—þy_ž#b2uËe†v¨ÃžÏÛ2ª‹_o ãþ£lr…ÍÇî0g`vMv¢Aõò\ËËÓß¹NvâÛ÷lØq:†§òž¾ý|—­Ç£ùªo#þœìÄ`—ÚLßz…ð»iêúm;Í{m,Øý¹î-ª3ëprrKvoB¼¯$8¿}û6£GfäÈ‘´jÕ KKK–-[ÆŽ;pvvæË/¿$,,ŒÑ£GÊïïïODDÞÞÞeÝ_¯Œ——Û·oW&oÞ¼OOOtuuY¿~=Æ ÃÆÆ\]]騱#[¶ly®cåææ2qâD 033cèСµk×fÈ!ìÚµ ]]]  %55;;;êÖ­ûLך¢¤áé`—Ú4©Y-ÀÅÆ”ô,·’ÒÕûU¡fCŒ uhQ·Õ+Rϼ<úºÚt°1U€ï9K›&¸Ø˜¢­öu+aW§b‘ìε05ÖWÿ¾ûL,­­ÿ›Ç²ªýœj²+$ïéÝÁwéëhA½jåÑÖç†UpjP…=gbÕet²­Jãÿ¯g»ª¤fä÷Øh¾¢ìé¾xOÍÀ騱#“'O.°ïí·ßæí·ß`üøñ˜˜˜Ð½{÷Be,Z´///LMM˺¿^™nݺñÝwßáïï­­-ÇgΜ9@ÞÈnÍš5 ¤¯U«W¯^})Ç®Zµêß’™™ÉêÕ«ùûï¿‰ŠŠâÑ£Gê)/ÏËÌ̬D#ÿ-[¶dþüùøøø0uêTÞ}÷]¾øâ ôôôÔi¢££±°°(°íY<©}ñññÌ›7~øA^¥R‘””¤>'ÿûeáîÝ»¬\¹’cÇŽCzz::txj=òþ:uê¹ÿÎ;®jÔ¨QäZ’\7ÿû">>žЬY3õ6EQh×®úúúlذeË–áâ₳³3Ó§O§FÏÕïB”¥ºfF\‹{øÔtW¢SÙv2†+Ñ©<øÿQî¬bÞ䢣£¾îÇÀtu´Õi“Ò²8y-™îs«÷ç*дVõï†úÇÏbïgbabX`[õÊê©2±÷31¯dPhq‹YõtòÊÏRÉ›h„Ð$¥œGGGÓ¿Ú´iÃ÷ß_lºµk×rüøqöîÝ[hŸ¿¿?áááüöÛoeÝW¯”žžýúõÃ××—K—.Ñ©S'ÌÍͼ€êxèëë9‡ÿInݺ………E±û§M›FLL 3fÌ aÆ,_¾œàààjïíÛ·©]»v‰êÜ£GzôèALL   Aƒxzzª÷›šš>ñËÅÓÊRûêԩÇ~H¯^½JÔ.EQèß¿?®®®¬\¹’š5k2xðàåÍŸÂuïÞ½"AתU È[Pjbbä}Q¸{÷n‘׳\7ùêÔ©Cݺu‹‰‡¼§ K—.åáÇL˜0yóæ±páµOMÒµY5¦m½ÂÅÛ)ØÕ.zzÖ{LXÊØ®ÖŒíjM%#=:ô\dz01äæÕ˜Ø£~‰óT¯l@|JÁQîØû™˜W6Pï+jÿÿìBÍVª¯Rìß¿?-[¶dîܹE.þ„¼¹è³gÏfîܹEÎ'_´hÄÌ̬¬ûê•8p ÇgçÎxyy©·4ÂÂÂP©TøûûsèÐ!ú÷ï_¨ŒFÍéÓ§ÉÊÊÂÏϯÈê={ö““CDD«V­R»FFF¤¥¥CDD·ØÒÁÁ;;;bbb ,P–‘‘×®]#))©ØWFGGLnn.øùù1`Àu÷îÝKff&111üúë¯ê|áááìÞ½›ììl P¥PàÚ¾}{üðCÚ´iCëÖ­qss£M›6,_¾œqãÆ1mÚ4Ö¬Yƒ³³3ƒ *üŽ1‚éÓ§sðàA¾ýö[ºtéRèx-Z´`ëÖ­|ôÑG”+WŽï¿ÿ^ÝÆ)S¦0yòdZµjEýúõ™0a‚úœºººìܹ“¯¿þ}}}úöíK×®] ”]£F ~üñGæÍ›Ç¬Y³èÔ©nnnêyãOë“'µ¯_¿~dff2jÔ(bcc177gРAE.b†¼à{üøñŒ5 CCC<<<ðððPï744äƒ>`Ê”)Ô©S‡¥K—bii €––?ÿü3S¦L¡uëÖØØØ0iÒ¤oX™;w.sçÎÅÝÝÌÌLìììX¿~=ÆÆ…_ûö,×M>Ö¬YÃÌ™3ùõ×_ÉÍÍÅÁÁ)S¦››Kxx8nnndeeáääT`q¬¯›É=Ðøl›ÝaÞît´µ¨ibÈ;öæØÕ®ˆ*W¡yÝJôÿå4UŒõÒ¡vi(Ï¢®Y9f¼ß˜_¢˜ñûU õµqidJ»FÅ¿ô AõòÌìׄU‡¢˜»+‚ÊåõèÕª:}Ûä=éìÓº*•Â7Ûø—–MMC¾yÏF^)ÄkFK)á+,nܸ••UY×÷µ¹¹æý'<ÁÁÁôë×ðððbÌƒÅ‹ÄæÍ›Ëº*BˆW@Sï¹Bˆ7Ó³ÄѯìUŠB!„Bˆ'“‘óWDFq„âÕ‘{®B“ÈȹB!„¯! Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCè>K⸸ø²®ïkMúO!^¹ç !^GÏœ››W+ëú¾¶âââ¥ÿ„â‘{®B“ܸq£ÄieZ‹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†(ÕàüܹsxyyÑ¢E œœœøê«¯ÈÈÈPï_´h666ØÚÚbkk‹½½='NœPïg„ ´iÓ†–-[âííÍ­[·ÊºÏ4Þ´iÓ7nÜK)KQ|||ž)Opp0çÏŸWÿž••EÇŽY¼x1>>>ôèÑ£ÔÚÿ¿ÇÛ½{7ŽŽŽêý;wfãÆ¥v|MË_ýUÖÕB㜺žL§™AtŸ{\ý3~ÝE.ÜJ)•ãí>Ëû?&%=€¡Ëΰ+$¶¬»A¡J-8OHH`èС¸»»ÌŸþÉ™3gøé§ŸÔi®_¿ÎŒ3 %44”sçÎáää¤ÞÿÑGahhH@@AAAT©R奢dÔAnIíܹ³@p®­­¥¥%ÕªU{%u~ÕÇ{œ:uŠ={ö”u5„ÐX›Ç:ð×mÙ9© ÍëVâËÍ—ˆ{ùÒSÙHZU ÑÓÑ*ë& !4L©çŸ|ò ýû÷GGGsss\]] W§‰ˆˆÀÊʪÈüiii8::òÕW_Q¾|yÊ•+GŸ>} ä¥/::ú…óèêêâããC¿~ý^I_õñ^'111e]!^ ºÚ íPU®Â™¨/½|S±£œ¾NY7U¡aJ-8oÒ¤ ~ø!ÙÙÙ\¸p={öðî»ï››Ë7°´´,2¿±±1S§N¥\¹räæærûömV¯^Í{ï½WÖ}Vê®^½JãÆ LáÉÉÉÁÅÅ…ýû÷£( k×®¥K—.ØÛÛÓ§O>\dY¾¾¾tîܹÀ¶ÁƒóóÏ?ySP\]]9tènnn4oÞœ±cÇ’’’ž={>|8ÉÉÉØÚÚª§†$''3uêT±··gܸq¤¥¥ðî»ïȬY³°µµeÍš5@ÑSIV¯^M»víhÕªß}÷99ywÙ½{·:]pp0ÖÖÖêß.\ˆ““ŽŽŽŒ;–¤¤¤Bí~–©+AAAxxx`ooOçÎYµj¹¹¹…Òíß¿Ÿ>}ú`kkK§N8xð zß¾}ûpuuÅÞÞOOO.^¼¨îëÍ›7óÙgŸaooO§NØ·oŸ:_TTÇÇÖÖ–Ž;²iÓ¦çÉÇÇoooìííY·n666>|˜¾}ûbggÇûï¿OTT”ú:Y¾|9o¿ý6vvv¼÷Þ{\¿~€™3gòË/¿pàÀlmm3f QQQX[[“ >îâÅ‹0`€úwþþûozôèA‹-¸téÒK¸Ê…Ð|ª\EC½¼?•ëÞfÞî–Œ¢Ï'™±ã*GÃ’µê<ÝçÇkIÇÂïySeŸ&ÓeÖ1zÎÏ›ºp)‘¾ O•u…è•,íÝ»7}úôÁÉɉwÞy€;wî‘‘¡ŠÜÝÝÙ¹sg‘ùgΜÉ[o½Err2Ÿ~úiY÷Y©kԨ͚5Ã××W½- •J…««+6là·ß~cÑ¢E„„„0vìXF¥ŸÕÍ›79}ú4»wïÆßߟÈÈH,X€»»;³gÏÆÄÄ„ÐÐPNÊûCrçά­­ñ÷÷'00PýÅ ò¦´Ô¯_Ÿ©S§ʰaÊ<æõë×166æèÑ£lܸ‘;v°cÇŽ§Ö5((ˆõë×óûï¿sìØ1Þÿ}Ê•+÷Ü}}åʼ½½9r$!!!,]º”µkײvíÚBi#""øú믹xñ"“&Mbâĉ<|øG1~üx¦Nʹsç˜6m+VTç[±bC‡åÌ™3|ôÑGL˜0»wïòèÑ#¼¼¼hܸ1gΜaݺu¬ZµŠÀÀ@uÞeË–ñÉ'ŸpîÜ9† €J¥bõêÕ,]º”ªT©Â÷ßÀ£GHKKcÆ œ?ž¦M›òÕW_ykÞÿ}ºtéBhh(K–,)Q©T*V¬XgÏž¥iÓ¦ÏÝßB¼.RÒsX´?«jF´odªÞîš@­*†ìœÔ†¯<•ðˆqݬñû¢-Þëòýá¤g©p¬gÂ__´å¯/ÚòûGjT6À»cݲnšBý’àü¯¿þÂÏÏsçÎ1cÆ jÖ¬‰ŸŸûöíãÔ©SŒ;–©S§räÈ‘Bù¿úê+Nœ8A­Zµµk×fÈ!ìÚµK·W¯^ØÛÛªËG}„¹¹9úúúôèÑC=š]±bE&OžL­ZµÐÖÖÆÍÍ­Àüÿç5jÔ(™Ã/Þ¦ûÜãxüx’×éï\«À¼ð:fF¸·¬Îã3ŻԦIÍ h‘7]%=KÅ­¤ôåþàwÕéݺFY7Q¡á^Ù«7nÌØ±cÕ‹Ñttthܸ1åË—G__Ÿ®]»Ò±cG8PdþjÕªñÍ7ßƵk×ʺßJ]·nÝP¢££9~ü8ýû÷òF®kÖ¬Y }­Zµ¸sçÎK9vÕªU‹œ*’/33“eË–ñî»ïÒ¢E V®\Ifæ‹-˜233#>>þ©éZ¶lÉüùóñññÁÉɉ™3g’ýÜÇ-®/‹škîÜ9Ƈ‹‹‹:`ÎÌÌD__Ÿ 6põêU\\\=z4wïÞ}b[“’’ˆçÁƒ4kÖLýÆ¢yóæqÿþ}uÚ’<ÐÓÓ+Ðÿ»víbÈ!8::âííýÂçx¡/@B¼Nò„þ=µs6eÙl úï½µœ^á?›W¢S™±ã*«§­dåüwjÜ®X"b2Ù½~Y7Oñ(µàüÇdîܹ¶¥§§£¯¯_lžÔÔT 8tèžžž…òO,ãßBOO~ýúáë닯¯/:uÂÜÜÈ ÿwa_QAf~_å¾—Ô­[·°°°(vÿ´iÓ dÆŒœ8q‚Ñ£G¿p{oß¾MíÚµKTç=z°eËvïÞÍJ4¦8%í˨¨(H»víØ¹sg¡'vvv,]º”   rrr˜7o^‘ÇS©TÄÄÄ`aaA:u¨[·®úmE¡¡¡\ºt‰•+W>w{¶oßμyó1bþþþO} fþgéE¾àño¤­6Æ8Õ7á܄޹—Á„õ¡´²ªÌÒš³çó¶öGÄ>dÕ¡›|óž,þB”H©ç:t`íÚµ8p•JEXX‹-RÏ› bÛ¶m¤§§“••Å–-[¸xñ"ƒ òFH£¢¢øñÇÉÌÌ$11‘¯¾úŠöíÛû†—›rüøqvî܉———zû Aƒðññ!,, •J…¿¿?‡R¬?®Q£FDGGsúôi²²²ðóó+rnúž={ÈÉÉ!""‚U«V©¿‘––FLL \»v ìì숉‰)0G:?ϵk×HJJ"11±È¶EGGLnn.øùù©!6jÔˆ½{÷’™™ILL ¿þú«:_xx8»wï&;;E¡råÊÏÝÇžžž=z”ýû÷£R©ç·ß~càÀÒEEEa``€››+VdË–-hkç}|>|ÈÚµkIMMEOOLLLÔy?N\\YYYüòË/(ŠBçÎqqqAQ–/_Nff&999=z”çnÏõë×±´´¤]»v¨Tª kÊ—/ÏíÛ·III!** sssLLLصk*•Š .˜V#Ä›JÂï¦q,â­¬+›.ú^:úºZ¸Ø˜bl¨‹ßÙXõ”—‡™*¾ý=Œ1nVXU•§OBˆ’)µà¼uëÖ,\¸Ÿ~ú‰-Z0zôh<==5jøùùáä䄳³3û÷ïgÓ¦Mê··TªT‰µk×röìYÚ¶m‹»»;æææüòË/eÝg¯Œ……...àìì¬Þ>xð`† Ƙ1chÑ¢?ýôK–,¡Y³f…ÊhÒ¤ ãÇg̘18;;såÊÜÝÝ ¥»|ù2íÚµ£_¿~ôîÝ[ý¦6mÚкukÜÜܘ3gÙÙÙŒ7ŽmÛ¶aooÏüùóÕ_¨ò1‚={öгgOΞ=[dÛZ´hÁÖ­[iݺ5_|ñßÿ½ºS¦LáÞ½{´jÕŠÑ£G3pà@ttòFœtuuÙ¹s'mÚ´¡GôìÙ“®]»>w7mÚ”+V°téRZ¶lÉÈ‘#8p`¡…¬ÎÎδiÓºuëF¹råhÙ²%7g?<<777Ú¶m‹¶¶v…ËUªTaäÈ‘´lْdz~ýzŒ100`Íš5œ9sgggX¿~=UªTyîö 0€û÷ïcooÏ|€»»»úi€‡‡>¤C‡ìر,X€¯¯/ööö,_¾œ¡C‡¢¥%ï^o¦ü9çîóN0ëÏpÞkS϶5‹MßÒª2ÍëV¢ÿ/§¾ü zÚ4­U€?‚ÁO{¯«ßØòñª_"„øwÓRE)IÂ7n¼1#Ö¥!..ssÍ[PL¿~ýGWW·¬«ó¯3xð`ìíí™4iRYWEˆ7ЦÞs…o¦g‰£_Ù‚P!„B!Ä“ÉPé®uëÖDFF–u5þµÖ¯__ÖUB!ÄkDFÎ…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†Ð}–Äqqñe]ßךôŸB¼:rÏB¼Žž)877¯VÖõ}mÅÅÅKÿ !Ä+"÷\!„&¹qãF‰ÓÊ´!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B !Á¹B!„B‚s!„B!4„çB!„Bh Î…B!„М !„B¡!$8B!„BCHp.„B!„†à\!„B QêÁyVVãÆÃÑѱоàà`<==±··ç­·ÞbõêÕê}>>>ØÚÚú±¶¶fß¾}eÝomÚ´iŒ7¥( >>>Ï”'88˜óçÏ«ÏÊÊ¢cÇŽ,^¼È;·=zôx¥}²xñb ÀåË—iÙ²%ÇŽ{æ¶!þN]O¦ÓÌ ºÏ=®þ¿î"n¥”ú±¿ßÎä¡eÚþÎßqîæƒBÛǬ>ÏÏû" mWå*tšÄ‹ eZï—I΃PÊÁyrr2^^^=z´Ð¾èèh¼½½8p !!!¬X±‚¥K—²k×.†NhhhŸ%K–`llŒ““SY÷Û#!!AT—ÔÎ; ´ÚÚÚXZZR­Zµ²nFFFXZZR±bÅgn‹âßmóXþú¢-;'µ¡yÝJ|¹ùq2Kõ˜æ• °0)WÖMãÉyš¢Ô‚óÜÜ\ DÍš5ùæ›o í?yò$]»v¥wïÞèèè`ccƒ««+Å–¹páBLåʕ˺ßÞÑÑÑ/œGWWúõëWÖÍÀÒÒ’;v`kk[*íB¼þ tµÚ¡ª\…3Q^¼À'ðîX—‰Ýë•u“ßxr„¦(µà\[[›¥K—²páBôõõ í÷ðð`îܹ¶ÅÅÅaddTdyþþþ\»v#F”q—•¾«W¯Ò¸qcnݺ¥Þ–““ƒ‹‹ û÷ïGQÖ®]K—.]°··§OŸ>>|¸È²|}}éܹsmƒæçŸò¦m¸ººrèÐ!ÜÜÜhÞ¼9cÇŽ%%%…={ö0|øp’““±µµUOMJNNfêÔ©8::booϸqãHKKàÝwß%00Y³fakkËš5kèܹ37n,PÕ«WÓ®];ZµjÅwß}GNNÎÎÎìÞ½[.88kkkõï .ÄÉÉ GGGÆŽKRRR¡v_»v÷Þ{[[[<<<¸råŠz_BBÖÖÖ\¿~€´´4¦OŸN‹-ppp`̘1ÄÇÇÛ–ýû÷Ó§OlmméÔ©,pìììlœœœøã? lŸ0aß~û-G¥{÷îØÚÚÒ»wo‚ƒƒ œ¼½½±··gݺuááá½½=;wfÕªUäææ–Á*Ä›A•« (`¨—÷§Rvœ¾ËÐegè9ÿ¯:Ï©ëÉêôG®$1di=çŸ`ÜÚ‹\½›w_\ô6ó÷\cÍ‘[ô]x O±!ð¶:ß]gÆŽ«êcn ºÃ ÅÁtŸ{œO|.p+1½PÝRÒsøÁï?ž¢çüÌØq•GY*.ÞNaÈÒ_IbðÒÜçà»WÉÈλ_ä*°îèmúüp’÷~:ÅÊ€¨î+Ø~2Ï_NÓsþ ¦ú^&!%K½ßmÖ1¯ÞãÕçèµà±ùtíÅÓˆ:Í bûÉ¢ïeðÅæKtŸ{¯%!ì>«.ëüÍF­:¯Î“ÿ•ðè‰çèëíaÌßs­@½ýÎÆ1bÅÙBçái}XÒó$Äó(Õi-–––%N{öìY‚‚‚èÛ·o‘û-ZÄàÁƒ©R¥Ê+í ²Ð¨Q#š5k†¯¯¯z[@@*• WWW6lØÀo¿ýÆ¢E‹ aìØ±Œ5Š‹/>×ñnÞ¼ÉéÓ§Ù½{7þþþDFF²`ÁÜÝÝ™={6&&&„††rêÔ)îܹƒµµ5þþþrûömõz;wR¿~}¦NJhh(Æ +ò˜×¯_ÇØØ˜£G²qãFvìØÁŽ;žZ×   Ö¯_Ïï¿ÿαcÇxÿý÷)W®àcÈGáåå…ƒƒ!!!,[¶ŒÄÄÄbËüüóÏ gïÞ½;vŒž={¢££Sl["""øú믹xñ"“&Mbâĉ<|øP]žžžžžžlذA½-))‰½{÷2pà@.]ºÄ‡~ȧŸ~Êùóç™2e £G.ð%cÙ²e|òÉ'œ;wŽ!C†påʼ½½9r$!!!,]º”µkײvíÚÒ¸…x㥤ç°h$VÕŒhßÈ€?ƒï²õx4_õmÄŸ“ìR›é[¯~7Œì\fî¼Ê¨ÎVìþ̉Oܬ¨`¨«.ïÐ¥,L Ù6Þ‘ïÛ°æð-Âÿ?x\zV.éY*~ð²cÏçmiPØŸö^/”.ö~µM˱~L+|?u ö~ÛOĨ÷ß¹—AXL*¿~Ø‚µ£[r9:•Ý!yîï'cØs&–Û²eœõÍÉU^¬¿vœŠaÛÉhæ hÊ“ÚЬN%¾Û¦ÞŸ«(l>v‡9›°k² ª—çç¡vüõE[þú¢-£»XaabÈ;öæddç2qÃEê™—çÏÉNÌd˶ÑGÞGfì£wëüõE[6}ÒšŠåt™Ù¯1–UžxŽú´®hi*u½þ¾K¯Ö5ŠlÓ“ú°¤çIˆç¡ok¹}û6£GfäÈ‘´jÕªÐ~"""ðöö.몾2^^^lß¾]=š¼yóf<==ÑÕÕeýúõ 6 tttpuu¥cÇŽlٲ幎•››Ëĉ100ÀÌÌŒ¡C‡ ~œÞÞÞT¨PcccÚ·oÏ… žé˜õêÕ£_¿~hkkcccƒ»»û™¯\¹rdddŠ¢(tèСÐÓ222Ôm277ÇÅÅ¥Èò’““Ù·oS¦L¡zõêèééÑ­[7LMM‹­Ã'Ÿ|B‹-ÐÒÒ¢k×®¤¥¥YpÁÔ€¸páW¯æÂlÛ¶–-[Ò A¶lÙÂÛo¿M×®]ÑÑÑÁÉɉ֭[óÏ?ÿ¨ó÷êÕ {{{õï›7o¦}ûöê< 6ÄÛÛ»À!Ä‹°è4ÝçÇãÇ“¾œHçZèéü_{wVU¹¿üͬˆêæÒY’IDAT$â<à†8`(•¢X©9Š˜šš–i“ÕÉŸCÇ!5ÍÌ):9¦'5+KÑÒBO9+)Nà€ $*âÀ ÃÞû÷Çý*˜¸·v¿®Ë«XëYk}Ö³îýìg-l€Ü ׳¥;u*—ÂÖüë»Òªž+û/`gkƒ£½-±ÒHË4P¿Š îåK˜÷[×­AÞ•±µ¯je¨R¾±Òòߥ„ƒÚÖà±rNØÚ@OWŽ'ÞÈ×®~‚[U¥”“ÎŽv4«]ŽcÿÓnpÛ”p°¥‚‹#Ík—#æBèñßó°·µ¡]ãŠØÚܾO"ö_È3ÂýÜ´t™¾+O›µû.ܪ*µ*9cgkCÈU9—œ‘g¾~¨¿\ò“~*)mŽãƒ^ (ådÇÎØ+dfܶ&v6T)çD·îDF_°³µ!Ç`Ä`4‘™cÄh2Q©ŒÓ]£¦5ËâVÖ‰Ÿ_àX SnÒÁ»ÒmÏýv}XØÇIä^Øÿõ]ü5 „††Ò¶m[FU`›9sævÇÀô¨yæ™g˜8q"‘‘‘xyy±sçN¦N äŽ\W­Z5O{sü«*UªTàT‘[233Y´h›6m"..ŽôôôïÆS+V,ÔÈ¿¯¯/Ó§OgñâÅŒ=šîÝ»óÞ{ïáàà`n“€»»{že·skNyõêÕ ]ëÁƒY´h %%ÅÜ'öØcȪU«;v,_ýµùù””į¿þšgλÑhÄ×××üóÿ~Ÿ¯F͉¹ÏVŒhAYg{Œ&ˆù#•q«‘˜r“¾­=¸p5·²NyÚ?VΉ3Óq°³af?/þ½=žÞ³÷â[«¯Õ¦RÇã`kKVNÁÓÒ"\⧃9}1Ô›9 ÖÎÊ1²zw"ÛO$%ƒŒ,#Mªßþ"w{;[²2r{.\½IÕ?}p¸›Î¾ñú3µó,3Mtøðÿîx•|#‹EƱpËÙ}úP¯^=BBBÌë+T¨pÇv«Ïâãã 5e*..ŽÐÐPÆϸqãpuu¥nݺ¶íÛ·/#FŒ €´´4:tèä~èÕ«×mŸó)Êc."­ 4pw¡UÝòŒ»FßÖ˾3WÙrô2ó^züžÎ·°“Ƚ°Ø´–[ÁÜ××—iÓ¦acSðwjsæÌ!44”Š+Z¬“,%44”;w²fÍÂÂÂÌËûöíËâÅ‹9~ü8ƒÈÈH¶lÙBïÞ½óíÃÓÓ“„„öîÝKVVëׯ/p„:""‚œœbccY¸p¡9ì:;;“ššJbb"±±±@îÅ–-Z´ÀÛÛ›ÄÄD¶mÛ–g_ÎÎΜ䞞žüøãdff’˜˜È_|aÞ.&&†uëÖ‘““&“)ßÝ{Ú´iõk×XµjF£‘cÇŽ±fÍšë(_¾<:t`êÔ©\¾|™ììl¾þúkŽ9Rà¹ÄÅÅáääDPPeÊ”aåÊ•ØÚü2jݺ5®®®L˜0Þ½{›Gò{÷îÍúõëùõ×_1¤¥¥ñý÷ßóÇÜö¹ÂÖ­[Ù¸q#ƒÁü544´¸Ÿ†"K&rGÎwÄ^1Ø>߬ ßîNäÔÅ4Œ&Øs…]±WèÔô12² |·÷Ò2 ØÛÙbkeK}üëÜåt<\KÒ¬V9 F›£/ݶ]“êe¨_Å…¤k™ì;}µÐÇð¯çÊÚ}HIË&Û`báÎbú‹ƒ¾]›WaùÖóœýïE‘—odq ‰œ;Œ&ÿõqÓyãîÒ¼v9Œ¦Üž•“;}%êôU¢ãs§xW+Ãâ_ÏÒiÚN:NÙAŸ9QüõÇ]#€RNvzUâ“ §¨÷˜ µ*9s/ û8‰Ü‹b9ÿâ‹/˜7oÙÙÙܼyÜÜÜØ¸q#cÇŽ%!!¬¬,|||0ý÷ÁÝÝM›6¹A&&&2tèPK÷“E¸»»ÀéÓ§ñ÷÷7/ïׯ999 >œK—.Q£F æÍ›G“&Mòí£Q£F¼ñÆ >£ÑHïÞ½éܹs¾vGeÊ”)deeÆ!Cðóó£yóæáççGxx8#GŽd̘1,Y²úöíË·ß~kÞ× Aƒ;v,¿üò ÿüç?Í#ÆÖ´iS¾þúk^~ùeJ–,ÉäÉ“Íçøþûï3jÔ(š5kFݺuyóÍ7Í·Ø´··gÍš5Œ?GGGzöìIÇŽóì»J•*Ìœ9“>úˆ?üvíÚ1tèÐ|wO¹eÚ´iLš4‰Ž;R²dIš7oN```çòÔSOáççG@@•+WfĈy¦£ü™ }ûöeÊ”)y>8Õ­[—ùóç3}útFŽIÉ’% ÊwW?kܸ1Ÿþ9ü1ï¾û.®®®„††Þö‚[¹7}æìr_¿•Ê8Ò˯*!Oä~CÕ­y |sœ+©ÙT-_‚z5 » i™â.¦1à³ýdŒøÔ,Kÿ§ ?]î–ξ±÷›ãt™¾‹Z•œÜ®&¿ü^ ¨ÎÌ 'ùvO"¾µÊñ|³*l<”T¨c¼Ô¶¯?Iؼ}Tpq`XûZT¯ø×îïý\S7²rŒŒ[}ŒK×3©PÚ‘ç›UÁî6“Ù&˜ÿóL&è1s·yùènž´ñtå£ÐÆÌÿù ÁŸîÅh2ѤzY†Ö$æT¶½Äׯ·Ä¥„FDF_bÖ§èڼʣ[º6¯Â†ƒI¼ôt{>ßÂ>N"÷ÂÆd*Üçå3gÎP«V-K×ûÐJJºˆ››uüž?‹ŠŠ"88˜˜˜ìí-~ ‚ˆÈ}a­ï¹ò×숹¬O1½¯5*–äf¶‘%¿ž#îR:Sû4²ty"·U”­4&"""ÿú®œ¹”Îÿ[u””´lJ—°§U½òü¿îE›Û/bÍÎÿæš7ožï6€"""Öªokú¶ö°t"ÅÆ*îs.""""" ç"""""VCá\DDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX û¢4NJºhézjê?‘Gï¹"ò0*R8ws«lézZIIÕ""ˆÞsEÄšœ9s¦Ðm5­EDDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8±ÅγzcÆŒaäÈ‘÷e_&“‰Å‹i›¨¨(~ÿý÷ûzN.\`Æ ÷uŸúŠzüÚµk[ìø"7{N¥ÐnÒvž›¶Óüï/sèÜõ¿´ß1_cî¦3÷¥ÆÃç¯ÓnÒvKw•ˆ³b ç—.]¢ÿþtîÜ™¨¨(~øáöïßϬY³ µàäÉ“téÒ…èèh¢££9|ø0¯¼òŠ¥ûìoåÒ¥KÌ;·HÛ¬Y³æ¾Û={öñÀλ8ÎAD¬ßŠ-ØðÞ¬yÛÇk”å+Žt-ÓÒe‰ÈßH±†ó×^{Þ½{cgg‡››ÄÄÄj=ä†óZµjYºþÖÈ6w“˜˜hõç-"'{[ú?YƒÑÄþ¸k–.GDþFŠ-œ7jÔˆ!C†Í¡C‡ˆˆˆ {÷î…Z¹á¼fÍš–î£îĉ4lØsçΙ—åääÀÆ1™L,]º”:àããC·nÝøõ×_ ÜתU«hß¾}žeýúõãÓO?r§O²eË‚‚‚xüñÇ1bׯ_'""‚’’’‚——-[¶ %%…Ñ£GÓ²eK|||9r$©©©tïÞmÛ¶ñá‡âååÅ’%Kˆ‹‹càÀxyyѶm[¾úê+s=?ýôøøøÂáÇóÔ;iÒ$fÏžÍÏ?ÿŒ——Ç 55•qãÆÑ´iSüüü˜0a™™¹#\sçÎåÿøŸ~ú)-[¶¤eË–Ì›7ϼÏ;m[Ð9ÄÅÅQ»vm.]ºdÞÇܹséÓ§¹Û¶mË?ü@@@ †<ç±qãFºu놗—íÚµã—_~1¯»qão¾ù&7&00ð~K "3M˜LPÂ!÷WåõŒ>^’3÷Ðeú.&|w‚ô¬Ü×ùáó× ›·_¢/ÑgN½gGa4åîçf–'é2}}æDñÓïÍÇØz<™a ç¹i; ›·1WÌëÒ2 L^óSwÒoþ>¶¹œ§¾Ä”›ŒùúÝ>ÞMð§{ùdÃ)Ò2 æm'|{‚®3vÓgNÿÚg®GD¬Û¹ ´k×®tëÖV­Zñì³Ïzý©S§˜4i>>>´k׎?þ˜¬¬,K÷Y±óôô¤I“&¬ZµÊ¼lóæÍ Y¾|9 ,`Μ9ìÛ·#F0lذ|¡¶°Îž=ËÞ½{Y·n‘‘‘œ>}š3fйsg¦L™Bùò剎ŽfÏž=ÄÇÇS»vm"##Ù¶mçÏŸgÑ¢E@îtºuë2zôh¢££0`ééé„……ѰaCöïßÏ—_~ÉÂ… Ù¶mééé¼ñÆŒ=šƒ2fÌÊ”)“§¾1cÆð /СC¢££Í!{Ô¨QÄÅÅIdd$.\`Μ9æí"""¨^½:»víbþüùÌš5ËÜGwÚ¶ s(ŒøøxvíÚÅÏ?ÿÌÖ­[±³³Ë³>66–ñãÇsøðaÞ~ûmÞzë-ÒÒÒxë­·¸|ù2¿ýök׮ͷ­ˆ¯ÜÏ•´,VŽlΡMy¼zYþ´¹ˆX±Î7lØÀúõë9xð &L(ôú/¿ü’ˆˆöïßÏìÙ³Y¿~½yÄ÷QÆ7ß|CNN+V¬ $${{{–-[Æ€hРvvvÒ¶m[V®\yOÇ2¼õÖ[899Q±bEú÷ïŸgT÷y{{3xð`J—.‹‹ mÚ´áСC·m¿yóf2225jŽŽŽT«V_|‘µk×boo““ÑÑÑܸqooojÔ¨qך/_¾Ì¦M›xÿý÷quuÅÅÅ…×_µkךÛ4jÔˆîÝ»ckkKóæÍ©^½:G-Ô¶÷Â`00zôhJ”(Qàú×^{¦M›bccCÇŽIMMåôéÓ\ºt‰ÈÈHÞyç*T¨@©R¥ ü+"ůϜ½<7m'=fîæ×£—éíïƒ]nª­_Å…àVU)åd‡³£Íj—ãXâ ó¶F¼Ò¡Nöyµ>Õ¨"=JczU£BIvL _@5UÍ]РYÎ%gp%5›±WÒ®&åœ(éhÇS +˜÷¹+ö i™9¼ôtuœìm)ëlϰöµØs…äÔ,J8Ø‘’–ÍÙË8;ÚáW·¼¥»VD ÉþA¨aÆŒ1‚Ñ£GÐ Zÿç»UxyyÑ¿–/_Î;ï¼cé~+vÏ<ó 'N$22///vîÜÉÔ©SÜÚªU«æiïááÁ‰'î˱+UªDrròm×gff²hÑ"6mÚD\\éééæ)/¹xñ"×®]£I“&æe&“‰Ö­[ãèèÈòåËùì³ÏÀßߟ±cÇR¥J•;Ö˜””À /¼g¹½ýíŸÒdffÞÓ¶…U²dÉÛ®;xð ‹-âÀ¤¤¤˜ûòÖüöêÕ«ÿåã‹È_³bD Ê:Ûc4AÌ©Œ[}ŒÄ”›ômíAVޑջÙ~"™ø+ddiR=ï7}·¦À܉«‹#WRs¿>–pƒÕ»9–pƒkéÙdåIº–{ç2÷òØ¿p5“Š¥°ûÓpøceÌë‚[Uض6†›ÙFÂÚT£SS7Kw¯ˆB±…ó™3g’Í{ï½g^–‘‘££c¡ÖäÆw ?‚ƒƒYµjGŽ¡]»v¸¹å¾±zxxä»@² Ààèèh}/¬sçÎáîî~ÛõcÆŒ!11‘ &P¿~}ÂÃÃ‰ŠŠºmûêÕ«S£F "## \ïííÍüùóIKKãÍ7ßä£>â“O>¹c·‚ìæÍ›©\¹r‘Îï^¶½õ¼ÌÎÎ.Ò±n‰‹‹#44”ñãÇ3nÜ8\]]©[·.®®®\¹r…råÊÝÓþEäþ²µî.´ª[žƒq×èÛÚƒ™NqñZ&o<[‡š•œùjG<‡‹x«E¹Ó_žnT‘ø+7ysY4#:ÖfDÇÚ”uv ýäÜ[%–uvàZz6eJæÿUýX9'’oda0šÌýÂï*ãVÖ ;B[{ÚÚƒßÏ^ç­e‡y¼FY<\K®P±˜b›Öòä“O²téR~þùg ÇgΜ9¼øâ‹…ZOxx8—.]Âh4²gÏ–.]ʰaÃ,ÝgLhh(;wîdÍš5„……™—÷íÛ—Å‹süøq ‘‘‘lÙ²…Þ½{çÛ‡§§' ìÝ»—¬¬,Ö¯__àÜôˆˆrrrˆeáÂ…„„„àììLjj*‰‰‰ÄÆÆ¹ê¶hÑoooÙ¶m[ž}9;;sòäI’““¹|ù2˜L&ÂÃÃÉÌÌ$''‡­[·²oß>ÒÒÒXºt)7nÜÀÁÁ;;;Ê—Ïÿõk©R¥8þ<ׯ_'..ŽÒ¥KÓµkWþùÏråJîTÇgýúõwí×Âlû¿çàææFùòåY»v-ƒC‡iL\\NNNQ¦LV®\‰­mî˯ZµjÔ¯_ŸÏ?ÿœÌÌL’““ @Ï2)ˆ‰Ü‘ó±WhV»ç.§Ó¤zêWq!éZ&ûN_-Ô¾½FÒµL² &Vl'õfmU$áJŽö64¨€K {Ö¸À­qð*åKP³¢3+vÄ“•cäjz6+vÄ›÷Ùªž+%íXüë92sŒ\ÏÈ!ü—3´ªWžŠ¥Ù{…èó×1Žö6ØÙÚPÊIײˆ< Š-œ7oÞœO>ù„Y³fÑ´iS^}õUBBBÌáúnëmmm9uê:u¢iÓ¦Lž<™‰'òÜsÏYºÏwwwprrÂßßß¼¼_¿~ 0€áÇӴiSf͚żyóòL¹¥Q£F¼ñÆ >Ž;FçÎóµ;zô(­[·&88˜®]»šï¤ãççGóæÍ bêÔ©dgg3räHV¯^Ó§O§oß¾yö5hÐ """èÒ¥ ÀÉɉ%K–°ÿ~üýýiѢ˖-ÃÕÕ£ÑHLL AAA<ñÄØÚÚòúë¯ç«¯G¤¥¥ñä“OòÝwß¹wq©\¹2]ºtÁÛۛѣGãââR¨¾½Û¶ÿ{vvv̘1ƒU«VáããCxx8ýû÷ÇÆ¦pWXùûûãççG@@Ï<ó %K–Ä××>ýôSbcciÞ¼9Æ cðàÁº(TÄnÍ9ïüÑ.>ü!†^~U y"÷[ɪ³á`]¦ïbÁ–³<߬ …y hZ³,Sˆ¡ëŒ]l9r™a^”)io­r<^£,½gïe`ø~œliìQ`LOÎ^J§ÛÇ»ûõ1‚[U5_ÔYÂÁ–a^œ¼Fȧ{ôù*–vbtWOlml˜»é ]þ{ïw«OùR–î^)“ÉT¨›+9sF÷ÿ ’’.âæV´éBTTÁÁÁÄÄÄÜ—9×""ÖÀZßsEäï©(9úÜ­EDDDDDîNC¥sÍ›7çôéÓ–.CDDDDÐȹˆˆˆˆˆÕP8± ç"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJØ¥qRÒEK×ûPSÿ‰ˆ<8zÏ‘‡Q‘¹›[eK×ûÐJJº¨þy@ôž+"ÖäÌ™3…n«i-"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•(ÖpEHH>>><õÔS,Z´¨Àv'Nœ Y³füûßÿ¶t<ÆŒÃÈ‘#ï˾L&‹/.Ò6QQQüþûï–î†ûnݺu´lÙÒüsûöí ýœ-Ž>ùßzîæÂ… lذáÁt–ÈCfÏ©ÚMÚÎsÓvšÿ½ñåa»^è}ÌÜpŠ ß Û`"lÞ>–m=×íN&¥ÑuÆnÄ]³t7ˆˆ(¶pžÀàÁƒ eß¾}|þùçÌŸ?Ÿµk׿i—žžÎk¯½F@@}ûöµtÈÿ¸tésçÎ-Ò6kÖ¬y$Ãù_a }²gÏ""",Ý"Vmňlxï Ö¼íÇã5ÊòGHº–YäýØÚ@U×T(íx×¶%ìðp-A©ö–>}±ÅÎwïÞMÇŽéÚµ+vvv4hЀÀÀ@6oÞœ§Ý˜1c°±±áÃ?´t_HÈ6:kè“ÄÄDK— òÐp²·¥ÿ“Õ1M쿇m;[¦õiÌs>nwm[Õµó^zœú•²ôi‹ˆ(¶pÞ£G¦M›–gYRRÎÎÎæŸW¯^ͦM›˜7o^žåw'Nœ aÆœ;wμ,''‡€€6n܈ÉdbéÒ¥tèкuëÆ¯¿þZà¾V­ZEûöíó,ëׯŸ~ú);Ý"00-[¶Äã?Έ#¸~ý: 8””¼¼¼ÌS(RRR=z4-[¶ÄÇLJ‘#G’šš @÷îÝÙ¶m~ø!^^^,Y²€¸¸8ˆ——m۶嫯¾ÊSÏ‚ èÓ§^^^¼ûî»ÄÅÅÑ«W/¼½½ &>>>ß¹Í;—÷Þ{)S¦Ð¼ysóTž;kîܹŒ=šððpžxâ Z¶lÉÌ™31™L¤¦¦2nÜ8š6mŠŸŸ&L 3óî£f999„‡‡óôÓOãííM¯^½8uêÔûä–7n0bÄ|}} à£>Â`0°}ûvzôèíÛ·gáÂ…Æ|Ç‹‹£víÚ\ºt)ϹöéÓ€I“&1{öl~þùg¼¼¼>|87n¤[·nxyyÑ®];~ùå—â~z‹<4 F&”pÈýUi¾Ûûý?ÛO—é»xeáïì9•rÛíû¶Ÿµû.pæR:§ì 1åfž}÷™ÅÖãÉ\Iͦݤ휻œÀ©‹i¼¿ò(]¦ï¢ç'{XòÛ9Däïã]zàÀ¶oßNÏž=Üù¯&L råÊôïߟ–-[2zôhnܸaé>±8OOOš4iªU«ÌË6oÞŒÁ` 00åË—³`ÁæÌ™Ã¾}û1bÆ ãðáÃ÷t¼³gϲwï^Ö­[Gdd$§OŸfÆŒtîÜ™)S¦P¾|y¢££Ù³gñññÔ®]›ÈÈH¶mÛÆùóçÍ׬Y³†ºuë2zôh¢££0`ééé„……ѰaCöïßÏ—_~ÉÂ… Ù¶m›¹†ï¾ûŽ)S¦°cÇ<È Aƒ˜8q"QQQ”)S†©S§XûÚµk©Y³&{÷îeöìÙ…:ÖºuëpwwgÇŽ,\¸Å‹óã?0jÔ(ââ∌Œ$22’ .0gΜ»öazz:©©©,_¾œßÿÆ3nܸÛöÉŸýë_ÿâÒ¥Klݺ•ü‘–-[bggDZcǃfùòå÷ô8äµ×^£iÓ¦ØØØÐ±cGRSS9}úô}Û¿ÈæϜ½<7m'=fîæ×£—éíïƒ]îûË÷Qг¥;u*—ÂÖüë»Òªž+û/Üu¿]›WáǃIŒ¹ßÒ­Ûÿš>†­M¾¶^•xºQEìmm¨êZ‚:•9ž +‘¿‹b¿ú$!!ÐÐPÚ¶m˨Q£ÌË=JŸ>}ÌS.êÕ«ÇÈ‘#=z´¥ûÄ*<óÌ3Lœ8‘ÈÈH¼¼¼Ø¹s§yô8>>žªU«æiïááÁ‰'î˱+UªDrròm×gff²hÑ"6mÚD\\éééw¼kÈÅ‹¹víMš41/3™L´nݺÀö899åùùvSKþw:TQuë|;FRRî¨Ö /¼g½½}á^&k×®å›o¾áøñãܸqÃüÁên „ ï¼ó >œâãã©^½zž¶÷uþúÁƒY´h %%÷ëùÂLãyT­Ñ‚²ÎöMóG*ãV#1å&}[{páj&neò´¬œg.¦ßu¿O6¬À¼M§Ùs…úU\8pæït®W`ÛK׳X¹3žýg®’t-“Ìl#-ê”·t׈ÈR¬á+̱Μ9áC‡0 lÞ¼™ÈÈHBBB(]º4]»våŸÿü'W®\àøñã¬_¿þ®Ç=uê5kÖ¤uëÖ Ö­[wÇ>ù³Í›7…ÉdÂÉÉ {{{J—.MHH[·neãÆ bbbX°`A¯777Ê—/ÏÚµk1 :t(ßtœR¥Jqþüy®_¿N\\qqq899D™2eX¹r%¶¶ú»d"’cþHeGìšÕ.ÀóͪðíîDN]LÃh‚1WØ{…NM+Ô>;û>Ƹ«ü|ø"ÝšW)°Mf¶‘ W3iíY*åKðûÙëœKΰtwˆÈTl¿‰ÇŽKBB»víÂÇÇ///¼¼¼  iÓ¦L™2…ñãÇãããÃðáà å•W^±tŸX wwwprrÂßßß¼¼_¿~ 0€áÇӴiSf͚żyóòL帥Q£F¼ñÆ >Ž;FçÎóµ;zô(­[·&88˜®]»2dÈüüühÞ¼9AAAL:•ììlFŽÉêÕ«ñññaúôéùîO?hÐ """èÒ¥ ÀÉɉ%K–°ÿ~üýýiѢ˖-ÃÕ5ÿ…PUaŽU¡B>ûì3š5kÆøñãùä“OhÚ´){W“Ê•+Ó¥K¼½½=z4...w=nŸ>}¸zõ*>>>¼ôÒKtîÜ™%JܶOþÌÖÖ–I“&áããÃÛo¿ÍŒ3¨P¡76ÿ}___†Jhhh¾ Jììì˜1c«V­ÂÇLJððpú÷ïŸçCq=HKKãÉ'Ÿä»ï¾Ãßß???xæ™g(Y²$¾¾¾÷ý1y˜ÜšsÞù£]|øC ½üªòDî·UÝšW¡gKw>øæ8]¦ïbÉ¯çø W¸ßý=À­¬-ê”ÇÑÞ–¦5Ëئ„ƒ-žªÎدÑë“=DN¡c“¢}“'"7Ó­{ÈÝÅ™3g¨U«–¥ë}h%%]ÄÍÍúÞ`£¢¢&&&¦Ðs«fsçÎå·ß~ã믿¶t)"RŒ¬õ=WDþžŠ’£õ¶ˆˆˆˆˆ•ÐÈù¢Q‘Gï¹"bM4r.""""òR8± ç"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJØ¥qRÒEK×ûPSÿ‰ˆ<8zÏ‘‡Q‘¹›[eK×ûÐJJº¨þy@ôž+"ÖäÌ™3…n«i-"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•(ÖpEHH>>><õÔS,Z´È¼îÕW_ÅËË+Ï¿FQ»vmRRR¸xñ"o¾ù&~~~øúú2xð`Î;gé>³zcÆŒaäÈ‘÷e_&“‰Å‹i›¨¨(~ÿýwKwCYYY´mÛ–¹sç>°cZc?ˆÈÝM]K»IÛ9ŸœQäm£Ï_çXbªùçlƒ‰°yûX¶õ¼¥Oëž>v“¶ç[ž•c¤Ý¤ítœ²ƒç¦í䙩;¾Ÿ “þÒñ¾ÙÈ,öúE¬Y±…ó„„Lhh(ûöíãóÏ?gþüù¬]»€ùóçç߀hÖ¬åË—àå—_¦D‰lÞ¼™íÛ·ãêêzßB§Î¥K—Šh׬Ycu¡ÔÖÖ–š5kR¹råvLkì¹³Ô›þsô2uÝJ±ÿB‘·ßtè"Çn˜¶µª®%¨PÚÑÒ§V,FwódÃ{O°á½'x¹]M>ÙpŠßŽ%[º,‘‡Z±…óÝ»wÓ±cGºvíŠ 4 00Í›7Ø>99™¥K—2bÄRSSiÙ²%ãÆ£T©R”,Y’nݺcé>û[IHHx Û7{{{/^Lppð;¦5öƒˆÜ٦éæZ’Amk°ñÐE² ¦"máZfžŸílm˜Ö§1Ïù¸YúÔŠ•­ ø×wŧFYvÄ\±t9"µb ç=zô`Ú´iy–%%%áìì\`ûððp<==yòÉ'pqqaôèÑ”,Y£ÑÈùóçY´h½zõ²tŸ»'NаaÃϲ~ýúñé§Ÿ¹S/Ù²e AAA<þøãŒ1‚ëׯÁÀIIIÁËË‹–-[’’ÂèÑ£iÙ²%>>>Œ9’ÔÔܯq»wïζmÛøðÃñòòbÉ’%ÄÅÅ1pà@¼¼¼hÛ¶-_}õUžz,X@Ÿ>}ðòòâÝwß%..Ž^½záííMpp0ñññùÎmîܹ¼û=ooozôèAbb"o¾ù&Mš4¡]»vlÛ¶Íܾ}ûöüûßÿ6oûüƒO?ý”–-[Ò²eKæÍ›gnëïïϺuëÌ?GEEQ»vmóÏŸ|ò ­Zµ¢eË–Œ1‚ää¼#E÷ÒE=Ÿ °sçNz÷îM“&MèÕ«Wž¯Û·o§GøøøÐ¾}{.\ˆÑh4ë½÷ÞcÊ”)4oÞœ‘#G’““Cxx8O?ý4ÞÞÞôêÕ‹S§NYâ% b1û/йÙc´¨Sž’ŽvüzìržõAî`Ï©†/>ÄsÓv2bÉ!®ÜàÕE¿³ïôU>ûù ÏMÛÉw{èÿÙ~ÖîË…¿x=“÷Wåù»h7i»ùß÷Q°þ@ý?ÛŸçxo/fÉo¹¿ ®gäðñú“ô˜¹‡.Ów1Ụgòé‹i¼¿ò(]¦ï¢ç'{ÌÛ,Ûzžé'YòÛ9z~²‡3÷°|ÛÿM¹IË40yM ÏNÝI¿ùûØrä2E‘c0âä- F_m§ïÜ(ž›¶“×âÜåÿ›*ôᶸÂäù»ˆ½f^·zw"ÁŸî¥ÛÇ»™»é cÉßÇ0sCÞ÷¥ö“·sðìµBÕ¿õx2ÃþÎsÓv6oŸ>HˆUz`„8p€íÛ·Ó³gÏ|ë’““ùꫯn;eeÒ¤I<õÔS¤¤¤ðúë¯[®·OOOš4iªU«ÌË6oÞŒÁ` 00åË—³`ÁæÌ™Ã¾}û1bÆ ãðáÃ÷t¼³gϲwï^Ö­[Gdd$§OŸfÆŒtîÜ™)S¦P¾|y¢££Ù³gñññÔ®]›ÈÈH¶mÛfþà¹S9êÖ­ËèÑ£ÍS•ÒÓÓ £aÆìß¿Ÿ/¿ü’… æ šß}÷S¦LaÇŽƒÏ?ÿœyóæ±wï^jÕªÅ믿ŽÉdâØ±c <˜¡C‡²oß>æÏŸÏÒ¥KYºt©ùXk×®¥fÍšìÝ»—Ù³g“žžNjj*Ë—/ç÷ß§qãÆŒ7®øžè"V&úüu.\½I{¯JØÚ@§¦n¬Û—wj‹Ñdâ›Ý‰ü³W~ÕŠ²ÎÌÿù ó_zœy¥C-6¼÷=Zºç;Æg?ŸÁÕÅ‘ïÞò#âÝV<^½ ½ý«ÒÙ÷±»ÖwáêMªU(ɲáÍXõz .\½É7»󵋻˜NÇ&•Yó¶³û7aõ®D~?wݼ~Ë‘K¸—/Áê7Z2á…,ùõ1ä°|ø} WÒ²X1²9ÿ샭­M¡úîf¶‘ “8s)^~¹ç‘e$#ËÀÇaÞD¼ûõª¸0ëÇÿ ÖF“‰;â™Úˆµ£ZQï±Rœ½œN)';VŽlÁÇý¼Øt(‰‡.ªŽ»Õw)‘ÏÔfý{O0¸m &CFpD,é„óóçÏóꫯ2tèPš5k–o}xx8õêÕãé§Ÿ.pûqãÆ±k×.<<<èׯãÿB ã›o¾!''€+V‚½½=Ë–-cÀ€4hÐ;;;iÛ¶-+W®¼§cFÞzë-œœœ¨X±"ýû÷ç—_~¹m{oooLéÒ¥qqq¡M›6:tè¶í7oÞLFF£FÂÑÑ‘jÕªñâ‹/š¯? ¤fÍš”)S†V­ZQ­Z56lˆ““;väÈ‘#î»~ýú´iÓ{{{Ú·oÏõë×éÙ³'¶¶¶téÒ…Ë—/sáBÁóF5jD÷îݱµµ¥yóæT¯^£GÞµ¿J–,ÉÍ›7‰ŽŽÆd2ñä“OÞö¡¢öCQÏgÈ!T¨P'''^{í5Nœ8ÁÙ³gY±bmÚ´¡cÇŽØÙÙQ¿~}ÌòåËÍÛÖ©S‡>}ú`c“ûË«L™2Œ5 lmm Òœyù[Y·ÿ^•(ådÀs>q4ág/ç½04䉪T,툃 mU$öBj¡á`g‹Áh"Ç`";Ç„Áh¢|)Gì ‚ëWq!¸UUJ9ÙáìhG³Úå8–x#_»@¯J<ݨ"ö¶6Tu-AÊÎyæÁ×u+EwelmÀ«Zª”/Aì…4®¤f³3ö CÚÕ¤œ³%íxªa…;Öôá÷'xnÚNºLßÅÌõ'éÞ·²N¸”°cPÛnØÚ@Ê¥hÛ¨R¡F¸ S¿€j4ªZ  A2² œ»‡ EŠ“}q !!ÐÐPÚ¶m˨Q£ò­¿5j~·‹+W®Ì|@³fÍ8yò$žžž–ëµà™gžaâĉDFFâååÅÎ;Í£ÇñññT­Z5O{Nœ8q_Ž]©R¥|Ó4þ,33“E‹±iÓ&âââHOO7Oy)ÈÅ‹¹víMš41/3™L´nݺÀö899åù933“»±··ÇÑÑÑ6ÌõFaãëëËôéÓY¼x1£G¦{÷î¼÷Þ{æãݯ~(êùTªT È}MÅÇÇS½zõ<ë=<<ò̃/èÅÚµkùæ›o8~ü87nÜ08yÔݸ™Ã¯Ç’±µÿÍ;"bÿ†Õ*p;{;[²rŒ…>Îð Ú ùâaó¢hãYnÍ«jÛ¬#«w'²ýD2ñW2ÈÈ2Ò¤z™|í.]ÏbåÎxöŸ¹JÒµL2³´¨Sþ¶ûu°Í=‡¤k¹ÓsÜË—(ôùŒîæiÀI×2™Ë‘¯¯óQhc"\⧃9}1Ô›9æé)·”p¼û¡«‹£ydÿN Sÿ±„¬Þȱ„\KÏ6÷«ˆ5)Öpž@ïÞ½ñóócòäɶ §N:´mÛ6Ïò-[¶žgjGFFî§[GÇGóª÷?spp 88˜U«VqäÈÚµk‡›[îE$&æý*³ À~«¯Š°Î;‡»»ûm×3†ÄÄD&L˜@ýúõ '**ê¶í«W¯N5ˆŒŒ´t·ÚÝú­S§NtêÔ‰ÄÄDúôéC½zõ ¹ã>‹»ΟÏ7êîî^¤çÈ-ß|ó ³fÍbòäÉøúúräÈúõëW\],bU6ºˆ{¹,Ö4Ïò1W˜º6†!íjàhÿ׿lŽØî.ŒíÑ»¼£åö6ängn8ÅÅk™¼ñljVræ«ñþÓtðú—‡ð¯çÊäF¸•ubÔòèBÕVÖ9wàZz6eJ=¸•uâY7¦ý‹ ØøûEÿz–·;Õ¥±Gb/¤º–?û#å&UÊånG; ·é£»Õå&o.‹fDÇÚŒèX›²Î´Ÿ¬Û,Šõ)Ö[)öîÝ___¦M›fýû³;Í5÷õõ%..Ž™3g’™™ÉåË—7nmÚ´¡V­Z…)á¡ÊÎ;Y³f aaaæå}ûöeñâÅ?~ƒÁ@dd$[¶l¡wïÞùöáééIBB{÷î%++‹õë×8¯:""‚œœbccY¸p¡9h:;;“ššJbb"±±±œ>>LŸ>¾}ûæÙ× Aƒˆˆˆ K—.8p'''–,YÂþýûñ÷÷§E‹,[¶ WWWKwñm½ÿþû\¹r…fÍšñꫯŠ]îD?äääЩS'Ú´iƒ³³3Ÿ|ò 76ÿm___†Jhh( ¸í¾úôéÃÕ«Wñññᥗ^¢sçΔ(Qø¯·EV‡Î]'1åfáÜÞÖ†Þ•ˆØW¸{ž¿Ðª*[Ž\f肃Ï;¿úf¶‘ÖÅ2wàãlxï 6¾ïϧý›ðó¡‹œº˜N]·R xª:|sœfíáTRmÿ_M/TgÃÁ$ºLßÅ‚-gy¾YþwÜ«„ƒ-žªÎدÑë“=DN¡c“Âý}`LOÎ^J§ÛÇ»ûõ1‚[UåNÓáoÍ9¿uçšKò~×útö}Œë9t™¾‹÷W¡mãJ…úö¡‘GiÖH¢ûÇ»ùh],owª‹o­²æ}6¬Zšçïçå/P½‚35+:ª~ßZåx¼FYzÏÞËÀðý89ØÒØ£ô}{‰Ü/6¦ÛÝÊâœ9sæo3b]’’.âæöàþNaEEELLL ööÅ~ ‚ÜGõêÕcÉ’%·³.òwfï¹7næÐcæ&¼Ð¿º¹Ê;~™9?fÙðf8;ÚYºD)&EÉÑJc"""@éö|гK=LJßÇàhoK÷Ò|ÜÏKÁ\DÌÎÿæš7oÎéÓ§-]†Üƒ[׈Èãµ§+­=­wJŸˆXÞû#D"""""rg ç"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX û¢4NJºhézjê?‘Gï¹"ò0*R8ws«lézZIIÕ""ˆÞsEÄšœ9s¦Ðm5­EDDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8±öŹó¨¨(¦OŸÎ‰'([¶,ýû÷祗^ÊÓ&++‹Q£F±k×.öìÙ“gÝÞ½{ ÅÉÉ ^}õU^yåK÷›U3f ׯ_göìÙy_&“‰%K–0pàÀBo…ƒƒ?þ¸¥»¢X]¸pýû÷óÜsÏYºù‹öœJá+ŽRÂáÿƬêWqᥧkФz™»nøüu^_z˜ŸGûcgkcéÓ)”×—&öB*ÙF£ §?ÿwoùñÿV¥‘Gi=]ÃÒåŠüm[8OHH`ðàÁüóŸÿ¤sçÎÄÆÆFÅŠyþùçHIIaèÐ¡ÄÆÆâàào'Ož¤K—.Ìœ9ÓÒýô·uéÒ%æÎ[¤p¾fÍ<==ùp¾gÏ~úé'…s‘GÈŠ-(ëlOfŽ‘¯¶ÇóGX<Ì·²N–.í¾û´¿·ùÿ?ûå ûO_ã‹—},]–Èß^±MkÙ½{7;v¤k×®ØÙÙÑ AÙ¼y3F£‘¾}ûRµjU>øàƒ÷qòäIjÕªeé>ú[KHHx Û<Œ-]‚ˆ'{[ú?YƒÑÄþ¸k–.GDþFŠ-œ÷èуiÓ¦åY–””„³³sîmm™?>Ÿ|ò ŽŽŽîãäɓԬYÓÒ}ôÀ8q‚† rîÜ9󲜜ظq#&“‰¥K—Ò¡C|||èÖ­¿þúkûZµjí۷ϳ¬_¿~|úé§@î”ÀÀ@¶lÙBPP?þ8#FŒàúõëDDD0pà@RRRðòò¢eË–@î7£G¦eË–øøø0räHRSs¿íÞ½;Û¶mãÃ?ÄËË‹%K–ÇÀñòò¢mÛ¶|õÕWyêY°`}úôÁËË‹wß}—¸¸8zõê…··7ÁÁÁÄÇÇç;7“ÉÄâÅ‹iÓ¦ >>> <˜?þøƒ3gÎàííÍ–-[Ìmß|óMÞ|óÍ|ûhР¿þú+={öÄÛÛ›^x¸¸8óú7Ò­[7¼¼¼h×®¿üò “&MböìÙüüóÏxyy1|øpüýýY·nyû¨¨(j×®mþù“O>¡U«V´lÙ’#Fœœlé§›ˆÜ†ÁhÂdÂ<ÕåzF¯?I™{è2}¾;Az–!Ï6ÿ9v™°yûèüÑ.&~w‚›ÙÆ»n»lëy¦­åãõ'ynÚN†/ú¤k™Lþ>†Ní"lÞ>¢N_5cëñd†-üç¦í$lÞ>vÄ\1¯ûíX2/ÎßG—黹ô0'þHýËýp##‡ ß0ײûdŠy]•›¼·âˆ¹–uû/Xúayè=° B8ÀöíÛéÙ³§yÙÝ‚÷©S§˜4i>>>´k׎?þ˜¬¬,‹uÖƒâééI“&MXµj•yÙæÍ›1 ²|ùr,XÀœ9sØ·o#FŒ`ذa>|øžŽwöìYöîÝ˺u눌ŒäôéÓ̘1ƒÎ;3eÊÊ—/Ott´ùš€øøxj×®Mdd$Û¶mãüùó,Z´ÈÒR·n]FMtt4 ==°°06lÈþýûùòË/Y¸p!Û¶m3×ðÝwß1eÊvìØÁÁƒ4h'N$**Š2eÊ0uêÔ|u/Y²„… ²páB¢¢¢hÙ²%¯¿þ:µjÕâ½÷ÞcìØ±¤¦¦òÓO?Å„ òíÃ`0°hÑ"æÏŸÏ¾}ûpuueòäÉæõ±±±Œ?žÃ‡óöÛoóÖ[o‘––Ƙ1cxá…èСÑÑÑÌ›7ï®ý¼}ûv–-[Æ·ß~ËŽ;xá…(Y²¤…že"r'×3r˜³ñ4µ*;ÓÆ³®Þ¤Z…’,ÞŒU¯·àÂÕ›|³+ï7h'/¤±àå¦,}Õ—£ 7X·ïB¡¶Ýv"™§VdÝ;­¨êZ’á‹Ç·V9Ö½ÓŠg¯Ì”b0ý·mÜ¥tF>S›õï=Áà¶5˜ü} Ynf™´æÃÚ×bÝ;­x-¨¥KüõÙ«{N¥ð‚Ÿ;ï¶¢sÓÇøðûrŒ&nfykùa긕â‡Q­˜Þ׋ջò|‘¢{ áüüùó¼úê« :”fÍšz»/¿ü’ˆˆöïßÏìÙ³Y¿~½yÄ÷QÆ7ß|CNN+V¬ $${{{–-[Æ€hРvvvÒ¶m[V®\yOÇ2¼õÖ[899Q±bEú÷ïo!.ˆ··7ƒ¦téÒ¸¸¸Ð¦M:tÛö›7o&##ƒQ£FáèèHµjÕxñÅY»v­¹M`` 5kÖ¤L™2´jÕŠjժѰaCœœœèر#GŽɷ߯¾úŠAƒáé鉽½=C† áÔ©S$$$Fݺuùàƒ?~<3fÌ téÒÖ÷òË/ãææ†££#:uÊs¬×^{¦M›bccCÇŽIMMåôéÓ÷ÔÏ%K–äæÍ›DGGc2™xòÉ'Íß$‰ˆuè3g/ÏMÛI™»ùõèezû{à`—{gý*.·ªJ)';œíhV»ÇoäÙ~pÛ”p°¥‚‹#Ík—#æ¿\ÞmÛZ•œi^»v¶6ø{º’zÓÀ3WÆÖÚ5®DJZ6—¯çNõ ¨F£ª¥±T #ËÀ¹ä ìlmp´·%öBi™êWqÁ½|‰¿Ü'^•høßãµ÷®Ä›9$]Ídgì2³ n[;ª”s¢[ w"£/Yúay¨ëÝZ wþqhh(mÛ¶eÔ¨QEÚöÏÓ¼¼¼èß¿?Ë—/çwÞyð=õ€=óÌ3Lœ8‘ÈÈH¼¼¼Ø¹s§yô8>>žªU«æiïááÁ‰'î˱+UªtÇé™™™,Z´ˆM›6GzzºyÊKA.^¼Èµk×hÒ¤‰y™Éd¢uëÖ¶wpp0ß¡çÖÏ™™™î÷£>âã?6/3 $''SµjU†NHHMš4ÁÏϯPçþ¿Ç:xð ‹-âÀ¤¤¤˜Ïÿ^øúú2}út/^ÌèÑ£éÞ½;ï½÷^C‹ˆeܺ Ôh‚˜?R·ú‰)7éÛÚƒ¬#«w'²ýD2ñW2ÈÈ2ÞñN.öv¶deä°e[{[[ìl¸uÏ;[ó>Ž%Ü`õîDŽ%ÜàZz¶yƒ 3ûyñïíñôž½ßZåx-¨6•Ê8r¿˜k1¹|#‹9tþh§y½ÉÍj—³Èc'ò¨(Öpž@ïÞ½ñóóË3Uà^ݸqão3 ÀÁÁàà`V­ZÅ‘#Gh×®nnn@nÿß‹ ìŽŽŽæÑ÷Â:wîîîî·]?fÌ™0aõë×'<<œ¨¨¨Û¶¯^½:5jÔ 22ò¾öQõêÕ2dˆùî?f0˜2e /½ô|ûí·y¦TF\\¡¡¡Œ?žqãÆáêêJݺuï¸ÍÝú»S§NtêÔ‰ÄÄDúôéC½zõ ¹¯ý""­ 4pw¡UÝòŒ»FßÖÌÜpŠ‹×2yãÙ:Ô¬äÌW;â9|îz¡ö÷W¶ý³ø+7ysY4#:ÖfDÇÚ”uv ýäíæõõ«¸ðÏ^ ÈÈ20ùûþµ9Žÿ×­~±ôQÕò%¨êZ‚/_-ü7â"rwÅ6­åV0÷õõeÚ´iØØí¾¯ñññ„‡‡séÒ%ŒF#{öìaéÒ¥ 6ÌÒ}öÀ„††²sçNÖ¬YCXX˜yyß¾}Y¼x1ÇÇ`0É–-[èÝ»w¾}xzz’ÀÞ½{ÉÊÊbýúõÎMˆˆ ''‡ØØX.\hŒÎÎΤ¦¦’˜˜Hll,{¡n‹-ðöö&111ÏÜñ[Ûœ_ô‘ú;q´·å£ÐÆM¸Að§{é>s7ßGýAÙ’Å>cVä‘fc*dZ9sæŒî9þ$%]ÄÍ­²¥ËÈ'**Šàà`bbb°·×ªˆ<¬õ=×RÖH¢ªk |j”µt)"KEÉÑìVŠ"""bi™9 æ" •þÍ5oÞüžo (""‡àVUÿúNDäÐȹˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJØ¥qLìIK×ûP»výº¥KùÛÐ{®ˆX {»B·-R8¯Y³¦¥ÏMDDDDä¡’¾Ðm5­EDDDDÄJ(œ‹ˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJé/„ŠÈÃËd2åù¯ˆˆˆMžÿ…s‘¿£ÑHzziiirrÐEDDŠÈÆÆ;{{J•*…³s)lm‹gŠÂ¹È#Îh4’ššJzZ*¥K—ÆÁÁ°Îô‹/Q§NmK—qWIIqs«lé2T§êTªó¡«óa>ƒÁ@zz:)))&\\\Š% +œ‹<ÂL&Sî›IZ*eË–ÅÖÖÖ*C¹ˆˆˆµ³³³£téÒØÚÚ’œœLÉ’%±±±¹ïS\tA¨È#Îh4b0Ì#æ"""rïJ–,‰Á`Àh4ËþÎEa&“ÉkéÒò0 Ìœ9“€€|||èÙ³'»ví²tYµ»½ŽÂÃÃiÓ¦ 7&88˜Ã‡[ºäGн¥ ‘‡Ë¡C‡èß¿?YYYÔ¯_ßÒåhãÆLŸ>… Ò¸qc"""6l?þø#Õ«W·tyf[¶laÒ¤I|ñÅøúúrðàA^|ñEjÔ¨AÛ¶m-]^>ÙÙÙ >;;;K—r['OžäǤF–.å®Nœ8Áرc;v,7¶t9ùìÝ»7ß²ž={òÄOXº´<¾øâ 6nÜÈW_}…»»; <˜Í›7S¹reK—÷PºÓëháÂ…¬X±‚… R«V-V­ZEXX?þø#îîî–.ý‘ ‘s)´sçÎÌ+¯¼B÷îÝ-]Îmmß¾7ß|ooolmmyþùç©T©Û·o·tiyܼy“)S¦àëë € 6$&&ÆÒ¥h„ TªT‰çŸÞÒ¥èúõë\½zK—rWééé¼öÚk<ûì³ôéÓÇÒåJdd$111 4ÈÒ¥äqàÀÚ·oOµjÕ°³³£k×®ØÛÛsìØ1K—öPºÓë(''‡ððpÞ~ûm<==qtt¤_¿~4jÔˆE‹YºôG†Â¹ˆZõêÕÙ°a/¿ü2&“ÉÒåÜÖ„ òž¬¬,RRR(Uª”¥KËãÙgŸ%((€ŒŒ 6lØ@\\œy™5ùúë¯Ù»w/}ô‘¥K¹­“'OâááaÕ#û·Œ;{{{&MšdéR mΜ9ôë×òåË[º”<ùé§Ÿ8uêYYY¬^½GGG|||,]ÚCéN¯£'NœœL@@@žåO>ù$;wî´té Mk‘"©]»¶¥K(²Å‹cooO»ví,]JRRRhÞ¼9öööLœ8‘ZµjYº¤<~ÿýwf̘Á×_mupþìÔ©S$&&ÒªU+²²²hÔ¨£F²ºöË/¿°fÍ4h@ëÖ­)S¦ ¡¡¡¼üòË–.í¶"##9yò$ .´t)ù¼ð ìÚµ‹:`ccƒ££#ááá”-[ÖÒ¥=”îô:JJJÂÑÑ1ß4777.\¸`éÒ ç"òHÛ²e ³fÍâ³Ï>ÃÅÅÅÒå¨|ùòœ8q‚­[·òæ›oRªT):uêd鲸|ù2ÇgÚ´iÔ¬YÓÒåÜQ§Nðöö¦N:ܼy“ 0`À~ùå*V¬héòÌÂÃéS§3fÌ ^½zìÚµ‹W^y…råÊléò 4gΨP¡‚¥KÉçƒ>àÔ©SlÞ¼™ªU«òÓO?ñÚk¯ñïÿ›&MšXº¼‡Î^G·ûÆÔš¿I}iZ‹ˆ<²~ûí7^{í5>üðCž~úiK—sGööö´mÛ–nݺ±~ýzK—c¶cÇ’““1b„ùÎ .dåÊ•4iÒ„””K—hæììLƒ ppp téÒ¼ùæ›ØÚÚZÝ;Ž9»ï¾K£Fppp €=ziéÒ Ill,C† ±t)ùäää°bÅ FŽIÍš5qpp K—.´lÙ’+VXº¼‡Ò^GnnndeeqõêÕ<Û\¼x777K—þÈP8‘GÒo¿ýưaÃ7nœÕ^¼Ê/¿ü’gYFFŽŽŽ–.ÍìùçŸçرcDGG›ÿ 4ˆÞ½{sèÐ!«›ügÙÙÙܼy“’%KZº”<ÜÝݱ·ÏûŵÉd²Ú)CsæÌ!44Ô*GÍM&vvvùæGÛÙÙåëc¹7~yzzâêêšïÖ´[·nÅßßßÒ¥>2ÎEä‘óÛo¿1tèPÞÿ}BBB,]Îm=ùä“L˜0'N““ÃÏ?ÿLDD}ûöµti¥¯¾úŠ­[·b0¸víãÆ£N:ù.^³´àà`fÏžMBBƒ-[¶ðý÷ßÓ»woK—–Ï­;´Xë|x:vìȬY³Ìý¹qãF~ûí7«™™™I¿~ýøé§ŸnûÿÖâN¯#†ÊÇLLL YYY,_¾œèèhhéÒúX)"EòÒK/±sçN2220™L4lØ.]ºXÍ]Q)>666ØÙÙáèè”&++ £Ñ€Ñ˜û™¼ŸÍEDDþ¶n…o[[lms§:::bggW,#ç ç"8[[[ì±µµÁÉɣѨP.""RD666ØÚÚš¾ŠëF ç"·ÞL@£å"""÷êÖïÒâ¼vKá\äoâA¼¡ˆˆˆÈ_£»µˆˆˆˆˆX …s+¡p.""""b%ÎEDDDD¬D¡Ã¹.")º¢äèB‡sGGGrr²-}n"""""œœìÿþ1ÀÂ)t8/[¶,—/]R@)„ìì,._ºDÙ²e ½©‘$==«W¯’Ñh´ôùŠˆˆˆˆX¥Ü¿Ðí@¹råpvv.ôvE ç"""""R|t·+¡p.""""b%ÎEDDDD¬„¹ˆˆˆˆˆ•P8± ç"""""VBá\DDDDÄJ(œ‹ˆˆˆˆX …s+ñÿxËeÅ£8@%tEXtdate:create2018-04-12T17:59:47+02:00¬ŽT%tEXtdate:modify2018-04-12T17:59:47+02:00ÝÓºèIEND®B`‚django-tables2-2.7.5/docs/index.rst000066400000000000000000000032701473544236200171260ustar00rootroot00000000000000.. default-domain:: py ================================================ django-tables2 - An app for creating HTML tables ================================================ Its features include: - Any iterable can be a data-source, but special support for Django QuerySets is included. - The built in UI does not rely on JavaScript. - Support for automatic table generation based on a Django model. - Supports custom column functionality via subclassing. - Pagination. - Column based table sorting. - Template tag to enable trivial rendering to HTML. - Generic view mixin. About the app: - `Available on pypi `_ - Tested against currently supported versions of Django `and the python versions Django supports `_ - `Documentation on readthedocs.org `_ - `Bug tracker `_ Table of contents ----------------- .. toctree:: :maxdepth: 1 :caption: Getting started pages/installation pages/tutorial pages/table-data .. toctree:: :maxdepth: 1 :caption: Customization pages/custom-data pages/ordering pages/column-attributes pages/column-headers-and-footers pages/swapping-columns pages/pagination pages/table-mixins pages/custom-rendering pages/query-string-fields pages/localization-control pages/generic-mixins pages/pinned-rows pages/filtering pages/export .. toctree:: :maxdepth: 1 :caption: Reference pages/reference pages/faq pages/upgrade-changelog pages/glossary django-tables2-2.7.5/docs/make.bat000066400000000000000000000106571473544236200167010ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-tables2.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-tables2.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end django-tables2-2.7.5/docs/pages/000077500000000000000000000000001473544236200163625ustar00rootroot00000000000000django-tables2-2.7.5/docs/pages/api-reference.rst000066400000000000000000000273101473544236200216240ustar00rootroot00000000000000.. _api-public: API Reference ============= `.Accessor` (`.A`) ------------------ .. autoclass:: django_tables2.utils.Accessor `.RequestConfig` ---------------- .. autoclass:: django_tables2.config.RequestConfig `.Table` -------- .. autoclass:: django_tables2.tables.Table :members: paginate, as_html, as_values, get_column_class_names, before_render, get_top_pinned_data, get_bottom_pinned_data `.Table.Meta` ------------- .. class:: Table.Meta Provides a way to define *global* settings for table, as opposed to defining them for each instance. For example, if you want to create a table of users with their primary key added as a `data-id` attribute on each ``, You can use the following:: class UsersTable(tables.Table): class Meta: row_attrs = {"data-id": lambda record: record.pk} Which adds the desired ``row_attrs`` to every instance of `UsersTable`, in contrast of defining it at construction time:: table = tables.Table(User.objects.all(), row_attrs={"data-id": lambda record: record.pk}) Some settings are only available in `Table.Meta` and not as an argument to the `~.Table` constructor. .. Note:: If you define a `class Meta` on a child of a table already having a `class Meta` defined, you need to specify the parent's `Meta` class as the parent for the `class Meta` in the child:: class PersonTable(table.Table): class Meta: model = Person exclude = ("email", ) class PersonWithEmailTable(PersonTable): class Meta(PersonTable.Meta): exclude = () All attributes are overwritten if defined in the child's `class Meta`, no merging is attempted. Arguments: attrs (`dict`): Add custom HTML attributes to the table. Allows custom HTML attributes to be specified which will be added to the ```` tag of any table rendered via :meth:`.Table.as_html` or the :ref:`template-tags.render_table` template tag. This is typically used to enable a theme for a table (which is done by adding a CSS class to the ``
`` element):: class SimpleTable(tables.Table): name = tables.Column() class Meta: attrs = {"class": "paleblue"} If you supply a a callable as a value in the dict, it will be called at table instantiation and the returned value will be used: Consider this example where each table gets an unique ``"id"`` attribute:: import itertools counter = itertools.count() class UniqueIdTable(tables.Table): name = tables.Column() class Meta: attrs = {"id": lambda: f"table_{next(counter)}"} .. note:: This functionality is also available via the ``attrs`` keyword argument to a table's constructor. row_attrs (`dict`): Add custom html attributes to the table rows. Allows custom HTML attributes to be specified which will be added to the ```` tag of the rendered table. Optional keyword arguments are `table` and `record`. This can be used to add each record's primary key to each row:: class PersonTable(tables.Table): class Meta: model = Person row_attrs = {"data-id": lambda record: record.pk} # will result in '...' .. note:: This functionality is also available via the ``row_attrs`` keyword argument to a table's constructor. empty_text (str): Defines the text to display when the table has no rows. If the table is empty and ``bool(empty_text)`` is `True`, a row is displayed containing ``empty_text``. This is allows a message such as *There are currently no FOO.* to be displayed. .. note:: This functionality is also available via the ``empty_text`` keyword argument to a table's constructor. show_header (bool): Whether or not to show the table header. Defines whether the table header should be displayed or not, by default, the header shows the column names. .. note:: This functionality is also available via the ``show_header`` keyword argument to a table's constructor. exclude (tuple): Exclude columns from the table. This is useful in subclasses to exclude columns in a parent:: >>> class Person(tables.Table): ... first_name = tables.Column() ... last_name = tables.Column() ... >>> Person.base_columns {'first_name': , 'last_name': } >>> class ForgetfulPerson(Person): ... class Meta: ... exclude = ("last_name", ) ... >>> ForgetfulPerson.base_columns {'first_name': } .. note:: This functionality is also available via the ``exclude`` keyword argument to a table's constructor. However, unlike some of the other `.Table.Meta` options, providing the ``exclude`` keyword to a table's constructor **won't override** the `.Meta.exclude`. Instead, it will be effectively be *added* to it. i.e. you can't use the constructor's ``exclude`` argument to *undo* an exclusion. fields (`tuple`): Fields to show in the table. Used in conjunction with `~.Table.Meta.model`, specifies which fields should have columns in the table. If `None`, all fields are used, otherwise only those named:: # models.py class Person(models.Model): first_name = models.CharField(max_length=200) last_name = models.CharField(max_length=200) # tables.py class PersonTable(tables.Table): class Meta: model = Person fields = ("first_name", ) model (:class:`django.core.db.models.Model`): Create columns from model. A model to inspect and automatically create corresponding columns. This option allows a Django model to be specified to cause the table to automatically generate columns that correspond to the fields in a model. order_by (tuple or str): The default ordering tuple or comma separated str. A hyphen `-` can be used to prefix a column name to indicate *descending* order, for example: `('name', '-age')` or `name,-age`. .. note:: This functionality is also available via the ``order_by`` keyword argument to a table's constructor. sequence (iterable): The sequence of the table columns. This allows the default order of columns (the order they were defined in the Table) to be overridden. The special item `'...'` can be used as a placeholder that will be replaced with all the columns that were not explicitly listed. This allows you to add columns to the front or back when using inheritance. Example:: >>> class Person(tables.Table): ... first_name = tables.Column() ... last_name = tables.Column() ... ... class Meta: ... sequence = ("last_name", "...") ... >>> Person.base_columns.keys() ['last_name', 'first_name'] The ``'...'`` item can be used at most once in the sequence value. If it is not used, every column *must* be explicitly included. For example in the above example, ``sequence = ('last_name', )`` would be **invalid** because neither ``"..."`` or ``"first_name"`` were included. .. note:: This functionality is also available via the ``sequence`` keyword argument to a table's constructor. orderable (bool): Default value for column's *orderable* attribute. If the table and column don't specify a value, a column's ``orderable`` value will fall back to this. This provides an easy mechanism to disable ordering on an entire table, without adding ``orderable=False`` to each column in a table. .. note:: This functionality is also available via the ``orderable`` keyword argument to a table's constructor. template_name (str): The name of template to use when rendering the table. .. note:: This functionality is also available via the ``template_name`` keyword argument to a table's constructor. localize (tuple): Specifies which fields should be localized in the table. Read :ref:`localization-control` for more information. unlocalize (tuple): Specifies which fields should be unlocalized in the table. Read :ref:`localization-control` for more information. Columns ------- `.Column` ~~~~~~~~~ .. autoclass:: django_tables2.columns.Column :members: render, value, order `.BooleanColumn` ~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.BooleanColumn `.CheckBoxColumn` ~~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.CheckBoxColumn :members: `.DateColumn` ~~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.DateColumn :members: `.DateTimeColumn` ~~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.DateTimeColumn :members: `.EmailColumn` ~~~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.EmailColumn :members: `.FileColumn` ~~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.FileColumn :members: `.JSONColumn` ~~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.JSONColumn :members: `.LinkColumn` ~~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.LinkColumn :members: `.ManyToManyColumn` ~~~~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.ManyToManyColumn :members: `.RelatedLinkColumn` ~~~~~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.RelatedLinkColumn :members: `.TemplateColumn` ~~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.TemplateColumn :members: `.URLColumn` ~~~~~~~~~~~~ .. autoclass:: django_tables2.columns.URLColumn :members: Views, view mixins and paginators --------------------------------- `.SingleTableMixin` ~~~~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.views.SingleTableMixin :members: `.MultiTableMixin` ~~~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.views.MultiTableMixin :members: `.SingleTableView` ~~~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.views.SingleTableView :members: get_table, get_table_kwargs `.export.TableExport` ~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.export.TableExport :members: `.export.ExportMixin` ~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.export.ExportMixin `.LazyPaginator` ~~~~~~~~~~~~~~~~ .. autoclass:: django_tables2.paginators.LazyPaginator See :doc:`internal` for internal classes. django-tables2-2.7.5/docs/pages/builtin-columns.rst000066400000000000000000000016021473544236200222370ustar00rootroot00000000000000.. _builtin-columns: Built-in columns ================ For common use-cases the following columns are included: - `.BooleanColumn` -- renders boolean values - `.CheckBoxColumn` -- renders ``checkbox`` form inputs - `.Column` -- generic column - `.DateColumn` -- date formatting - `.DateTimeColumn` -- ``datetime`` formatting in the local timezone - `.EmailColumn` -- renders ```` tags - `.FileColumn` -- renders files as links - `.JSONColumn` -- renders JSON as an indented string in ``
``
- `.LinkColumn` -- renders ``
`` tags (compose a Django URL) - `.ManyToManyColumn` -- renders a list objects from a `ManyToManyField` - `.RelatedLinkColumn` -- renders ```` tags linking related objects - `.TemplateColumn` -- renders template code - `.TimeColumn` -- time formatting - `.URLColumn` -- renders ```` tags (absolute URL) django-tables2-2.7.5/docs/pages/column-attributes.rst000066400000000000000000000066731473544236200226110ustar00rootroot00000000000000.. _column-attributes: Column and row attributes ========================= Column attributes ~~~~~~~~~~~~~~~~~ Column attributes can be specified using the `dict` with specific keys. The dict defines HTML attributes for one of more elements within the column. Depending on the column, different elements are supported, however ``th``, ``td``, and ``cell`` are supported universally:: >>> import django_tables2 as tables >>> >>> class SimpleTable(tables.Table): ... name = tables.Column(attrs={"th": {"id": "foo"}}) ... >>> # will render something like this: '{snip}
`` element on each row. By default, class names *odd* and *even* are supplied to the rows, which can be customized using the ``row_attrs`` `.Table.Meta` attribute or as argument to the constructor of `.Table`. String-like values will just be added, callables will be called with optional keyword arguments `record` and `table`, the return value will be added. For example:: class Table(tables.Table): class Meta: model = User row_attrs = { "data-id": lambda record: record.pk } will render tables with the following ```` tag .. sourcecode:: django [...] [...] django-tables2-2.7.5/docs/pages/column-headers-and-footers.rst000066400000000000000000000120451473544236200242430ustar00rootroot00000000000000.. _column-headers-and-footers: Customizing headers and footers =============================== By default an header and no footer will be rendered. Adding column headers --------------------- The header cell for each column comes from `~.Column.header`. By default this method returns `~.Column.verbose_name`, falling back to the capitalized attribute name of the column in the table class. When using QuerySet data and a verbose name has not been explicitly defined for a column, the corresponding model field's `verbose_name` will be used. Consider the following: >>> class Region(models.Model): ... name = models.CharField(max_length=200) ... >>> class Person(models.Model): ... first_name = models.CharField(verbose_name="model verbose name", max_length=200) ... last_name = models.CharField(max_length=200) ... region = models.ForeignKey('Region') ... >>> class PersonTable(tables.Table): ... first_name = tables.Column() ... ln = tables.Column(accessor="last_name") ... region_name = tables.Column(accessor="region__name") ... >>> table = PersonTable(Person.objects.all()) >>> table.columns["first_name"].header 'Model Verbose Name' >>> table.columns["ln"].header 'Last Name' >>> table.columns["region_name"].header 'Name' As you can see in the last example (region name), the results are not always desirable when an accessor is used to cross relationships. To get around this be careful to define `.Column.verbose_name`. .. _ordering-class-name: Changing class names for ordered column headers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When a column is ordered in an ascending state there needs to be a way to show it in the interface. django-tables2 does this by adding an `asc` class for ascending or a `desc` class for descending. It should also be known that any orderable column is added with an `orderable` class to the column header. Sometimes there may be a need to change these default classes. On the `attrs` attribute of the table, you can add a `th` key with the value of a dictionary. Within that `th` dictionary, you may add an `_ordering` key also with the value of a dictionary. The `_ordering` element is optional and all elements within it are optional. Inside you can have an `orderable` element, which will change the default `orderable` class name. You can also have `ascending` which will will change the default `asc` class name. And lastly, you can have `descending` which will change the default `desc` class name. Example:: class Table(tables.Table): Meta: attrs = { "th" : { "_ordering": { "orderable": "sortable", # Instead of `orderable` "ascending": "ascend", # Instead of `asc` "descending": "descend" # Instead of `desc` } } } It can also be specified at initialization using the `attrs` for both: table and column:: ATTRIBUTES = { "th" : { "_ordering": { "orderable": "sortable", # Instead of `orderable` "ascending": "ascend", # Instead of `asc` "descending": "descend" # Instead of `desc` } } } table = tables.Table(queryset, attrs=ATTRIBUTES) # or class Table(tables.Table): my_column = tables.Column(attrs=ATTRIBUTES) .. _column-footers: Adding column footers --------------------- By default, no footer will be rendered. If you want to add a footer, define a footer on at least one column. That will make the table render a footer on every view of the table. It is up to you to decide if that makes sense if your table is paginated. Pass `footer`-argument to the `~.Column` constructor. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The simplest case is just passing a `str` as the footer argument to a column:: country = tables.Column(footer="Total:") This will just render the string in the footer. If you need to do more complex things, like showing a sum or an average, you can pass a callable:: population = tables.Column( footer=lambda table: sum(x["population"] for x in table.data) ) You can expect `table`, `column` and `bound_column` as argument. Define `render_footer` on a custom column. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you need the same footer in multiple columns, you can create your own custom column. For example this column that renders the sum of the values in the column:: class SummingColumn(tables.Column): def render_footer(self, bound_column, table): return sum(bound_column.accessor.resolve(row) for row in table.data) Then use this column like so:: class Table(tables.Table): name = tables.Column() country = tables.Column(footer="Total:") population = SummingColumn() .. note:: If you are summing over tables with big datasets, chances are it is going to be slow. You should use some database aggregation function instead. django-tables2-2.7.5/docs/pages/custom-data.rst000066400000000000000000000146211473544236200213410ustar00rootroot00000000000000.. _accessors: Alternative column data ======================= Various options are available for changing the way the table is :term:`rendered `. Each approach has a different balance of ease-of-use and flexibility. Using `~.Accessors` ------------------- Each column has a 'key' that describes which value to pull from each record to populate the column's cells. By default, this key is just the name given to the column, but it can be changed to allow foreign key traversal or other complex cases. To reduce ambiguity, rather than calling it a 'key', we use the name 'accessor'. Accessors are just double-underscore separated paths that describe how an object should be traversed to reach a specific value, for example:: >>> from django_tables2 import A >>> data = {"abc": {"one": {"two": "three"}}} >>> A("abc__one__two").resolve(data) 'three' The separators ``__`` represent relationships, and are attempted in this order: 1. Dictionary lookup ``a[b]`` 2. Attribute lookup ``a.b`` 3. List index lookup ``a[int(b)]`` If the resulting value is callable, it is called and the return value is used. Moreover, you can use `accessor` with the table columns to access nested values. For example:: >>> data = [{"abc": {"one": {"two": "three"}}}, {"abc": {"one": {"two": "four"}}}] >>> class MyTable(tables.Table): ... abc = tables.Column(accessor="abc__one__two") >>> table = MyTable(data) >>> list(map(str, table.rows[1])) ['four'] .. _table.render_foo: `Table.render_foo` methods -------------------------- To change how a column is rendered, define a ``render_foo`` method on the table for example: `render_row_number()` for a column named `row_number`. This approach is suitable if you have a one-off change that you do not want to use in multiple tables or if you want to combine the data from two columns into one. Supported keyword arguments include: - ``record`` -- the entire record for the row from the :term:`table data` - ``value`` -- the value for the cell retrieved from the :term:`table data` - ``column`` -- the `.Column` object - ``bound_column`` -- the `.BoundColumn` object - ``bound_row`` -- the `.BoundRow` object - ``table`` -- alias for ``self`` This example shows how to render the row number in the first row:: >>> import django_tables2 as tables >>> import itertools >>> >>> class SimpleTable(tables.Table): ... row_number = tables.Column(empty_values=()) ... id = tables.Column() ... age = tables.Column() ... ... def __init__(self, *args, **kwargs): ... super().__init__(*args, **kwargs) ... self.counter = itertools.count() ... ... def render_row_number(self): ... return f"Row {next(self.counter)}" ... ... def render_id(self, value): ... return f"<{value}>" ... >>> table = SimpleTable([{"age": 31, "id": 10}, {"age": 34, "id": 11}]) >>> print(", ".join(map(str, table.rows[0]))) Row 0, <10>, 31 Python's `inspect.getargspec` is used to only pass the arguments declared by the function. This means it's not necessary to add a catch all (``**``) keyword argument. The `render_foo` method can also be used to combine data from two columns into one column. The following example shows how the the value for the `last_name` field is appended to the `name` field using the `render_name` function. Note that `value` is the value in the column and `record` is used to access the values in the `last_name` column:: # models.py class Customers(models.Model): name = models.CharField(max_length=50, null=False, blank=False) last_name = models.CharField(max_length=50, null=False, blank=False) description = models.TextField(blank=True) # tables.py from .models import Customers from django.utils.html import format_html class CustomerTable(tables.Table): name = tables.Column() description = tables.Column() def render_name(self, value, record): return format_html("{} {}", value, record.last_name) If you need to access logged-in user (or request in general) in your render methods, you can reach it through `self.request`:: def render_count(self, value): if self.request.user.is_authenticated(): return value else: return '---' .. important:: `render_foo` methods are *only* called if the value for a cell is determined to be not an :term:`empty value`. When a value is in `.Column.empty_values`, a default value is rendered instead (both `.Column.render` and ``Table.render_FOO`` are skipped). .. important:: `render_foo` methods determine what value is rendered, but which make sorting the column have unexpected results. In those cases, you might want to also define a :ref:`table.order_foo` method. .. _table.value_foo: `Table.value_foo` methods ------------------------- If you want to use `Table.as_values` to export your data, you might want to define a method ``value_foo``, which is analogous to ``render_foo``, but used to render the values rather than the HTML output. Please refer to `.Table.as_values` for an example. .. _subclassing-column: Subclassing `.Column` --------------------- Defining a column subclass allows functionality to be reused across tables. Columns have a `render` method that behaves the same as :ref:`table.render_foo` methods on tables:: >>> import django_tables2 as tables >>> >>> class UpperColumn(tables.Column): ... def render(self, value): ... return value.upper() ... >>> class Example(tables.Table): ... normal = tables.Column() ... upper = UpperColumn() ... >>> data = [{"normal": "Hi there!", ... "upper": "Hi there!"}] ... >>> table = Example(data) >>> # renders to something like this: '''
{snip}
{snip}' Have a look at each column's API reference to find which elements are supported. If you need to add some extra attributes to column's tags rendered in the footer, use key name ``tf``, as described in section on :ref:`css`. Callables passed in this dict will be called, with optional kwargs ``table``, ``bound_column`` ``record`` and ``value``, with the return value added. For example:: class Table(tables.Table): person = tables.Column(attrs={ "td": { "data-length": lambda value: len(value) } }) will render the ````'s in the tables ```` with a ``data-length`` attribute containing the number of characters in the value. .. note:: The keyword arguments ``record`` and ``value`` only make sense in the context of a row containing data. If you supply a callable with one of these keyword arguments, it will not be executed for the header and footer rows. If you also want to customize the attributes of those tags, you must define a callable with a catchall (``**kwargs``) argument:: def data_first_name(**kwargs): first_name = kwargs.get("value", None) if first_name is None: return "header" else: return first_name class Table(tables.Table): first_name = tables.Column(attrs={ "td": { 'data-first-name': data_first_name } }) This `attrs` can also be defined when subclassing a column, to allow better reuse:: class PersonColumn(tables.Column): attrs = { "td": { "data-first-name": lambda record: record.first_name "data-last-name": lambda record: record.last_name } } def render(self, record): return f"{record.first_name} {record.last_name}"" class Table(tables.Table): person = PersonColumn() is equivalent to the previous example. .. _row-attributes: Row attributes ~~~~~~~~~~~~~~ Row attributes can be specified using a dict defining the HTML attributes for the ``
NormalUpper
Hi there!HI THERE!
''' See :ref:`table.render_foo` for a list of arguments that can be accepted. For complicated columns, you may want to return HTML from the :meth:`~Column.render` method. Make sure to use Django's html formatting functions:: >>> from django.utils.html import format_html >>> >>> class ImageColumn(tables.Column): ... def render(self, value): ... return format_html('', value) ... django-tables2-2.7.5/docs/pages/custom-rendering.rst000066400000000000000000000105501473544236200224020ustar00rootroot00000000000000Customizing table style ======================= .. _css: CSS --- In order to use CSS to style a table, you'll probably want to add a ``class`` or ``id`` attribute to the ```` element. django-tables2 has a hook that allows arbitrary attributes to be added to the ``
`` tag. .. sourcecode:: python >>> import django_tables2 as tables >>> >>> class SimpleTable(tables.Table): ... id = tables.Column() ... age = tables.Column() ... ... class Meta: ... attrs = {"class": "mytable"} ... >>> table = SimpleTable() >>> # renders to something like this: '
...' By default, django-tables2 looks for the ``DJANGO_TABLES2_TABLE_ATTRS`` setting which allows you to define attributes globally for all tables. For example, to have a Bootstrap5 table with hoverable rows and a light table header define it as follows: .. sourcecode:: python DJANGO_TABLES2_TABLE_ATTRS = { 'class': 'table table-hover', 'thead': { 'class': 'table-light', }, } You can also specify ``attrs`` attribute when creating a column. ``attrs`` is a dictionary which contains attributes which by default get rendered on various tags involved with rendering a column. You can read more about them in :ref:`column-attributes`. django-tables2 supports three different dictionaries, this way you can give different attributes to column tags in table header (``th``), rows (``td``) or footer (``tf``). .. sourcecode:: python >>> import django_tables2 as tables >>> >>> class SimpleTable(tables.Table): ... id = tables.Column(attrs={"td": {"class": "my-class"}}) ... age = tables.Column(attrs={"tf": {"bgcolor": "red"}}) ... >>> table = SimpleTable() >>> # renders to something like this: '' >>> # and the footer will look like this: ' ... '' .. _available-templates: Available templates ------------------- We ship a couple of different templates: ========================================= ====================================================== Template name Description ========================================= ====================================================== django_tables2/table.html Basic table template (default). django_tables2/bootstrap.html Template using bootstrap 3 structure/classes django_tables2/bootstrap-responsive.html Same as bootstrap, but wrapped in ``.table-responsive`` django_tables2/bootstrap4.html Template using bootstrap 4 structure/classes django_tables2/bootstrap4-responsive.html Same as bootstrap4, but wrapped in ``.table-responsive`` django_tables2/bootstrap5.html Template using bootstrap 5 structure/classes django_tables2/bootstrap5-responsive.html Same as bootstrap5, but wrapped in ``.table-responsive`` django_tables2/semantic.html Template using semantic UI ========================================= ====================================================== By default, django-tables2 looks for the ``DJANGO_TABLES2_TEMPLATE`` setting which is ``django_tables2/table.html`` by default. If you use bootstrap 5 for your site, it makes sense to set the default to the bootstrap 5 template:: DJANGO_TABLES2_TEMPLATE = "django_tables2/bootstrap5.html" If you want to specify a custom template for selected tables in your project, you can set a ``template_name`` attribute to your custom ``Table.Meta`` class:: class PersonTable(tables.Table): class Meta: model = Person template_name = "django_tables2/semantic.html" You can also use the ``template_name`` argument to the ``Table`` constructor to override the template for a certain instance:: table = PersonTable(data, template_name="django_tables2/bootstrap-responsive.html") For none of the templates any CSS file is added to the HTML. You are responsible for including the relevant style sheets for a template. .. _custom-template: Custom Template --------------- And of course if you want full control over the way the table is rendered, ignore the built-in generation tools, and instead pass an instance of your `.Table` subclass into your own template, and render it yourself. You should use one of the provided templates as a basis. django-tables2-2.7.5/docs/pages/export.rst000066400000000000000000000133651473544236200204450ustar00rootroot00000000000000.. _export: Exporting table data ==================== .. versionadded:: 1.8.0 If you want to allow exporting the data present in your django-tables2 tables to various formats, you must install the `tablib `_ package:: pip install tablib .. note:: For all supported formats (xls, xlsx, etc.), you must install additional dependencies: `Installing tablib `_ Adding ability to export the table data to a class based views looks like this:: import django_tables2 as tables from django_tables2.export.views import ExportMixin from .models import Person from .tables import MyTable class TableView(ExportMixin, tables.SingleTableView): table_class = MyTable model = Person template_name = "django_tables2/bootstrap.html" Now, if you append ``_export=csv`` to the query string, the browser will download a csv file containing your data. Supported export formats are: csv, json, latex, ods, tsv, xls, xlsx, yaml To customize the name of the query parameter add an ``export_trigger_param`` attribute to your class. By default, the file will be named ``table.ext``, where ``ext`` is the requested export format extension. To customize this name, add a ``export_name`` attribute to your class. The correct extension will be appended automatically to this value. If you must use a function view, you might use something like this:: from django_tables2.config import RequestConfig from django_tables2.export.export import TableExport from .models import Person from .tables import MyTable def table_view(request): table = MyTable(Person.objects.all()) RequestConfig(request).configure(table) export_format = request.GET.get("_export", None) if TableExport.is_valid_format(export_format): exporter = TableExport(export_format, table) return exporter.response(f"table.{export_format}") return render(request, "table.html", { "table": table }) What exactly is exported? ------------------------- The export views use the `.Table.as_values()` method to get the data from the table. Because we often use HTML in our table cells, we need to specify something else for the export to make sense. If you use :ref:`table.render_foo`-methods to customize the output for a column, you should define a :ref:`table.value_foo`-method, returning the value you want to be exported. If you are creating your own custom columns, you should know that each column defines a `value()` method, which is used in `Table.as_values()`. By default, it just calls the `render()` method on that column. If your custom column produces HTML, you should override this method and return the actual value. Including and excluding columns ------------------------------- Some data might be rendered in the HTML version of the table using color coding, but need a different representation in an export format. Use columns with `visible=False` to include columns in the export, but not visible in the regular rendering:: class Table(tables.Table): name = columns.Column(exclude_from_export=True) first_name = columns.Column(visible=False) last_name = columns.Column(visible=False) Certain columns do not make sense while exporting data: you might show images or have a column with buttons you want to exclude from the export. You can define the columns you want to exclude in several ways:: # exclude a column while defining Columns on a table: class Table(tables.Table): name = columns.Column() buttons = columns.TemplateColumn(template_name="...", exclude_from_export=True) # exclude columns while creating the TableExport instance: exporter = TableExport("csv", table, exclude_columns=("image", "buttons")) If you use the ``django_tables2.export.ExportMixin``, add an ``exclude_columns`` attribute to your class:: class TableView(ExportMixin, tables.SingleTableView): table_class = MyTable model = Person template_name = 'django_tables2/bootstrap.html' exclude_columns = ("buttons", ) Tablib Dataset Configuration ---------------------------- django-tables2 uses ``tablib`` to export the table data. You may pass kwargs to the ``tablib.Dataset`` via the ``TableExport`` constructor ``dataset_kwargs`` parameter:: exporter = TableExport("xlsx", table, dataset_kwargs={"title": "My Custom Sheet Name"}) Default for ``tablib.Dataset.title`` is based on ``table.Meta.model._meta.verbose_name_plural.title()``, if available. If you use the ``django_tables2.export.ExportMixin``, simply add a ``dataset_kwargs`` attribute to your class:: class View(ExportMixin, tables.SingleTableView): table_class = MyTable model = Person dataset_kwargs = {"title": "People"} or override the ``ExportMixin.get_dataset_kwargs`` method to return the kwargs dictionary dynamically. Generating export URLs ---------------------- .. note:: To use ``export_url`` you must first load it in your template:: {% load export_url from django_tables2 %} You can use the ``export_url`` template tag included with django_tables2 to render a link to export the data as ``csv``:: {% export_url "csv" %} This will make sure any other query string parameters will be preserved, for example in combination when filtering table items. If you want to render more than one button, you could use something like this:: {% for format in view.export_formats %} download .{{ format }} {% endfor %} .. note:: This example assumes you define a list of possible export formats on your view instance in attribute ``export_formats``. django-tables2-2.7.5/docs/pages/faq.rst000066400000000000000000000066131473544236200176710ustar00rootroot00000000000000.. _faq: .. Any code examples in this file should have a corresponding test in tests/test_faq.py FAQ === Some frequently requested questions/examples. All examples assume you import django-tables2 like this:: import django_tables2 as tables How should I fix error messages about the request context processor? -------------------------------------------------------------------- The error message looks something like this:: Tag {% querystring %} requires django.template.context_processors.request to be in the template configuration in settings.TEMPLATES[]OPTIONS.context_processors) in order for the included template tags to function correctly. which should be pretty clear, but here is an example template configuration anyway:: TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": ["templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.contrib.auth.context_processors.auth", "django.template.context_processors.request", "django.template.context_processors.static", ], } } ] How to create a row counter? ---------------------------- You can use ``itertools.counter`` to add row count to a table. Note that in a paginated table, every page's counter will start at zero:: class CountryTable(tables.Table): counter = tables.TemplateColumn("{{ row_counter }}") How to add a footer containing a column total? ---------------------------------------------- Using the `footer`-argument to `~.Column`:: class CountryTable(tables.Table): population = tables.Column( footer=lambda table: sum(x["population"] for x in table.data) ) Or by creating a custom column:: class SummingColumn(tables.Column): def render_footer(self, bound_column, table): return sum(bound_column.accessor.resolve(row) for row in table.data) class Table(tables.Table): name = tables.Column(footer="Total:") population = SummingColumn() Documentation: :ref:`column-footers` .. note :: Your table template must include a block rendering the table footer! Can I use inheritance to build Tables that share features? ---------------------------------------------------------- Yes, like this:: class CountryTable(tables.Table): name = tables.Column() language = tables.Column() A `CountryTable` will show columns `name` and `language`:: class TouristCountryTable(CountryTable): tourist_info = tables.Column() A `TouristCountryTable` will show columns `name`, `language` and `tourist_info`. Overwriting a `Column` attribute from the base class with anything that is not a `Column` will result in removing that Column from the `Table`. For example:: class SimpleCountryTable(CountryTable): language = None A `SimpleCountryTable` will only show column `name`. How can I use with Jinja2 template? ----------------------------------- In Jinja2 templates, the ``{% render_table %}`` tag is not available, but you can still use *django-tables2* like this:: {{ table.as_html(request) }} where ``request`` need to be passed from view, or from *context processors* (which is supported by `django-jinja `_). django-tables2-2.7.5/docs/pages/filtering.rst000066400000000000000000000030341473544236200210770ustar00rootroot00000000000000.. _filtering: Filtering data in your table ============================ When presenting a large amount of data, filtering is often a necessity. Fortunately, filtering the data in your django-tables2 table is simple with `django-filter `_. The basis of a filtered table is a `SingleTableMixin` combined with a `FilterView` from django-filter:: from django_filters.views import FilterView from django_tables2.views import SingleTableMixin class FilteredPersonListView(SingleTableMixin, FilterView): table_class = PersonTable model = Person template_name = "template.html" filterset_class = PersonFilter The ``PersonFilter`` is defined the following way:: from django_filters import FilterSet from .models import Person class PersonFilter(FilterSet): class Meta: model = Person fields = {"name": ["exact", "contains"], "country": ["exact"]} The ``FilterSet`` is added to the template context in a ``filter`` variable by default. A basic template rendering the filter (using django-bootstrap3)[https://pypi.org/project/django-bootstrap3/] and table looks like this:: {% load render_table from django_tables2 %} {% load bootstrap3 %} {% if filter %}
{% bootstrap_form filter.form layout='inline' %} {% bootstrap_button 'filter' %} {% endif %} {% render_table table 'django_tables2/bootstrap.html' %} django-tables2-2.7.5/docs/pages/generic-mixins.rst000066400000000000000000000051741473544236200220440ustar00rootroot00000000000000Class Based Generic Mixins ========================== Django-tables2 comes with two class based view mixins: `.SingleTableMixin` and `.MultiTableMixin`. A single table using `.SingleTableMixin` ---------------------------------------- `.SingleTableMixin` makes it trivial to incorporate a table into a view or template. The following view parameters are supported: - ``table_class`` –- the table class to use, e.g. ``SimpleTable``, if not specified and ``model`` is provided, a default table will be created on-the-fly. - ``table_data`` (or ``get_table_data()``) -- the data used to populate the table - ``context_table_name`` -- the name of template variable containing the table object - ``table_pagination`` (or ``get_table_pagination``) -- pagination options to pass to `.RequestConfig`. Set ``table_pagination=False`` to disable pagination. - ``get_table_kwargs()`` allows the keyword arguments passed to the ``Table`` constructor. For example:: from django_tables2 import SingleTableView class Person(models.Model): first_name = models.CharField(max_length=200) last_name = models.CharField(max_length=200) class PersonTable(tables.Table): class Meta: model = Person class PersonList(SingleTableView): model = Person table_class = PersonTable The template could then be as simple as: .. sourcecode:: django {% load django_tables2 %} {% render_table table %} Such little code is possible due to the example above taking advantage of default values and `.SingleTableMixin`'s eagerness at finding data sources when one is not explicitly defined. .. note:: You don't have to base your view on `ListView`, you're able to mix `SingleTableMixin` directly. Multiple tables using `.MultiTableMixin` ---------------------------------------- If you need more than one table in a single view you can use `MultiTableMixin`. It manages multiple tables for you and takes care of adding the appropriate prefixes for them. Just define a list of tables in the tables attribute:: from django_tables2 import MultiTableMixin from django.views.generic.base import TemplateView class PersonTablesView(MultiTableMixin, TemplateView): template_name = "multiTable.html" tables = [ PersonTable(qs), PersonTable(qs, exclude=("country", )) ] table_pagination = { "per_page": 10 } In the template, you get a variable `tables`, which you can loop over like this: .. sourcecode:: django {% load django_tables2 %} {% for table in tables %} {% render_table table %} {% endfor %} django-tables2-2.7.5/docs/pages/glossary.rst000066400000000000000000000031241473544236200207570ustar00rootroot00000000000000Glossary ======== .. glossary:: accessor Refers to an `.Accessor` object column name The name given to a column. In the follow example, the *column name* is ``age``. .. sourcecode:: python class SimpleTable(tables.Table): age = tables.Column() empty value An empty value is synonymous with "no value". Columns have an ``empty_values`` attribute that contains values that are considered empty. It's a way to declare which values from the database correspond to *null*/*blank*/*missing* etc. order by alias A prefixed column name that describes how a column should impact the order of data within the table. This allows the implementation of how a column affects ordering to be abstracted, which is useful (e.g. in query strings). .. sourcecode:: python class ExampleTable(tables.Table): name = tables.Column(order_by=("first_name", "last_name")) In this example ``-name`` and ``name`` are valid order by aliases. In a query string you might then have ``?order=-name``. table The traditional concept of a table. i.e. a grid of rows and columns containing data. view A Django view. record A single Python object used as the data for a single row. render The act of serializing a `.Table` into HTML. template A Django template. table data An iterable of :term:`records ` that `.Table` uses to populate its rows. django-tables2-2.7.5/docs/pages/installation.rst000066400000000000000000000006041473544236200216150ustar00rootroot00000000000000Installation ============ Django-tables2 is `Available on pypi `_ and can be installed using pip:: pip install django-tables2 After installing, add ``'django_tables2'`` to ``INSTALLED_APPS`` and make sure that ``"django.template.context_processors.request"`` is added to the ``context_processors`` in your template setting ``OPTIONS``. django-tables2-2.7.5/docs/pages/internal.rst000066400000000000000000000032751473544236200207370ustar00rootroot00000000000000============= Internal APIs ============= The items documented here are internal and subject to change. `.BoundColumns` --------------- .. autoclass:: django_tables2.columns.BoundColumns :members: :private-members: :special-members: `.BoundColumn` -------------- .. autoclass:: django_tables2.columns.BoundColumn :members: :private-members: :special-members: `.BoundRows` ------------ .. autoclass:: django_tables2.rows.BoundRows :members: :private-members: :special-members: `.BoundRow` ----------- .. autoclass:: django_tables2.rows.BoundRow :members: :private-members: :special-members: `.BoundPinnedRow` ----------------- .. autoclass:: django_tables2.rows.BoundPinnedRow :members: :private-members: :special-members: `.TableData` ------------ .. autoclass:: django_tables2.tables.TableData :members: :private-members: :special-members: `.utils` -------- .. autoclass:: django_tables2.utils.Sequence :members: :private-members: :special-members: .. autoclass:: django_tables2.utils.OrderBy :members: :private-members: :special-members: .. autoclass:: django_tables2.utils.OrderByTuple :members: :private-members: :special-members: .. autoclass:: django_tables2.utils.Accessor :noindex: :members: :private-members: :special-members: .. autoclass:: django_tables2.utils.AttributeDict :noindex: :members: :private-members: :special-members: .. autofunction:: django_tables2.utils.signature :noindex: .. autofunction:: django_tables2.utils.call_with_appropriate :noindex: .. autofunction:: django_tables2.utils.computed_values :noindex: django-tables2-2.7.5/docs/pages/localization-control.rst000066400000000000000000000035421473544236200232660ustar00rootroot00000000000000.. _localization-control: Controlling localization ======================== Django-tables2 allows you to define which column of a table should or should not be localized. For example you may want to use this feature in following use cases: * You want to format some columns representing for example numeric values in the given locales even if you don't enable `USE_L10N` in your settings file. * You don't want to format primary key values in your table even if you enabled `USE_L10N` in your settings file. This control is done by using two filter functions in Django's `l10n` library named `localize` and `unlocalize`. Check out Django docs about `localization ` for more information about them. There are two ways of controlling localization in your columns. First one is setting the `~.Column.localize` attribute in your column definition to `True` or `False`. Like so:: class PersonTable(tables.Table): id = tables.Column(accessor="pk", localize=False) class Meta: model = Person .. note:: The default value of the `localize` attribute is `None` which means the formatting of columns is depending on the `USE_L10N` setting. The second way is to define a `~.Table.Meta.localize` and/or `~.Table.Meta.unlocalize` tuples in your tables Meta class (like with `~.Table.Meta.fields` or `~.Table.Meta.exclude`). You can do this like so:: class PersonTable(tables.Table): id = tables.Column(accessor='pk') value = tables.Column(accessor='some_numerical_field') class Meta: model = Person unlocalize = ("id", ) localize = ("value", ) If you define the same column in both `localize` and `unlocalize` then the value of this column will be 'unlocalized' which means that `unlocalize` has higher precedence. django-tables2-2.7.5/docs/pages/ordering.rst000066400000000000000000000112171473544236200207270ustar00rootroot00000000000000Alternative column ordering =========================== When using QuerySet data, one might want to show a computed value which is not in the database. In this case, attempting to order the column will cause an exception:: # models.py class Person(models.Model): first_name = models.CharField(max_length=200) family_name = models.CharField(max_length=200) @property def name(self): return f"{self.first_name} {self.family_name}" # tables.py class PersonTable(tables.Table): name = tables.Column() :: >>> table = PersonTable(Person.objects.all()) >>> table.order_by = "name" >>> >>> # will result in: FieldError: Cannot resolve keyword 'name' into field. Choices are: first_name, family_name To prevent this, django-tables2 allows two ways to specify custom ordering: accessors and :meth:`~.order_FOO` methods. .. _order-by-accessors: Ordering by accessors --------------------- You can supply an ``order_by`` argument containing a name or a tuple of the names of the columns the database should use to sort it:: class PersonTable(tables.Table): name = tables.Column(order_by=("first_name", "family_name")) `~.Accessor` syntax can be used as well, as long as they point to a model field. If ordering does not make sense for a particular column, it can be disabled via the ``orderable`` argument:: class SimpleTable(tables.Table): name = tables.Column() actions = tables.Column(orderable=False) .. _table.order_foo: :meth:`table.order_FOO` methods -------------------------------- Another solution for alternative ordering is being able to chain functions on to the original QuerySet. This method allows more complex functionality giving the ability to use all of Django's QuerySet API. Adding a `Table.order_FOO` method (where `FOO` is the name of the column), gives you the ability to chain to, or modify, the original QuerySet when that column is selected to be ordered. The method takes two arguments: `QuerySet`, and `is_descending`. The return must be a tuple of two elements. The first being the QuerySet and the second being a boolean; note that modified QuerySet will only be used if the boolean is `True`. For example, let's say instead of ordering alphabetically, ordering by amount of characters in the first_name is desired. The implementation would look like this: :: # tables.py from django.db.models.functions import Length class PersonTable(tables.Table): name = tables.Column() def order_name(self, queryset, is_descending): queryset = queryset.annotate( length=Length("first_name") ).order_by(("-" if is_descending else "") + "length") return (queryset, True) As another example, presume the situation calls for being able to order by a mathematical expression. In this scenario, the table needs to be able to be ordered by the sum of both the shirts and the pants. The custom column will have its value rendered using :ref:`table.render_FOO`. This can be achieved like this:: # models.py class Person(models.Model): first_name = models.CharField(max_length=200) family_name = models.CharField(max_length=200) shirts = models.IntegerField() pants = models.IntegerField() # tables.py from django.db.models import F class PersonTable(tables.Table): clothing = tables.Column() class Meta: model = Person def render_clothing(self, record): return str(record.shirts + record.pants) def order_clothing(self, queryset, is_descending): queryset = queryset.annotate( amount=F("shirts") + F("pants") ).order_by(("-" if is_descending else "") + "amount") return (queryset, True) Using :meth:`Column.order` on custom columns -------------------------------------------- If you created a custom column, which also requires custom ordering like explained above, you can add the body of your ``order_foo`` method to the order method on your custom column, to allow easier reuse. For example, the `PersonTable` from above could also be defined like this:: class ClothingColumn(tables.Column): def render(self, record): return str(record.shirts + record.pants) def order(self, queryset, is_descending): queryset = queryset.annotate( amount=F("shirts") + F("pants") ).order_by(("-" if is_descending else "") + "amount") return (queryset, True) class PersonTable(tables.Table): clothing = ClothingColumn() class Meta: model = Person django-tables2-2.7.5/docs/pages/pagination.rst000066400000000000000000000033551473544236200212530ustar00rootroot00000000000000.. _pagination: Pagination ========== Pagination is easy, just call :meth:`.Table.paginate` and pass in the current page number:: def people_listing(request): table = PeopleTable(Person.objects.all()) table.paginate(page=request.GET.get("page", 1), per_page=25) return render(request, "people_listing.html", {"table": table}) If you are using `.RequestConfig`, pass pagination options to the constructor:: def people_listing(request): table = PeopleTable(Person.objects.all()) RequestConfig(request, paginate={"per_page": 25}).configure(table) return render(request, "people_listing.html", {"table": table}) If you are using `SingleTableView`, the table will get paginated by default:: class PeopleListView(SingleTableView): table_class = PeopleTable Disabling pagination ~~~~~~~~~~~~~~~~~~~~ If you are using `SingleTableView` and want to disable the default behavior, set `SingleTableView.table_pagination = False` Lazy pagination ~~~~~~~~~~~~~~~ The default `~django.core.paginators.Paginator` wants to count the number of items, which might be an expensive operation for large QuerySets. In those cases, you can use `.LazyPaginator`, which does not perform a count, but also does not know what the total amount of pages will be, until you've hit the last page. The `.LazyPaginator` does this by fetching `n + 1` records where the number of records per page is `n`. If it receives `n` or less records, it knows it is on the last page, preventing rendering of the 'next' button and further "..." ellipsis. Usage with `SingleTableView`:: class UserListView(SingleTableView): table_class = UserTable table_data = User.objects.all() paginator_class = LazyPaginator django-tables2-2.7.5/docs/pages/pinned-rows.rst000066400000000000000000000031731473544236200213650ustar00rootroot00000000000000.. _pinned_rows: Pinned rows =========== This feature allows one to pin certain rows to the top or bottom of your table. Provide an implementation for one or two of these methods, returning an iterable (QuerySet, list of dicts, list objects) representing the pinned data: * `get_top_pinned_data(self)` - Displays the returned rows on top. * `get_bottom_pinned_data(self)` - Displays the returned rows at the bottom. Pinned rows are not affected by sorting and pagination, they will be present on every page of the table, regardless of ordering. Values will be rendered just like you are used to for normal rows. Example:: class Table(tables.Table): first_name = tables.Column() last_name = tables.Column() def get_top_pinned_data(self): return [ {"first_name": "Janet", "last_name": "Crossen"}, # key "last_name" is None here, so the default value will be rendered. {"first_name": "Trine", "last_name": None} ] .. note:: If you need very different rendering for the bottom pinned rows, chances are you actually want to use column footers: :ref:`column-footers` .. _pinned_row_attributes: Attributes for pinned rows ~~~~~~~~~~~~~~~~~~~~~~~~~~ You can override the attributes used to render the ``
`` tag of the pinned rows using: ``pinned_row_attrs``. This works exactly like :ref:`row-attributes`. .. note:: By default the ```` tags for pinned rows will get the attribute ``class="pinned-row"``. .. sourcecode:: django [...] [...] django-tables2-2.7.5/docs/pages/query-string-fields.rst000066400000000000000000000022451473544236200230340ustar00rootroot00000000000000.. _query-string-fields: Query string fields =================== Tables pass data via the query string to indicate ordering and pagination preferences. The names of the query string variables are configurable via the options: - ``order_by_field`` -- default: ``'sort'`` - ``page_field`` -- default: ``"page"`` - ``per_page_field`` -- default: ``"per_page"``, **note:** this field currently is not used by ``{% render_table %}`` Each of these can be specified in three places: - ``Table.Meta.foo`` - ``Table(..., foo=...)`` - ``Table(...).foo = ...`` If you are using multiple tables on a single page, you will want to prefix these fields with a table-specific name, in order to prevent links on one table interfere with those on another table:: def people_listing(request): config = RequestConfig(request) table1 = PeopleTable(Person.objects.all(), prefix="1-") # prefix specified table2 = PeopleTable(Person.objects.all(), prefix="2-") # prefix specified config.configure(table1) config.configure(table2) return render(request, 'people_listing.html', { 'table1': table1, 'table2': table2 }) django-tables2-2.7.5/docs/pages/reference.rst000066400000000000000000000001331473544236200210470ustar00rootroot00000000000000API === .. toctree:: builtin-columns template-tags api-reference internal django-tables2-2.7.5/docs/pages/swapping-columns.rst000066400000000000000000000014101473544236200224160ustar00rootroot00000000000000.. _swapping-columns: Swapping the position of columns ================================ By default columns are positioned in the same order as they are declared, however when mixing auto-generated columns (via `Table.Meta.model`) with manually declared columns, the column sequence becomes ambiguous. To resolve the ambiguity, columns sequence can be declared via the `.Table.Meta.sequence` option:: class PersonTable(tables.Table): selection = tables.CheckBoxColumn(accessor="pk", orderable=False) class Meta: model = Person sequence = ('selection', 'first_name', 'last_name') The special value ``'...'`` can be used to indicate that any omitted columns should inserted at that location. As such it can be used at most once. django-tables2-2.7.5/docs/pages/table-data.rst000066400000000000000000000064201473544236200211140ustar00rootroot00000000000000.. _table_data: Populating a table with data ============================ Tables can be created from a range of input data structures. If you have seen the tutorial you will have seen a ``QuerySet`` being used, however any iterable that supports :func:`len` and contains items that exposes key-based access to column values is fine. List of dicts ------------- In an example we will demonstrate using list of dicts. When defining a table it is necessary to declare each column:: import django_tables2 as tables data = [ {"name": "Bradley"}, {"name": "Stevie"}, ] class NameTable(tables.Table): name = tables.Column() table = NameTable(data) QuerySets --------- If your build uses tables to display `~django.db.models.query.QuerySet` data, rather than defining each column manually in the table, the `.Table.Meta.model` option allows tables to be dynamically created based on a model:: # models.py from django.contrib.auth import get_user_model from django.db import models class Person(models.Model): first_name = models.CharField(max_length=200) last_name = models.CharField(max_length=200) user = models.ForeignKey(get_user_model(), null=True, on_delete=models.CASCADE) birth_date = models.DateField() # tables.py import django_tables2 as tables class PersonTable(tables.Table): class Meta: model = Person # views.py def person_list(request): table = PersonTable(Person.objects.all()) return render(request, "person_list.html", { "table": table }) This has a number of benefits: - Less repetition - Column headers are defined using the field's `~.models.Field.verbose_name` - Specialized columns are used where possible (e.g. `.DateColumn` for a `~.models.DateField`) When using this approach, the following options might be useful to customize what fields to show or hide: - `~.Table.Meta.sequence` -- reorder columns (if used alone, columns that are not specified are still rendered in the table after the specified columns) - `~.Table.Meta.fields` -- specify model fields to *include* - `~.Table.Meta.exclude` -- specify model fields to *exclude* These options can be specified as tuples. In this example we will demonstrate how this can be done:: # tables.py class PersonTable(tables.Table): class Meta: model = Person sequence = ("last_name", "first_name", "birth_date", ) exclude = ("user", ) With these options specified, the columns would be show according to the order defined in the `~.Table.Meta.sequence`, while the ``user`` column will be hidden. Performance ----------- Django-tables tries to be efficient in displaying big datasets. It tries to avoid converting the `~django.db.models.query.QuerySet` instances to lists by using SQL to slice the data and should be able to handle datasets with 100k records without a problem. However, when performance is degrading, these tips might help: 1. For large datasets, try to use `.LazyPaginator`. 2. Try to strip the table of customizations and check if performance improves. If so, re-add them one by one, checking for performance after each step. This should help to narrow down the source of your performance problems. django-tables2-2.7.5/docs/pages/table-mixins.rst000066400000000000000000000016011473544236200215060ustar00rootroot00000000000000Table Mixins ============ It's possible to create a mixin for a table that overrides something, however unless it itself is a subclass of `.Table` class variable instances of `.Column` will **not** be added to the class which is using the mixin. Example:: >>> class UselessMixin: ... extra = tables.Column() ... >>> class TestTable(UselessMixin, tables.Table): ... name = tables.Column() ... >>> TestTable.base_columns.keys() ["name"] To have a mixin contribute a column, it needs to be a subclass of `~django_tables2.tables.Table`. With this in mind the previous example *should* have been written as follows:: >>> class UsefulMixin(tables.Table): ... extra = tables.Column() ... >>> class TestTable(UsefulMixin, tables.Table): ... name = tables.Column() ... >>> TestTable.base_columns.keys() ["extra", "name"] django-tables2-2.7.5/docs/pages/template-tags.rst000066400000000000000000000040411473544236200216620ustar00rootroot00000000000000.. _template_tags: Template tags ============= .. _template-tags.render_table: render_table ------------ Renders a `~django_tables2.tables.Table` object to HTML and enables as many features in the output as possible. .. sourcecode:: django {% load django_tables2 %} {% render_table table %} {# Alternatively a specific template can be used #} {% render_table table "path/to/custom_table_template.html" %} If the second argument (template path) is given, the template will be rendered with a `.RequestContext` and the table will be in the variable ``table``. .. note:: This tag temporarily modifies the `.Table` object during rendering. A ``context`` attribute is added to the table, providing columns with access to the current context for their own rendering (e.g. `.TemplateColumn`). This tag requires that the template in which it's rendered contains the `~.http.HttpRequest` inside a ``request`` variable. This can be achieved by ensuring the ``TEMPLATES[]['OPTIONS']['context_processors']`` setting contains ``django.template.context_processors.request``. Please refer to the Django documentation for the TEMPLATES-setting_. .. _TEMPLATES-setting: https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-TEMPLATES .. _template-tags.querystring: ``querystring`` --------------- A utility that allows you to update a portion of the query-string without overwriting the entire thing. Let's assume we have the query string ``?search=pirates&sort=name&page=5`` and we want to update the ``sort`` parameter: .. sourcecode:: django {% querystring "sort"="dob" %} # ?search=pirates&sort=dob&page=5 {% querystring "sort"="" %} # ?search=pirates&page=5 {% querystring "sort"="" "search"="" %} # ?page=5 {% with "search" as key %} # supports variables as keys {% querystring key="robots" %} # ?search=robots&page=5 {% endwith %} This tag requires the ``django.template.context_processors.request`` context processor, see :ref:`template-tags.render_table`. django-tables2-2.7.5/docs/pages/tutorial.rst000066400000000000000000000103011473544236200207520ustar00rootroot00000000000000Tutorial ~~~~~~~~ This is a step-by-step guide to learn how to install and use django-tables2 using Django 2.0 or later. 1. ``pip install django-tables2`` 2. Start a new Django app using `python manage.py startapp tutorial` 3. Add both ``"django_tables2"`` and ``"tutorial"`` to your ``INSTALLED_APPS`` setting in ``settings.py``. Now, add a model to your ``tutorial/models.py``:: # tutorial/models.py class Person(models.Model): name = models.CharField(max_length=100, verbose_name="full name") Create the database tables for the newly added model:: $ python manage.py makemigrations tutorial $ python manage.py migrate tutorial Add some data so you have something to display in the table:: $ python manage.py shell >>> from tutorial.models import Person >>> Person.objects.bulk_create([Person(name="Jieter"), Person(name="Bradley")]) [, ] Now use a generic ``ListView`` to pass a ``Person`` QuerySet into a template. Note that the context name used by `ListView` is `object_list` by default:: # tutorial/views.py from django.views.generic import ListView from .models import Person class PersonListView(ListView): model = Person template_name = 'tutorial/people.html' Add the view to your ``urls.py``:: # urls.py from django.urls import path from django.contrib import admin from tutorial.views import PersonListView urlpatterns = [ path("admin/", admin.site.urls), path("people/", PersonListView.as_view()) ] Finally, create the template:: {# tutorial/templates/tutorial/people.html #} {% load render_table from django_tables2 %} List of persons {% render_table object_list %} You should be able to load the page in the browser (http://localhost:8000/people/ by default), you should see: .. figure:: /_static/tutorial.png :align: center :alt: An example table rendered using django-tables2 This view supports pagination and ordering by default. While simple, passing a QuerySet directly to ``{% render_table %}`` does not allow for any customization. For that, you must define a custom `.Table` class:: # tutorial/tables.py import django_tables2 as tables from .models import Person class PersonTable(tables.Table): class Meta: model = Person template_name = "django_tables2/bootstrap.html" fields = ("name", ) You will then need to instantiate and configure the table in the view, before adding it to the context:: # tutorial/views.py from django_tables2 import SingleTableView from .models import Person from .tables import PersonTable class PersonListView(SingleTableView): model = Person table_class = PersonTable template_name = 'tutorial/people.html' Rather than passing a QuerySet to ``{% render_table %}``, instead pass the table instance:: {# tutorial/templates/tutorial/people.html #} {% load render_table from django_tables2 %} List of persons {% render_table table %} This results in a table rendered with the bootstrap3 style sheet: .. figure:: /_static/tutorial-bootstrap.png :align: center :alt: An example table rendered using django-tables2 with the bootstrap template At this point you have only changed the columns rendered in the table and the template. There are several topic you can read into to further customize the table: - Table data - :ref:`Populating the table with data `, - :ref:`Filtering table data ` - Customizing the rendered table - :ref:`Headers and footers ` - :ref:`pinned_rows` - :ref:`api-public` If you think you don't have a lot customization to do and don't want to make a full class declaration use ``django_tables2.tables.table_factory``. django-tables2-2.7.5/docs/pages/upgrade-changelog.rst000066400000000000000000000003701473544236200224700ustar00rootroot00000000000000Upgrading and change log ======================== Recent versions of django-tables2 have a corresponding git tag for each version released to `pypi `_. .. toctree:: :maxdepth: 1 CHANGELOG.md django-tables2-2.7.5/docs/requirements.txt000066400000000000000000000002151473544236200205450ustar00rootroot00000000000000-r ../requirements/common.pip Sphinx==7.2.6 sphinx_rtd_theme==1.3.0 django sphinxcontrib-spelling==8.0.0 pyenchant==3.2.2 myst-parser==2.0.0 django-tables2-2.7.5/docs/spelling_wordlist.txt000066400000000000000000000012671473544236200215760ustar00rootroot00000000000000accessor accessors app arg args attrs backend blocktrans boolean brianmay callables cardinality checkbox checkboxes computable configurator css csv customizations dataset datasets dicts distutils django django_tables2 django-tables2 docstring dramon fallback filename getter github html hyperlink hyperlinked hyperlinks instantiation iterable jinja koledennix kwarg kwargs linkification lookup lookups mixin mixins ods orderable paginator paginators py pylint pypi pytest QuerySet QuerySets readthedocs refactor rst Sapovits screenshots sortable sortability str subclasses subclassing th truthy tsv tuple tuples unicode unlocalize unlocalized uppercased viewname webkit whitespace xls xlsx yaml django-tables2-2.7.5/example/000077500000000000000000000000001473544236200157665ustar00rootroot00000000000000django-tables2-2.7.5/example/README.md000066400000000000000000000006361473544236200172520ustar00rootroot00000000000000# Django-tables2 example project This example project only supports the latest version of Django. # To get it up and running: ``` git clone https://github.com/jieter/django-tables2.git cd django-tables2/example pip install -r requirements.txt python manage.py migrate python manage.py loaddata app/fixtures/initial_data.json python manage.py runserver ``` Server should be live at http://127.0.0.1:8000/ now. django-tables2-2.7.5/example/__init__.py000066400000000000000000000000001473544236200200650ustar00rootroot00000000000000django-tables2-2.7.5/example/app/000077500000000000000000000000001473544236200165465ustar00rootroot00000000000000django-tables2-2.7.5/example/app/__init__.py000066400000000000000000000000001473544236200206450ustar00rootroot00000000000000django-tables2-2.7.5/example/app/admin.py000066400000000000000000000003541473544236200202120ustar00rootroot00000000000000from django.contrib import admin from .models import Continent, Country @admin.register(Country) class CountryAdmin(admin.ModelAdmin): list_per_page = 20 list_display = ("name", "continent") admin.site.register(Continent) django-tables2-2.7.5/example/app/data.py000066400000000000000000000015521473544236200200340ustar00rootroot00000000000000COUNTRIES = """Aruba;104822 Afghanistan;34656032 Angola;28813463 Albania;2876101 Andorra;77281 Arab World;406452690 United Arab Emirates;9269612 Argentina;43847430 Armenia;2924816 American Samoa;55599 Antigua and Barbuda;100963 Australia;24127159 Austria;8747358 Azerbaijan;9762274 Burundi;10524117 Belgium;11348159 Benin;10872298 Burkina Faso;18646433 Bangladesh;162951560 Bulgaria;7127822 Bahrain;1425171 Bahamas, The;391232 Bosnia and Herzegovina;3516816 Belarus;9507120 Belize;366954 Bermuda;65331 Bolivia;10887882 Brazil;207652865 Barbados;284996 Brunei Darussalam;423196 Bhutan;797765 Botswana;2250260 Central African Republic;4594621 Canada;36286425 Switzerland;8372098 Channel Islands;164541 Chile;17909754 China;1378665000 Cote d'Ivoire;23695919 Cameroon;23439189 Congo, Dem. Rep.;78736153 Congo, Rep.;5125821 Colombia;48653419 Comoros;795601 Cabo Verde;539560 """ django-tables2-2.7.5/example/app/filters.py000066400000000000000000000003201473544236200205630ustar00rootroot00000000000000from django_filters import FilterSet from .models import Person class PersonFilter(FilterSet): class Meta: model = Person fields = {"name": ["exact", "contains"], "country": ["exact"]} django-tables2-2.7.5/example/app/fixtures/000077500000000000000000000000001473544236200204175ustar00rootroot00000000000000django-tables2-2.7.5/example/app/fixtures/initial_data.json000066400000000000000000000014511473544236200237350ustar00rootroot00000000000000[ { "pk": 1, "model": "app.country", "fields": { "tz": "Australia/Brisbane", "name": "Australia", "visits": 2, "population": 20000000, "flag": "country/flags/australia.svg" } }, { "pk": 2, "model": "app.country", "fields": { "tz": "NZST", "name": "New Zealand", "visits": 1, "population": 12000000, "flag": "country/flags/new_zealand.svg" } }, { "pk": 4, "model": "app.country", "fields": { "tz": "UTC\u22123.5", "name": "Canada", "visits": 1, "population": 34447000, "flag": "country/flags/canada.svg" } } ] django-tables2-2.7.5/example/app/migrations/000077500000000000000000000000001473544236200207225ustar00rootroot00000000000000django-tables2-2.7.5/example/app/migrations/0001_initial.py000066400000000000000000000033561473544236200233740ustar00rootroot00000000000000# Generated by Django 1.11.5 on 2017-09-22 13:23 from django.db import migrations, models import django.db.models.deletion class Migration(migrations.Migration): initial = True dependencies = [] operations = [ migrations.CreateModel( name="Country", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID" ), ), ("name", models.CharField(max_length=100)), ("population", models.PositiveIntegerField(verbose_name="population")), ("tz", models.CharField(max_length=50)), ("visits", models.PositiveIntegerField()), ("commonwealth", models.NullBooleanField()), ("flag", models.FileField(upload_to="country/flags/")), ], options={"verbose_name_plural": "countries"}, ), migrations.CreateModel( name="Person", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID" ), ), ("name", models.CharField(max_length=200, verbose_name="full name")), ("friendly", models.BooleanField(default=True)), ( "country", models.ForeignKey( null=True, on_delete=django.db.models.deletion.CASCADE, to="app.Country" ), ), ], options={"verbose_name_plural": "people"}, ), ] django-tables2-2.7.5/example/app/migrations/0002_auto_20180416_0959.py000066400000000000000000000015471473544236200243670ustar00rootroot00000000000000# Generated by Django 2.0.1 on 2018-04-16 09:59 from django.db import migrations, models import django.db.models.deletion class Migration(migrations.Migration): dependencies = [("app", "0001_initial")] operations = [ migrations.CreateModel( name="Continent", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID" ), ), ("name", models.CharField(max_length=100)), ], ), migrations.AddField( model_name="country", name="continent", field=models.ForeignKey( null=True, on_delete=django.db.models.deletion.CASCADE, to="app.Continent" ), ), ] django-tables2-2.7.5/example/app/migrations/0003_auto_20180416_1020.py000066400000000000000000000010171473544236200243340ustar00rootroot00000000000000# Generated by Django 2.0.1 on 2018-04-16 10:20 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [("app", "0002_auto_20180416_0959")] operations = [ migrations.AlterField( model_name="country", name="flag", field=models.FileField(blank=True, upload_to="country/flags/"), ), migrations.AlterField( model_name="country", name="tz", field=models.CharField(blank=True, max_length=50) ), ] django-tables2-2.7.5/example/app/migrations/0004_auto_fix_deprecation_warnings.py000066400000000000000000000021271473544236200300440ustar00rootroot00000000000000# Generated by Django 3.2.6 on 2021-08-09 08:50 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("app", "0003_auto_20180416_1020"), ] operations = [ migrations.AlterField( model_name="continent", name="id", field=models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID" ), ), migrations.AlterField( model_name="country", name="commonwealth", field=models.BooleanField(null=True), ), migrations.AlterField( model_name="country", name="id", field=models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID" ), ), migrations.AlterField( model_name="person", name="id", field=models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID" ), ), ] django-tables2-2.7.5/example/app/migrations/__init__.py000066400000000000000000000000001473544236200230210ustar00rootroot00000000000000django-tables2-2.7.5/example/app/models.py000066400000000000000000000026661473544236200204150ustar00rootroot00000000000000from django.db import models from django.urls import reverse from django.utils.translation import gettext_lazy as _ class Continent(models.Model): name = models.CharField(max_length=100) def __str__(self): return self.name class Country(models.Model): """ Represents a geographical Country """ name = models.CharField(max_length=100) population = models.PositiveIntegerField(verbose_name=_("population")) tz = models.CharField(max_length=50, blank=True) visits = models.PositiveIntegerField() commonwealth = models.BooleanField(null=True) flag = models.FileField(upload_to="country/flags/", blank=True) continent = models.ForeignKey(Continent, null=True, on_delete=models.CASCADE) class Meta: verbose_name_plural = _("countries") def __str__(self): return self.name def get_absolute_url(self): return reverse("country_detail", args=(self.pk,)) @property def summary(self): return f"{self.name} (pop. {self.population})" class Person(models.Model): name = models.CharField(max_length=200, verbose_name="full name") friendly = models.BooleanField(default=True) country = models.ForeignKey(Country, null=True, on_delete=models.CASCADE) class Meta: verbose_name_plural = "people" def __str__(self): return self.name def get_absolute_url(self): return reverse("person_detail", args=(self.pk,)) django-tables2-2.7.5/example/app/tables.py000066400000000000000000000045311473544236200203750ustar00rootroot00000000000000import django_tables2 as tables from .models import Country, Person class CountryTable(tables.Table): name = tables.Column() population = tables.Column() tz = tables.Column(verbose_name="time zone") visits = tables.Column() summary = tables.Column(order_by=("name", "population")) class Meta: model = Country class ThemedCountryTable(CountryTable): class Meta: attrs = {"class": "paleblue"} class CheckboxTable(tables.Table): select = tables.CheckBoxColumn(empty_values=(), footer="") population = tables.Column(attrs={"cell": {"class": "population"}}) class Meta: model = Country template_name = "django_tables2/bootstrap.html" fields = ("select", "name", "population") class BootstrapTable(tables.Table): class Meta: model = Person template_name = "django_tables2/bootstrap.html" fields = ("id", "country", "country__continent__name") linkify = ("country", "country__continent__name") class BootstrapTablePinnedRows(BootstrapTable): class Meta(BootstrapTable.Meta): pinned_row_attrs = {"class": "info"} def get_top_pinned_data(self): return [ { "name": "Most used country: ", "country": Country.objects.filter(name="Cameroon").first(), } ] class Bootstrap4Table(tables.Table): country = tables.Column(linkify=True) continent = tables.Column(accessor="country__continent", linkify=True) class Meta: model = Person template_name = "django_tables2/bootstrap4.html" attrs = {"class": "table table-hover"} exclude = ("friendly",) class Bootstrap5Table(tables.Table): country = tables.Column(linkify=True) continent = tables.Column(accessor="country__continent", linkify=True) class Meta: model = Person template_name = "django_tables2/bootstrap5.html" exclude = ("friendly",) class SemanticTable(tables.Table): country = tables.RelatedLinkColumn() class Meta: model = Person template_name = "django_tables2/semantic.html" exclude = ("friendly",) class PersonTable(tables.Table): id = tables.Column(linkify=True) country = tables.Column(linkify=True) class Meta: model = Person template_name = "django_tables2/bootstrap.html" django-tables2-2.7.5/example/app/views.py000066400000000000000000000141051473544236200202560ustar00rootroot00000000000000from random import choice from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.utils.lorem_ipsum import words from django.views.generic.base import TemplateView from django_filters.views import FilterView from django_tables2 import MultiTableMixin, RequestConfig, SingleTableMixin, SingleTableView from django_tables2.export.views import ExportMixin from .data import COUNTRIES from .filters import PersonFilter from .models import Country, Person from .tables import ( Bootstrap4Table, Bootstrap5Table, BootstrapTable, BootstrapTablePinnedRows, CheckboxTable, CountryTable, PersonTable, SemanticTable, ThemedCountryTable, ) def create_fake_data(): # create some fake data to make sure we need to paginate if Country.objects.all().count() < 50: for country in COUNTRIES.splitlines(): name, population = country.split(";") Country.objects.create(name=name, visits=0, population=int(population)) if Person.objects.all().count() < 500: countries = list(Country.objects.all()) + [None] Person.objects.bulk_create( [Person(name=words(3, common=False), country=choice(countries)) for i in range(50)] ) def index(request): create_fake_data() table = PersonTable(Person.objects.all()) RequestConfig(request, paginate={"per_page": 5}).configure(table) return render( request, "index.html", { "table": table, "urls": ( (reverse("tutorial"), "Tutorial"), (reverse("multiple"), "Multiple tables"), (reverse("filtertableview"), "Filtered tables (with export)"), (reverse("singletableview"), "Using SingleTableMixin"), (reverse("multitableview"), "Using MultiTableMixin"), ( reverse("template_example", args=["bootstrap3"]), "template: Bootstrap 3 (bootstrap.html)", ), ( reverse("template_example", args=["bootstrap4"]), "template: Bootstrap 4 (bootstrap4.html)", ), ( reverse("template_example", args=["bootstrap5"]), "template: Bootstrap 5 (bootstrap5.html)", ), ( reverse("template_example", args=["semantic"]), "template: Semantic UI (semantic.html)", ), ), }, ) def multiple(request): qs = Country.objects.all() example1 = CountryTable(qs, prefix="1-") RequestConfig(request, paginate=False).configure(example1) example2 = CountryTable(qs, prefix="2-") RequestConfig(request, paginate={"per_page": 2}).configure(example2) example3 = ThemedCountryTable(qs, prefix="3-") RequestConfig(request, paginate={"per_page": 3}).configure(example3) example4 = ThemedCountryTable(qs, prefix="4-") RequestConfig(request, paginate={"per_page": 3}).configure(example4) example5 = ThemedCountryTable(qs, prefix="5-") example5.template = "extended_table.html" RequestConfig(request, paginate={"per_page": 3}).configure(example5) return render( request, "multiple.html", { "example1": example1, "example2": example2, "example3": example3, "example4": example4, "example5": example5, }, ) def checkbox(request): create_fake_data() table = CheckboxTable(Country.objects.all(), order_by="name") RequestConfig(request, paginate={"per_page": 15}).configure(table) return render(request, "checkbox_example.html", {"table": table}) def template_example(request, version): """Demonstrate the use of the bootstrap template""" versions = { "bootstrap3": (BootstrapTable, "bootstrap_template.html"), "bootstrap4": (Bootstrap4Table, "bootstrap4_template.html"), "bootstrap5": (Bootstrap5Table, "bootstrap5_template.html"), "semantic": (SemanticTable, "semantic_template.html"), } table_class, template_name = versions[version] create_fake_data() table = table_class(Person.objects.all().select_related("country"), order_by="-name") RequestConfig(request, paginate={"per_page": 10}).configure(table) return render(request, template_name, {"table": table}) class ClassBased(SingleTableView): table_class = ThemedCountryTable queryset = Country.objects.all() template_name = "class_based.html" class MultipleTables(MultiTableMixin, TemplateView): template_name = "multiTable.html" table_pagination = {"per_page": 10} def get_tables(self): qs = Person.objects.all() return [ PersonTable(qs), PersonTable(qs, exclude=("country",)), BootstrapTablePinnedRows(qs), ] def tutorial(request): table = PersonTable( Person.objects.all(), attrs={"class": "paleblue"}, template_name="django_tables2/table.html" ) RequestConfig(request, paginate={"per_page": 10}).configure(table) return render(request, "tutorial.html", {"table": table}) class FilteredPersonListView(ExportMixin, SingleTableMixin, FilterView): table_class = PersonTable model = Person template_name = "bootstrap_template.html" filterset_class = PersonFilter export_formats = ("csv", "xls") def get_queryset(self): return super().get_queryset().select_related("country") def get_table_kwargs(self): return {"template_name": "django_tables2/bootstrap.html"} def country_detail(request, pk): country = get_object_or_404(Country, pk=pk) # hide the country column, as it is not very interesting for a list of persons for a country. table = PersonTable(country.person_set.all(), extra_columns=(("country", None),)) return render(request, "country_detail.html", {"country": country, "table": table}) def person_detail(request, pk): person = get_object_or_404(Person, pk=pk) return render(request, "person_detail.html", {"person": person}) django-tables2-2.7.5/example/manage.py000077500000000000000000000003631473544236200175750ustar00rootroot00000000000000#!/usr/bin/env python3 import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-tables2-2.7.5/example/media/000077500000000000000000000000001473544236200170455ustar00rootroot00000000000000django-tables2-2.7.5/example/media/country/000077500000000000000000000000001473544236200205505ustar00rootroot00000000000000django-tables2-2.7.5/example/media/country/flags/000077500000000000000000000000001473544236200216445ustar00rootroot00000000000000django-tables2-2.7.5/example/media/country/flags/australia.svg000066400000000000000000000045051473544236200243560ustar00rootroot00000000000000 django-tables2-2.7.5/example/media/country/flags/canada.svg000066400000000000000000000026171473544236200236020ustar00rootroot00000000000000django-tables2-2.7.5/example/media/country/flags/new_zealand.svg000066400000000000000000000031741473544236200246610ustar00rootroot00000000000000 Flag of New Zealand django-tables2-2.7.5/example/requirements.txt000066400000000000000000000002141473544236200212470ustar00rootroot00000000000000-e .. django-bootstrap3==22.2 django-bootstrap4==22.3 django-bootstrap5==22.2 django-debug-toolbar==3.8.1 django-filter==22.1 tablib==3.3.0 django-tables2-2.7.5/example/settings.py000066400000000000000000000120531473544236200202010ustar00rootroot00000000000000from os.path import abspath, dirname, join from django.utils.translation import gettext_lazy as _ ROOT = dirname(abspath(__file__)) DEBUG = True ADMINS = ( # ('Your Name', 'your_email@example.com'), ) ALLOWED_HOSTS = ["*"] MANAGERS = ADMINS DATABASES = { "default": {"ENGINE": "django.db.backends.sqlite3", "NAME": join(ROOT, "database.sqlite")} } DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # Local time zone for this installation. Choices can be found here: # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. # On Unix systems, a value of None will cause Django to use the same # timezone as the operating system. # If running in a Windows environment this must be set to the same as your # system time zone. TIME_ZONE = "America/Chicago" # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGE_CODE = "en" SITE_ID = 1 # If you set this to False, Django will make some optimizations so as not # to load the internationalization machinery. USE_I18N = True LANGUAGES = [ ("cs", _("Czech")), ("de", _("German")), ("el", _("Greek")), ("en", _("English")), ("es", _("Spanish")), ("fr", _("French")), ("hu", _("Hungarian")), ("it", _("Italian")), ("nb", _("Norwegian bokmål")), ("nl", _("Dutch")), ("pl", _("Polish")), ("pt-br", _("Portuguese (Brasil)")), ("pt-pt", _("Portuguese (Portugal)")), ("ru", _("Russian")), ("sv", _("Swedish")), ("uk", _("Ukrainian")), ("zh-hans", _("Chinese (Simplified)")), ] # Absolute filesystem path to the directory that will hold user-uploaded files. # Example: "/home/media/media.lawrence.com/media/" MEDIA_ROOT = join(ROOT, "media") # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash. # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" MEDIA_URL = "/media/" # Absolute path to the directory static files should be collected to. # Don't put anything in this directory yourself; store your static files # in apps' "static/" subdirectories and in STATICFILES_DIRS. # Example: "/home/media/media.lawrence.com/static/" STATIC_ROOT = "" # URL prefix for static files. # Example: "http://media.lawrence.com/static/" STATIC_URL = "/static/" # URL prefix for admin static files -- CSS, JavaScript and images. # Make sure to use a trailing slash. # Examples: "http://foo.com/static/admin/", "/static/admin/". ADMIN_MEDIA_PREFIX = "/static/admin/" # Additional locations of static files STATICFILES_DIRS = ( # Put strings here, like "/home/html/static" or "C:/www/django/static". # Always use forward slashes, even on Windows. # Don't forget to use absolute paths, not relative paths. ) # List of finder classes that know how to find static files in # various locations. STATICFILES_FINDERS = ( "django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder", # 'django.contrib.staticfiles.finders.DefaultStorageFinder', ) # Make this unique, and don't share it with anybody. SECRET_KEY = "=nzw@mkqk)tz+_#vf%li&8sn7yn8z7!2-4njuyf1rxs*^muhvh" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": ["templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.contrib.auth.context_processors.auth", "django.template.context_processors.request", "django.template.context_processors.static", "django.contrib.messages.context_processors.messages", ] }, } ] MIDDLEWARE = ( "debug_toolbar.middleware.DebugToolbarMiddleware", "django.middleware.common.CommonMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.locale.LocaleMiddleware", ) ROOT_URLCONF = "urls" INSTALLED_APPS = ( "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "django.contrib.messages", "django.contrib.staticfiles", "django_filters", "bootstrap3", "bootstrap4", "django_bootstrap5", "django_tables2", "debug_toolbar", "app", ) INTERNAL_IPS = ("127.0.0.1",) # A sample logging configuration. The only tangible logging # performed by this configuration is to send an email to # the site admins on every HTTP 500 error. # See http://docs.djangoproject.com/en/dev/topics/logging for # more details on how to customize your logging configuration. LOGGING = { "version": 1, "disable_existing_loggers": False, "handlers": {"mail_admins": {"level": "ERROR", "class": "django.utils.log.AdminEmailHandler"}}, "loggers": { "django.request": {"handlers": ["mail_admins"], "level": "ERROR", "propagate": True} }, } django-tables2-2.7.5/example/templates/000077500000000000000000000000001473544236200177645ustar00rootroot00000000000000django-tables2-2.7.5/example/templates/base.html000066400000000000000000000011511473544236200215620ustar00rootroot00000000000000{% load static %} django-tables2 examples {% block extrahead %}{% endblock %} {% block body %} {% load django_tables2 %} {% render_table table %} {% endblock %} django-tables2-2.7.5/example/templates/bootstrap4_template.html000066400000000000000000000021611473544236200246460ustar00rootroot00000000000000{% load static %} {% load render_table from django_tables2 %} {% load bootstrap4 %} django_tables2 with bootstrap 4 template example {% bootstrap_css %}
{% block body %} Bootstrap 4 - tables docs | Bootstrap 4 - pagination docs

django_tables2 with Bootstrap 4 template example

{% if filter %}
{% bootstrap_form filter.form layout='inline' %} {% bootstrap_button 'filter' %}
{% endif %}
{% render_table table %}
{% endblock %}
django-tables2-2.7.5/example/templates/bootstrap5_template.html000066400000000000000000000022241473544236200246470ustar00rootroot00000000000000{% load static %} {% load render_table from django_tables2 %} {% load django_bootstrap5 %} django_tables2 with bootstrap 5 template example {% bootstrap_css %}
{% block body %} Bootstrap 5 - tables docs | Bootstrap 5 - pagination docs

django_tables2 with Bootstrap 5 template example

{% if filter %}
{% bootstrap_form filter.form layout='inline' %} {% bootstrap_button 'filter' %}
{% endif %}
{% render_table table %}
{% endblock %}
django-tables2-2.7.5/example/templates/bootstrap_template.html000066400000000000000000000031761473544236200245710ustar00rootroot00000000000000{% load static %} {% load django_tables2 %} {% load bootstrap3 %} django_tables2 with bootstrap 3 template example {% bootstrap_css %}
{% block body %} Bootstrap 3 - table docs | Bootstrap 3 - pagination docs

{% block title %}django_tables2 with Bootstrap 3 template example{% endblock %}

{% if view.export_formats %} {% for format in view.export_formats %} download .{{ format }} {% endfor %} {% endif %}
{% if filter %}
{% bootstrap_form filter.form layout='inline' %} {% bootstrap_button 'filter' %} {% endif %}
{% render_table table %}
{% endblock %}
django-tables2-2.7.5/example/templates/checkbox_example.html000066400000000000000000000014531473544236200241560ustar00rootroot00000000000000{% extends 'bootstrap_template.html' %} {% load bootstrap3 %} {% block title %}django-tables2 CheckBoxColumn example{% endblock %} {% block body %} {{ block.super }} {% bootstrap_javascript jquery=True %} {% endblock %} django-tables2-2.7.5/example/templates/class_based.html000066400000000000000000000003751473544236200231220ustar00rootroot00000000000000{% extends "base.html" %} {% load django_tables2 %} {% block body %}

class based view via render_table

{{ "{%" }} load django_tables2 {{ "%}" }}
{{ "{%" }} render_table example {{ "%}" }}
{% render_table table %} {% endblock %} django-tables2-2.7.5/example/templates/country_detail.html000066400000000000000000000003231473544236200236750ustar00rootroot00000000000000{% extends 'semantic_template.html' %} {% load django_tables2 %} {% block body %}

{{ country }}

Persons in this country

{% render_table table 'django_tables2/semantic.html' %} {% endblock %} django-tables2-2.7.5/example/templates/extended_table.html000066400000000000000000000002751473544236200236250ustar00rootroot00000000000000{% extends "django_tables2/table.html" %} {% block table.tfoot %}
{% endblock %} django-tables2-2.7.5/example/templates/index.html000066400000000000000000000056031473544236200217650ustar00rootroot00000000000000{% extends "base.html" %} {% load django_tables2 %} {% load static %} {% load i18n %} {% block extrahead %} {% endblock %} {% block body %}

Some examples of using django-tables2

Welcome to the django-tables2 example project. Below is a list of different examples using django-tables2.

    {% for url, text in urls %}
  • {{ text }}
  • {% endfor %}

Basic example of a table

{% render_table table "django_tables2/bootstrap.html" %}

Same table, but responsive (.table-responsive)

{% render_table table "django_tables2/bootstrap-responsive.html" %}
{% endblock %} django-tables2-2.7.5/example/templates/multiTable.html000066400000000000000000000013761473544236200227630ustar00rootroot00000000000000{% extends "base.html" %} {% load django_tables2 %} {% load static %} {% block extrahead %} {% endblock %} {% block body %}

Multiple tables on a single page using MultiTableMixin

Pagination should work independently for each table, the second table excludes the column 'country'.

{% for table in tables %}
{% render_table table %}
{% endfor %}
{% endblock %} django-tables2-2.7.5/example/templates/multiple.html000066400000000000000000000046131473544236200225110ustar00rootroot00000000000000{% extends "base.html" %} {% block body %}

django-tables2 examples

This page demonstrates various types of tables being rendered via django-tables2.

Example 1 — QuerySet

via as_html()

{% templatetag openvariable %} example1.as_html {% templatetag closevariable %}
{{ example1.as_html }}

via template tag

{% templatetag openblock %} load django_tables2 {% templatetag closeblock %}
{% templatetag openblock %} render_table example1 {% templatetag closeblock %}
{% load django_tables2 %} {% render_table example1 %}

Example 2 — QuerySet + pagination

via as_html()

{% templatetag openvariable %} example2.as_html {% templatetag closevariable %}
{{ example2.as_html }}

via template tag

{% templatetag openblock %} load django_tables2 {% templatetag closeblock %}
{% templatetag openblock %} render_table example2 {% templatetag closeblock %}
{% load django_tables2 %} {% render_table example2 %}

Example 3 — QuerySet + paleblue theme

via as_html()

{% templatetag openvariable %} example3.as_html {% templatetag closevariable %}
{{ example3.as_html }}

via template tag

{% templatetag openblock %} load django_tables2 {% templatetag closeblock %}
{% templatetag openblock %} render_table example3 {% templatetag closeblock %}
{% load django_tables2 %} {% render_table example3 %}

Example 4 — QuerySet + pagination + paleblue theme

via as_html()

{% templatetag openvariable %} example4.as_html {% templatetag closevariable %}
{{ example4.as_html }}

via template tag

{% templatetag openblock %} load django_tables2 {% templatetag closeblock %}
{% templatetag openblock %} render_table example4 {% templatetag closeblock %}
{% load django_tables2 %} {% render_table example4 %}

Example 5 – QuerySet + pagination + paleblue theme + custom template

via as_html()

{% templatetag openvariable %} example5.as_html {% templatetag closevariable %}
{{ example5.as_html }}

via template tag

{% templatetag openblock %} load django_tables2 {% templatetag closeblock %}
{% templatetag openblock %} render_table example5 {% templatetag closeblock %}
{% load django_tables2 %} {% render_table example5 %} {% endblock %} django-tables2-2.7.5/example/templates/person_detail.html000066400000000000000000000003131473544236200234770ustar00rootroot00000000000000{% extends 'semantic_template.html' %} {% load django_tables2 %} {% block body %}

{{ person }}

country: {{ person.country }} {% endblock %} django-tables2-2.7.5/example/templates/semantic_template.html000066400000000000000000000012761473544236200243560ustar00rootroot00000000000000{% load render_table from django_tables2 %} django_tables2 with semantic template example
{% block body %}

django_tables2 with Semantic UI template example

{% render_table table %} {% endblock %}
django-tables2-2.7.5/example/templates/tutorial.html000066400000000000000000000005031473544236200225130ustar00rootroot00000000000000{% load django_tables2 %} {% load static %}

django_tables2 with the default template example

{% render_table table %} django-tables2-2.7.5/example/urls.py000066400000000000000000000025201473544236200173240ustar00rootroot00000000000000from app.views import ( ClassBased, FilteredPersonListView, MultipleTables, checkbox, country_detail, index, multiple, person_detail, template_example, tutorial, ) from django.conf import settings from django.contrib import admin from django.urls import include, path from django.views import static urlpatterns = [ path("", index), path("multiple/", multiple, name="multiple"), path("class-based/", ClassBased.as_view(), name="singletableview"), path("class-based-multiple/", MultipleTables.as_view(), name="multitableview"), path("class-based-filtered/", FilteredPersonListView.as_view(), name="filtertableview"), path("checkbox/", checkbox, name="checkbox"), path("tutorial/", tutorial, name="tutorial"), path("template//", template_example, name="template_example"), path("admin/doc/", include("django.contrib.admindocs.urls")), path("admin/", admin.site.urls), path("country//", country_detail, name="country_detail"), path("person//", person_detail, name="person_detail"), path("media/", static.serve, {"document_root": settings.MEDIA_ROOT}), path("i18n/", include("django.conf.urls.i18n")), ] if settings.DEBUG: import debug_toolbar urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns django-tables2-2.7.5/maintenance.py000077500000000000000000000037711473544236200172020ustar00rootroot00000000000000#!/usr/bin/env python3 import os import subprocess import sys import time try: import hatch # noqa except ImportError: print("Package `hatch` is required: pip install hatch") sys.exit() VERSION = subprocess.check_output(["hatch version"], shell=True).decode().strip() if sys.argv[-1] == "bump": os.system("hatch version patch") print("\n- Commit CHANGELOG and django_tables2/__init__.py") print("- Run `./maintenance.py tag` to tag the new version") elif sys.argv[-1] == "publish": os.system("hatch publish") os.system("rm -f dist/django_tables2-2.7.4*") elif sys.argv[-1] == "tag": os.system("hatch build") tag_cmd = f"git tag -a v{VERSION} -m 'tagging v{VERSION}'" if exitcode := os.system(tag_cmd): print(f"Failed tagging with command: {tag_cmd}") else: os.system("git push --tags && git push origin master") print("\nTag created, run `./maintenance.py publish` to publish it") elif sys.argv[-1] == "screenshots": def screenshot(url, filename="screenshot.png", delay=2): print(f"Making screenshot of url: {url}") chrome = subprocess.Popen( ["chromium-browser", "--incognito", "--headless", "--screenshot", url], close_fds=False ) print(f"Starting to sleep for {delay}s...") time.sleep(delay) chrome.kill() os.system(f"convert screenshot.png -trim -bordercolor White -border 10x10 {dest}") os.remove("screenshot.png") print(f"Saved file to {dest}") images = { "{url}/tutorial/": "docs/img/example.png", "{url}/bootstrap/": "docs/img/bootstrap.png", "{url}/bootstrap4/": "docs/img/bootstrap4.png", "{url}/semantic/": "docs/img/semantic.png", } print( "Make sure the devserver is running: \n cd example/\n PYTHONPATH=.. ./manage.py runserver --insecure\n\n" ) for url, dest in images.items(): screenshot(url.format(url="http://localhost:8000"), dest) else: print(f"Current version: {VERSION}") django-tables2-2.7.5/manage.py000077500000000000000000000003751473544236200161450ustar00rootroot00000000000000#!/usr/bin/env python3 import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.app.settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-tables2-2.7.5/pyproject.toml000066400000000000000000000033731473544236200172550ustar00rootroot00000000000000[build-system] build-backend = "hatchling.build" requires = ["hatchling"] [project] authors = [ {name = "Bradley Ayers", email = "bradley.ayers@gmail.com"}, {name = "Jan Pieter Waagmeester", email = "jieter@jieter.nl"} ] classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Django", "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", "Framework :: Django :: 5.1", "Intended Audience :: Developers", "License :: OSI Approved :: BSD 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", "Topic :: Internet :: WWW/HTTP", "Topic :: Software Development :: Libraries" ] dependencies = ["Django>=4.2"] description = "Table/data-grid framework for Django" dynamic = ["version"] license = {file = "LICENSE"} name = "django-tables2" readme = "README.md" requires-python = ">=3.9" [project.optional-dependencies] tablib = ["tablib"] [project.urls] Changelog = "https://github.com/jieter/django-tables2/blob/master/CHANGELOG.md" Documentation = "https://django-tables2.readthedocs.io/en/latest/" Homepage = "https://github.com/jieter/django-tables2/" Readme = "https://github.com/jieter/django-tables2/blob/master/README.md" [tool.black] line-length = 100 [tool.hatch.build.targets.sdist] exclude = ["docs"] [tool.hatch.build.targets.wheel] packages = ["django_tables2"] [tool.hatch.version] path = "django_tables2/__init__.py" [tool.setuptools.dynamic] version = {attr = "django_tables2.__version__"} django-tables2-2.7.5/requirements/000077500000000000000000000000001473544236200170565ustar00rootroot00000000000000django-tables2-2.7.5/requirements/common.pip000066400000000000000000000001731473544236200210610ustar00rootroot00000000000000# xml parsing lxml==5.3.0 pytz==2024.2 tablib[xls,yaml]==3.7.0 openpyxl==3.1.5 psycopg2-binary==2.9.10 django-filter==24.3 django-tables2-2.7.5/requirements/django-dev.pip000066400000000000000000000000341473544236200216030ustar00rootroot00000000000000-r common.pip Django==4.2.6 django-tables2-2.7.5/tests/000077500000000000000000000000001473544236200154755ustar00rootroot00000000000000django-tables2-2.7.5/tests/__init__.py000066400000000000000000000000001473544236200175740ustar00rootroot00000000000000django-tables2-2.7.5/tests/app/000077500000000000000000000000001473544236200162555ustar00rootroot00000000000000django-tables2-2.7.5/tests/app/__init__.py000066400000000000000000000000001473544236200203540ustar00rootroot00000000000000django-tables2-2.7.5/tests/app/locale/000077500000000000000000000000001473544236200175145ustar00rootroot00000000000000django-tables2-2.7.5/tests/app/locale/ua/000077500000000000000000000000001473544236200201215ustar00rootroot00000000000000django-tables2-2.7.5/tests/app/locale/ua/LC_MESSAGES/000077500000000000000000000000001473544236200217065ustar00rootroot00000000000000django-tables2-2.7.5/tests/app/locale/ua/LC_MESSAGES/django.mo000066400000000000000000000004431473544236200235060ustar00rootroot00000000000000Þ•4L`arQˆÚ,ötranslation testtranslation test lazyReport-Msgid-Bugs-To: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 теÑÑ‚ перекладутеÑÑ‚ ленивого перекладуdjango-tables2-2.7.5/tests/app/locale/ua/LC_MESSAGES/django.po000066400000000000000000000004041473544236200235060ustar00rootroot00000000000000msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" msgid "translation test" msgstr "теÑÑ‚ перекладу" msgid "translation test lazy" msgstr "теÑÑ‚ ленивого перекладу" django-tables2-2.7.5/tests/app/migrations/000077500000000000000000000000001473544236200204315ustar00rootroot00000000000000django-tables2-2.7.5/tests/app/migrations/0001_initial.py000066400000000000000000000125071473544236200231010ustar00rootroot00000000000000# Generated by Django 4.0a1 on 2021-09-22 18:51 from django.db import migrations, models import django.db.models.deletion class Migration(migrations.Migration): initial = True dependencies = [ ("contenttypes", "0002_remove_content_type_name"), ] operations = [ migrations.CreateModel( name="Occupation", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID" ), ), ("name", models.CharField(max_length=200)), ("boolean", models.BooleanField(null=True)), ( "boolean_with_choices", models.BooleanField(choices=[(True, "Yes"), (False, "No")], null=True), ), ], ), migrations.CreateModel( name="Person", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID" ), ), ("first_name", models.CharField(max_length=200)), ("last_name", models.CharField(max_length=200, verbose_name="surname")), ( "trans_test", models.CharField(blank=True, max_length=200, verbose_name="translation test"), ), ( "trans_test_lazy", models.CharField( blank=True, max_length=200, verbose_name="translation test lazy" ), ), ("safe", models.CharField(blank=True, max_length=200, verbose_name="Safe")), ("website", models.URLField(blank=True, null=True, verbose_name="web site")), ("birthdate", models.DateField(null=True)), ("object_id", models.PositiveIntegerField(blank=True, null=True)), ( "content_type", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="contenttypes.contenttype", ), ), ("friends", models.ManyToManyField(to="app.Person")), ( "occupation", models.ForeignKey( null=True, on_delete=django.db.models.deletion.CASCADE, related_name="people", to="app.occupation", verbose_name="occupation of the person", ), ), ], options={ "verbose_name": "person", "verbose_name_plural": "people", }, ), migrations.CreateModel( name="Region", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID" ), ), ("name", models.CharField(max_length=200)), ( "mayor", models.OneToOneField( null=True, on_delete=django.db.models.deletion.CASCADE, to="app.person" ), ), ], options={ "ordering": ["name"], }, ), migrations.CreateModel( name="PersonInformation", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID" ), ), ( "person", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="info_list", to="app.person", verbose_name="Information", ), ), ], ), migrations.AddField( model_name="occupation", name="region", field=models.ForeignKey( null=True, on_delete=django.db.models.deletion.CASCADE, to="app.region" ), ), migrations.CreateModel( name="Group", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID" ), ), ("name", models.CharField(max_length=200)), ("members", models.ManyToManyField(to="app.Person")), ], ), migrations.CreateModel( name="PersonProxy", fields=[], options={ "ordering": ("last_name",), "proxy": True, "indexes": [], "constraints": [], }, bases=("app.person",), ), ] django-tables2-2.7.5/tests/app/migrations/__init__.py000066400000000000000000000000001473544236200225300ustar00rootroot00000000000000django-tables2-2.7.5/tests/app/models.py000066400000000000000000000055131473544236200201160ustar00rootroot00000000000000from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import models from django.urls import reverse from django.utils.safestring import mark_safe from django.utils.translation import gettext, gettext_lazy class Person(models.Model): first_name = models.CharField(max_length=200) last_name = models.CharField(max_length=200, verbose_name="surname") occupation = models.ForeignKey( "Occupation", related_name="people", null=True, verbose_name="occupation of the person", on_delete=models.CASCADE, ) trans_test = models.CharField( max_length=200, blank=True, verbose_name=gettext("translation test") ) trans_test_lazy = models.CharField( max_length=200, blank=True, verbose_name=gettext_lazy("translation test lazy") ) safe = models.CharField(max_length=200, blank=True, verbose_name=mark_safe("Safe")) website = models.URLField(max_length=200, null=True, blank=True, verbose_name="web site") birthdate = models.DateField(null=True) content_type = models.ForeignKey(ContentType, null=True, blank=True, on_delete=models.CASCADE) object_id = models.PositiveIntegerField(null=True, blank=True) foreign_key = GenericForeignKey() friends = models.ManyToManyField("Person") class Meta: verbose_name = "person" verbose_name_plural = "people" def __str__(self): return self.first_name @property def name(self): return f"{self.first_name} {self.last_name}" def get_absolute_url(self): return reverse("person", args=(self.pk,)) class PersonProxy(Person): class Meta: proxy = True ordering = ("last_name",) class Group(models.Model): name = models.CharField(max_length=200) members = models.ManyToManyField("Person") def __str__(self): return self.name def get_absolute_url(self): return f"/group/{self.pk}/" class Occupation(models.Model): name = models.CharField(max_length=200) region = models.ForeignKey("Region", null=True, on_delete=models.CASCADE) boolean = models.BooleanField(null=True) boolean_with_choices = models.BooleanField(null=True, choices=((True, "Yes"), (False, "No"))) def get_absolute_url(self): return reverse("occupation", args=(self.pk,)) def __str__(self): return self.name class Region(models.Model): name = models.CharField(max_length=200) mayor = models.OneToOneField(Person, null=True, on_delete=models.CASCADE) class Meta: ordering = ["name"] def __str__(self): return self.name class PersonInformation(models.Model): person = models.ForeignKey( Person, related_name="info_list", verbose_name="Information", on_delete=models.CASCADE ) django-tables2-2.7.5/tests/app/settings.py000066400000000000000000000011231473544236200204640ustar00rootroot00000000000000DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" INSTALLED_APPS = [ "django.contrib.auth", "django.contrib.contenttypes", "django_tables2", "tests.app", ] ROOT_URLCONF = "tests.app.urls" SECRET_KEY = "this is super secret" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "APP_DIRS": True, "OPTIONS": {"context_processors": ["django.template.context_processors.request"]}, } ] TIME_ZONE = "Europe/Amsterdam" USE_TZ = True django-tables2-2.7.5/tests/app/templates/000077500000000000000000000000001473544236200202535ustar00rootroot00000000000000django-tables2-2.7.5/tests/app/templates/child/000077500000000000000000000000001473544236200213365ustar00rootroot00000000000000django-tables2-2.7.5/tests/app/templates/child/foo.html000066400000000000000000000000041473544236200230010ustar00rootroot00000000000000bar django-tables2-2.7.5/tests/app/templates/csrf.html000066400000000000000000000000211473544236200220670ustar00rootroot00000000000000{% csrf_token %} django-tables2-2.7.5/tests/app/templates/dummy.html000066400000000000000000000000301473544236200222650ustar00rootroot00000000000000dummy template contents django-tables2-2.7.5/tests/app/templates/minimal.html000066400000000000000000000010071473544236200225650ustar00rootroot00000000000000{% load django_tables2 %}
...
This is a footer
{% for column in table.columns %} {% endfor %} {% for row in table.paginated_rows %} {% for column, cell in row.items %} {% endfor %} {% endfor %} {% if table.page.has_next %} next {% endif %}
{{ column.header }}
{{ cell }}
django-tables2-2.7.5/tests/app/templates/multiple.html000066400000000000000000000002161473544236200227730ustar00rootroot00000000000000{% load django_tables2 %}

Multiple tables using MultiTableMixin

{% for table in tables %} {% render_table table %} {% endfor %} django-tables2-2.7.5/tests/app/templates/test_template_column.html000066400000000000000000000000601473544236200253640ustar00rootroot00000000000000name:{{ record.col }}-{{ foo|default:"empty" }} django-tables2-2.7.5/tests/app/urls.py000066400000000000000000000006471473544236200176230ustar00rootroot00000000000000from django.urls import path, re_path from . import views urlpatterns = [ path("people/delete//", views.person, name="person_delete"), path("people/edit//", views.person, name="person_edit"), path("people//", views.person, name="person"), path("occupations//", views.occupation, name="occupation"), re_path(r'^&\'"/(?P\d+)/$', lambda req: None, name="escaping"), ] django-tables2-2.7.5/tests/app/views.py000066400000000000000000000010361473544236200177640ustar00rootroot00000000000000from django.http import HttpResponse from django.shortcuts import get_object_or_404 from .models import Occupation, Person def person(request, pk): """A really simple view to provide an endpoint for the 'person' URL.""" person = get_object_or_404(Person, pk=pk) return HttpResponse(f"Person: {person}") def occupation(request, pk): """Another really simple view to provide an endpoint for the 'occupation' URL.""" occupation = get_object_or_404(Occupation, pk=pk) return HttpResponse(f"Occupation: {occupation}") django-tables2-2.7.5/tests/columns/000077500000000000000000000000001473544236200171555ustar00rootroot00000000000000django-tables2-2.7.5/tests/columns/__init__.py000066400000000000000000000000001473544236200212540ustar00rootroot00000000000000django-tables2-2.7.5/tests/columns/test_booleancolumn.py000066400000000000000000000134251473544236200234300ustar00rootroot00000000000000from unittest import skipIf from django import VERSION as django_version from django.db import models from django.test import TestCase import django_tables2 as tables from ..app.models import Occupation, Person from ..utils import attrs, build_request, parse class BooleanColumnTest(TestCase): def test_should_be_used_for_booleanfield(self): class BoolModel(models.Model): field = models.BooleanField() class Meta: app_label = "django_tables2_test" class Table(tables.Table): class Meta: model = BoolModel column = Table.base_columns["field"] self.assertEqual(type(column), tables.BooleanColumn) self.assertEqual(column.empty_values, ()) @skipIf(django_version < (2, 1, 0), "Feature added in django 2.1") def test_should_use_nullability_for_booloanfield(self): """ Django 2.1 supports null=(True|False) for BooleanField. """ class BoolModel2(models.Model): field = models.BooleanField(null=True) class Meta: app_label = "django_tables2_test" class Table(tables.Table): class Meta: model = BoolModel2 column = Table.base_columns["field"] self.assertEqual(type(column), tables.BooleanColumn) self.assertEqual(column.empty_values, (None, "")) def test_should_be_used_for_nullbooleanfield(self): class NullBoolModel(models.Model): field = models.NullBooleanField() class Meta: app_label = "django_tables2_test" class Table(tables.Table): class Meta: model = NullBoolModel column = Table.base_columns["field"] self.assertEqual(type(column), tables.BooleanColumn) self.assertNotEqual(column.empty_values, ()) def test_treat_none_different_from_false(self): class Table(tables.Table): col = tables.BooleanColumn(null=True, default="---") table = Table([{"col": None}]) self.assertEqual(table.rows[0].get_cell("col"), "---") def test_treat_none_as_false(self): class Table(tables.Table): col = tables.BooleanColumn(null=False) table = Table([{"col": None}]) self.assertEqual(table.rows[0].get_cell("col"), '✘') def test_value_returns_a_raw_value_without_html(self): class Table(tables.Table): col = tables.BooleanColumn() table = Table([{"col": True}, {"col": False}]) self.assertEqual(table.rows[0].get_cell_value("col"), "True") self.assertEqual(table.rows[1].get_cell_value("col"), "False") def test_span_attrs(self): class Table(tables.Table): col = tables.BooleanColumn(attrs={"span": {"key": "value"}}) col_linkify = tables.BooleanColumn( accessor="col", attrs={"span": {"key": "value"}}, linkify=lambda value: f"/bool/{value}", ) table = Table([{"col": True}, {"col": False}]) self.assertEqual(attrs(table.rows[0].get_cell("col")), {"class": "true", "key": "value"}) self.assertEqual(attrs(table.rows[1].get_cell("col")), {"class": "false", "key": "value"}) self.assertIn(table.rows[0].get_cell("col"), table.rows[0].get_cell("col_linkify")) def test_boolean_field_choices_with_real_model_instances(self): """ If a booleanField has choices defined, the value argument passed to BooleanColumn.render() is the rendered value, not a bool. """ class BoolModelChoices(models.Model): field = models.BooleanField(choices=((True, "Yes"), (False, "No"))) class Meta: app_label = "django_tables2_test" class Table(tables.Table): class Meta: model = BoolModelChoices table = Table([BoolModelChoices(field=True), BoolModelChoices(field=False)]) self.assertEqual(table.rows[0].get_cell("field"), '✔') self.assertEqual(table.rows[1].get_cell("field"), '✘') def test_boolean_field_choices_spanning_relations(self): "The inverse lookup voor boolean choices should also work on related models" class Table(tables.Table): boolean = tables.BooleanColumn(accessor="occupation__boolean_with_choices") class Meta: model = Person model_true = Occupation.objects.create(name="true-name", boolean_with_choices=True) model_false = Occupation.objects.create(name="false-name", boolean_with_choices=False) table = Table( [ Person(first_name="True", last_name="False", occupation=model_true), Person(first_name="True", last_name="False", occupation=model_false), ] ) self.assertEqual(table.rows[0].get_cell("boolean"), '✔') self.assertEqual(table.rows[1].get_cell("boolean"), '✘') def test_boolean_should_not_prevent_rendering_of_other_columns(self): """Test for issue 360""" class Table(tables.Table): boolean = tables.BooleanColumn(yesno="waar,onwaar") class Meta: model = Occupation fields = ("boolean", "name") Occupation.objects.create(name="Waar", boolean=True), Occupation.objects.create(name="Onwaar", boolean=False), Occupation.objects.create(name="Onduidelijk") html = Table(Occupation.objects.all()).as_html(build_request()) root = parse(html) self.assertEqual(root.findall(".//tbody/tr[1]/td")[1].text, "Waar") self.assertEqual(root.findall(".//tbody/tr[2]/td")[1].text, "Onwaar") django-tables2-2.7.5/tests/columns/test_checkboxcolumn.py000066400000000000000000000072061473544236200235770ustar00rootroot00000000000000from django.test import SimpleTestCase import django_tables2 as tables from ..utils import attrs class CheckBoxColumnTest(SimpleTestCase): def test_new_attrs_should_be_supported(self): class TestTable(tables.Table): col1 = tables.CheckBoxColumn( attrs=dict(th__input={"th_key": "th_value"}, td__input={"td_key": "td_value"}) ) col2 = tables.CheckBoxColumn(attrs=dict(input={"key": "value"})) table = TestTable([{"col1": "data", "col2": "data"}]) assert attrs(table.columns["col1"].header) == {"type": "checkbox", "th_key": "th_value"} assert attrs(table.rows[0].get_cell("col1")) == { "type": "checkbox", "td_key": "td_value", "value": "data", "name": "col1", } assert attrs(table.columns["col2"].header) == {"type": "checkbox", "key": "value"} assert attrs(table.rows[0].get_cell("col2")) == { "type": "checkbox", "key": "value", "value": "data", "name": "col2", } def test_column_is_checked(self): class TestTable(tables.Table): col = tables.CheckBoxColumn(attrs={"name": "col"}, checked="is_selected") table = TestTable([{"col": "1", "is_selected": True}, {"col": "2", "is_selected": False}]) assert attrs(table.rows[0].get_cell("col")) == { "type": "checkbox", "value": "1", "name": "col", "checked": "checked", } assert attrs(table.rows[1].get_cell("col")) == { "type": "checkbox", "value": "2", "name": "col", } def test_column_is_not_checked_for_non_existing_column(self): class TestTable(tables.Table): col = tables.CheckBoxColumn(checked="does_not_exist") table = TestTable([{"col": "1", "is_selected": True}, {"col": "2", "is_selected": False}]) assert attrs(table.rows[0].get_cell("col")) == { "type": "checkbox", "value": "1", "name": "col", } assert attrs(table.rows[1].get_cell("col")) == { "type": "checkbox", "value": "2", "name": "col", } def test_column_is_alway_checked(self): class TestTable(tables.Table): col = tables.CheckBoxColumn(checked=True) table = TestTable([{"col": 1, "foo": "bar"}, {"col": 2, "foo": "baz"}]) assert attrs(table.rows[0].get_cell("col"))["checked"] == "checked" assert attrs(table.rows[1].get_cell("col"))["checked"] == "checked" def test_column_is_checked_callback(self): def is_selected(value, record): return value == "1" class TestTable(tables.Table): col = tables.CheckBoxColumn(attrs={"name": "col"}, checked=is_selected) table = TestTable([{"col": "1"}, {"col": "2"}]) assert attrs(table.rows[0].get_cell("col")) == { "type": "checkbox", "value": "1", "name": "col", "checked": "checked", } assert attrs(table.rows[1].get_cell("col")) == { "type": "checkbox", "value": "2", "name": "col", } def test_column_callable_attrs(self): class TestTable(tables.Table): col = tables.CheckBoxColumn( attrs={"input": {"data-source": lambda record: record["col"]}} ) table = TestTable([{"col": "1"}]) assert attrs(table.rows[0].get_cell("col")) == { "type": "checkbox", "value": "1", "name": "col", "data-source": "1", } django-tables2-2.7.5/tests/columns/test_datecolumn.py000066400000000000000000000054511473544236200227260ustar00rootroot00000000000000from datetime import date from django.db import models from django.test import SimpleTestCase import django_tables2 as tables def isoformat_link(value): return f"/test/{value.isoformat()}/" class DateColumnTest(SimpleTestCase): """ Format string: https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date D -- Day of the week, textual, 3 letters -- 'Fri' b -- Month, textual, 3 letters, lowercase -- 'jan' Y -- Year, 4 digits. -- '1999' """ def test_should_handle_explicit_format(self): class TestTable(tables.Table): date = tables.DateColumn(format="D b Y") date_linkify = tables.DateColumn( accessor="date", format="D b Y", linkify=isoformat_link ) class Meta: default = "—" table = TestTable([{"date": date(2012, 9, 11)}, {"date": None}]) self.assertEqual(table.rows[0].get_cell("date"), "Tue sep 2012") self.assertEqual( table.rows[0].get_cell("date_linkify"), 'Tue sep 2012' ) self.assertEqual(table.rows[1].get_cell("date"), "—") def test_should_handle_long_format(self): class TestTable(tables.Table): date = tables.DateColumn(short=False) class Meta: default = "—" table = TestTable([{"date": date(2012, 9, 11)}, {"date": None}]) self.assertEqual(table.rows[0].get_cell("date"), "Sept. 11, 2012") self.assertEqual(table.rows[1].get_cell("date"), "—") def test_should_handle_short_format(self): class TestTable(tables.Table): date = tables.DateColumn(short=True) class Meta: default = "—" table = TestTable([{"date": date(2012, 9, 11)}, {"date": None}]) self.assertEqual(table.rows[0].get_cell("date"), "09/11/2012") self.assertEqual(table.rows[1].get_cell("date"), "—") def test_should_be_used_for_datefields(self): class DateModel(models.Model): field = models.DateField() class Meta: app_label = "django_tables2_test" class Table(tables.Table): class Meta: model = DateModel self.assertEqual(type(Table.base_columns["field"]), tables.DateColumn) def test_value_returns_a_raw_value_without_html(self): class Table(tables.Table): date = tables.DateColumn() date_linkify = tables.DateColumn(accessor="date", linkify=isoformat_link) table = Table([{"date": date(2012, 9, 12)}]) self.assertEqual(table.rows[0].get_cell_value("date"), "09/12/2012") self.assertEqual( table.rows[0].get_cell("date_linkify"), '09/12/2012' ) django-tables2-2.7.5/tests/columns/test_datetimecolumn.py000066400000000000000000000056671473544236200236160ustar00rootroot00000000000000from datetime import datetime import pytz from django.conf import settings from django.db import models from django.test import SimpleTestCase import django_tables2 as tables def isoformat_link(value): return f"/test/{value.isoformat()}/" class DateTimeColumnTest(SimpleTestCase): """ Format string: https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date D -- Day of the week, textual, 3 letters -- 'Fri' b -- Month, textual, 3 letters, lowercase -- 'jan' Y -- Year, 4 digits. -- '1999' A -- 'AM' or 'PM'. -- 'AM' f -- Time, in 12-hour hours[:minutes] -- '1', '1:30' """ def dt(self): dt = datetime(2012, 9, 11, 12, 30, 0) return pytz.timezone(settings.TIME_ZONE).localize(dt) def test_should_handle_explicit_format(self): class TestTable(tables.Table): date = tables.DateTimeColumn(format="D b Y") date_linkify = tables.DateTimeColumn( format="D b Y", accessor="date", linkify=isoformat_link ) class Meta: default = "—" table = TestTable([{"date": self.dt()}, {"date": None}]) self.assertEqual(table.rows[0].get_cell("date"), "Tue sep 2012") self.assertEqual( table.rows[0].get_cell("date_linkify"), 'Tue sep 2012', ) self.assertEqual(table.rows[1].get_cell("date"), "—") def test_should_handle_long_format(self): class TestTable(tables.Table): date = tables.DateTimeColumn(short=False) class Meta: default = "—" table = TestTable([{"date": self.dt()}, {"date": None}]) self.assertEqual(table.rows[0].get_cell("date"), "Sept. 11, 2012, 12:30 p.m.") self.assertEqual(table.rows[1].get_cell("date"), "—") def test_should_handle_short_format(self): class TestTable(tables.Table): date = tables.DateTimeColumn(short=True) class Meta: default = "—" table = TestTable([{"date": self.dt()}, {"date": None}]) self.assertEqual(table.rows[0].get_cell("date"), "09/11/2012 12:30 p.m.") self.assertEqual(table.rows[1].get_cell("date"), "—") def test_should_be_used_for_datetimefields(self): class DateTimeModel(models.Model): field = models.DateTimeField() class Meta: app_label = "django_tables2_test" class Table(tables.Table): class Meta: model = DateTimeModel self.assertIsInstance(Table.base_columns["field"], tables.DateTimeColumn) def test_value_returns_a_raw_value_without_html(self): class Table(tables.Table): col = tables.DateTimeColumn() table = Table([{"col": self.dt()}]) self.assertEqual(table.rows[0].get_cell_value("col"), "09/11/2012 12:30 p.m.") django-tables2-2.7.5/tests/columns/test_emailcolumn.py000066400000000000000000000031741473544236200231000ustar00rootroot00000000000000from django.db import models from django.test import SimpleTestCase import django_tables2 as tables class EmailColumnTest(SimpleTestCase): def test_should_turn_email_address_into_hyperlink(self): class Table(tables.Table): email = tables.EmailColumn() table = Table([{"email": "test@example.com"}]) self.assertEqual( table.rows[0].get_cell("email"), 'test@example.com', ) def test_should_render_default_for_blank(self): class Table(tables.Table): email = tables.EmailColumn(default="---") table = Table([{"email": ""}]) self.assertEqual(table.rows[0].get_cell("email"), "---") def test_should_be_used_for_emailfields(self): class EmailModel(models.Model): field = models.EmailField() class Meta: app_label = "test" class Table(tables.Table): class Meta: model = EmailModel self.assertEqual(type(Table.base_columns["field"]), tables.EmailColumn) def test_text_should_be_overridable(self): class Table(tables.Table): email = tables.EmailColumn(text="@") table = Table([{"email": "test@example.com"}]) self.assertEqual(table.rows[0].get_cell("email"), '@') def test_value_returns_a_raw_value_without_html(self): class Table(tables.Table): col = tables.EmailColumn() table = Table([{"col": "test@example.com"}]) self.assertEqual(table.rows[0].get_cell_value("col"), "test@example.com") django-tables2-2.7.5/tests/columns/test_filecolumn.py000066400000000000000000000062211473544236200227240ustar00rootroot00000000000000import os from django.core.files.base import ContentFile from django.core.files.storage import FileSystemStorage from django.db import models from django.db.models.fields.files import FieldFile from django.test import SimpleTestCase import django_tables2 as tables from ..utils import parse def storage(): """Provide a storage that exposes the test templates""" root = os.path.join(os.path.dirname(__file__), "..", "app", "templates") return FileSystemStorage(location=root, base_url="/baseurl/") def column(): return tables.FileColumn(attrs={"span": {"class": "span"}, "a": {"class": "a"}}) class FileColumnTest(SimpleTestCase): def test_should_be_used_for_filefields(self): class FileModel(models.Model): field = models.FileField() class Meta: app_label = "django_tables2_test" class Table(tables.Table): class Meta: model = FileModel self.assertEqual(type(Table.base_columns["field"]), tables.FileColumn) def test_filecolumn_supports_storage_file(self): file_ = storage().open("child/foo.html") try: root = parse(column().render(value=file_, record=None)) finally: file_.close() self.assertEqual(root.tag, "span") self.assertEqual(root.attrib, {"class": "span exists", "title": file_.name}) self.assertEqual(root.text, "foo.html") def test_filecolumn_supports_contentfile(self): name = "foobar.html" file_ = ContentFile("") file_.name = name root = parse(column().render(value=file_, record=None)) self.assertEqual(root.tag, "span") self.assertEqual(root.attrib, {"title": name, "class": "span"}) self.assertEqual(root.text, "foobar.html") def test_filecolumn_supports_fieldfile(self): field = models.FileField(storage=storage()) name = "child/foo.html" class Table(tables.Table): filecolumn = column() table = Table([{"filecolumn": FieldFile(instance=None, field=field, name=name)}]) html = table.rows[0].get_cell("filecolumn") root = parse(html) self.assertEqual(root.tag, "a") self.assertEqual(root.attrib, {"class": "a", "href": "/baseurl/child/foo.html"}) span = root.find("span") self.assertEqual(span.tag, "span") self.assertEqual(span.text, "foo.html") # Now try a file that doesn't exist name = "child/does_not_exist.html" fieldfile = FieldFile(instance=None, field=field, name=name) root = parse(column().render(value=fieldfile, record=None)) self.assertEqual(root.tag, "span") self.assertEqual(root.attrib, {"class": "span missing", "title": name}) self.assertEqual(root.text, "does_not_exist.html") def test_filecolumn_text_custom_value(self): file_ = ContentFile("") file_.name = "foobar.html" root = parse(tables.FileColumn(text="Download").render(value=file_, record=None)) self.assertEqual(root.tag, "span") self.assertEqual(root.attrib, {"title": file_.name, "class": ""}) self.assertEqual(root.text, "Download") django-tables2-2.7.5/tests/columns/test_general.py000066400000000000000000000447511473544236200222160ustar00rootroot00000000000000from django.core.exceptions import ImproperlyConfigured from django.db import models from django.test import TestCase from django.utils.safestring import SafeData, mark_safe from django.utils.translation import gettext_lazy import django_tables2 as tables from ..app.models import Person from ..utils import build_request, parse request = build_request("/") class ColumnGeneralTest(TestCase): def test_column_render_supports_kwargs(self): class TestColumn(tables.Column): def render(self, **kwargs): return set(kwargs.keys()) class TestTable(tables.Table): foo = TestColumn() table = TestTable([{"foo": "bar"}]) expected = {"record", "value", "column", "bound_column", "bound_row", "table"} self.assertEqual(table.rows[0].get_cell("foo"), expected) def test_column_header_should_use_titlised_verbose_name_unless_given_explicitly(self): class SimpleTable(tables.Table): basic = tables.Column() acronym = tables.Column(verbose_name="has FBI help") table = SimpleTable([]) self.assertEqual(table.columns["basic"].header, "Basic") self.assertEqual(table.columns["acronym"].header, "has FBI help") def test_should_support_safe_verbose_name(self): class SimpleTable(tables.Table): safe = tables.Column(verbose_name=mark_safe("Safe")) table = SimpleTable([]) self.assertIsInstance(table.columns["safe"].header, SafeData) def test_should_raise_on_invalid_accessor(self): with self.assertRaises(TypeError): class SimpleTable(tables.Table): column = tables.Column(accessor={}) def test_column_with_callable_accessor_should_not_have_default(self): with self.assertRaises(TypeError): class SimpleTable(tables.Table): column = tables.Column(accessor=lambda: "foo", default="") def test_should_support_safe_verbose_name_via_model(self): class PersonTable(tables.Table): safe = tables.Column() table = PersonTable(Person.objects.all()) self.assertIsInstance(table.columns["safe"].header, SafeData) def test_should_support_empty_string_as_explicit_verbose_name(self): class SimpleTable(tables.Table): acronym = tables.Column(verbose_name="") table = SimpleTable([]) self.assertEqual(table.columns["acronym"].header, "") def test_handle_verbose_name_of_many2onerel(self): class Table(tables.Table): count = tables.Column(accessor="info_list__count") Person.objects.create(first_name="bradley", last_name="ayers") table = Table(Person.objects.all()) self.assertEqual(table.columns["count"].verbose_name, "Information") def test_orderable(self): class SimpleTable(tables.Table): name = tables.Column() table = SimpleTable([]) self.assertTrue(table.columns["name"].orderable) class SimpleTable(tables.Table): name = tables.Column() class Meta: orderable = False table = SimpleTable([]) self.assertFalse(table.columns["name"].orderable) class SimpleTable(tables.Table): name = tables.Column() class Meta: orderable = True table = SimpleTable([]) self.assertTrue(table.columns["name"].orderable) def test_order_by_defaults_to_accessor(self): class SimpleTable(tables.Table): foo = tables.Column(accessor="bar") table = SimpleTable([]) self.assertEqual(table.columns["foo"].order_by, ("bar",)) def test_supports_order_by(self): class SimpleTable(tables.Table): name = tables.Column(order_by=("last_name", "-first_name")) age = tables.Column() table = SimpleTable([], order_by=("-age",)) # alias self.assertEqual(table.columns["name"].order_by_alias, "name") self.assertEqual(table.columns["age"].order_by_alias, "-age") # order by self.assertEqual(table.columns["name"].order_by, ("last_name", "-first_name")) self.assertEqual(table.columns["age"].order_by, ("-age",)) # now try with name ordered table = SimpleTable([], order_by=("-name",)) # alias self.assertEqual(table.columns["name"].order_by_alias, "-name") self.assertEqual(table.columns["age"].order_by_alias, "age") # alias next self.assertEqual(table.columns["name"].order_by_alias.next, "name") self.assertEqual(table.columns["age"].order_by_alias.next, "age") # order by self.assertEqual(table.columns["name"].order_by, ("-last_name", "first_name")) self.assertEqual(table.columns["age"].order_by, ("age",)) def test_supports_is_ordered(self): class SimpleTable(tables.Table): name = tables.Column() # sorted table = SimpleTable([], order_by="name") self.assertTrue(table.columns["name"].is_ordered) # unsorted table = SimpleTable([]) self.assertFalse(table.columns["name"].is_ordered) def test_translation(self): """ Tests different types of values for the ``verbose_name`` property of a column. """ class TranslationTable(tables.Table): text = tables.Column(verbose_name=gettext_lazy("Text")) table = TranslationTable([]) self.assertEqual(table.columns["text"].header, "Text") def test_sequence(self): """ Ensures that the sequence of columns is configurable. """ class TestTable(tables.Table): a = tables.Column() b = tables.Column() c = tables.Column() self.assertEqual(["a", "b", "c"], TestTable([]).columns.names()) self.assertEqual(["b", "a", "c"], TestTable([], sequence=("b", "a", "c")).columns.names()) class TestTable2(TestTable): class Meta: sequence = ("b", "a", "c") self.assertEqual(["b", "a", "c"], TestTable2([]).columns.names()) self.assertEqual(["a", "b", "c"], TestTable2([], sequence=("a", "b", "c")).columns.names()) class TestTable3(TestTable): class Meta: sequence = ("c",) self.assertEqual(["c", "a", "b"], TestTable3([]).columns.names()) self.assertEqual(["c", "a", "b"], TestTable([], sequence=("c",)).columns.names()) class TestTable4(TestTable): class Meta: sequence = ("...",) self.assertEqual(["a", "b", "c"], TestTable4([]).columns.names()) self.assertEqual(["a", "b", "c"], TestTable([], sequence=("...",)).columns.names()) class TestTable5(TestTable): class Meta: sequence = ("b", "...") self.assertEqual(["b", "a", "c"], TestTable5([]).columns.names()) self.assertEqual(["b", "a", "c"], TestTable([], sequence=("b", "...")).columns.names()) class TestTable6(TestTable): class Meta: sequence = ("...", "b") self.assertEqual(["a", "c", "b"], TestTable6([]).columns.names()) self.assertEqual(["a", "c", "b"], TestTable([], sequence=("...", "b")).columns.names()) class TestTable7(TestTable): class Meta: sequence = ("b", "...", "a") self.assertEqual(["b", "c", "a"], TestTable7([]).columns.names()) self.assertEqual(["b", "c", "a"], TestTable([], sequence=("b", "...", "a")).columns.names()) # Let's test inheritence class TestTable8(TestTable): d = tables.Column() e = tables.Column() f = tables.Column() class Meta: sequence = ("d", "...") class TestTable9(TestTable): d = tables.Column() e = tables.Column() f = tables.Column() self.assertEqual(["d", "a", "b", "c", "e", "f"], TestTable8([]).columns.names()) self.assertEqual( ["d", "a", "b", "c", "e", "f"], TestTable9([], sequence=("d", "...")).columns.names() ) def test_should_support_both_meta_sequence_and_constructor_exclude(self): """ Issue #32 describes a problem when both ``Meta.sequence`` and ``Table(..., exclude=...)`` are used on a single table. The bug caused an exception to be raised when the table was iterated. """ class SequencedTable(tables.Table): a = tables.Column() b = tables.Column() c = tables.Column() class Meta: sequence = ("a", "...") table = SequencedTable([], exclude=("c",)) table.as_html(request) self.assertEqual(table.columns.names(), ["a", "b"]) def test_bound_columns_should_support_indexing(self): class SimpleTable(tables.Table): a = tables.Column() b = tables.Column() table = SimpleTable([]) self.assertEqual(table.columns[1].name, "b") self.assertEqual(table.columns["b"].name, "b") def test_cell_attrs_applies_to_td_and_th_and_footer_td(self): class SimpleTable(tables.Table): a = tables.Column( attrs={"cell": {"key": "value"}}, footer=lambda table: len(table.data) ) # providing data ensures 1 row is rendered table = SimpleTable([{"a": "value"}]) root = parse(table.as_html(request)) self.assertEqual( root.findall(".//thead/tr/th")[0].attrib, {"key": "value", "class": "orderable"} ) self.assertEqual(root.findall(".//tbody/tr/td")[0].attrib, {"key": "value"}) self.assertEqual(root.findall(".//tfoot/tr/td")[0].attrib, {"key": "value"}) def test_th_are_given_orderable_class_if_column_is_orderable(self): class SimpleTable(tables.Table): a = tables.Column() b = tables.Column(orderable=False) table = SimpleTable([{"a": "value"}]) root = parse(table.as_html(request)) # return classes of an element as a set classes = lambda x: set(x.attrib.get("class", "").split()) self.assertIn("orderable", classes(root.findall(".//thead/tr/th")[0])) self.assertNotIn("orderable", classes(root.findall(".//thead/tr/th")[1])) # Now try with an ordered table table = SimpleTable([], order_by="a") root = parse(table.as_html(request)) # return classes of an element as a set self.assertIn("orderable", classes(root.findall(".//thead/tr/th")[0])) self.assertIn("asc", classes(root.findall(".//thead/tr/th")[0])) self.assertNotIn("orderable", classes(root.findall(".//thead/tr/th")[1])) def test_empty_values_triggers_default(self): class Table(tables.Table): a = tables.Column(empty_values=(1, 2), default="--") table = Table([{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}]) self.assertEqual([row.get_cell("a") for row in table.rows], ["--", "--", 3, 4]) def test_register_skips_non_columns(self): from django_tables2.columns.base import library currently_registered = len(library.columns) with self.assertRaises(ImproperlyConfigured): @library.register class Klass: pass self.assertEqual(len(library.columns), currently_registered) def test_raises_when_using_non_supported_index(self): class Table(tables.Table): column = tables.Column() table = Table([{"column": "foo"}]) row = table.rows[0] with self.assertRaises(TypeError): row[table] def test_related_fields_get_correct_type(self): """ Types of related fields should also lead to the correct type of column. """ class PersonTable(tables.Table): class Meta: model = Person fields = ["first_name", "occupation__boolean"] table = PersonTable([]) self.assertEqual( [type(column).__name__ for column in table.base_columns.values()], ["Column", "BooleanColumn"], ) class MyModel(models.Model): item1 = models.CharField(max_length=10) class Meta: app_label = "django_tables2_tests" class MyTable(tables.Table): item1 = tables.Column(verbose_name="Nice column name") class Meta: model = MyModel fields = ("item1",) class ColumnInheritanceTest(TestCase): def test_column_params_should_be_preserved_under_inheritance(self): """ Github issue #337 Columns explicitly defined on MyTable get overridden by columns implicitly defined on it's child. If the column is not redefined, the explicit definition of MyTable is used, preserving the specialized verbose_name defined on it. """ class MyTableA(MyTable): """ having an empty `class Meta` should not undo the explicit definition of column item1 in MyTable. """ class Meta(MyTable.Meta): pass class MyTableB(MyTable): """ having a non-empty `class Meta` should not undo the explicit definition of column item1 in MyTable. """ class Meta(MyTable.Meta): per_page = 22 table = MyTable(MyModel.objects.all()) tableA = MyTableA(MyModel.objects.all()) tableB = MyTableB(MyModel.objects.all()) self.assertEqual(table.columns["item1"].verbose_name, "Nice column name") self.assertEqual(tableA.columns["item1"].verbose_name, "Nice column name") self.assertEqual(tableB.columns["item1"].verbose_name, "Nice column name") def test_explicit_column_can_be_overridden_by_other_explicit_column(self): class MyTableC(MyTable): """ If we define a new explict item1 column, that one should be used. """ item1 = tables.Column(verbose_name="New nice column name") table = MyTable(MyModel.objects.all()) tableC = MyTableC(MyModel.objects.all()) self.assertEqual(table.columns["item1"].verbose_name, "Nice column name") self.assertEqual(tableC.columns["item1"].verbose_name, "New nice column name") def test_override_column_class_names(self): """ We control the output of CSS class names for a column by overriding get_column_class_names """ class MyTable(tables.Table): population = tables.Column(verbose_name="Population") def get_column_class_names(self, classes_set, bound_column): classes_set.add(f"prefix-{bound_column.name}") return classes_set TEST_DATA = [ {"name": "Belgium", "population": 11200000}, {"name": "Luxembourgh", "population": 540000}, {"name": "France", "population": 66000000}, ] html = MyTable(TEST_DATA).as_html(build_request()) self.assertIn('11200000', html) class ColumnAttrsTest(TestCase): def setUp(self): Person.objects.create(first_name="Jan", last_name="Pietersz.") Person.objects.create(first_name="Sjon", last_name="Jansen") def test_computable_td_attrs(self): """Computable attrs for columns, using table argument""" class Table(tables.Table): person = tables.Column(attrs={"cell": {"data-length": lambda table: len(table.data)}}) first_name = tables.Column( attrs={"td": {"class": lambda table: f"status-{len(table.data)}"}} ) table = Table(Person.objects.all()) html = table.as_html(request) # cell should affect both and self.assertIn('', html) self.assertIn('', html) # td should only affect self.assertIn('', html) def test_computable_td_attrs_defined_in_column_class_attribute(self): """Computable attrs for columns, using custom Column""" class MyColumn(tables.Column): attrs = {"td": {"data-test": lambda table: len(table.data)}} class Table(tables.Table): last_name = MyColumn() table = Table(Person.objects.all()) html = table.as_html(request) root = parse(html) self.assertEqual(root.findall(".//tbody/tr/td")[0].attrib, {"data-test": "2"}) self.assertEqual(root.findall(".//tbody/tr/td")[1].attrib, {"data-test": "2"}) def test_computable_td_attrs_defined_in_column_class_attribute_record(self): """Computable attrs for columns, using custom column""" class PersonColumn(tables.Column): attrs = { "td": { "data-first-name": lambda record: record.first_name, "data-last-name": lambda record: record.last_name, } } def render(self, record): return f"{record.first_name} {record.last_name}" class Table(tables.Table): person = PersonColumn(empty_values=()) table = Table(Person.objects.all()) html = table.as_html(request) root = parse(html) self.assertEqual( root.findall(".//tbody/tr/td")[0].attrib, {"data-first-name": "Jan", "data-last-name": "Pietersz."}, ) def test_computable_column_td_attrs_record_header(self): """ Computable attrs for columns, using custom column with a callable containing a catch-all argument. """ def data_first_name(**kwargs): record = kwargs.get("record", None) return "header" if not record else record.first_name class Table(tables.Table): first_name = tables.Column( attrs={ "cell": { "data-first-name": data_first_name, "class": lambda value: f"status-{value}", } } ) table = Table(Person.objects.all()) html = table.as_html(request) root = parse(html) self.assertEqual( root.findall(".//thead/tr/th")[0].attrib, {"class": "orderable", "data-first-name": "header"}, ) self.assertEqual( root.findall(".//tbody/tr/td")[0].attrib, {"class": "status-Jan", "data-first-name": "Jan"}, ) self.assertEqual( root.findall(".//tbody/tr/td")[1].attrib, {"class": "status-Sjon", "data-first-name": "Sjon"}, ) django-tables2-2.7.5/tests/columns/test_initialsortcolumn.py000066400000000000000000000043721473544236200243530ustar00rootroot00000000000000from django.db import models from django.test import TestCase import django_tables2 as tables class InitialSortColumnTest(TestCase): def test_initial_sort_descending_affects_order_by_alias_next(self): class IntModel(models.Model): field = models.IntegerField() class Meta: app_label = "django_tables2_test" class Table(tables.Table): class Meta: model = IntModel class TableDescOrd(tables.Table): field = tables.Column(initial_sort_descending=True) class Meta: model = IntModel data = [{"field": 1}, {"field": 5}, {"field": 3}] # no initial ordering table = Table(data) table_desc = TableDescOrd(data) self.assertEqual(table.columns[1].order_by_alias.next, "field") self.assertEqual(table_desc.columns[1].order_by_alias.next, "-field") # with ascending ordering table = Table(data, order_by=("field",)) table_desc = TableDescOrd(data, order_by=("field",)) self.assertEqual(table.columns[1].order_by_alias.next, "-field") self.assertEqual(table_desc.columns[1].order_by_alias.next, "-field") self.assertEqual(table.rows[0].get_cell("field"), 1) self.assertEqual(table.rows[1].get_cell("field"), 3) self.assertEqual(table.rows[2].get_cell("field"), 5) self.assertEqual(table_desc.rows[0].get_cell("field"), 1) self.assertEqual(table_desc.rows[1].get_cell("field"), 3) self.assertEqual(table_desc.rows[2].get_cell("field"), 5) # with initial descending ordering table = Table(data, order_by=("-field",)) table_desc = TableDescOrd(data, order_by=("-field",)) self.assertEqual(table.columns[1].order_by_alias.next, "field") self.assertEqual(table_desc.columns[1].order_by_alias.next, "field") self.assertEqual(table.rows[0].get_cell("field"), 5) self.assertEqual(table.rows[1].get_cell("field"), 3) self.assertEqual(table.rows[2].get_cell("field"), 1) self.assertEqual(table_desc.rows[0].get_cell("field"), 5) self.assertEqual(table_desc.rows[1].get_cell("field"), 3) self.assertEqual(table_desc.rows[2].get_cell("field"), 1) django-tables2-2.7.5/tests/columns/test_jsoncolumn.py000066400000000000000000000033411473544236200227560ustar00rootroot00000000000000from django.contrib.postgres.fields import HStoreField from django.db import models from django.db.models import JSONField from django.test import SimpleTestCase import django_tables2 as tables class JsonColumnTestCase(SimpleTestCase): def test_should_be_used_for_json_and_hstore_fields(self): class Model(models.Model): json = JSONField() hstore = HStoreField() class Meta: app_label = "django_tables2_test" class Table(tables.Table): class Meta: model = Model self.assertIsInstance(Table.base_columns["json"], tables.JSONColumn) self.assertIsInstance(Table.base_columns["hstore"], tables.JSONColumn) def test_jsoncolumn_attrs(self): column = tables.JSONColumn(attrs={"pre": {"class": "json"}}) record = {"json": "foo"} html = column.render(value=record["json"], record=record) self.assertEqual(html, '
"foo"
') def test_jsoncolumn_dict(self): column = tables.JSONColumn() record = {"json": {"species": "Falcon"}} html = column.render(value=record["json"], record=record) self.assertEqual(html, "
{\n  "species": "Falcon"\n}
") def test_jsoncolumn_string(self): column = tables.JSONColumn() record = {"json": "really?"} html = column.render(value=record["json"], record=record) self.assertEqual(html, "
"really?"
") def test_jsoncolumn_number(self): column = tables.JSONColumn() record = {"json": 3.14} html = column.render(value=record["json"], record=record) self.assertEqual(html, "
3.14
") django-tables2-2.7.5/tests/columns/test_linkcolumn.py000066400000000000000000000215451473544236200227500ustar00rootroot00000000000000from django.template import Context, Template from django.test import TestCase from django.urls import reverse from django.utils.html import mark_safe import django_tables2 as tables from django_tables2 import A from ..app.models import Occupation, Person from ..utils import attrs, build_request class LinkColumnTest(TestCase): def test_unicode(self): """Test LinkColumn for unicode values + headings""" class UnicodeTable(tables.Table): first_name = tables.LinkColumn("person", args=[A("pk")]) last_name = tables.LinkColumn( "person", args=[A("pk")], verbose_name="äÚ¨´ˆÃ˜¨ˆ˜˘Ú…Ò˚ˆπ∆ˆ´" ) dataset = [ {"pk": 1, "first_name": "Brädley", "last_name": "∆yers"}, {"pk": 2, "first_name": "Chr…s", "last_name": "DÃ’ble"}, ] template = Template("{% load django_tables2 %}{% render_table table %}") html = template.render( Context({"request": build_request(), "table": UnicodeTable(dataset)}) ) self.assertIn("Brädley", html) self.assertIn("∆yers", html) self.assertIn("Chr…s", html) self.assertIn("DÃ’ble", html) def test_link_text_custom_value(self): class CustomLinkTable(tables.Table): first_name = tables.LinkColumn("person", text="foo::bar", args=[A("pk")]) last_name = tables.LinkColumn( "person", text=lambda row: f'{row["last_name"]} {row["first_name"]}', args=[A("pk")], ) dataset = [{"pk": 1, "first_name": "John", "last_name": "Doe"}] html = CustomLinkTable(dataset).as_html(build_request()) self.assertIn("foo::bar", html) self.assertIn("Doe John", html) def test_link_text_escaping(self): class CustomLinkTable(tables.Table): editlink = tables.LinkColumn("person", text=mark_safe("edit"), args=[A("pk")]) dataset = [{"pk": 1, "first_name": "John", "last_name": "Doe"}] html = CustomLinkTable(dataset).as_html(build_request()) url = reverse("person", args=(1,)) self.assertIn(f'edit', html) def test_null_foreign_key(self): class PersonTable(tables.Table): first_name = tables.Column() last_name = tables.Column() occupation = tables.LinkColumn("occupation", args=[A("occupation__pk")]) occupation_linkify = tables.Column(linkify=("occupation", A("occupation__pk"))) Person.objects.create(first_name="bradley", last_name="ayers") table = PersonTable(Person.objects.all()) html = table.as_html(build_request()) self.assertIn("—", html) def test_linkcolumn_non_field_based(self): """Test for issue 257, non-field based columns""" class Table(tables.Table): first_name = tables.Column() delete_link = tables.LinkColumn( "person_delete", text="delete", kwargs={"pk": tables.A("id")} ) willem = Person.objects.create(first_name="Willem", last_name="Wever") table = Table(Person.objects.all()) url = reverse("person_delete", kwargs={"pk": willem.pk}) self.assertEqual(table.rows[0].get_cell("delete_link"), f'delete') def test_kwargs(self): class PersonTable(tables.Table): a = tables.LinkColumn("occupation", kwargs={"pk": A("a")}) table = PersonTable([{"a": 0}, {"a": 1}]) self.assertIn(reverse("occupation", kwargs={"pk": 0}), table.rows[0].get_cell("a")) self.assertIn(reverse("occupation", kwargs={"pk": 1}), table.rows[1].get_cell("a")) def test_html_escape_value(self): class PersonTable(tables.Table): name = tables.LinkColumn("escaping", kwargs={"pk": A("pk")}) name_linkify = tables.Column(accessor="name", linkify=("escaping", {"pk": A("pk")})) table = PersonTable([{"name": "", "pk": 1}]) # django==3.0 replaces ' with ', drop first option if django==2.2 support is removed self.assertIn( table.rows[0].get_cell("name"), ( '<brad>' '<brad>' ), ) # the two columns should result in the same rendered cell contents self.assertEqual(table.rows[0].get_cell("name"), table.rows[0].get_cell("name_linkify")) def test_a_attrs_should_be_supported(self): class TestTable(tables.Table): attrs = {"a": {"title": "Occupation Title", "id": lambda record: str(record["id"])}} col = tables.LinkColumn("occupation", kwargs={"pk": A("col")}, attrs=attrs) col_linkify = tables.Column( accessor="col", attrs=attrs, linkify=("occupation", {"pk": A("col")}), ) table = TestTable([{"col": 0, "id": 1}]) self.assertEqual( attrs(table.rows[0].get_cell("col")), { "href": reverse("occupation", kwargs={"pk": 0}), "title": "Occupation Title", "id": "1", }, ) self.assertEqual(table.rows[0].get_cell("col"), table.rows[0].get_cell("col_linkify")) def test_td_attrs_should_be_supported(self): """LinkColumn should support both and attrs""" person = Person.objects.create(first_name="Bob", last_name="Builder") class Table(tables.Table): first_name = tables.LinkColumn( attrs={"a": {"style": "color: red;"}, "td": {"style": "background-color: #ddd;"}} ) last_name = tables.Column() table = Table(Person.objects.all()) a_tag = table.rows[0].get_cell("first_name") url = reverse("person", args=(person.pk,)) self.assertIn(f'href="{url}"', a_tag) self.assertIn('style="color: red;"', a_tag) self.assertIn(person.first_name, a_tag) html = table.as_html(build_request()) self.assertIn('', html) def test_defaults(self): class Table(tables.Table): link = tables.LinkColumn("occupation", kwargs={"pk": 1}, default="xyz") table = Table([{}]) self.assertEqual(table.rows[0].get_cell("link"), "xyz") def test_get_absolute_url(self): class PersonTable(tables.Table): first_name = tables.Column() last_name = tables.LinkColumn() other_last_name = tables.Column(accessor="last_name", linkify=True) person = Person.objects.create(first_name="Jan Pieter", last_name="Waagmeester") table = PersonTable(Person.objects.all()) expected = f'{person.last_name}' self.assertEqual(table.rows[0].cells["last_name"], expected) # Explicit LinkColumn and regular column using linkify should have equal output self.assertEqual( table.rows[0].get_cell("other_last_name"), table.rows[0].get_cell("last_name") ) def test_get_absolute_url_not_defined(self): """A dict doesn't have a get_absolute_url(), so creating the table should raise a TypeError.""" class Table(tables.Table): first_name = tables.Column() last_name = tables.LinkColumn() table = Table([{"first_name": "Jan Pieter", "last_name": "Waagmeester"}]) message = "for linkify=True, 'Waagmeester' must have a method get_absolute_url" with self.assertRaisesMessage(TypeError, message): table.as_html(build_request()) def test_RelatedLinkColumn(self): carpenter = Occupation.objects.create(name="Carpenter") Person.objects.create(first_name="Bob", last_name="Builder", occupation=carpenter) class Table(tables.Table): occupation = tables.RelatedLinkColumn() occupation_linkify = tables.Column(accessor="occupation", linkify=True) table = Table(Person.objects.all()) url = reverse("occupation", args=[carpenter.pk]) self.assertEqual(table.rows[0].cells["occupation"], f'Carpenter') def test_RelatedLinkColumn_without_model(self): class Table(tables.Table): occupation = tables.RelatedLinkColumn() table = Table([{"occupation": "Fabricator"}]) message = "for linkify=True, 'Fabricator' must have a method get_absolute_url" with self.assertRaisesMessage(TypeError, message): table.rows[0].cells["occupation"] def test_value_returns_a_raw_value_without_html(self): class Table(tables.Table): col = tables.LinkColumn("occupation", args=(A("id"),)) table = Table([{"col": "link-text", "id": 1}]) self.assertEqual(table.rows[0].get_cell_value("col"), "link-text") django-tables2-2.7.5/tests/columns/test_manytomanycolumn.py000066400000000000000000000161561473544236200242110ustar00rootroot00000000000000from random import randint, sample from django.test import TestCase from django.utils.html import format_html, mark_safe, strip_tags import django_tables2 as tables from tests.app.models import Group, Occupation, Person class ManyToManyColumnTest(TestCase): FAKE_NAMES = ( ("Kyle", "Strader"), ("Francis", "Fisher"), ("James", "Jury"), ("Florentina", "Floyd"), ("Mark", "Boyd"), ("Simone", "Fong"), ) @classmethod def setUpTestData(cls): cls.carpenter = Occupation.objects.create(name="Carpenter") for first, last in cls.FAKE_NAMES: Person.objects.create(first_name=first, last_name=last, occupation=cls.carpenter) persons = list(Person.objects.all()) # give everyone 1 to 3 friends for person in persons: person.friends.add(*sample(persons, randint(1, 3))) # Add a person without friends cls.remi = Person.objects.create(first_name="Remi", last_name="Barberin") cls.developers = Group.objects.create(name="developers") cls.developers.members.add( Person.objects.get(first_name="James"), Person.objects.get(first_name="Simone") ) def test_from_model(self): """ Automatically uses the ManyToManyColumn for a ManyToManyField, and calls the Models's `__str__` method to transform the model instance to string. """ class Table(tables.Table): name = tables.Column(accessor="name", order_by=("last_name", "first_name")) class Meta: model = Person fields = ("name", "friends") table = Table(Person.objects.all()) for row in table.rows: cell = row.get_cell("friends") if cell is table.default: continue for friend in cell.split(", "): self.assertTrue(Person.objects.filter(first_name=friend).exists()) def test_linkify_item(self): class Table(tables.Table): name = tables.Column(accessor="name", order_by=("last_name", "first_name")) friends = tables.ManyToManyColumn(linkify_item=True) table = Table(Person.objects.all()) for row in table.rows: friends = row.get_cell("friends") for friend in row.record.friends.all(): self.assertIn(friend.get_absolute_url(), friends) self.assertIn(str(friend), friends) def test_linkify_item_different_model(self): """ Make sure the correct get_absolute_url() is used to linkify the items. """ class GroupTable(tables.Table): name = tables.Column(linkify=True) members = tables.ManyToManyColumn(linkify_item=True) row = GroupTable(Group.objects.all()).rows[0] self.assertEqual( row.get_cell("name"), f'{self.developers.name}', ) self.assertEqual( row.get_cell("members"), 'James, Simone', ) def test_linkify_item_foreign_key(self): class OccupationTable(tables.Table): name = tables.Column(linkify=True) people = tables.ManyToManyColumn(linkify_item=True) row = OccupationTable(Occupation.objects.all()).rows[0] self.assertEqual( row.get_cell("name"), f'{self.carpenter.name}', ) self.assertEqual( row.get_cell("people"), ", ".join( ( 'Kyle', 'Francis', 'James', 'Florentina', 'Mark', 'Simone', ) ), ) def test_custom_separator(self): def assert_sep(sep): class Table(tables.Table): friends = tables.ManyToManyColumn(separator=sep) table = Table(Person.objects.all().order_by("last_name")) for row in table.rows: cell = row.get_cell("friends") if cell is table.default: continue for friend in cell.split(sep): self.assertTrue(Person.objects.filter(first_name=friend).exists()) # normal string, will not be escaped assert_sep("|") # html tag, would normally be escaped, but should not be escaped because it is mark_safe()'ed assert_sep(mark_safe("
")) def test_transform_returns_html(self): class Table(tables.Table): friends = tables.ManyToManyColumn( transform=lambda m: format_html("{}", m.first_name) ) table = Table(Person.objects.all().order_by("last_name")) for row in table.rows: cell = row.get_cell("friends") if cell is table.default: continue for friend in cell.split(", "): stripped = strip_tags(friend) self.assertTrue(Person.objects.filter(first_name=stripped).exists()) def test_orderable_is_false(self): class Table(tables.Table): friends = tables.ManyToManyColumn(orderable=False) table = Table([]) self.assertFalse(table.columns["friends"].orderable) def test_complete_example(self): class Table(tables.Table): name = tables.Column(accessor="name", order_by=("last_name", "first_name")) friends = tables.ManyToManyColumn( transform=lambda o: o.name, filter=lambda o: o.order_by("-last_name") ) table = Table(Person.objects.all().order_by("last_name")) for row in table.rows: friends = row.get_cell("friends") if friends is table.default: self.assertEqual(row.get_cell("name"), self.remi.name) continue # Verify the list is sorted descending friends = list(map(lambda o: o.split(" "), friends.split(", "))) self.assertEqual(friends, sorted(friends, key=lambda item: item[1], reverse=True)) def test_default(self): """A ManyToManyColumn without explicit default should use the table default.""" class Table(tables.Table): name = tables.Column(accessor="name", order_by=("last_name", "first_name")) friends = tables.ManyToManyColumn() table = Table(Person.objects.filter(first_name="Remi")) self.assertEqual(table.rows[0].get_cell("friends"), "—") def test_custom_default(self): """A ManyToManyColumn with explicit default should use it.""" class Table(tables.Table): name = tables.Column(accessor="name", order_by=("last_name", "first_name")) friends = tables.ManyToManyColumn(default="--") table = Table(Person.objects.filter(first_name="Remi")) self.assertEqual(table.rows[0].get_cell("friends"), "--") django-tables2-2.7.5/tests/columns/test_templatecolumn.py000066400000000000000000000106651473544236200236270ustar00rootroot00000000000000from django.template import Context, Template from django.test import SimpleTestCase import django_tables2 as tables from ..utils import build_request class TemplateColumnTest(SimpleTestCase): def test_should_render_in_pinned_row(self): class TestOnlyPinnedTable(tables.Table): foo = tables.TemplateColumn("value={{ value }}") def __init__(self, data): self.pinned = data revised_data = [] super().__init__(revised_data) def get_top_pinned_data(self): return self.pinned table = TestOnlyPinnedTable([{"foo": "bar"}]) for row in table.rows: self.assertEqual(row.get_cell("foo"), "value=bar") template = Template("{% load django_tables2 %}{% render_table table %}") html = template.render(Context({"request": build_request(), "table": table})) self.assertIn("value=bar", html) def test_should_handle_context_on_table(self): class TestTable(tables.Table): col_code = tables.TemplateColumn(template_code="code:{{ record.col }}-{{ foo }}") col_name = tables.TemplateColumn(template_name="test_template_column.html") col_context = tables.TemplateColumn( template_code="{{ label }}:{{ record.col }}-{{ foo }}", extra_context={"label": "label"}, ) table = TestTable([{"col": "brad"}]) self.assertEqual(table.rows[0].get_cell("col_code"), "code:brad-") self.assertEqual(table.rows[0].get_cell("col_name"), "name:brad-empty\n") self.assertEqual(table.rows[0].get_cell("col_context"), "label:brad-") table.context = Context({"foo": "author"}) self.assertEqual(table.rows[0].get_cell("col_code"), "code:brad-author") self.assertEqual(table.rows[0].get_cell("col_name"), "name:brad-author\n") self.assertEqual(table.rows[0].get_cell("col_context"), "label:brad-author") # new table and render using the 'render_table' template tag. table = TestTable([{"col": "brad"}]) template = Template("{% load django_tables2 %}{% render_table table %}") html = template.render( Context({"request": build_request(), "table": table, "foo": "author"}) ) self.assertIn("name:brad-author\n", html) def test_should_support_default(self): class Table(tables.Table): foo = tables.TemplateColumn("default={{ default }}", default="bar") table = Table([{}]) self.assertEqual(table.rows[0].get_cell("foo"), "default=bar") def test_should_support_value(self): class Table(tables.Table): foo = tables.TemplateColumn("value={{ value }}") table = Table([{"foo": "bar"}]) self.assertEqual(table.rows[0].get_cell("foo"), "value=bar") template = Template("{% load django_tables2 %}{% render_table table %}") html = template.render(Context({"request": build_request(), "table": table})) self.assertIn("value=bar", html) def test_should_support_column(self): class Table(tables.Table): tcol = tables.TemplateColumn("column={{ column.name }}") table = Table([{"foo": "bar"}]) self.assertEqual(table.rows[0].get_cell("tcol"), "column=tcol") def test_should_raise_when_called_without_template(self): with self.assertRaises(ValueError): class Table(tables.Table): col = tables.TemplateColumn() def test_should_support_value_with_curly_braces(self): """ https://github.com/bradleyayers/django-tables2/issues/441 """ class Table(tables.Table): track = tables.TemplateColumn("track: {{ value }}") table = Table([{"track": "Beat it {Freestyle}"}]) self.assertEqual(table.rows[0].get_cell("track"), "track: Beat it {Freestyle}") def test_should_strip_tags_for_value(self): class Table(tables.Table): track = tables.TemplateColumn("{{ value }}") table = Table([{"track": "Space Oddity"}]) self.assertEqual(list(table.as_values()), [["Track"], ["Space Oddity"]]) def test_should_strip_whitespace_for_value(self): class Table(tables.Table): track = tables.TemplateColumn(" {{ value }} ") table = Table([{"track": "Space Oddity"}]) self.assertEqual(list(table.as_values()), [["Track"], ["Space Oddity"]]) django-tables2-2.7.5/tests/columns/test_timecolumn.py000066400000000000000000000024411473544236200227430ustar00rootroot00000000000000from datetime import time from django.db import models from django.test import SimpleTestCase import django_tables2 as tables class TimeColumnTest(SimpleTestCase): """ Format string for TimeColumn: https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date """ def test_should_handle_explicit_format(self): class TestTable(tables.Table): time = tables.TimeColumn(format="H:i:s") class Meta: default = "—" table = TestTable([{"time": time(11, 11, 11)}, {"time": None}]) assert table.rows[0].get_cell("time") == "11:11:11" assert table.rows[1].get_cell("time") == "—" def test_should_be_used_for_timefields(self): class TimeModel(models.Model): field = models.TimeField() class Meta: app_label = "django_tables2_test" class Table(tables.Table): class Meta: model = TimeModel assert type(Table.base_columns["field"]) is tables.TimeColumn def test_value_returns_a_raw_value_without_html(self): class Table(tables.Table): col = tables.TimeColumn(format="H:i:s") table = Table([{"col": time(11, 11, 11)}]) assert table.rows[0].get_cell_value("col") == "11:11:11" django-tables2-2.7.5/tests/columns/test_urlcolumn.py000066400000000000000000000040031473544236200226030ustar00rootroot00000000000000from django.db import models from django.test import SimpleTestCase import django_tables2 as tables MEMORY_DATA = [ {"url": "http://example.com", "name": "Example"}, {"url": "https://example.com", "name": "Example (https)"}, {"url": "ftp://example.com", "name": "Example (ftp)"}, ] class UrlColumnTest(SimpleTestCase): def test_should_turn_url_into_hyperlink(self): class TestTable(tables.Table): url = tables.URLColumn() table = TestTable(MEMORY_DATA) self.assertEqual( table.rows[0].get_cell("url"), 'http://example.com' ) self.assertEqual( table.rows[1].get_cell("url"), 'https://example.com' ) def test_should_be_used_for_urlfields(self): class URLModel(models.Model): field = models.URLField() class Meta: app_label = "django_tables2_test" class Table(tables.Table): class Meta: model = URLModel assert type(Table.base_columns["field"]) is tables.URLColumn def test_text_can_be_overridden(self): class Table(tables.Table): url = tables.URLColumn(text="link") table = Table(MEMORY_DATA) assert table.rows[0].get_cell("url") == 'link' def test_text_can_be_overridden_with_callable(self): class Table(tables.Table): url = tables.URLColumn(text=lambda record: record["name"]) table = Table(MEMORY_DATA) assert table.rows[0].get_cell("url") == 'Example' assert table.rows[1].get_cell("url") == 'Example (https)' def test_value_returns_a_raw_value_without_html(self): class TestTable(tables.Table): col = tables.URLColumn() table = TestTable([{"col": "http://example.com"}]) assert table.rows[0].get_cell_value("col") == "http://example.com" django-tables2-2.7.5/tests/test_config.py000066400000000000000000000064701473544236200203620ustar00rootroot00000000000000from unittest.mock import MagicMock, Mock from django.core.paginator import EmptyPage, PageNotAnInteger from django.test import SimpleTestCase, TestCase from django_tables2 import Column, RequestConfig, Table from .app.models import Person from .utils import build_request NOTSET = object() # unique value def MockTable(**kwargs): return MagicMock( prefixed_page_field="page", prefixed_per_page_field="per_page", prefixed_order_by_field="sort", **kwargs, ) class ConfigTest(SimpleTestCase): def test_no_querystring(self): table = MockTable(order_by=NOTSET) RequestConfig(build_request("/")).configure(table) table.paginate.assert_called() self.assertEqual(table.order_by, NOTSET) def test_full_querystring(self): request = build_request("/?page=1&per_page=5&sort=abc") table = MockTable() RequestConfig(request).configure(table) table.paginate.assert_called_with(page=1, per_page=5) self.assertEqual(table.order_by, ["abc"]) def test_partial_querystring(self): table = MockTable() request = build_request("/?page=1&sort=abc") RequestConfig(request, paginate={"per_page": 5}).configure(table) table.paginate.assert_called_with(page=1, per_page=5) self.assertEqual(table.order_by, ["abc"]) def test_silent_page_not_an_integer_error(self): request = build_request("/") table = MockTable(paginate=Mock(side_effect=PageNotAnInteger), paginator=MagicMock()) RequestConfig(request, paginate={"page": "abc", "silent": True}).configure(table) table.paginate.assert_called_with(page="abc") table.paginator.page.assert_called_with(1) def test_silent_empty_page_error(self): request = build_request("/") table = MockTable(paginate=Mock(side_effect=EmptyPage), paginator=MagicMock(num_pages=987)) RequestConfig(request, paginate={"page": 123, "silent": True}).configure(table) table.paginator.page.assert_called_with(987) def test_passing_request_to_constructor(self): """Table constructor should call RequestConfig if a request is passed.""" request = build_request("/?page=1&sort=abc") class SimpleTable(Table): abc = Column() table = SimpleTable([{}], request=request) self.assertTrue(table.columns["abc"].is_ordered) def test_request_is_added_to_the_table(self): table = MockTable() request = build_request("/") RequestConfig(request, paginate=False).configure(table) self.assertEqual(table.request, request) class NoPaginationQueriesTest(TestCase): def test_should_not_count_with_paginate_False(self): """ No extra queries with pagination turned off. https://github.com/jieter/django-tables2/issues/551 """ class MyTable(Table): first_name = Column() class Meta: template_name = "minimal.html" request = build_request() Person.objects.create(first_name="Talip", last_name="Molenschot") table = MyTable(Person.objects.all()) RequestConfig(request, paginate=False).configure(table) with self.assertNumQueries(1): html = table.as_html(request) self.assertIn("", html) django-tables2-2.7.5/tests/test_core.py000066400000000000000000000573071473544236200200520ustar00rootroot00000000000000"""Test the core table functionality.""" import copy import itertools from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator from django.test import SimpleTestCase, TestCase, override_settings from django.utils.translation import gettext_lazy, override import django_tables2 as tables from django_tables2.tables import DeclarativeColumnsMetaclass from .app.models import Occupation, Person from .utils import build_request, parse request = build_request("/") MEMORY_DATA = [ {"i": 2, "alpha": "b", "beta": "b"}, {"i": 1, "alpha": "a", "beta": "c"}, {"i": 3, "alpha": "c", "beta": "a"}, ] class UnorderedTable(tables.Table): i = tables.Column() alpha = tables.Column() beta = tables.Column() class CoreTest(SimpleTestCase): def test_omitting_data(self): with self.assertRaisesMessage(TypeError, "Argument data to UnorderedTable is required"): UnorderedTable() def test_column_named_items(self): """A column named items must not make the table fail.""" # https://github.com/bradleyayers/django-tables2/issues/316 class ItemsTable(tables.Table): items = tables.Column() table = ItemsTable([{"items": 123}, {"items": 2345}]) html = table.as_html(request) self.assertIn("123", html) self.assertIn("2345", html) def test_declarations(self): """Test defining tables by declaration.""" class GeoAreaTable(tables.Table): name = tables.Column() population = tables.Column() self.assertEqual(len(GeoAreaTable.base_columns), 2) self.assertIn("name", GeoAreaTable.base_columns) self.assertFalse(hasattr(GeoAreaTable, "name")) class CountryTable(GeoAreaTable): capital = tables.Column() self.assertEqual(len(CountryTable.base_columns), 3) self.assertIn("capital", CountryTable.base_columns) # multiple inheritance class AddedMixin(tables.Table): added = tables.Column() class CityTable(GeoAreaTable, AddedMixin): mayor = tables.Column() self.assertEqual(len(CityTable.base_columns), 4) self.assertIn("added", CityTable.base_columns) # overwrite a column with a non-column class MayorlessCityTable(CityTable): mayor = None self.assertEqual(len(MayorlessCityTable.base_columns), 3) def test_metaclass_inheritance(self): class Tweaker(type): """Adds an attribute "tweaked" to all classes""" def __new__(cls, name, bases, attrs): attrs["tweaked"] = True return super().__new__(cls, name, bases, attrs) class Meta(Tweaker, DeclarativeColumnsMetaclass): pass class TweakedTableBase(tables.Table): __metaclass__ = Meta name = tables.Column() TweakedTable = Meta("TweakedTable", (TweakedTableBase,), {}) table = TweakedTable([]) self.assertIn("name", table.columns) self.assertTrue(table.tweaked) # now flip the order class FlippedMeta(DeclarativeColumnsMetaclass, Tweaker): pass class FlippedTweakedTableBase(tables.Table): name = tables.Column() FlippedTweakedTable = FlippedMeta("FlippedTweakedTable", (FlippedTweakedTableBase,), {}) table = FlippedTweakedTable([]) self.assertIn("name", table.columns) self.assertTrue(table.tweaked) def test_Meta_attribute_incorrect_types(self): message = "Table1.exclude = 'foo' (type str), but type must be one of (tuple, list, set)" with self.assertRaisesMessage(TypeError, message): class Table1(tables.Table): class Meta: exclude = "foo" message = "Table2.sequence = '...' (type str), but type must be one of (tuple, list, set)" with self.assertRaisesMessage(TypeError, message): class Table2(tables.Table): class Meta: sequence = "..." message = "Table3.model = {} (type dict), but type must be one of (ModelBase)" with self.assertRaisesMessage(TypeError, message): class Table3(tables.Table): class Meta: model = {} def test_table_attrs(self): class TestTable(tables.Table): class Meta: attrs = {} self.assertEqual(TestTable([]).attrs.as_html(), "") class TestTable2(tables.Table): class Meta: attrs = {"a": "b"} self.assertEqual(TestTable2([]).attrs.as_html(), 'a="b"') class TestTable3(tables.Table): pass self.assertEqual(TestTable3([]).attrs.as_html(), "") self.assertEqual(TestTable3([], attrs={"a": "b"}).attrs.as_html(), 'a="b"') class TestTable4(tables.Table): class Meta: attrs = {"a": "b"} self.assertEqual(TestTable4([], attrs={"c": "d"}).attrs.as_html(), 'c="d"') def test_attrs_support_computed_values(self): counter = itertools.count() class TestTable(tables.Table): class Meta: attrs = {"id": lambda: f"test_table_{next(counter)}"} self.assertEqual('id="test_table_0"', TestTable([]).attrs.as_html()) self.assertEqual('id="test_table_1"', TestTable([]).attrs.as_html()) @override_settings(DJANGO_TABLES2_TABLE_ATTRS={"class": "table-compact"}) def test_attrs_from_settings(self): class Table(tables.Table): pass table = Table({}) self.assertEqual(table.attrs.as_html(), 'class="table-compact"') def test_table_attrs_thead_tbody_tfoot(self): class Table(tables.Table): column = tables.Column(footer="foo") class Meta: attrs = { "class": "table-class", "thead": {"class": "thead-class"}, "tbody": {"class": "tbody-class"}, "tfoot": {"class": "tfoot-class"}, } html = Table([]).as_html(build_request()) self.assertIn('
', html) self.assertIn('', html) self.assertIn('', html) self.assertIn('', html) def test_datasource_untouched(self): """Ensure that data the data datasource is not modified by table operations.""" original_data = copy.deepcopy(MEMORY_DATA) table = UnorderedTable(MEMORY_DATA) table.order_by = "i" list(table.rows) self.assertEqual(MEMORY_DATA, original_data) table = UnorderedTable(MEMORY_DATA) table.order_by = "beta" list(table.rows) self.assertEqual(MEMORY_DATA, original_data) def test_should_support_tuple_data_source(self): class SimpleTable(tables.Table): name = tables.Column() table = SimpleTable(({"name": "brad"}, {"name": "davina"})) self.assertEqual(len(table.rows), 2) def test_column_count(self): class SimpleTable(tables.Table): visible = tables.Column(visible=True) hidden = tables.Column(visible=False) # The columns container supports the len() builtin self.assertEqual(len(SimpleTable([]).columns), 1) def test_column_accessor(self): class SimpleTable(UnorderedTable): col1 = tables.Column(accessor="alpha__upper__isupper") col2 = tables.Column(accessor="alpha__upper") beta = tables.Column() table = SimpleTable(MEMORY_DATA) self.assertEqual(table.columns["col1"].accessor, "alpha__upper__isupper") self.assertEqual(table.columns["col2"].accessor, "alpha__upper") self.assertEqual(table.columns["beta"].accessor, "beta") self.assertTrue(table.rows[0].get_cell("col1")) self.assertEqual(table.rows[0].get_cell("col2"), "B") def test_exclude_columns(self): """ Defining ``Table.Meta.exclude`` or providing an ``exclude`` argument when instantiating a table should have the same effect -- exclude those columns from the table. It should have the same effect as not defining the columns originally. """ table = UnorderedTable([], exclude=("i")) self.assertEqual(table.columns.names(), ["alpha", "beta"]) # Table.Meta: exclude=... class PartialTable(UnorderedTable): class Meta: exclude = ("alpha",) table = PartialTable([]) self.assertEqual(table.columns.names(), ["i", "beta"]) # Inheritence -- exclude in parent, add in child class AddonTable(PartialTable): added = tables.Column() table = AddonTable([]) self.assertEqual(table.columns.names(), ["i", "beta", "added"]) # Inheritence -- exclude in child class ExcludeTable(UnorderedTable): added = tables.Column() class Meta: exclude = ("beta",) table = ExcludeTable([]) self.assertEqual(table.columns.names(), ["i", "alpha", "added"]) def test_table_exclude_property_should_override_constructor_argument(self): class SimpleTable(tables.Table): a = tables.Column() b = tables.Column() table = SimpleTable([], exclude=("b",)) self.assertEqual(table.columns.names(), ["a"]) table.exclude = ("a",) self.assertEqual(table.columns.names(), ["b"]) def test_exclude_should_work_on_sequence_too(self): """It should be possible to define a sequence on a table and exclude it in a child of that table.""" class PersonTable(tables.Table): first_name = tables.Column() last_name = tables.Column() occupation = tables.Column() class Meta: sequence = ("first_name", "last_name", "occupation") class AnotherPersonTable(PersonTable): class Meta(PersonTable.Meta): exclude = ("first_name", "last_name") tableA = PersonTable([]) self.assertEqual(tableA.columns.names(), ["first_name", "last_name", "occupation"]) tableB = AnotherPersonTable([]) self.assertEqual(tableB.columns.names(), ["occupation"]) tableC = PersonTable([], exclude=("first_name")) self.assertEqual(tableC.columns.names(), ["last_name", "occupation"]) def test_pagination(self): class BookTable(tables.Table): name = tables.Column() # create some sample data data = list([{"name": f"Book No. {i}"} for i in range(100)]) books = BookTable(data) # external paginator paginator = Paginator(books.rows, 10) self.assertEqual(paginator.num_pages, 10) page = paginator.page(1) self.assertFalse(page.has_previous()) self.assertTrue(page.has_next()) # integrated paginator books.paginate(page=1) self.assertTrue(hasattr(books, "page")) books.paginate(page=1, per_page=10) self.assertEqual(len(list(books.page.object_list)), 10) # new attributes self.assertEqual(books.paginator.num_pages, 10) self.assertFalse(books.page.has_previous()) self.assertTrue(books.page.has_next()) # accessing a non-existant page raises 404 with self.assertRaisesMessage(EmptyPage, "That page contains no results"): books.paginate(Paginator, page=9999, per_page=10) with self.assertRaisesMessage(PageNotAnInteger, "That page number is not an integer"): books.paginate(Paginator, page="abc", per_page=10) def test_pagination_shouldnt_prevent_multiple_rendering(self): class SimpleTable(tables.Table): name = tables.Column() table = SimpleTable([{"name": "brad"}]) table.paginate() self.assertEqual(table.as_html(request), table.as_html(request)) def test_empty_text(self): class TestTable(tables.Table): a = tables.Column() table = TestTable([]) self.assertEqual(table.empty_text, None) class TestTable2(tables.Table): a = tables.Column() class Meta: empty_text = "nothing here" table = TestTable2([]) self.assertEqual(table.empty_text, "nothing here") table = TestTable2([], empty_text="still nothing") self.assertEqual(table.empty_text, "still nothing") def test_empty_text_gettext_lazy(self): class TestTable(tables.Table): a = tables.Column() class Meta: empty_text = gettext_lazy("next") table = TestTable([]) self.assertEqual(table.empty_text, "next") with override("nl"): table = TestTable([]) self.assertEqual(table.empty_text, "volgende") def test_prefix(self): """Verify table prefixes affect the names of querystring parameters.""" class TableA(tables.Table): name = tables.Column() class Meta: prefix = "x" table = TableA([]) html = table.as_html(build_request("/")) self.assertEqual("x", table.prefix) self.assertIn("xsort=name", html) class TableB(tables.Table): last_name = tables.Column() self.assertEqual("", TableB([]).prefix) self.assertEqual("x", TableB([], prefix="x").prefix) table = TableB([]) table.prefix = "x-" html = table.as_html(build_request("/")) self.assertEqual("x-", table.prefix) self.assertIn("x-sort=last_name", html) def test_field_names(self): class TableA(tables.Table): class Meta: order_by_field = "abc" page_field = "def" per_page_field = "ghi" table = TableA([]) self.assertEqual("abc", table.order_by_field) self.assertEqual("def", table.page_field) self.assertEqual("ghi", table.per_page_field) def test_field_names_with_prefix(self): class TableA(tables.Table): class Meta: order_by_field = "sort" page_field = "page" per_page_field = "per_page" prefix = "1-" table = TableA([]) self.assertEqual("1-sort", table.prefixed_order_by_field) self.assertEqual("1-page", table.prefixed_page_field) self.assertEqual("1-per_page", table.prefixed_per_page_field) class TableB(tables.Table): class Meta: order_by_field = "sort" page_field = "page" per_page_field = "per_page" table = TableB([], prefix="1-") self.assertEqual("1-sort", table.prefixed_order_by_field) self.assertEqual("1-page", table.prefixed_page_field) self.assertEqual("1-per_page", table.prefixed_per_page_field) table = TableB([]) table.prefix = "1-" self.assertEqual("1-sort", table.prefixed_order_by_field) self.assertEqual("1-page", table.prefixed_page_field) self.assertEqual("1-per_page", table.prefixed_per_page_field) def test_should_support_a_template_name_to_be_specified(self): class ConstructorSpecifiedTemplateTable(tables.Table): name = tables.Column() table = ConstructorSpecifiedTemplateTable([], template_name="dummy.html") self.assertEqual(table.template_name, "dummy.html") class PropertySpecifiedTemplateTable(tables.Table): name = tables.Column() table = PropertySpecifiedTemplateTable([]) table.template_name = "dummy.html" self.assertEqual(table.template_name, "dummy.html") class DefaultTable(tables.Table): pass table = DefaultTable([]) self.assertEqual(table.template_name, "django_tables2/table.html") def test_template_name_in_meta_class_declaration_should_be_honored(self): class MetaDeclarationSpecifiedTemplateTable(tables.Table): name = tables.Column() class Meta: template_name = "dummy.html" table = MetaDeclarationSpecifiedTemplateTable([]) self.assertEqual(table.template_name, "dummy.html") self.assertEqual(table.as_html(request), "dummy template contents\n") def test_should_support_rendering_multiple_times(self): class MultiRenderTable(tables.Table): name = tables.Column() # test list data table = MultiRenderTable([{"name": "brad"}]) self.assertEqual(table.as_html(request), table.as_html(request)) def test_column_defaults_are_honored(self): class Table(tables.Table): name = tables.Column(default="abcd") class Meta: default = "efgh" table = Table([{}], default="ijkl") self.assertEqual(table.rows[0].get_cell("name"), "abcd") def test_table_meta_defaults_are_honored(self): class Table(tables.Table): name = tables.Column() class Meta: default = "abcd" table = Table([{}]) self.assertEqual(table.rows[0].get_cell("name"), "abcd") def test_table_defaults_are_honored(self): class Table(tables.Table): name = tables.Column() table = Table([{}], default="abcd") self.assertEqual(table.rows[0].get_cell("name"), "abcd") table = Table([{}], default="abcd") table.default = "efgh" self.assertEqual(table.rows[0].get_cell("name"), "efgh") class BoundColumnTest(SimpleTestCase): def test_attrs_bool_error(self): class Table(tables.Table): c_element = tables.Column() class ErrorObject: def __bool__(self): raise NotImplementedError table = Table([{"c_element": ErrorObject()}]) list(table.rows[0].items()) try: table.columns[0].attrs except NotImplementedError: self.fail("__bool__ should not be evaluated!") def test_attrs_falsy_object(self): """Computed attrs in BoundColumn should be passed the column value, even if its __bool__ returns False.""" class Table(tables.Table): c_element = tables.Column() class Meta: attrs = {"td": {"data-column-name": lambda value: value.name}} class FalsyObject: name = "FalsyObject1" def __bool__(self): return False table = Table([{"c_element": FalsyObject()}]) list(table.rows[0].items()) self.assertEqual("FalsyObject1", table.columns[0].attrs["td"]["data-column-name"]) class AsValuesTest(TestCase): AS_VALUES_DATA = [ {"name": "Adrian", "country": "Australia"}, {"name": "Adrian", "country": "Brazil"}, {"name": "Audrey", "country": "Chile"}, {"name": "Bassie", "country": "Belgium"}, ] def test_as_values(self): class Table(tables.Table): name = tables.Column() country = tables.Column() expected = [["Name", "Country"]] + [[r["name"], r["country"]] for r in self.AS_VALUES_DATA] table = Table(self.AS_VALUES_DATA) self.assertEqual(list(table.as_values()), expected) def test_as_values_exclude(self): class Table(tables.Table): name = tables.Column() country = tables.Column() expected = [["Name"]] + [[r["name"]] for r in self.AS_VALUES_DATA] table = Table(self.AS_VALUES_DATA) self.assertEqual(list(table.as_values(exclude_columns=("country",))), expected) def test_as_values_exclude_from_export(self): class Table(tables.Table): name = tables.Column() buttons = tables.Column(exclude_from_export=True) self.assertEqual(list(Table([]).as_values()), [["Name"]]) def test_as_values_visible_False(self): class Table(tables.Table): name = tables.Column() website = tables.Column(visible=False) self.assertEqual(list(Table([]).as_values()), [["Name", "Website"]]) def test_as_values_empty_values(self): """Table's as_values() method returns `None` for missing values.""" class Table(tables.Table): name = tables.Column() country = tables.Column() data = [ {"name": "Adrian", "country": "Brazil"}, {"name": "Audrey"}, {"name": "Bassie", "country": "Belgium"}, {"country": "France"}, ] expected = [["Name", "Country"]] + [[r.get("name"), r.get("country")] for r in data] table = Table(data) self.assertEqual(list(table.as_values()), expected) def test_render_FOO_exception(self): message = "Custom render-method fails" class Table(tables.Table): country = tables.Column() def render_country(self, value): raise Exception(message) return value + " test" with self.assertRaisesMessage(Exception, message): Table(self.AS_VALUES_DATA).as_html(build_request()) def test_as_values_render_FOO(self): class Table(tables.Table): name = tables.Column() country = tables.Column() def render_country(self, value): return value + " test" expected = [["Name", "Country"]] + [ [r["name"], r["country"] + " test"] for r in self.AS_VALUES_DATA ] self.assertEqual(list(Table(self.AS_VALUES_DATA).as_values()), expected) def test_as_values_value_FOO(self): class Table(tables.Table): name = tables.Column() country = tables.Column() def render_country(self, value): return value + " test" def value_country(self, value): return value + " different" expected = [["Name", "Country"]] + [ [r["name"], r["country"] + " different"] for r in self.AS_VALUES_DATA ] self.assertEqual(list(Table(self.AS_VALUES_DATA).as_values()), expected) def test_as_values_accessor_relation(self): programmer = Occupation.objects.create(name="Programmer") henk = Person.objects.create( first_name="Henk", last_name="Voornaman", occupation=programmer ) class Table(tables.Table): name = tables.Column(accessor=tables.A("first_name")) occupation = tables.Column( accessor=tables.A("occupation__name"), verbose_name="Occupation" ) expected = [["First name", "Occupation"], [henk.first_name, programmer.name]] self.assertEqual(list(Table(Person.objects.all()).as_values()), expected) class RowAttrsTest(SimpleTestCase): def test_row_attrs(self): class Table(tables.Table): alpha = tables.Column() beta = tables.Column() table = Table( MEMORY_DATA, row_attrs={"class": lambda table, record: f"row-id-{record['i']}"} ) self.assertEqual(table.rows[0].attrs, {"class": "row-id-2 even"}) def test_row_attrs_in_meta(self): class Table(tables.Table): alpha = tables.Column() beta = tables.Column() class Meta: row_attrs = {"class": lambda record: f"row-id-{record['i']}"} table = Table(MEMORY_DATA) self.assertEqual(table.rows[0].attrs, {"class": "row-id-2 even"}) def test_td_attrs_from_table(self): class Table(tables.Table): alpha = tables.Column() beta = tables.Column() class Meta: attrs = {"td": {"data-column-name": lambda bound_column: bound_column.name}} table = Table(MEMORY_DATA) html = table.as_html(request) td = parse(html).find(".//tbody/tr[1]/td[1]") self.assertEqual(td.attrib, {"data-column-name": "alpha"}) django-tables2-2.7.5/tests/test_export.py000066400000000000000000000344111473544236200204320ustar00rootroot00000000000000import json import os from datetime import date, datetime, time from tempfile import NamedTemporaryFile from unittest import skipIf import pytz import yaml from django.core.exceptions import ImproperlyConfigured from django.shortcuts import render from django.test import TestCase from openpyxl import load_workbook import django_tables2 as tables from django_tables2 import A from django_tables2.config import RequestConfig from .app.models import Occupation, Person, Region from .utils import build_request try: from django_tables2.export.export import TableExport from django_tables2.export.views import ExportMixin except ImproperlyConfigured: TableExport = None NAMES = [("Yildiz", "van der Kuil"), ("Lindi", "Hakvoort"), ("Gerardo", "Castelein")] NAMES_LIST_OF_DICTS = [ {"first_name": first_name, "last_name": last_name} for first_name, last_name in NAMES ] CSV_SEP = "\r\n" EXPECTED_CSV_DATA = tuple(",".join(name) for name in NAMES) EXPECTED_CSV = CSV_SEP.join(("First name,Surname",) + EXPECTED_CSV_DATA) + CSV_SEP EXPECTED_JSON = list( [{"First name": first_name, "Surname": last_name} for first_name, last_name in NAMES] ) class Table(tables.Table): first_name = tables.Column() last_name = tables.Column() class AccessorTable(tables.Table): given_name = tables.Column(accessor=A("first_name"), verbose_name="Given name") surname = tables.Column(accessor=A("last_name")) class View(ExportMixin, tables.SingleTableView): table_class = Table table_pagination = {"per_page": 1} model = Person # required for ListView template_name = "django_tables2/bootstrap.html" @skipIf(TableExport is None, "Tablib is required to run the export tests") class TableExportTest(TestCase): """ github issue #474: null/None values in exports """ def test_None_values(self): table = Table( [ {"first_name": "Yildiz", "last_name": "van der Kuil"}, {"first_name": "Jan", "last_name": None}, ] ) exporter = TableExport("csv", table) expected = ("First name,Last name", "Yildiz,van der Kuil", "Jan,") self.assertEqual(exporter.export(), CSV_SEP.join(expected) + CSV_SEP) def test_null_values(self): Person.objects.create(first_name="Jan", last_name="Coen") class Table(tables.Table): first_name = tables.Column() last_name = tables.Column(verbose_name="Last name") occupation = tables.Column(verbose_name="Occupation") table = Table(Person.objects.all()) exporter = TableExport("csv", table) expected = ("First name,Last name,Occupation", "Jan,Coen,") self.assertEqual(exporter.export(), CSV_SEP.join(expected) + CSV_SEP) def test_export_accessors_list_of_dicts(self): table = AccessorTable(NAMES_LIST_OF_DICTS) exporter = TableExport("csv", table) expected = ("Given name,Surname",) + EXPECTED_CSV_DATA self.assertEqual(exporter.export(), CSV_SEP.join(expected) + CSV_SEP) def test_export_accessors_queryset(self): programmer = Occupation.objects.create(name="Programmer") for first_name, last_name in NAMES: Person.objects.create(first_name=first_name, last_name=last_name, occupation=programmer) class AccessorRelationTable(AccessorTable): occupation = tables.Column(accessor=A("occupation__name"), verbose_name="Occupation") table = AccessorRelationTable(Person.objects.all()) exporter = TableExport("csv", table) expected = ("Given name,Surname,Occupation",) + tuple( row + "," + programmer.name for row in EXPECTED_CSV_DATA ) self.assertEqual(exporter.export(), CSV_SEP.join(expected) + CSV_SEP) def test_export_dataset_kwargs(self): table = Table( [ {"first_name": "Yildiz", "last_name": "van der Kuil"}, {"first_name": "Jan", "last_name": None}, ] ) title = "My Custom Title" exporter = TableExport("xlsx", table, dataset_kwargs={"title": title}) self.assertEqual(exporter.dataset.title, title) def test_export_default_dataset_title(self): class PersonTable(Table): class Meta: model = Person # provides default title table = PersonTable(Person.objects.all()) exporter = TableExport("xlsx", table) self.assertEqual(exporter.dataset.title, Person._meta.verbose_name_plural.title()) @skipIf(TableExport is None, "Tablib is required to run the export tests") class ExportViewTest(TestCase): def setUp(self): for first_name, last_name in NAMES: Person.objects.create(first_name=first_name, last_name=last_name) def test_view_should_support_csv_export(self): response = View.as_view()(build_request("/?_export=csv")) self.assertEqual(response.getvalue().decode("utf8"), EXPECTED_CSV) # should just render the normal table without the _export query response = View.as_view()(build_request("/")) html = response.render().rendered_content self.assertIn("Yildiz", html) self.assertNotIn("Lindy", html) def test_should_raise_error_for_unsupported_file_type(self): table = Table([]) with self.assertRaisesMessage(TypeError, 'Export format "exe" is not supported.'): TableExport(table=table, export_format="exe") def test_should_support_json_export(self): response = View.as_view()(build_request("/?_export=json")) self.assertEqual(json.loads(response.getvalue().decode("utf8")), EXPECTED_JSON) def test_should_support_yaml_export(self): response = View.as_view()(build_request("/?_export=yaml")) self.assertEqual( yaml.load(response.getvalue().decode("utf8"), Loader=yaml.FullLoader), EXPECTED_JSON ) def test_should_support_custom_trigger_param(self): class View(ExportMixin, tables.SingleTableView): table_class = Table export_trigger_param = "export_to" model = Person # required for ListView response = View.as_view()(build_request("/?export_to=json")) self.assertEqual(json.loads(response.getvalue().decode("utf8")), EXPECTED_JSON) def test_should_support_custom_filename(self): class View(ExportMixin, tables.SingleTableView): table_class = Table export_name = "people" model = Person # required for ListView response = View.as_view()(build_request("/?_export=json")) self.assertEqual(response["Content-Disposition"], 'attachment; filename="people.json"') def test_function_view(self): """Test the code used in the docs.""" def table_view(request): table = Table(Person.objects.all()) RequestConfig(request).configure(table) export_format = request.GET.get("_export", None) if TableExport.is_valid_format(export_format): exporter = TableExport(export_format, table) return exporter.response(f"table.{export_format}") return render(request, "django_tables2/table.html", {"table": table}) response = table_view(build_request("/?_export=csv")) self.assertEqual(response.getvalue().decode("utf8"), EXPECTED_CSV) # must also support the normal html table. response = table_view(build_request("/")) html = response.content.decode("utf8") self.assertIn("Yildiz", html) self.assertNotIn("Lindy", html) def test_should_support_custom_dataset_kwargs(self): title = "The Sheet Name" class View(ExportMixin, tables.SingleTableView): table_class = Table model = Person # required for ListView dataset_kwargs = {"title": title} response = View.as_view()(build_request("/?_export=xlsx")) self.assertEqual(response.status_code, 200) tmp = NamedTemporaryFile(suffix=".xlsx", delete=False) try: tmp.write(response.content) tmp.seek(0) wb = load_workbook(tmp.name) self.assertIn(title, wb.sheetnames) finally: tmp.close() os.unlink(tmp.name) class OccupationTable(tables.Table): name = tables.Column() boolean = tables.Column() region = tables.Column() class OccupationView(ExportMixin, tables.SingleTableView): model = Occupation table_class = OccupationTable table_pagination = {"per_page": 1} template_name = "django_tables2/bootstrap.html" @skipIf(TableExport is None, "Tablib is required to run the export tests") class AdvancedExportViewTest(TestCase): @classmethod def setUpClass(cls): super().setUpClass() richard = Person.objects.create(first_name="Richard", last_name="Queener") vlaanderen = Region.objects.create(name="Vlaanderen", mayor=richard) Occupation.objects.create(name="Timmerman", boolean=True, region=vlaanderen) Occupation.objects.create(name="Ecoloog", boolean=False, region=vlaanderen) def test_should_work_with_foreign_keys(self): response = OccupationView.as_view()(build_request("/?_export=xls")) data = response.content # binary data, so not possible to compare to an exact expectation self.assertTrue(data.find(b"Vlaanderen")) self.assertTrue(data.find(b"Ecoloog")) self.assertTrue(data.find(b"Timmerman")) def test_datetime_xls(self): """Verify datatime objects can be exported to xls.""" utc = pytz.timezone("UTC") class Table(tables.Table): date = tables.DateColumn() time = tables.TimeColumn() datetime = tables.DateTimeColumn() class View(ExportMixin, tables.SingleTableView): table_class = Table table_pagination = {"per_page": 1} template_name = "django_tables2/bootstrap.html" def get_queryset(self): return [ { "date": date(2019, 7, 22), "time": time(11, 11, 11), "datetime": utc.localize(datetime(2019, 7, 22, 11, 11, 11)), } ] response = View.as_view()(build_request("/?_export=csv")) data = response.getvalue().decode("utf8") expected_csv = "Date,Time,Datetime\r\n07/22/2019,11:11 a.m.,07/22/2019 1:11 p.m.\r\n" self.assertEqual(data, expected_csv) response = View.as_view()(build_request("/?_export=xls")) self.assertIn(b"07/22/2019 1:11 p.m.", response.content) def test_export_invisible_columns(self): """Verify columns with visible=False *do* get exported.""" DATA = [{"name": "Bess W. Fletcher", "website": "teammonka.com"}] class Table(tables.Table): name = tables.Column() website = tables.Column(visible=False) class View(ExportMixin, tables.SingleTableView): table_class = Table table_pagination = {"per_page": 1} template_name = "django_tables2/bootstrap.html" def get_queryset(self): return DATA response = View.as_view()(build_request()) self.assertNotContains(response, "teammonka.com") response = View.as_view()(build_request("/?_export=csv")) data = response.getvalue().decode() expected_csv = "\r\n".join(("Name,Website", "Bess W. Fletcher,teammonka.com", "")) self.assertEqual(data, expected_csv) def test_should_work_with_foreign_key_fields(self): class OccupationWithForeignKeyFieldsTable(tables.Table): name = tables.Column() boolean = tables.Column() region = tables.Column() mayor = tables.Column(accessor="region__mayor__first_name") class View(ExportMixin, tables.SingleTableView): table_class = OccupationWithForeignKeyFieldsTable table_pagination = {"per_page": 1} model = Occupation template_name = "django_tables2/bootstrap.html" response = View.as_view()(build_request("/?_export=csv")) data = response.getvalue().decode("utf8") expected_csv = "\r\n".join( ( "Name,Boolean,Region,First name", "Timmerman,True,Vlaanderen,Richard", "Ecoloog,False,Vlaanderen,Richard", "", ) ) self.assertEqual(data, expected_csv) def test_should_allow_exclude_columns(self): class OccupationExcludingView(ExportMixin, tables.SingleTableView): table_class = OccupationTable table_pagination = {"per_page": 1} model = Occupation template_name = "django_tables2/bootstrap.html" exclude_columns = ("boolean",) response = OccupationExcludingView.as_view()(build_request("/?_export=csv")) data = response.getvalue().decode("utf8") self.assertEqual(data.splitlines()[0], "Name,Region") @skipIf(TableExport is None, "Tablib is required to run the export tests") class UnicodeExportViewTest(TestCase): def test_exporting_unicode_data(self): unicode_name = "木匠" Occupation.objects.create(name=unicode_name) expected_csv = f"Name,Boolean,Region\r\n{unicode_name},,\r\n" response = OccupationView.as_view()(build_request("/?_export=csv")) self.assertEqual(response.getvalue().decode("utf8"), expected_csv) # smoke tests, hard to test this binary format for string containment response = OccupationView.as_view()(build_request("/?_export=xls")) self.assertGreater(len(response.content), len(expected_csv)) response = OccupationView.as_view()(build_request("/?_export=xlsx")) self.assertGreater(len(response.content), len(expected_csv)) def test_exporting_unicode_header(self): unicode_header = "hé" class Table(tables.Table): name = tables.Column(verbose_name=unicode_header) exporter = TableExport("csv", Table([])) response = exporter.response() self.assertEqual(response.getvalue().decode("utf8"), unicode_header + "\r\n") exporter = TableExport("xls", Table([])) # this would fail if the header contains unicode and string converstion is attempted. exporter.export() django-tables2-2.7.5/tests/test_extra_columns.py000066400000000000000000000157721473544236200220050ustar00rootroot00000000000000from django.contrib.auth import get_user_model from django.template import Context, Template from django.test import TestCase from django.utils.translation import gettext_lazy as _ import django_tables2 as tables from .app.models import Person from .utils import build_request, parse User = get_user_model() data = [ {"name": "Adrian", "country": "Australia"}, {"name": "Roy", "country": "Brazil"}, {"name": "Audrey", "country": "Chile"}, {"name": "Bassie", "country": "Belgium"}, ] class DynamicColumnsTest(TestCase): def test_dynamically_adding_columns(self): """ When adding columns to self.base_columns, they were actually added to the class attribute `Table.base_columns`, and not to the instance attribute, `table.base_columns` issue #403 """ class MyTable(tables.Table): name = tables.Column() # this is obvious: self.assertEqual(list(MyTable(data).columns.columns.keys()), ["name"]) self.assertEqual( list( MyTable(data, extra_columns=[("country", tables.Column())]).columns.columns.keys() ), ["name", "country"], ) # this new instance should not have the extra columns added to the first instance. self.assertEqual(list(MyTable(data).columns.columns.keys()), ["name"]) def test_dynamically_removing_columns(self): class MyTable(tables.Table): name = tables.Column() # this is obvious: self.assertEqual(list(MyTable(data).columns.columns.keys()), ["name"]) self.assertEqual( list( MyTable( data, extra_columns=[("country", tables.Column()), ("name", None)] ).columns.columns.keys() ), ["country"], ) # this new instance should not have the extra columns added to the first instance. self.assertEqual(list(MyTable(data).columns.columns.keys()), ["name"]) def test_sorting_on_dynamically_added_columns(self): class MyTable(tables.Table): name = tables.Column() table = MyTable( data, order_by="-country", extra_columns=[("country", tables.Column(verbose_name=_("country")))], ) root = parse(table.as_html(build_request())) self.assertEqual(root.find(".//tbody/tr/td[2]").text, "Chile") self.assertEqual(root.find(".//tbody/tr[4]/td[2]").text, "Australia") def test_dynamically_override_auto_generated_columns(self): for name, country in data: Person.objects.create(first_name=name, last_name=country) queryset = Person.objects.all() class MyTable(tables.Table): class Meta: model = Person fields = ("first_name", "last_name") self.assertEqual( list(MyTable(queryset).columns.columns.keys()), ["first_name", "last_name"] ) table = MyTable( queryset, extra_columns=[("first_name", tables.Column(attrs={"td": {"style": "color: red;"}}))], ) # we still should have two columns self.assertEqual(list(table.columns.columns.keys()), ["first_name", "last_name"]) # the attrs should be applied to the `first_name` column self.assertEqual( table.columns["first_name"].attrs["td"], {"style": "color: red;", "class": None} ) def test_dynamically_add_column_with_sequence(self): class MyTable(tables.Table): name = tables.Column() class Meta: sequence = ("...", "name") self.assertEqual( list( MyTable(data, extra_columns=[("country", tables.Column())]).columns.columns.keys() ), ["country", "name"], ) # override sequence with an argument. self.assertEqual( list( MyTable( data, extra_columns=[("country", tables.Column())], sequence=("name", "...") ).columns.columns.keys() ), ["name", "country"], ) def test_dynamically_hide_columns(self): class MyTable(tables.Table): name = tables.Column(orderable=False) country = tables.Column(orderable=False) def before_render(self, request): if request.user.username == "Bob": self.columns.hide("country") else: self.columns.show("country") template = Template("{% load django_tables2 %}{% render_table table %}") re_Name = r"" re_Country = r"" table = MyTable(data) request = build_request(user=User.objects.create(username="Bob")) html = table.as_html(request) self.assertRegex(html, re_Name) self.assertNotRegex(html, re_Country) html = template.render(Context({"request": request, "table": table})) self.assertRegex(html, re_Name) self.assertNotRegex(html, re_Country) request = build_request(user=User.objects.create(username="Alice")) html = table.as_html(request) self.assertRegex(html, re_Name) self.assertRegex(html, re_Country) html = template.render(Context({"request": request, "table": table})) self.assertRegex(html, re_Name) self.assertRegex(html, re_Country) def test_sequence_and_extra_columns(self): """ https://github.com/jieter/django-tables2/issues/486 The exact moment the '...' is expanded is crucial here. """ add_occupation_column = True class MyTable(tables.Table): class Meta: model = Person fields = ("first_name", "friends") sequence = ("first_name", "...", "friends") def __init__(self, data, *args, **kwargs): kwargs["extra_columns"] = kwargs.get("extra_columns", []) if add_occupation_column: kwargs["extra_columns"].append( ("occupation", tables.RelatedLinkColumn(orderable=False)) ) super().__init__(data, *args, **kwargs) table = MyTable(Person.objects.all()) self.assertEqual([c.name for c in table.columns], ["first_name", "occupation", "friends"]) add_occupation_column = False table = MyTable(Person.objects.all()) self.assertEqual([c.name for c in table.columns], ["first_name", "friends"]) def test_change_attributes(self): """ https://github.com/jieter/django-tables2/issues/574 """ class Table(tables.Table): mycolumn = tables.Column(orderable=False) def __init__(self, *args, **kwargs): self.base_columns["mycolumn"].verbose_name = "Monday" super().__init__(*args, **kwargs) table = Table([]) self.assertEqual(table.columns["mycolumn"].verbose_name, "Monday") django-tables2-2.7.5/tests/test_faq.py000066400000000000000000000024641473544236200176630ustar00rootroot00000000000000from django.test import SimpleTestCase import django_tables2 as tables from .utils import build_request, parse TEST_DATA = [ {"name": "Belgium", "population": 11200000}, {"name": "Luxembourgh", "population": 540000}, {"name": "France", "population": 66000000}, ] class FaqTest(SimpleTestCase): def test_row_counter_using_templateColumn(self): class CountryTable(tables.Table): counter = tables.TemplateColumn("{{ row_counter }}") name = tables.Column() expected = "" table = CountryTable(TEST_DATA) html = table.as_html(build_request()) self.assertIn(expected, html) # the counter should start at zero the second time too table = CountryTable(TEST_DATA) html = table.as_html(build_request()) self.assertIn(expected, html) def test_row_footer_total(self): class CountryTable(tables.Table): name = tables.Column() population = tables.Column( footer=lambda table: f'Total: {sum(x["population"] for x in table.data)}' ) table = CountryTable(TEST_DATA) html = table.as_html(build_request()) columns = parse(html).findall(".//tfoot/tr")[-1].findall("td") self.assertEqual(columns[1].text, "Total: 77740000") django-tables2-2.7.5/tests/test_footer.py000066400000000000000000000072101473544236200204040ustar00rootroot00000000000000from django.test import SimpleTestCase import django_tables2 as tables from .utils import build_request, parse MEMORY_DATA = [ {"name": "Queensland", "country": "Australia", "population": 4750500}, {"name": "New South Wales", "country": "Australia", "population": 7565500}, {"name": "Victoria", "country": "Australia", "population": 6000000}, {"name": "Tasmania", "country": "Australia", "population": 517000}, ] class FooterTest(SimpleTestCase): def test_has_footer_is_False_without_footer(self): class Table(tables.Table): name = tables.Column() country = tables.Column() population = tables.Column() table = Table(MEMORY_DATA) self.assertFalse(table.has_footer()) def test_footer(self): class Table(tables.Table): name = tables.Column() country = tables.Column(footer="Total:") population = tables.Column( footer=lambda table: sum(x["population"] for x in table.data) ) table = Table(MEMORY_DATA) self.assertTrue(table.has_footer()) html = table.as_html(build_request("/")) columns = parse(html).findall(".//tfoot/tr/td") self.assertEqual(columns[1].text, "Total:") self.assertEqual(columns[2].text, "18833000") def test_footer_disable_on_table(self): """ Showing the footer can be disabled using show_footer argument to the Table constructor """ class Table(tables.Table): name = tables.Column() country = tables.Column(footer="Total:") table = Table(MEMORY_DATA, show_footer=False) self.assertFalse(table.has_footer()) def test_footer_column_method(self): class SummingColumn(tables.Column): def render_footer(self, bound_column, table): return sum(bound_column.accessor.resolve(row) for row in table.data) class TestTable(tables.Table): name = tables.Column() country = tables.Column(footer="Total:") population = SummingColumn() table = TestTable(MEMORY_DATA) html = table.as_html(build_request("/")) columns = parse(html).findall(".//tfoot/tr/td") self.assertEqual(columns[1].text, "Total:") self.assertEqual(columns[2].text, "18833000") def test_footer_has_class(self): class SummingColumn(tables.Column): def render_footer(self, bound_column, table): return sum(bound_column.accessor.resolve(row) for row in table.data) class TestTable(tables.Table): name = tables.Column() country = tables.Column(footer="Total:") population = SummingColumn(attrs={"tf": {"class": "population_sum"}}) table = TestTable(MEMORY_DATA) html = table.as_html(build_request("/")) columns = parse(html).findall(".//tfoot/tr/td") self.assertEqual(columns[2].attrib, {"class": "population_sum"}) def test_footer_custom_attriubtes(self): class SummingColumn(tables.Column): def render_footer(self, bound_column, table): return sum(bound_column.accessor.resolve(row) for row in table.data) class TestTable(tables.Table): name = tables.Column() country = tables.Column(footer="Total:", attrs={"tf": {"align": "right"}}) population = SummingColumn() table = TestTable(MEMORY_DATA) table.columns["country"].attrs["tf"] = {"align": "right"} html = table.as_html(build_request("/")) columns = parse(html).findall(".//tfoot/tr/td") assert "align" in columns[1].attrib django-tables2-2.7.5/tests/test_models.py000066400000000000000000000562501473544236200204010ustar00rootroot00000000000000from collections import defaultdict from unittest import mock from django.contrib.contenttypes.models import ContentType from django.db import models from django.db.models.functions import Length from django.template import Context, Template from django.test import TestCase from django.utils.translation import override as translation_override import django_tables2 as tables from .app.models import Occupation, Person, PersonProxy, Region from .utils import build_request, parse request = build_request() class PersonTable(tables.Table): first_name = tables.Column() last_name = tables.Column() occupation = tables.Column() class ModelsTest(TestCase): def setUp(self): occupation = Occupation.objects.create(name="Programmer", boolean=True) Person.objects.create(first_name="Bradley", last_name="Ayers", occupation=occupation) Person.objects.create(first_name="Chris", last_name="Doble", occupation=occupation) def test_check_types_model(self): class Abstract(models.Model): name = models.CharField(max_length=100) class Meta: abstract = True app_label = "django_tables2_test" class Concrete(Abstract): pass def test(Model): class Table(tables.Table): class Meta: model = Model return Table([]) valid = [ Abstract, Concrete, Occupation, Person, PersonProxy, ContentType.objects.get(model="person").model_class(), ] for Model in valid: test(Model) for Model in [object, {}, dict]: with self.assertRaises(TypeError): test(Model) def test_boundrows_iteration(self): table = PersonTable(Person.objects.all()) expected = list(Person.objects.all()) for i, actual in enumerate([row.record for row in table.rows]): self.assertEqual(expected[i], actual) def test_should_support_rendering_multiple_times(self): class MultiRenderTable(tables.Table): name = tables.Column() # test queryset data table = MultiRenderTable(Person.objects.all()) self.assertEqual(table.as_html(request), table.as_html(request)) def test_doesnotexist_from_accessor_should_use_default(self): class Table(tables.Table): class Meta: model = Person default = "abc" fields = ("first_name", "last_name", "region") table = Table(Person.objects.all()) self.assertEqual(table.rows[0].get_cell("first_name"), "Bradley") self.assertEqual(table.rows[0].get_cell("region"), "abc") def test_unicode_field_names(self): class Table(tables.Table): class Meta: model = Person fields = ("first_name",) table = Table(Person.objects.all()) self.assertEqual(table.rows[0].get_cell("first_name"), "Bradley") def test_Meta_option_model_table(self): """ The ``model`` option on a table causes the table to dynamically add columns based on the fields. """ class OccupationTable(tables.Table): class Meta: model = Occupation expected = ["id", "name", "region", "boolean", "boolean_with_choices"] self.assertEqual(expected, list(OccupationTable.base_columns.keys())) class OccupationTable2(tables.Table): extra = tables.Column() class Meta: model = Occupation expected.append("extra") self.assertEqual(expected, list(OccupationTable2.base_columns.keys())) # be aware here, we already have *models* variable, but we're importing # over the top from django.db import models class ComplexModel(models.Model): char = models.CharField(max_length=200) fk = models.ForeignKey("self", on_delete=models.CASCADE) m2m = models.ManyToManyField("self") class Meta: app_label = "django_tables2_test" class ComplexTable(tables.Table): class Meta: model = ComplexModel self.assertEqual(["id", "char", "fk"], list(ComplexTable.base_columns.keys())) def test_mixins(self): class TableMixin(tables.Table): extra = tables.Column() class OccupationTable(TableMixin, tables.Table): extra2 = tables.Column() class Meta: model = Occupation self.assertEqual( list(OccupationTable.base_columns.keys()), ["extra", "id", "name", "region", "boolean", "boolean_with_choices", "extra2"], ) def test_fields_empty_list_means_no_fields(self): class Table(tables.Table): class Meta: model = Person fields = () table = Table(Person.objects.all()) self.assertEqual(len(table.columns.names()), 0) def test_compound_ordering(self): class SimpleTable(tables.Table): name = tables.Column(order_by=("first_name", "last_name")) table = SimpleTable(Person.objects.all(), order_by="name") html = table.as_html(request) self.assertEqual(parse(html).findall(".//thead/tr/th/a")[0].attrib, {"href": "?sort=-name"}) def test_default_order(self): """ If orderable=False, do not sort queryset. https://github.com/bradleyayers/django-tables2/issues/204 """ class PersonTable(tables.Table): first_name = tables.Column() last_name = tables.Column() table = PersonTable(PersonProxy.objects.all()) table.data.order_by([]) self.assertEqual(list(table.rows[0]), ["Bradley", "Ayers"]) def test_fields_should_implicitly_set_sequence(self): class PersonTable(tables.Table): extra = tables.Column() class Meta: model = Person fields = ("last_name", "first_name") table = PersonTable(Person.objects.all()) self.assertEqual(table.columns.names(), ["last_name", "first_name", "extra"]) def test_model_properties_should_be_useable_for_columns(self): class PersonTable(tables.Table): class Meta: model = Person fields = ("name", "first_name") table = PersonTable(Person.objects.all()) self.assertEqual(list(table.rows[0]), ["Bradley Ayers", "Bradley"]) def test_meta_fields_may_be_list(self): class PersonTable(tables.Table): class Meta: model = Person fields = ["name", "first_name"] table = PersonTable(Person.objects.all()) self.assertEqual(list(table.rows[0]), ["Bradley Ayers", "Bradley"]) def test_meta_fields_can_traverse_relations(self): mayor = Person.objects.create(first_name="Buddy", last_name="Boss") region = Region.objects.create(name="Zuid-Holland", mayor=mayor) occupation = Occupation.objects.create(name="carpenter", region=region) Person.objects.create(first_name="Bob", last_name="Builder", occupation=occupation) class PersonTable(tables.Table): class Meta: model = Person fields = [ "name", "occupation__name", "occupation__region__name", "occupation__region__mayor__name", ] table = PersonTable(Person.objects.filter(occupation=occupation)) self.assertEqual( list(table.rows[0]), ["Bob Builder", "carpenter", "Zuid-Holland", "Buddy Boss"] ) def test_meta_linkify_iterable(self): person = Person.objects.first() class PersonTable(tables.Table): class Meta: model = Person fields = ["name", "occupation__boolean"] linkify = ["name", "occupation__boolean"] table = PersonTable(Person.objects.all()) html = table.as_html(build_request()) self.assertIn(f'{person.name}', html) self.assertIn(f'✔', html) def test_meta_linkify_dict(self): person = Person.objects.first() occupation = person.occupation class PersonTable(tables.Table): class Meta: model = Person fields = ["name", "occupation", "occupation__boolean"] linkify = { "name": lambda record: record.get_absolute_url(), "occupation": True, "occupation__boolean": ("occupation", {"pk": tables.A("occupation__id")}), } table = PersonTable(Person.objects.all()) html = table.as_html(build_request()) self.assertIn(f'{person.name}', html) self.assertIn(f'{occupation.name}', html) self.assertIn( f'✔', html ) class ColumnNameTest(TestCase): def setUp(self): for i in range(10): Person.objects.create(first_name=f"Bob {i}", last_name="Builder") def test_column_verbose_name(self): """ When using queryset data as input for a table, default to using model field verbose names rather than an autogenerated string based on the column name. However if a column does explicitly describe a verbose name, it should be used. """ class PersonTable(tables.Table): """ The test_colX columns are to test that the accessor is used to determine the field on the model, rather than the column name. """ first_name = tables.Column() fn1 = tables.Column(accessor="first_name") fn2 = tables.Column(accessor="first_name__upper") fn3 = tables.Column(accessor="last_name", verbose_name="OVERRIDE") fn4 = tables.Column(accessor="last_name", verbose_name="override") last_name = tables.Column() ln1 = tables.Column(accessor="last_name") ln2 = tables.Column(accessor="last_name__upper") ln3 = tables.Column(accessor="last_name", verbose_name="OVERRIDE") region = tables.Column(accessor="occupation__region__name") r1 = tables.Column(accessor="occupation__region__name") r2 = tables.Column(accessor="occupation__region__name__upper") r3 = tables.Column(accessor="occupation__region__name", verbose_name="OVERRIDE") trans_test = tables.Column() trans_test_lazy = tables.Column() # The Person model has a ``first_name`` and ``last_name`` field, but only # the ``last_name`` field has an explicit ``verbose_name`` set. This means # that we should expect that the two columns that use the ``last_name`` # field should both use the model's ``last_name`` field's ``verbose_name``, # however both fields that use the ``first_name`` field should just use a # titlised version of the column name as the column header. table = PersonTable(Person.objects.all()) # Should be generated (capitalized column name) self.assertEqual("First name", table.columns["first_name"].verbose_name) self.assertEqual("First name", table.columns["fn1"].verbose_name) self.assertEqual("First name", table.columns["fn2"].verbose_name) self.assertEqual("OVERRIDE", table.columns["fn3"].verbose_name) self.assertEqual("override", table.columns["fn4"].verbose_name) # Should use the titlised model field's verbose_name self.assertEqual("Surname", table.columns["last_name"].verbose_name) self.assertEqual("Surname", table.columns["ln1"].verbose_name) self.assertEqual("Surname", table.columns["ln2"].verbose_name) self.assertEqual("OVERRIDE", table.columns["ln3"].verbose_name) self.assertEqual("Name", table.columns["region"].verbose_name) self.assertEqual("Name", table.columns["r1"].verbose_name) self.assertEqual("Name", table.columns["r2"].verbose_name) self.assertEqual("OVERRIDE", table.columns["r3"].verbose_name) self.assertEqual("Translation test", table.columns["trans_test"].verbose_name) self.assertEqual("Translation test lazy", table.columns["trans_test_lazy"].verbose_name) def test_using_Meta_model(self): # Now we'll try using a table with Meta.model class PersonTable(tables.Table): first_name = tables.Column(verbose_name="OVERRIDE") class Meta: model = Person # Issue #16 table = PersonTable(Person.objects.all()) self.assertEqual("Translation test", table.columns["trans_test"].verbose_name) self.assertEqual("Translation test lazy", table.columns["trans_test_lazy"].verbose_name) self.assertEqual("Web site", table.columns["website"].verbose_name) self.assertEqual("Birthdate", table.columns["birthdate"].verbose_name) self.assertEqual("OVERRIDE", table.columns["first_name"].verbose_name) class PersonTable(tables.Table): class Meta: model = Person table = PersonTable(Person.objects.all()) with translation_override("ua"): self.assertEqual( "ТеÑÑ‚ ленивого перекладу", table.columns["trans_test_lazy"].verbose_name ) def test_data_verbose_name(self): table = tables.Table(Person.objects.all()) self.assertEqual(table.data.verbose_name, "person") self.assertEqual(table.data.verbose_name_plural, "people") def test_column_named_delete(self): class DeleteTable(tables.Table): delete = tables.TemplateColumn("[delete button]", verbose_name="") class Meta: model = Person fields = ("name", "delete") person1 = Person.objects.create(first_name="Jan", last_name="Pieter") person2 = Person.objects.create(first_name="John", last_name="Peter") DeleteTable(Person.objects.all()).as_html(build_request()) self.assertEqual(Person.objects.get(pk=person1.pk), person1) self.assertEqual(Person.objects.get(pk=person2.pk), person2) class ModelFieldTest(TestCase): def test_use_to_translated_value(self): """ When a model field uses the ``choices`` option, a table should render the 'pretty' value rather than the database value. See issue #30 for details. """ LANGUAGES = (("en", "English"), ("ru", "Russian")) from django.db import models class Article(models.Model): name = models.CharField(max_length=200) language = models.CharField(max_length=200, choices=LANGUAGES) class Meta: app_label = "tests" def __unicode__(self): return self.name class ArticleTable(tables.Table): class Meta: model = Article table = ArticleTable( [ Article(name="English article", language="en"), Article(name="Russian article", language="ru"), ] ) self.assertEqual("English", table.rows[0].get_cell("language")) self.assertEqual("Russian", table.rows[1].get_cell("language")) def test_column_mapped_to_nonexistant_field(self): """ Issue #9 describes how if a Table has a column that has an accessor that targets a non-existent field, a FieldDoesNotExist error is raised. """ class FaultyPersonTable(PersonTable): missing = tables.Column() table = FaultyPersonTable(Person.objects.all()) table.as_html(request) # the bug would cause this to raise FieldDoesNotExist class OrderingDataTest(TestCase): NAMES = ("Bradley Ayers", "Stevie Armstrong", "VeryLongFirstName VeryLongLastName") @classmethod def setUpClass(cls): super().setUpClass() for name in cls.NAMES: first_name, last_name = name.split() Person.objects.create(first_name=first_name, last_name=last_name) def test_order_by_derived_from_queryset(self): queryset = Person.objects.order_by("first_name", "last_name", "-occupation__name") class PersonTable(tables.Table): name = tables.Column(order_by=("first_name", "last_name")) occupation = tables.Column(order_by=("occupation__name",)) self.assertEqual( PersonTable(queryset.order_by("first_name", "last_name", "-occupation__name")).order_by, ("name", "-occupation"), ) class PersonTable(PersonTable): class Meta: order_by = ("occupation",) self.assertEqual(PersonTable(queryset.all()).order_by, ("occupation",)) def test_queryset_table_data_supports_ordering(self): class Table(tables.Table): class Meta: model = Person table = Table(Person.objects.all()) self.assertEqual(table.rows[0].get_cell("first_name"), "Bradley") table.order_by = "-first_name" self.assertEqual(table.rows[0].get_cell("first_name"), "VeryLongFirstName") def test_queryset_table_data_supports_custom_ordering(self): class Table(tables.Table): class Meta: model = Person order_by = "first_name" def order_first_name(self, queryset, is_descending): # annotate to order by length of first_name + last_name queryset = queryset.annotate( length=Length("first_name") + Length("last_name") ).order_by(("-" if is_descending else "") + "length") return (queryset, True) table = Table(Person.objects.all()) # Shortest full names first self.assertEqual(table.rows[0].get_cell("first_name"), "Bradley") # Longest full names first table.order_by = "-first_name" self.assertEqual(table.rows[0].get_cell("first_name"), "VeryLongFirstName") class ModelSanityTest(TestCase): @classmethod def setUpClass(cls): super().setUpClass() for i in range(10): Person.objects.create(first_name=f"Bob {i}", last_name="Builder") def test_column_with_delete_accessor_shouldnt_delete_records(self): class PersonTable(tables.Table): delete = tables.Column() table = PersonTable(Person.objects.all()) table.as_html(request) self.assertEqual(Person.objects.all().count(), 10) def test_model__str__calls(self): """ Model.__str__ should not be called when not necessary. """ calls = defaultdict(int) def counting__str__(self): calls[self.pk] += 1 return self.first_name with mock.patch("tests.app.models.Person.__str__", counting__str__): for i in range(1, 4): Person.objects.create(first_name=f"Bob {i}", last_name="Builder") class PersonTable(tables.Table): edit = tables.Column() class Meta: model = Person fields = ["first_name", "last_name"] self.assertEqual(calls, {}) table = PersonTable(Person.objects.all()) table.as_html(build_request()) self.assertEqual(calls, {}) def test_render_table_template_tag_numqueries(self): class PersonTable(tables.Table): class Meta: model = Person per_page = 1 request = build_request("/") with self.assertNumQueries(0): table = PersonTable(Person.objects.all()) with self.assertNumQueries(1): # one query for pagination: .count() tables.RequestConfig(request).configure(table) template = Template("{% load django_tables2 %}{% render_table table %}") context = Context({"table": table, "request": request}) with self.assertNumQueries(1): # one query for page records template.render(context) with self.assertNumQueries(0): # re-render should not produce extra queries template.render(context) # second page request = build_request("/?page=2") context = Context({"table": table, "request": request}) with self.assertNumQueries(0): # count is already done, not needed anymore tables.RequestConfig(request).configure(table) with self.assertNumQueries(1): # one query for page records template.render(context) def test_single_query_for_non_paginated_table(self): """ A non-paginated table should not generate a query for each row, but only one query fetch the rows. """ class PersonTable(tables.Table): class Meta: model = Person fields = ("first_name", "last_name") order_by = ("last_name", "first_name") table = PersonTable(Person.objects.all()) with self.assertNumQueries(1): list(table.as_values()) def test_as_html_db_queries_nonpaginated(self): """ Basic tables without pagination should NOT result in a COUNT(*) being done, but only fetch the rows. """ class PersonTable(tables.Table): class Meta: model = Person with self.assertNumQueries(1): html = PersonTable(Person.objects.all()).as_html(build_request()) self.assertIn("Bob 0", html) class TableFactoryTest(TestCase): def test_factory(self): occupation = Occupation.objects.create(name="Programmer") Person.objects.create(first_name="Bradley", last_name="Ayers", occupation=occupation) persons = Person.objects.all() Table = tables.table_factory(Person) table = Table(persons) self.assertIsInstance(table, tables.Table) self.assertEqual(Table.__name__, "PersonAutogeneratedTable") def test_factory_fields_argument(self): fields = ("username",) Table = tables.table_factory(Person, fields=fields) self.assertEqual(Table.Meta.fields, fields) self.assertEqual(Table._meta.fields, fields) def test_factory_exclude_argument(self): exclude = ("username",) Table = tables.table_factory(Person, exclude=exclude) self.assertEqual(Table.Meta.exclude, exclude) self.assertEqual(Table._meta.exclude, exclude) def test_factory_localize_argument(self): localize = ("username",) Table = tables.table_factory(Person, localize=localize) self.assertEqual(Table.Meta.localize, localize) self.assertEqual(Table._meta.localize, localize) def test_factory_with_meta(self): fields = ("first_name",) class TableWithMeta(tables.Table): first_name = tables.Column() class Meta: fields = ("first_name",) Table = tables.table_factory(Person, table=TableWithMeta) self.assertEqual(Table.Meta.fields, fields) django-tables2-2.7.5/tests/test_ordering.py000066400000000000000000000250751473544236200207300ustar00rootroot00000000000000from datetime import datetime from django.test import TestCase import django_tables2 as tables from django_tables2 import RequestConfig from .app.models import Person from .utils import build_request request = build_request("/") MEMORY_DATA = [ {"i": 2, "alpha": "b", "beta": "b"}, {"i": 1, "alpha": "a", "beta": "c"}, {"i": 3, "alpha": "c", "beta": "a"}, ] PEOPLE = [ {"first_name": "Bradley", "last_name": "Ayers"}, {"first_name": "Bradley", "last_name": "Fake"}, {"first_name": "Chris", "last_name": "Doble"}, {"first_name": "Davina", "last_name": "Adisusila"}, {"first_name": "Ross", "last_name": "Ayers"}, ] class UnorderedTable(tables.Table): i = tables.Column() alpha = tables.Column() beta = tables.Column() class OrderedTable(UnorderedTable): class Meta: order_by = "alpha" class OrderingTest(TestCase): def test_meta_ordering_list(self): class Table(UnorderedTable): class Meta: order_by = ["i", "alpha"] self.assertEqual(Table([]).order_by, ("i", "alpha")) self.assertEqual(Table([], order_by=["alpha", "i"]).order_by, ("alpha", "i")) def test_meta_ordering_tuple(self): class Table(UnorderedTable): class Meta: order_by = ("i", "alpha") self.assertEqual(Table([]).order_by, ("i", "alpha")) def test_ordering(self): # fallback to Table.Meta self.assertEqual(OrderedTable([], order_by=None).order_by, ("alpha",)) self.assertEqual(OrderedTable([]).order_by, ("alpha",)) # values of order_by are wrapped in tuples before being returned self.assertEqual(OrderedTable([], order_by="alpha").order_by, ("alpha",)) self.assertEqual(OrderedTable([], order_by=("beta",)).order_by, ("beta",)) for test in [[], (), ""]: table = OrderedTable([]) table.order_by = test self.assertEqual(table.order_by, ()) self.assertEqual(table.order_by, OrderedTable([], order_by=[]).order_by) # apply an ordering table = UnorderedTable([]) table.order_by = "alpha" self.assertEqual(table.order_by, ("alpha",)) self.assertEqual(UnorderedTable([], order_by="alpha").order_by, ("alpha",)) # let's check the data table = OrderedTable(MEMORY_DATA, order_by="beta") self.assertEqual(table.rows[0].get_cell("i"), 3) table = OrderedTable(MEMORY_DATA, order_by="-beta") self.assertEqual(table.rows[0].get_cell("i"), 1) # allow fallback to Table.Meta.order_by table = OrderedTable(MEMORY_DATA) self.assertEqual(table.rows[0].get_cell("i"), 1) # column's can't be ordered if they're not allowed to be class TestTable2(tables.Table): a = tables.Column(orderable=False) b = tables.Column() table = TestTable2([], order_by="a") self.assertEqual(table.order_by, ()) table = TestTable2([], order_by="b") self.assertEqual(table.order_by, ("b",)) # ordering disabled by default class TestTable3(tables.Table): a = tables.Column(orderable=True) b = tables.Column() class Meta: orderable = False table = TestTable3([], order_by="a") self.assertEqual(table.order_by, ("a",)) table = TestTable3([], order_by="b") self.assertEqual(table.order_by, ()) table = TestTable3([], orderable=True, order_by="b") self.assertEqual(table.order_by, ("b",)) def test_ordering_different_types(self): data = [ {"i": 1, "alpha": datetime.now(), "beta": [1]}, {"i": {}, "alpha": None, "beta": ""}, {"i": 2, "alpha": None, "beta": []}, ] table = OrderedTable(data) self.assertEqual(table.rows[0].get_cell("alpha"), "—") table = OrderedTable(data, order_by="i") self.assertEqual(table.rows[0].get_cell("i"), {}) table = OrderedTable(data, order_by="beta") self.assertEqual(table.rows[0].get_cell("beta"), []) def test_multi_column_ordering_by_table(self): class PersonTable(tables.Table): first_name = tables.Column() last_name = tables.Column() brad, brad2, chris, davina, ross = PEOPLE table = PersonTable(PEOPLE, order_by=("first_name", "last_name")) self.assertEqual([brad, brad2, chris, davina, ross], [r.record for r in table.rows]) table = PersonTable(PEOPLE, order_by=("first_name", "-last_name")) self.assertEqual([brad2, brad, chris, davina, ross], [r.record for r in table.rows]) def test_multi_column_ordering_by_column(self): # let's try column order_by using multiple keys class PersonTable(tables.Table): name = tables.Column(order_by=("first_name", "last_name")) brad, brad2, chris, davina, ross = PEOPLE # add 'name' key for each person. for person in PEOPLE: person["name"] = f"{person['first_name']} {person['last_name']}" self.assertEqual(brad["name"], "Bradley Ayers") table = PersonTable(PEOPLE, order_by="name") self.assertEqual([brad, brad2, chris, davina, ross], [r.record for r in table.rows]) table = PersonTable(PEOPLE, order_by="-name") self.assertEqual([ross, davina, chris, brad2, brad], [r.record for r in table.rows]) def test_ordering_by_custom_field(self): """ When defining a custom field in a table, as name=tables.Column() with methods to render and order render_name and order_name, sorting by this column causes an error if the custom field is not in last position. https://github.com/jieter/django-tables2/issues/413 """ Person.objects.create(first_name="Alice", last_name="Beta") Person.objects.create(first_name="Bob", last_name="Alpha") from django.db.models import F, Value from django.db.models.functions import Concat class PersonTable(tables.Table): first_name = tables.Column() last_name = tables.Column() full_name = tables.Column() class Meta: model = Person fields = ("first_name", "last_name", "full_name") def render_full_name(self, record): return record.last_name + " " + record.first_name def order_full_name(self, queryset, is_descending): queryset = queryset.annotate( full_name=Concat(F("last_name"), Value(" "), F("first_name")) ).order_by(("-" if is_descending else "") + "full_name") return queryset, True table = PersonTable(Person.objects.all()) request = build_request("/?sort=full_name&sort=first_name") RequestConfig(request).configure(table) self.assertEqual(table.rows[0].record.first_name, "Bob") def test_list_table_data_supports_ordering(self): class Table(tables.Table): name = tables.Column() data = [{"name": "Bradley"}, {"name": "Davina"}] table = Table(data) self.assertEqual(table.rows[0].get_cell("name"), "Bradley") table.order_by = "-name" self.assertEqual(table.rows[0].get_cell("name"), "Davina") def test_ordering_non_database_data(self): class Table(tables.Table): name = tables.Column() country = tables.Column() data = [ {"name": "Adrian", "country": "Australia"}, {"name": "Adrian", "country": "Brazil"}, {"name": "Audrey", "country": "Chile"}, {"name": "Bassie", "country": "Belgium"}, ] table = Table(data, order_by=("-name", "-country")) self.assertEqual( [(row.get_cell("name"), row.get_cell("country")) for row in table.rows], [ ("Bassie", "Belgium"), ("Audrey", "Chile"), ("Adrian", "Brazil"), ("Adrian", "Australia"), ], ) def test_table_ordering_attributes(self): class Table(tables.Table): alpha = tables.Column() beta = tables.Column() table = Table( MEMORY_DATA, attrs={ "th": { "class": "custom-header-class", "_ordering": { "orderable": "sortable", "ascending": "ascend", "descending": "descend", }, } }, order_by="alpha", ) self.assertIn("sortable", table.columns[0].attrs["th"]["class"]) self.assertIn("ascend", table.columns[0].attrs["th"]["class"]) self.assertIn("custom-header-class", table.columns[1].attrs["th"]["class"]) def test_table_ordering_attributes_in_meta(self): class Table(tables.Table): alpha = tables.Column() beta = tables.Column() class Meta(OrderedTable.Meta): attrs = { "th": { "class": "custom-header-class-in-meta", "_ordering": { "orderable": "sortable", "ascending": "ascend", "descending": "descend", }, } } table = Table(MEMORY_DATA) self.assertIn("sortable", table.columns[0].attrs["th"]["class"]) self.assertIn("ascend", table.columns[0].attrs["th"]["class"]) self.assertIn("custom-header-class-in-meta", table.columns[1].attrs["th"]["class"]) def test_column_ordering_attributes(self): class Table(tables.Table): alpha = tables.Column( attrs={ "th": { "class": "custom-header-class", "_ordering": {"orderable": "sort", "ascending": "ascending"}, } } ) beta = tables.Column( attrs={"th": {"_ordering": {"orderable": "canOrder"}}, "td": {"class": "cell-2"}} ) table = Table(MEMORY_DATA, attrs={"class": "only-on-table"}, order_by="alpha") self.assertNotIn("only-on-table", table.columns[0].attrs["th"]["class"]) self.assertIn("custom-header-class", table.columns[0].attrs["th"]["class"]) self.assertIn("ascending", table.columns[0].attrs["th"]["class"]) self.assertIn("sort", table.columns[0].attrs["th"]["class"]) self.assertIn("canOrder", table.columns[1].attrs["th"]["class"]) django-tables2-2.7.5/tests/test_paginators.py000066400000000000000000000057741473544236200212720ustar00rootroot00000000000000from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator from django.test import TestCase from django_tables2 import LazyPaginator class FakeQuerySet: objects = range(1, 10**6) def count(self): raise AssertionError("LazyPaginator should not call QuerySet.count()") def __getitem__(self, key): return self.objects[key] def __iter__(self): yield next(self.objects) class LazyPaginatorTest(TestCase): def test_compare_to_default_paginator(self): objects = list(range(1, 1000)) paginator = Paginator(objects, 10) lazy_paginator = LazyPaginator(objects, 10) self.assertEqual(paginator.page(1).object_list, lazy_paginator.page(1).object_list) self.assertEqual(paginator.page(10).object_list, lazy_paginator.page(10).object_list) self.assertEqual(paginator.page(100).object_list, lazy_paginator.page(100).object_list) def test_no_count_call(self): paginator = LazyPaginator(FakeQuerySet(), 10) # num_pages initially is None, but is page_number + 1 after requesting a page. self.assertEqual(paginator.num_pages, None) paginator.page(1) self.assertEqual(paginator.num_pages, 2) paginator.page(3) self.assertEqual(paginator.num_pages, 4) paginator.page(1.0) # and again decreases when a lower page nu self.assertEqual(paginator.num_pages, 2) with self.assertRaisesMessage(PageNotAnInteger, "That page number is not an integer"): paginator.page(1.5) with self.assertRaisesMessage(EmptyPage, "That page number is less than 1"): paginator.page(-1) with self.assertRaises(NotImplementedError): paginator.count() with self.assertRaises(NotImplementedError): paginator.page_range() # last page last_page_number = 10**5 paginator.page(last_page_number) with self.assertRaisesMessage(EmptyPage, "That page contains no results"): paginator.page(last_page_number + 1) def test_lookahead(self): objects = list(range(1, 1000)) paginator = LazyPaginator(objects, 10, look_ahead=3) self.assertEqual(paginator.look_ahead, 3) self.assertEqual(paginator.page(1).object_list, list(range(1, 11))) self.assertEqual(paginator.num_pages, 4) self.assertEqual(paginator.page(98).object_list, list(range(971, 981))) self.assertEqual(paginator.num_pages, 100) def test_number_is_none(self): """ When number=None is supplied, the paginator should serve its first page. """ objects = list(range(1, 1000)) paginator = LazyPaginator(objects, 10, look_ahead=3) self.assertEqual(paginator.page(None).object_list, list(range(1, 11))) self.assertEqual(paginator.num_pages, 4) objects = list(range(1, 20)) paginator = LazyPaginator(objects, 10, look_ahead=3) self.assertEqual(paginator.page(None).object_list, list(range(1, 11))) django-tables2-2.7.5/tests/test_pinned_rows.py000066400000000000000000000134341473544236200214420ustar00rootroot00000000000000from django.test import SimpleTestCase import django_tables2 as tables from django_tables2.rows import BoundRow, BoundRows from .utils import build_request, parse class PinnedObj: def __init__(self, name, age): self.name = name self.age = age class SimpleTable(tables.Table): name = tables.Column() occupation = tables.Column() age = tables.Column() def get_top_pinned_data(self): return [PinnedObj("Ron", 90), PinnedObj("Jon", 10)] def get_bottom_pinned_data(self): return [{"occupation": "Sum age", "age": 130}] class PinnedRowsTest(SimpleTestCase): def test_bound_rows_with_pinned_data(self): record = {"name": "Grzegorz", "age": 30, "occupation": "programmer"} table = SimpleTable([record]) self.assertEqual(len(table.rows), 4) # rows + pinned data row = table.rows[0] with self.assertRaises(IndexError): table.rows[1] with self.assertRaises(IndexError): row.get_cell(3) self.assertEqual(row.get_cell("name"), record["name"]) self.assertEqual(row.get_cell("occupation"), record["occupation"]) self.assertEqual(row.get_cell("age"), record["age"]) with self.assertRaises(KeyError): row.get_cell("gamma") self.assertIn("name", row) self.assertIn("occupation", row) self.assertNotIn("gamma", row) def test_as_html(self): """ Ensure that html render correctly. """ request = build_request("/") table = SimpleTable([{"name": "Grzegorz", "age": 30, "occupation": "programmer"}]) root = parse(table.as_html(request)) # One row for header self.assertEqual(len(root.findall(".//thead/tr")), 1) # In the header should be 3 cell. self.assertEqual(len(root.findall(".//thead/tr/th")), 3) # In the body, should be one original record and 3 pinned rows. self.assertEqual(len(root.findall(".//tbody/tr")), 4) self.assertEqual(len(root.findall(".//tbody/tr/td")), 12) # First top pinned row. tr = root.findall(".//tbody/tr") td = tr[0].findall("td") self.assertEqual(td[0].text, "Ron") self.assertEqual(td[1].text, table.default) self.assertEqual(td[2].text, "90") # Second top pinned row. td = tr[1].findall("td") self.assertEqual(td[0].text, "Jon") self.assertEqual(td[1].text, table.default) self.assertEqual(td[2].text, "10") # Original row td = tr[2].findall("td") self.assertEqual(td[0].text, "Grzegorz") self.assertEqual(td[1].text, "programmer") self.assertEqual(td[2].text, "30") # First bottom pinned row. td = tr[3].findall("td") self.assertEqual(td[0].text, table.default) self.assertEqual(td[1].text, "Sum age") self.assertEqual(td[2].text, "130") def test_pinned_row_attrs(self): """ Testing attrs for pinned rows. """ pinned_row_attrs = {"class": "super-mega-row", "data-foo": "bar"} request = build_request("/") record = {"name": "Grzegorz", "age": 30, "occupation": "programmer"} table = SimpleTable([record], pinned_row_attrs=pinned_row_attrs) html = table.as_html(request) self.assertIn("pinned-row", html) self.assertIn("super-mega-row", html) self.assertIn("data-foo", html) def test_ordering(self): """ Change sorting should not change ordering pinned rows. """ request = build_request("/") records = [ {"name": "Alex", "age": 42, "occupation": "programmer"}, {"name": "Greg", "age": 30, "occupation": "programmer"}, ] table = SimpleTable(records, order_by="age") root = parse(table.as_html(request)) tr = root.findall(".//tbody/tr") self.assertEqual(tr[0].findall("td")[2].text, "90") self.assertEqual(tr[1].findall("td")[2].text, "10") self.assertEqual(tr[2].findall("td")[2].text, "30") self.assertEqual(tr[3].findall("td")[2].text, "42") self.assertEqual(tr[4].findall("td")[2].text, "130") table = SimpleTable(records, order_by="-age") root = parse(table.as_html(request)) tr = root.findall(".//tbody/tr") self.assertEqual(tr[0].findall("td")[2].text, "90") self.assertEqual(tr[1].findall("td")[2].text, "10") self.assertEqual(tr[2].findall("td")[2].text, "42") self.assertEqual(tr[3].findall("td")[2].text, "30") self.assertEqual(tr[4].findall("td")[2].text, "130") def test_bound_rows_getitem(self): """ Testing BoundRows.__getitem__() method. Checking the return class for simple value and for slice. Ensure that inside of BoundRows pinned rows are included in length. """ records = [ {"name": "Greg", "age": 30, "occupation": "policeman"}, {"name": "Alex", "age": 42, "occupation": "programmer"}, {"name": "John", "age": 72, "occupation": "official"}, ] table = SimpleTable(records, order_by="age") self.assertIsInstance(table.rows[0], BoundRow) self.assertIsInstance(table.rows[0:2], BoundRows) self.assertEqual(table.rows[0:2][0].get_cell("name"), "Greg") self.assertEqual(len(table.rows[:]), 6) def test_uniterable_pinned_data(self): """ Ensure that, when data for pinned rows are not iterable, the ValueError exception will be raised. """ class FooTable(tables.Table): col = tables.Column() def get_top_pinned_data(self): return 1 tab = FooTable([1, 2, 3]) with self.assertRaises(ValueError): for row in tab.rows: pass django-tables2-2.7.5/tests/test_rows.py000066400000000000000000000117541473544236200201100ustar00rootroot00000000000000from itertools import count from django.db import models from django.test import SimpleTestCase import django_tables2 as tables class RowsTest(SimpleTestCase): def test_bound_rows(self): class SimpleTable(tables.Table): name = tables.Column() data = [{"name": "Bradley"}, {"name": "Chris"}, {"name": "Davina"}] table = SimpleTable(data) # iteration records = [] for row in table.rows: records.append(row.record) self.assertEqual(records, data) def test_bound_row(self): class SimpleTable(tables.Table): name = tables.Column() occupation = tables.Column() age = tables.Column() record = {"name": "Bradley", "age": 20, "occupation": "programmer"} table = SimpleTable([record]) row = table.rows[0] # integer indexing into a row self.assertEqual(row.get_cell(0), record["name"]) self.assertEqual(row.get_cell(1), record["occupation"]) self.assertEqual(row.get_cell(2), record["age"]) with self.assertRaises(IndexError): row.get_cell(3) # column name indexing into a row self.assertEqual(row.get_cell("name"), record["name"]) self.assertEqual(row.get_cell("occupation"), record["occupation"]) self.assertEqual(row.get_cell("age"), record["age"]) with self.assertRaises(KeyError): row.get_cell("gamma") # row should support contains check self.assertIn("name", row) self.assertIn("occupation", row) self.assertNotIn("gamma", row) def test_boud_row_cells(self): class SimpleTable(tables.Table): name = tables.Column() occupation = tables.Column() age = tables.Column() record = {"name": "Bradley", "age": 20, "occupation": "programmer"} table = SimpleTable([record]) row = table.rows[0] self.assertEqual(row.cells.name, record["name"]) self.assertEqual(row.cells.age, record["age"]) self.assertEqual(row.cells.name, row.get_cell("name")) self.assertEqual(row.cells[0], record["name"]) self.assertEqual(row.cells[0], row.get_cell(0)) with self.assertRaises(IndexError): row.cells[3] with self.assertRaises(KeyError): row.cells["gamma"] def test_row_attrs(self): """ If a callable returns an empty string, do not add a space to the CSS class attribute. (#416) """ counter = count() class Table(tables.Table): name = tables.Column() class Meta: row_attrs = {"class": lambda: "" if next(counter) % 2 == 0 else "bla"} table = Table([{"name": "Brian"}, {"name": "Thomas"}, {"name": "John"}]) self.assertEqual(table.rows[0].attrs["class"], "even") self.assertEqual(table.rows[1].attrs["class"], "bla odd") self.assertEqual(table.rows[1].attrs["class"], "even") def test_get_cell_display(self): class A(models.Model): foo = models.CharField(max_length=1, choices=(("a", "valA"), ("b", "valB"))) class Meta: app_label = "tests" class B(models.Model): a = models.ForeignKey(A, on_delete=models.CASCADE) class Meta: app_label = "tests" class C(models.Model): b = models.ForeignKey(B, on_delete=models.CASCADE) class Meta: app_label = "tests" class Tab(tables.Table): a = tables.Column(accessor="b__a__foo") class Meta: model = C a = A(foo="a") b = B(a=a) c = C(b=b) tab = Tab([c]) row = tab.rows[0] self.assertEqual(row.get_cell("a"), "valA") def test_even_odd_css_class(self): """ Test for BoundRow.get_even_odd_css_class() method """ class SimpleTable(tables.Table): foo = tables.Column() def get_top_pinned_data(self): return [{"foo": "top-pinned"}] def get_bottom_pinned_data(self): return [{"foo": "bottom-pinned"}] data = [{"foo", "bar"}, {"foo", "bas"}, {"foo", "baz"}] simple_table = SimpleTable(data) count = 0 prev = None for row in simple_table.rows: if prev: self.assertNotEqual(row.get_even_odd_css_class(), prev.get_even_odd_css_class()) prev = row count += 1 # count should be 5 because: # - First row is a top pinned row. # - Three defaults rows with data. # - Last row is a bottom pinned row. self.assertEqual(count, 5) # Important! # Length of data is five because pinned rows are added to data list. # If pinned rows are added only in the iteration on BoundRows, # then nothing will display if there are *only* pinned rows self.assertEqual(len(simple_table.rows), 5) django-tables2-2.7.5/tests/test_tabledata.py000066400000000000000000000104141473544236200210270ustar00rootroot00000000000000import warnings from django.test import TestCase from django_tables2 import Table from django_tables2.data import TableData, TableListData, TableQuerysetData from .app.models import Person, Region from .utils import build_request class TableDataFactoryTest(TestCase): def test_invalid_data(self): message = "data must be QuerySet-like (have count() and order_by()) or support list(data)" class Klass: pass class Bad: def __len__(self): pass invalid = [None, 1, Klass(), Bad()] for data in invalid: with self.subTest(data=data), self.assertRaisesMessage(ValueError, message): TableData.from_data(data) def test_valid_QuerySet(self): data = TableData.from_data(Person.objects.all()) self.assertIsInstance(data, TableQuerysetData) def test_valid_list_of_dicts(self): data = TableData.from_data([{"name": "John"}, {"name": "Pete"}]) self.assertIsInstance(data, TableListData) self.assertEqual(len(data), 2) def test_valid_tuple_of_dicts(self): data = TableData.from_data(({"name": "John"}, {"name": "Pete"})) self.assertIsInstance(data, TableListData) self.assertEqual(len(data), 2) def test_valid_class(self): class Datasource: def __len__(self): return 1 def __getitem__(self, pos): if pos != 0: raise IndexError() return {"a": 1} data = TableData.from_data(Datasource()) self.assertEqual(len(data), 1) class TableDataTest(TestCase): def test_knows_its_default_name(self): data = TableData.from_data([{}]) self.assertEqual(data.verbose_name, "item") self.assertEqual(data.verbose_name_plural, "items") def test_knows_its_name(self): data = TableData.from_data(Person.objects.all()) self.assertEqual(data.verbose_name, "person") self.assertEqual(data.verbose_name_plural, "people") def generator(max_value): for i in range(max_value): yield {"foo": i, "bar": chr(i), "baz": hex(i), "inv": max_value - i} class TableListsDataTest(TestCase): def test_TableListData_basic_list(self): list_data = list(generator(100)) data = TableListData(list_data) self.assertEqual(len(list_data), len(data)) self.assertEqual(data.verbose_name, "item") self.assertEqual(data.verbose_name_plural, "items") def test_TableListData_with_verbose_name(self): """ TableListData uses the attributes on the listlike object to generate it's verbose_name. """ class listlike(list): verbose_name = "unit" verbose_name_plural = "units" list_data = listlike(generator(100)) data = TableListData(list_data) self.assertEqual(len(list_data), len(data)) self.assertEqual(data.verbose_name, "unit") self.assertEqual(data.verbose_name_plural, "units") class TableQuerysetDataTest(TestCase): def test_custom_TableData(self): """If TableQuerysetData._length is set, no count() query will be performed""" for i in range(11): Person.objects.create(first_name=f"first {i}") data = TableQuerysetData(Person.objects.all()) data._length = 10 table = Table(data=data) self.assertEqual(len(table.data), 10) def test_model_mismatch(self): class MyTable(Table): class Meta: model = Person with warnings.catch_warnings(record=True): MyTable(Region.objects.all()) def test_queryset_union(self): for i in range(10): Person.objects.create(first_name=f"first {i}", last_name="foo") Person.objects.create(first_name=f"first {i * 2}", last_name="bar") class MyTable(Table): class Meta: model = Person fields = ("first_name",) qs = Person.objects.filter(last_name="bar").union(Person.objects.filter(last_name="foo")) table = MyTable(qs.order_by("-last_name")) self.assertEqual(len(table.rows), 20) html = table.as_html(build_request()) self.assertIn("first 18", html) self.assertIn("first 10", html) django-tables2-2.7.5/tests/test_templates.py000066400000000000000000000341551473544236200211140ustar00rootroot00000000000000from django.template import Context, Template from django.test import SimpleTestCase, TestCase, override_settings from django.utils.translation import gettext_lazy, override as translation_override from lxml import etree import django_tables2 as tables from django_tables2.config import RequestConfig from .app.models import Person from .utils import build_request, parse class CountryTable(tables.Table): name = tables.Column() capital = tables.Column(orderable=False, verbose_name=gettext_lazy("Capital")) population = tables.Column(verbose_name="Population Size") currency = tables.Column(visible=False) tld = tables.Column(visible=False, verbose_name="Domain") calling_code = tables.Column(accessor="cc", verbose_name="Phone Ext.") MEMORY_DATA = [ { "name": "Germany", "capital": "Berlin", "population": 83, "currency": "Euro (€)", "tld": "de", "cc": 49, }, {"name": "France", "population": 64, "currency": "Euro (€)", "tld": "fr", "cc": 33}, {"name": "Netherlands", "capital": "Amsterdam", "cc": "31"}, {"name": "Austria", "cc": 43, "currency": "Euro (€)", "population": 8}, ] class TemplateTest(TestCase): @override_settings(DJANGO_TABLES2_TEMPLATE="foo/bar.html") def test_template_override_in_settings(self): class Table(tables.Table): column = tables.Column() table = Table({}) self.assertEqual(table.template_name, "foo/bar.html") def test_as_html(self): request = build_request("/") table = CountryTable(MEMORY_DATA) root = parse(table.as_html(request)) self.assertEqual(len(root.findall(".//thead/tr")), 1) self.assertEqual(len(root.findall(".//thead/tr/th")), 4) self.assertEqual(len(root.findall(".//tbody/tr")), 4) self.assertEqual(len(root.findall(".//tbody/tr/td")), 16) # no data with no empty_text table = CountryTable([]) root = parse(table.as_html(request)) self.assertEqual(1, len(root.findall(".//thead/tr"))) self.assertEqual(4, len(root.findall(".//thead/tr/th"))) self.assertEqual(0, len(root.findall(".//tbody/tr"))) # no data WITH empty_text table = CountryTable([], empty_text="this table is empty") root = parse(table.as_html(request)) self.assertEqual(1, len(root.findall(".//thead/tr"))) self.assertEqual(4, len(root.findall(".//thead/tr/th"))) self.assertEqual(1, len(root.findall(".//tbody/tr"))) self.assertEqual(1, len(root.findall(".//tbody/tr/td"))) self.assertEqual( int(root.find(".//tbody/tr/td").get("colspan")), len(root.findall(".//thead/tr/th")) ) self.assertEqual(root.find(".//tbody/tr/td").text, "this table is empty") # data without header table = CountryTable(MEMORY_DATA, show_header=False) root = parse(table.as_html(request)) self.assertEqual(len(root.findall(".//thead")), 0) self.assertEqual(len(root.findall(".//tbody/tr")), 4) self.assertEqual(len(root.findall(".//tbody/tr/td")), 16) # with custom template table = CountryTable([], template_name="django_tables2/table.html") table.as_html(request) def test_custom_rendering(self): """For good measure, render some actual templates.""" countries = CountryTable(MEMORY_DATA) context = Context({"countries": countries}) # automatic and manual column verbose names template = Template( "{% for column in countries.columns %}{{ column }}/" "{{ column.name }} {% endfor %}" ) result = "Name/name Capital/capital Population Size/population " "Phone Ext./calling_code " assert result == template.render(context) # row values template = Template( "{% for row in countries.rows %}{% for value in row %}" "{{ value }} {% endfor %}{% endfor %}" ) result = "Germany Berlin 83 49 France — 64 33 Netherlands Amsterdam " "— 31 Austria — 8 43 " assert result == template.render(context) class TestQueries(TestCase): def test_as_html_db_queries(self): class PersonTable(tables.Table): class Meta: model = Person request = build_request("/") with self.assertNumQueries(1): PersonTable(Person.objects.all()).as_html(request) def test_render_table_db_queries(self): """ Paginated tables should result in two queries: - one query for pagination: .count() - one query for records on the current page: .all()[start:end] """ Person.objects.create(first_name="brad", last_name="ayers") Person.objects.create(first_name="davina", last_name="adisusila") class PersonTable(tables.Table): class Meta: model = Person per_page = 1 request = build_request("/") with self.assertNumQueries(2): request = build_request("/") table = PersonTable(Person.objects.all()) RequestConfig(request).configure(table) html = Template("{% load django_tables2 %}{% render_table table %}").render( Context({"table": table, "request": request}) ) self.assertIn("brad", html) self.assertIn("ayers", html) class TemplateLocalizeTest(TestCase): simple_test_data = [{"name": 1234.5}] expected_results = {None: "1234.5", False: "1234.5", True: "1 234,5"} # non-breaking space def assert_cond_localized_table(self, localizeit=None, expected=None): """ helper function for defining Table class conditionally """ class TestTable(tables.Table): name = tables.Column(verbose_name="my column", localize=localizeit) self.assert_table_localization(TestTable, expected) def assert_table_localization(self, TestTable, expected): html = TestTable(self.simple_test_data).as_html(build_request()) self.assertIn(f"", html) def test_localization_check(self): self.assert_cond_localized_table(None, None) # unlocalize self.assert_cond_localized_table(False, False) @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True) def test_localization_different_locale(self): with translation_override("pl"): # with default polish locales and enabled thousand separator # 1234.5 is formatted as "1 234,5" with nbsp self.assert_cond_localized_table(True, True) # with localize = False there should be no formatting self.assert_cond_localized_table(False, False) # with localize = None and USE_L10N = True # there should be the same formatting as with localize = True self.assert_cond_localized_table(None, True) def test_localization_check_in_meta(self): class TableNoLocalize(tables.Table): name = tables.Column(verbose_name="my column") class Meta: default = "---" self.assert_table_localization(TableNoLocalize, None) @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True) def test_localization_check_in_meta_different_locale(self): class TableNoLocalize(tables.Table): name = tables.Column(verbose_name="my column") class Meta: default = "---" class TableLocalize(tables.Table): name = tables.Column(verbose_name="my column") class Meta: default = "---" localize = ("name",) class TableUnlocalize(tables.Table): name = tables.Column(verbose_name="my column") class Meta: default = "---" unlocalize = ("name",) class TableLocalizePrecedence(tables.Table): name = tables.Column(verbose_name="my column") class Meta: default = "---" unlocalize = ("name",) localize = ("name",) with translation_override("pl"): # the same as in localization_check. # with localization and polish locale we get formatted output self.assert_table_localization(TableNoLocalize, True) # localize self.assert_table_localization(TableLocalize, True) # unlocalize self.assert_table_localization(TableUnlocalize, False) # test unlocalize has higher precedence self.assert_table_localization(TableLocalizePrecedence, False) def test_localization_of_pagination_strings(self): class Table(tables.Table): foo = tables.Column(verbose_name="my column") bar = tables.Column() class Meta: default = "---" table = Table(map(lambda x: [x, x + 100], range(40))) request = build_request("/?page=2") RequestConfig(request, paginate={"per_page": 10}).configure(table) with translation_override("en"): html = table.as_html(request) self.assertIn("previous", html) self.assertIn("next", html) with translation_override("nl"): html = table.as_html(request) self.assertIn("vorige", html) self.assertIn("volgende", html) with translation_override("fr"): html = table.as_html(request) self.assertIn("précédent", html) self.assertIn("suivant", html) class BootstrapTable(CountryTable): class Meta: template_name = "django_tables2/bootstrap.html" prefix = "bootstrap-" per_page = 2 class BootstrapTemplateTest(SimpleTestCase): def test_bootstrap_template(self): table = BootstrapTable(MEMORY_DATA) request = build_request("/") RequestConfig(request).configure(table) template = Template("{% load django_tables2 %}{% render_table table %}") html = template.render(Context({"request": request, "table": table})) root = parse(html) self.assertEqual(root.find(".//table").attrib, {"class": "table"}) self.assertEqual(len(root.findall(".//thead/tr")), 1) self.assertEqual(len(root.findall(".//thead/tr/th")), 4) self.assertEqual(len(root.findall(".//tbody/tr")), 2) self.assertEqual(len(root.findall(".//tbody/tr/td")), 8) self.assertEqual(root.find('.//ul[@class="pagination"]/li[2]/a').text.strip(), "2") # make sure the link is prefixed self.assertEqual( root.find('.//ul[@class="pagination"]/li[@class="next"]/a').get("href"), "?bootstrap-page=2", ) def test_bootstrap_responsive_template(self): class BootstrapResponsiveTable(BootstrapTable): class Meta(BootstrapTable.Meta): template_name = "django_tables2/bootstrap-responsive.html" table = BootstrapResponsiveTable(MEMORY_DATA) request = build_request("/") RequestConfig(request).configure(table) template = Template("{% load django_tables2 %}{% render_table table %}") html = template.render(Context({"request": request, "table": table})) root = parse(html) self.assertEqual(len(root.findall(".//thead/tr")), 1) self.assertEqual(len(root.findall(".//thead/tr/th")), 4) self.assertEqual(len(root.findall(".//tbody/tr")), 2) self.assertEqual(len(root.findall(".//tbody/tr/td")), 8) self.assertEqual(root.find('.//ul[@class="pagination"]/li[2]/a').text.strip(), "2") class SemanticTemplateTest(SimpleTestCase): def test_semantic_template(self): class SemanticTable(CountryTable): class Meta: template_name = "django_tables2/semantic.html" prefix = "semantic-" per_page = 2 table = SemanticTable(MEMORY_DATA) request = build_request("/") RequestConfig(request).configure(table) template = Template("{% load django_tables2 %}{% render_table table %}") html = template.render(Context({"request": request, "table": table})) root = parse(html) self.assertEqual(len(root.findall(".//thead/tr")), 1) self.assertEqual(len(root.findall(".//thead/tr/th")), 4) self.assertEqual(len(root.findall(".//tbody/tr")), 2) self.assertEqual(len(root.findall(".//tbody/tr/td")), 8) # make sure the link is prefixed next_page = './/tfoot/tr/th/div[@class="ui right floated pagination menu"]/a[1]' self.assertEqual(root.find(next_page).get("href"), "?semantic-page=1") class ValidHTMLTest(SimpleTestCase): template = """Basic html template to render a table {% load django_tables2 %}{% render_table table %} """ allowed_errors = {etree.ErrorTypes.HTML_UNKNOWN_TAG: ["Tag nav invalid"]} context_lines = 4 def test_templates(self): parser = etree.HTMLParser() for name in ("table", "semantic", "bootstrap", "bootstrap4", "bootstrap5"): table = CountryTable( list([MEMORY_DATA] * 10), template_name=f"django_tables2/{name}.html" ).paginate(per_page=5) html = Template(self.template).render( Context({"request": build_request(), "table": table}) ) # will raise lxml.etree.XMLSyntaxError if markup is incorrect etree.fromstring(html, parser) for error in parser.error_log: if ( error.type in self.allowed_errors and error.message in self.allowed_errors[error.type] ): continue lines = html.splitlines() start, end = ( max(0, error.line - self.context_lines), min(error.line + self.context_lines, len(lines)), ) context = "\n".join( [f"{i}: {line}" for i, line in zip(range(start + 1, end + 1), lines[start:end])] ) raise AssertionError(f"template: {table.template_name}; {error} \n {context}") django-tables2-2.7.5/tests/test_templatetags.py000066400000000000000000000321231473544236200216010ustar00rootroot00000000000000from urllib.parse import parse_qs from django.core.exceptions import ImproperlyConfigured from django.core.paginator import Paginator from django.template import Context, RequestContext, Template, TemplateSyntaxError from django.test import SimpleTestCase, TestCase, override_settings from django_tables2 import LazyPaginator, RequestConfig, Table, TemplateColumn from django_tables2.export import ExportMixin from django_tables2.templatetags.django_tables2 import table_page_range from django_tables2.utils import AttributeDict from .app.models import Region from .test_templates import MEMORY_DATA, CountryTable from .utils import build_request, parse class RenderTableTagTest(TestCase): def test_invalid_type(self): template = Template("{% load django_tables2 %}{% render_table table %}") with self.assertRaisesMessage(ValueError, "Expected table or queryset, not dict"): template.render(Context({"request": build_request(), "table": dict()})) def test_basic(self): request = build_request("/") # ensure it works with a multi-order-by table = CountryTable(MEMORY_DATA, order_by=("name", "population")) RequestConfig(request).configure(table) template = Template("{% load django_tables2 %}{% render_table table %}") html = template.render(Context({"request": request, "table": table})) root = parse(html) self.assertEqual(len(root.findall(".//thead/tr")), 1) self.assertEqual(len(root.findall(".//thead/tr/th")), 4) self.assertEqual(len(root.findall(".//tbody/tr")), 4) self.assertEqual(len(root.findall(".//tbody/tr/td")), 16) def test_does_not_mutate_context(self): """ Make sure the tag does not change the context of the template the tag is called from https://github.com/jieter/django-tables2/issues/547 """ class MyTable(Table): col = TemplateColumn(template_code="{{ value }}") table = MyTable([{"col": "foo"}, {"col": "bar"}], template_name="minimal.html") template = Template( "{% load django_tables2 %}" '{% with "foo" as table %}{{ table }}{% render_table mytable %}\n{{ table }}{% endwith %}' ) html = template.render(Context({"request": build_request(), "mytable": table})) lines = html.splitlines() self.assertEqual(lines[0], "foo") self.assertEqual(lines[-1], "foo") def test_table_context_is_RequestContext(self): class MyTable(Table): col = TemplateColumn(template_code="{{ value }}") template = Template("{% load django_tables2 %}{% render_table table %}") html = template.render( Context({"request": build_request(), "table": MyTable([], template_name="csrf.html")}) ) input_tag = parse(html) self.assertEqual(input_tag.get("type"), "hidden") self.assertEqual(input_tag.get("name"), "csrfmiddlewaretoken") self.assertEqual(len(input_tag.get("value")), 64) def test_no_data_without_empty_text(self): table = CountryTable([]) template = Template("{% load django_tables2 %}{% render_table table %}") html = template.render(Context({"request": build_request("/"), "table": table})) root = parse(html) self.assertEqual(len(root.findall(".//thead/tr")), 1) self.assertEqual(len(root.findall(".//thead/tr/th")), 4) self.assertEqual(len(root.findall(".//tbody/tr")), 0) def test_no_data_with_empty_text(self): # no data WITH empty_text request = build_request("/") table = CountryTable([], empty_text="this table is empty") RequestConfig(request).configure(table) template = Template("{% load django_tables2 %}{% render_table table %}") html = template.render(Context({"request": request, "table": table})) root = parse(html) self.assertEqual(len(root.findall(".//thead/tr")), 1) self.assertEqual(len(root.findall(".//thead/tr/th")), 4) self.assertEqual(len(root.findall(".//tbody/tr")), 1) self.assertEqual(len(root.findall(".//tbody/tr/td")), 1) self.assertEqual( int(root.find(".//tbody/tr/td").get("colspan")), len(root.findall(".//thead/tr/th")) ) self.assertEqual(root.find(".//tbody/tr/td").text, "this table is empty") @override_settings(DEBUG=True) def test_missing_variable(self): """Variable that doesn't exist (issue #8)""" template = Template("{% load django_tables2 %}{% render_table this_doesnt_exist %}") with self.assertRaisesMessage(ValueError, "Expected table or queryset, not str"): template.render(Context()) with self.subTest("Also works with DEBUG=False"), override_settings(DEBUG=False): with self.assertRaisesMessage(ValueError, "Expected table or queryset, not str"): template.render(Context()) def test_should_support_template_argument(self): table = CountryTable(MEMORY_DATA, order_by=("name", "population")) template = Template("{% load django_tables2 %}" '{% render_table table "dummy.html" %}') context = RequestContext(build_request(), {"table": table}) self.assertEqual(template.render(context), "dummy template contents\n") def test_template_argument_list(self): template = Template("{% load django_tables2 %}" "{% render_table table template_list %}") context = RequestContext( build_request(), { "table": CountryTable(MEMORY_DATA, order_by=("name", "population")), "template_list": ("dummy.html", "child/foo.html"), }, ) self.assertEqual(template.render(context), "dummy template contents\n") def test_render_table_supports_queryset(self): for name in ("Mackay", "Brisbane", "Maryborough"): Region.objects.create(name=name) template = Template("{% load django_tables2 %}{% render_table qs %}") html = template.render(Context({"qs": Region.objects.all(), "request": build_request("/")})) root = parse(html) self.assertEqual( [e.text for e in root.findall(".//thead/tr/th/a")], ["ID", "Name", "Mayor"] ) td = [[td.text for td in tr.findall("td")] for tr in root.findall(".//tbody/tr")] db = [] for region in Region.objects.all(): db.append([str(region.id), region.name, "—"]) self.assertEqual(td, db) class QuerystringTagTest(SimpleTestCase): def test_basic(self): template = Template( "{% load django_tables2 %}" '{% querystring "name"="Brad" foo.bar=value %}' ) # Should be something like: ?name=Brad&a=b&c=5&age=21 xml = template.render( Context( {"request": build_request("/?a=b&name=dog&c=5"), "foo": {"bar": "age"}, "value": 21} ) ) # Ensure it's valid XML, retrieve the URL url = parse(xml).text qs = parse_qs(url[1:]) # everything after the ? self.assertEqual(qs["name"], ["Brad"]) self.assertEqual(qs["age"], ["21"]) self.assertEqual(qs["a"], ["b"]) self.assertEqual(qs["c"], ["5"]) def test_requires_request(self): template = Template('{% load django_tables2 %}{% querystring "name"="Brad" %}') message = "Tag {% querystring %} requires django.template.context_processors.request to " with self.assertRaisesMessage(ImproperlyConfigured, message): template.render(Context()) def test_supports_without(self): context = Context({"request": build_request("/?a=b&name=dog&c=5"), "a_var": "a"}) template = Template( "{% load django_tables2 %}" '{% querystring "name"="Brad" without a_var %}' ) url = parse(template.render(context)).text qs = parse_qs(url[1:]) # trim the ? self.assertEqual(set(qs.keys()), {"name", "c"}) def test_only_without(self): context = Context({"request": build_request("/?a=b&name=dog&c=5"), "a_var": "a"}) template = Template( "{% load django_tables2 %}" '{% querystring without "a" "name" %}' ) url = parse(template.render(context)).text qs = parse_qs(url[1:]) # trim the ? self.assertEqual(set(qs.keys()), {"c"}) def test_querystring_syntax_error(self): with self.assertRaisesMessage(TemplateSyntaxError, "Malformed arguments to 'querystring'"): Template("{% load django_tables2 %}{% querystring foo= %}") def test_querystring_as_var(self): def assert_querystring_asvar(template_code, expected): template = Template( "{% load django_tables2 %}" "{% querystring " + template_code + " %}" "{{ varname }}" ) # Should be something like: ?name=Brad&a=b&c=5&age=21 xml = template.render( Context({"request": build_request("/?a=b"), "foo": {"bar": "age"}, "value": 21}) ) self.assertIn("", xml) qs = parse(xml).xpath(".//strong")[0].text[1:] self.assertEqual(parse_qs(qs), expected) tests = ( ('"name"="Brad" as=varname', dict(name=["Brad"], a=["b"])), ('as=varname "name"="Brad"', dict(name=["Brad"], a=["b"])), ('"name"="Brad" as=varname without "a" ', dict(name=["Brad"])), ) for argstr, expected in tests: assert_querystring_asvar(argstr, expected) def test_export_url_tag(self): class View(ExportMixin): export_trigger_param = "_do_export" template = Template('{% load django_tables2 %}{% export_url "csv" %}') html = template.render(Context({"request": build_request("?q=foo"), "view": View()})) self.assertEqual(dict(parse_qs(html[1:])), dict(parse_qs("q=foo&_do_export=csv"))) # using a template context variable and a view template = Template("{% load django_tables2 %}{% export_url format %}") html = template.render( Context({"request": build_request("?q=foo"), "format": "xls", "view": View()}) ) self.assertEqual(dict(parse_qs(html[1:])), dict(parse_qs("q=foo&_do_export=xls"))) # using a template context variable template = Template("{% load django_tables2 %}{% export_url format %}") html = template.render(Context({"request": build_request("?q=foo"), "format": "xls"})) self.assertEqual(dict(parse_qs(html[1:])), dict(parse_qs("q=foo&_export=xls"))) # using a template context and change export parameter template = Template('{% load django_tables2 %}{% export_url "xls" "_other_export_param" %}') html = template.render(Context({"request": build_request("?q=foo"), "format": "xls"})) self.assertEqual( dict(parse_qs(html[1:])), dict(parse_qs("q=foo&_other_export_param=xls")) ) def test_render_attributes_test(self): template = Template('{% load django_tables2 %}{% render_attrs attrs class="table" %}') html = template.render(Context({})) self.assertEqual(html, 'class="table"') html = template.render(Context({"attrs": AttributeDict({"class": "table table-striped"})})) self.assertEqual(html, 'class="table table-striped"') class TablePageRangeTest(SimpleTestCase): def test_table_page_range(self): paginator = Paginator(range(1, 1000), 10) self.assertEqual( table_page_range(paginator.page(1), paginator), [1, 2, 3, 4, 5, 6, 7, 8, "...", 100] ) self.assertEqual( table_page_range(paginator.page(10), paginator), [1, "...", 7, 8, 9, 10, 11, 12, "...", 100], ) self.assertEqual( table_page_range(paginator.page(100), paginator), [1, "...", 93, 94, 95, 96, 97, 98, 99, 100], ) def test_table_page_range_num_pages_equals_page_range_plus_one(self): paginator = Paginator(range(1, 11 * 10), 10) self.assertEqual( table_page_range(paginator.page(1), paginator), [1, 2, 3, 4, 5, 6, 7, 8, "...", 11] ) self.assertEqual( table_page_range(paginator.page(6), paginator), [1, 2, 3, 4, 5, 6, 7, 8, "...", 11] ) self.assertEqual( table_page_range(paginator.page(7), paginator), [1, "...", 4, 5, 6, 7, 8, 9, 10, 11] ) def test_table_page_range_lazy_beginning(self): paginator = LazyPaginator(range(1, 1000), 10) self.assertEqual(table_page_range(paginator.page(1), paginator), range(1, 3)) def test_table_page_range_lazy_middle(self): paginator = LazyPaginator(range(1, 1000), 10) self.assertEqual( table_page_range(paginator.page(10), paginator), [1, "...", 4, 5, 6, 7, 8, 9, 10, 11, "..."], ) def test_table_page_range_lazy_last_page(self): paginator = LazyPaginator(range(1, 1000), 10) self.assertEqual( table_page_range(paginator.page(100), paginator), [1, "...", 93, 94, 95, 96, 97, 98, 99, 100], ) django-tables2-2.7.5/tests/test_utils.py000066400000000000000000000205631473544236200202540ustar00rootroot00000000000000from django.db import models from django.test import TestCase from django_tables2.utils import ( Accessor, AttributeDict, OrderBy, OrderByTuple, Sequence, call_with_appropriate, computed_values, segment, signature, ) class OrderByTupleTest(TestCase): def test_basic(self): obt = OrderByTuple(("a", "b", "c")) assert obt == (OrderBy("a"), OrderBy("b"), OrderBy("c")) def test_indexing(self): obt = OrderByTuple(("a", "b", "c")) assert obt[0] == OrderBy("a") assert obt["b"] == OrderBy("b") with self.assertRaises(KeyError): obt["d"] with self.assertRaises(TypeError): obt[("tuple",)] def test_get(self): obt = OrderByTuple(("a", "b", "c")) sentinel = object() assert obt.get("b", sentinel) is obt["b"] # keying assert obt.get("-", sentinel) is sentinel assert obt.get(0, sentinel) is obt["a"] # indexing assert obt.get(3, sentinel) is sentinel def test_opposite(self): assert OrderByTuple(("a", "-b", "c")).opposite == ("-a", "b", "-c") def test_in(self): obt = OrderByTuple(("a", "b", "c")) assert "a" in obt and "-a" in obt def test_sort_key_multiple(self): obt = OrderByTuple(("a", "-b")) items = [{"a": 1, "b": 2}, {"a": 1, "b": 3}] assert sorted(items, key=obt.key) == [{"a": 1, "b": 3}, {"a": 1, "b": 2}] def test_sort_key_empty_comes_first(self): obt = OrderByTuple("a") items = [{"a": 1}, {"a": ""}, {"a": 2}] assert sorted(items, key=obt.key) == [{"a": ""}, {"a": 1}, {"a": 2}] class OrderByTest(TestCase): def test_orderby_ascending(self): a = OrderBy("a") self.assertEqual(a, "a") self.assertEqual(a.bare, "a") self.assertEqual(a.opposite, "-a") self.assertTrue(a.is_ascending) self.assertFalse(a.is_descending) def test_orderby_descending(self): b = OrderBy("-b") self.assertEqual(b, "-b") self.assertEqual(b.bare, "b") self.assertEqual(b.opposite, "b") self.assertTrue(b.is_descending) self.assertFalse(b.is_ascending) def test_error_on_legacy_separator(self): message = "Use '__' to separate path components, not '.' in accessor 'a.b'" with self.assertWarnsRegex(DeprecationWarning, message): OrderBy("a.b") def test_for_queryset(self): ab = OrderBy("a.b") self.assertEqual(ab.for_queryset(), "a__b") ab = OrderBy("a__b") self.assertEqual(ab.for_queryset(), "a__b") class AccessorTest(TestCase): def test_bare(self): self.assertEqual(Accessor("").resolve("Brad"), "Brad") self.assertEqual(Accessor("").resolve({"Brad"}), {"Brad"}) self.assertEqual(Accessor("").resolve({"Brad": "author"}), {"Brad": "author"}) def test_index_lookup(self): self.assertEqual(Accessor("0").resolve("Brad"), "B") self.assertEqual(Accessor("1").resolve("Brad"), "r") self.assertEqual(Accessor("-1").resolve("Brad"), "d") self.assertEqual(Accessor("-2").resolve("Brad"), "a") def test_calling_methods(self): self.assertEqual(Accessor("2__upper").resolve("Brad"), "A") def test_error_on_legacy_separator(self): message = "Use '__' to separate path components, not '.' in accessor '2.upper'" with self.assertWarnsRegex(DeprecationWarning, message): Accessor("2.upper") def test_honors_alters_data(self): class Foo: deleted = False def delete(self): self.deleted = True delete.alters_data = True foo = Foo() with self.assertRaisesMessage(ValueError, "Refusing to call delete() because"): Accessor("delete").resolve(foo) self.assertFalse(foo.deleted) def test_accessor_can_be_quiet(self): self.assertIsNone(Accessor("bar").resolve({}, quiet=True)) def test_penultimate(self): context = {"a": {"a": 1, "b": {"c": 2, "d": 4}}} self.assertEqual(Accessor("a__b__c").penultimate(context), (context["a"]["b"], "c")) self.assertEqual(Accessor("a___b___c___d___e").penultimate(context), (None, "e")) def test_short_circuit_dict(self): context = {"occupation__name": "Carpenter"} self.assertEqual(Accessor("occupation__name").resolve(context), "Carpenter") def test_callable_args_kwargs(self): class MyClass: def method(self, *args, **kwargs): return args, kwargs callable_args = ("arg1", "arg2") callable_kwargs = {"kwarg1": "val1", "kwarg2": "val2"} obj = MyClass() result = Accessor("method", callable_args, callable_kwargs).resolve(obj) self.assertEqual(result, (callable_args, callable_kwargs)) class AccessorTestModel(models.Model): foo = models.CharField(max_length=20) class Meta: app_label = "tests" class AccessorModelTests(TestCase): def test_can_return_field(self): context = AccessorTestModel(foo="bar") self.assertIsInstance(Accessor("foo").get_field(context), models.CharField) def test_returns_None_when_doesnt_exist(self): context = AccessorTestModel(foo="bar") self.assertIsNone(Accessor("bar").get_field(context)) def test_returns_None_if_not_a_model(self): context = {"bar": 234} self.assertIsNone(Accessor("bar").get_field(context)) class AttributeDictTest(TestCase): def test_handles_escaping(self): # django==3.0 replaces ' with ', drop first option if django==2.2 support is removed self.assertIn( AttributeDict({"x": "\"'x&"}).as_html(), ('x=""'x&"', 'x=""'x&"'), ) def test_omits_None(self): self.assertEqual(AttributeDict({"x": None}).as_html(), "") def test_self_wrap(self): x = AttributeDict({"x": "y"}) self.assertEqual(x, AttributeDict(x)) class ComputedValuesTest(TestCase): def test_supports_shallow_structures(self): x = computed_values({"foo": lambda: "bar"}) self.assertEqual(x, {"foo": "bar"}) def test_supports_nested_structures(self): x = computed_values({"foo": lambda: {"bar": lambda: "baz"}}) self.assertEqual(x, {"foo": {"bar": "baz"}}) def test_with_argument(self): x = computed_values({"foo": lambda y: {"bar": lambda y: f"baz-{y}"}}, kwargs=dict(y=2)) self.assertEqual(x, {"foo": {"bar": "baz-2"}}) def test_returns_None_if_not_enough_kwargs(self): x = computed_values({"foo": lambda x: "bar"}) self.assertEqual(x, {"foo": None}) class SegmentTest(TestCase): def test_should_return_all_candidates(self): assert set(segment(("a", "-b", "c"), {"x": "a", "y": ("b", "-c"), "-z": ("b", "-c")})) == { ("x", "-y"), ("x", "z"), } class SequenceTest(TestCase): def test_multiple_ellipsis(self): sequence = Sequence(["foo", "...", "bar", "..."]) with self.assertRaisesMessage(ValueError, "'...' must be used at most once in a sequence."): sequence.expand(["foo"]) class SignatureTest(TestCase): def test_basic(self): def foo(bar, baz): pass args, keywords = signature(foo) assert args == ("bar", "baz") assert keywords is None def test_signature_method(self): class Foo: def foo(self): pass def bar(self, bar, baz): pass def baz(self, bar, *bla, **boo): pass obj = Foo() args, keywords = signature(obj.foo) assert args == () assert keywords is None args, keywords = signature(obj.bar) assert args == ("bar", "baz") assert keywords is None args, keywords = signature(obj.baz) assert args == ("bar",) assert keywords == "boo" def test_catch_all_kwargs(self): def foo(bar, baz, **kwargs): pass args, keywords = signature(foo) assert args == ("bar", "baz") assert keywords == "kwargs" class CallWithAppropriateTest(TestCase): def test_basic(self): def foo(): return "bar" assert call_with_appropriate(foo, {"a": "d", "c": "e"}) == "bar" def bar(baz): return baz assert call_with_appropriate(bar, dict(baz=23)) == 23 django-tables2-2.7.5/tests/test_views.py000066400000000000000000000432571473544236200202560ustar00rootroot00000000000000from math import ceil from typing import Optional import django_filters as filters from django.core.exceptions import ImproperlyConfigured from django.test import TestCase from django.views.generic import TemplateView from django_filters.views import FilterView import django_tables2 as tables from .app.models import Person, Region from .utils import build_request MEMORY_DATA = [ {"name": "Queensland"}, {"name": "New South Wales"}, {"name": "Victoria"}, {"name": "Tasmania"}, ] class SimpleTable(tables.Table): class Meta: model = Region exclude = ("id",) class SimpleView(tables.SingleTableView): table_class = SimpleTable model = Region # required for ListView class SingleTableViewTest(TestCase): @classmethod def setUpClass(cls): super().setUpClass() for region in MEMORY_DATA: Region.objects.create(name=region["name"]) def test_should_support_pagination_options(self): class SimplePaginatedView(tables.SingleTableView): table_class = SimpleTable table_pagination = {"per_page": 1} model = Region response = SimplePaginatedView.as_view()(build_request()) table = response.context_data["table"] self.assertEqual(table.paginator.num_pages, len(MEMORY_DATA)) self.assertEqual(table.paginator.per_page, 1) def test_should_support_pagination_options_listView(self): class SimplePaginatedView(tables.SingleTableView): table_class = SimpleTable paginate_by = 1 model = Region response = SimplePaginatedView.as_view()(build_request()) table = response.context_data["table"] self.assertEqual(table.paginator.num_pages, len(MEMORY_DATA)) self.assertEqual(table.paginator.per_page, 1) def test_should_support_default_pagination(self): total_records = 50 for i in range(1, total_records + 1): Region.objects.create(name=f"region {i:02d} / {total_records}") expected_per_page = 25 class PaginateDefault(tables.SingleTableMixin, TemplateView): table_class = SimpleTable template_name = "minimal.html" def get_table_data(self): return Region.objects.all() response = PaginateDefault.as_view()(build_request()) response.render() table = response.context_data["table"] self.assertEqual(table.paginator.per_page, expected_per_page) self.assertEqual(table.paginator.num_pages, 3) self.assertEqual(len(table.page), expected_per_page) # add one for the header row. self.assertEqual(response.content.decode().count(""), expected_per_page + 1) # in addition to New South Wales, Queensland, Tasmania and Victoria, # 21 records should be displayed. self.assertContains(response, f"21 / {total_records}") self.assertNotContains(response, f"22 / {total_records}") def test_should_support_default_pagination_with_table_options(self): class Table(tables.Table): class Meta: model = Region per_page = 2 class PaginateByDefinedOnView(tables.SingleTableView): table_class = Table model = Region table_data = MEMORY_DATA response = PaginateByDefinedOnView.as_view()(build_request()) table = response.context_data["table"] self.assertEqual(table.paginator.per_page, 2) self.assertEqual(len(table.page), 2) def test_should_support_disabling_pagination_options(self): class SimpleNotPaginatedView(tables.SingleTableView): table_class = SimpleTable table_data = MEMORY_DATA table_pagination = False model = Region # required for ListView response = SimpleNotPaginatedView.as_view()(build_request()) table = response.context_data["table"] self.assertFalse(hasattr(table, "page")) def test_data_from_get_queryset(self): class GetQuerysetView(SimpleView): def get_queryset(self): return Region.objects.filter(name__startswith="Q") response = GetQuerysetView.as_view()(build_request()) table = response.context_data["table"] self.assertEqual(len(table.rows), 1) self.assertEqual(table.rows[0].get_cell("name"), "Queensland") def test_should_support_explicit_table_data(self): class ExplicitDataView(tables.SingleTableView): table_class = SimpleTable table_pagination = {"per_page": 1} model = Region table_data = MEMORY_DATA response = ExplicitDataView.as_view()(build_request()) table = response.context_data["table"] self.assertEqual(table.paginator.num_pages, len(MEMORY_DATA)) def test_paginate_by_on_view_class(self): class Table(tables.Table): class Meta: model = Region class PaginateByDefinedOnView(tables.SingleTableView): table_class = Table model = Region paginate_by = 2 table_data = MEMORY_DATA def get_queryset(self): return Region.objects.all().order_by("name") response = PaginateByDefinedOnView.as_view()(build_request()) table = response.context_data["table"] self.assertEqual(table.paginator.per_page, 2) def test_with_custom_paginator(self): # defined in paginator_class class View(tables.SingleTableView): table_class = tables.Table queryset = Region.objects.all() paginator_class = tables.LazyPaginator response = View.as_view()(build_request()) self.assertIsInstance(response.context_data["table"].paginator, tables.LazyPaginator) # defined in table_pagination class View(tables.SingleTableView): table_class = tables.Table queryset = Region.objects.all() table_pagination = {"paginator_class": tables.LazyPaginator} paginate_orphans = 10 response = View.as_view()(build_request()) paginator = response.context_data["table"].paginator self.assertIsInstance(paginator, tables.LazyPaginator) self.assertEqual(paginator.orphans, 10) def test_get_paginate_by_has_priority_over_paginate_by(self): class View(tables.SingleTableView): table_class = tables.Table queryset = Region.objects.all() # This paginate_by should be ignored because get_paginated_by is overridden paginate_by = 4 def get_paginate_by(self, queryset): # Should paginate by 10 return 10 response = View.as_view()(build_request()) self.assertEqual(response.context_data["table"].paginator.per_page, 10) def test_pass_queryset_to_get_paginate_by(self): # defined in paginator_class class View(tables.SingleTableView): table_class = tables.Table queryset = Region.objects.all() def get_paginate_by(self, table_data): # Should split table items into 2 pages return ceil(len(table_data) / 2) response = View.as_view()(build_request()) self.assertEqual(response.context_data["table"].paginator.num_pages, 2) def test_should_pass_kwargs_to_table_constructor(self): class PassKwargsView(SimpleView): table_data = [] def get_table(self, **kwargs): kwargs.update({"orderable": False}) return super().get_table(**kwargs) response = SimpleView.as_view()(build_request("/")) self.assertTrue(response.context_data["table"].orderable) response = PassKwargsView.as_view()(build_request("/")) self.assertFalse(response.context_data["table"].orderable) def test_should_override_table_pagination(self): class PrefixedTable(SimpleTable): class Meta(SimpleTable.Meta): prefix = "p_" class PrefixedView(SimpleView): table_class = PrefixedTable class PaginationOverrideView(PrefixedView): table_data = MEMORY_DATA def get_table_pagination(self, table): assert isinstance(table, tables.Table) per_page = self.request.GET.get(f"{table.prefixed_per_page_field}_override") if per_page is not None: return {"per_page": per_page} return super().get_table_pagination(table) response = PaginationOverrideView.as_view()(build_request("/?p_per_page_override=2")) self.assertEqual(response.context_data["table"].paginator.per_page, 2) def test_using_get_queryset(self): """ Should not raise a value-error for a View using View.get_queryset() (test for reverting regressing in #423) """ Person.objects.create(first_name="Anton", last_name="Sam") class Table(tables.Table): class Meta: model = Person fields = ("first_name", "last_name") class TestView(tables.SingleTableView): model = Person table_class = Table def get(self, request, *args, **kwargs): self.get_table() from django.http import HttpResponse return HttpResponse() def get_queryset(self): return Person.objects.all() TestView.as_view()(build_request()) def test_get_tables_class(self): view = SimpleView() table_class = view.get_table_class() self.assertEqual(table_class, view.table_class) def test_get_tables_class_auto(self): class SimpleNoTableClassView(tables.SingleTableView): model = Region view = SimpleNoTableClassView() table_class = view.get_table_class() self.assertEqual(table_class.__name__, "RegionAutogeneratedTable") def test_get_tables_class_raises_no_model(self): class Table(tables.SingleTableView): model = None table_class = None view = Table() message = "You must either specify Table.table_class or Table.model" with self.assertRaisesMessage(ImproperlyConfigured, message): view.get_table_class() class SingleTableMixinTest(TestCase): def test_with_non_paginated_view(self): """ SingleTableMixin should not assume it is mixed with a ListView Github issue #326 """ class Table(tables.Table): class Meta: model = Region class View(tables.SingleTableMixin, TemplateView): table_class = Table table_data = MEMORY_DATA template_name = "dummy.html" View.as_view()(build_request()) def test_should_paginate_by_default(self): """ When mixing SingleTableMixin with FilterView, the table should paginate by default """ total_records = 60 for i in range(1, total_records + 1): Region.objects.create(name=f"region {i:02d} / {total_records}") expected_per_page = 25 class RegionFilter(filters.FilterSet): name = filters.CharFilter(lookup_expr="icontains") class PaginateDefault(tables.SingleTableMixin, FilterView): table_class = SimpleTable model = Region filterset_class = RegionFilter template_name = "minimal.html" response = PaginateDefault.as_view()(build_request()) response.render() table = response.context_data["table"] self.assertEqual(table.paginator.per_page, expected_per_page) self.assertEqual(table.paginator.num_pages, 3) self.assertEqual(len(table.page), expected_per_page) self.assertEqual(response.content.decode().count(""), expected_per_page + 1) class TableA(tables.Table): class Meta: model = Person class TableB(tables.Table): class Meta: model = Region exclude = ("id",) class MultiTableMixinTest(TestCase): @classmethod def setUpClass(cls): super().setUpClass() Person.objects.create(first_name="Jan Pieter", last_name="W") NL_PROVICES = ( "Flevoland", "Friesland", "Gelderland", "Groningen", "Limburg", "Noord-Brabant", "Noord-Holland", "Overijssel", "Utrecht", "Zeeland", "Zuid-Holland", ) for name in NL_PROVICES: Region.objects.create(name=name) def test_basic(self): class View(tables.MultiTableMixin, TemplateView): tables = (TableA, TableB) tables_data = (Person.objects.all(), Region.objects.all()) template_name = "multiple.html" response = View.as_view()(build_request("/")) response.render() html = response.rendered_content self.assertIn("table_0-sort=first_name", html) self.assertIn("table_1-sort=name", html) self.assertIn("", html) self.assertIn("", html) def test_supplying_instances(self): class View(tables.MultiTableMixin, TemplateView): tables = (TableA(Person.objects.all()), TableB(Region.objects.all())) template_name = "multiple.html" response = View.as_view()(build_request("/")) response.render() html = response.rendered_content self.assertIn("table_0-sort=first_name", html) self.assertIn("table_1-sort=name", html) self.assertIn("", html) self.assertIn("", html) def test_without_tables(self): class View(tables.MultiTableMixin, TemplateView): template_name = "multiple.html" message = "No tables were specified. Define View.tables" with self.assertRaisesMessage(ImproperlyConfigured, message): View.as_view()(build_request("/")) def test_with_empty_get_tables_list(self): class View(tables.MultiTableMixin, TemplateView): template_name = "multiple.html" def get_tables(self): return [] response = View.as_view()(build_request("/")) response.render() html = response.rendered_content self.assertIn("

Multiple tables using MultiTableMixin

", html) def test_with_empty_class_tables_list(self): class View(tables.MultiTableMixin, TemplateView): template_name = "multiple.html" tables = [] response = View.as_view()(build_request("/")) response.render() html = response.rendered_content self.assertIn("

Multiple tables using MultiTableMixin

", html) def test_length_mismatch(self): class View(tables.MultiTableMixin, TemplateView): tables = (TableA, TableB) tables_data = (Person.objects.all(),) template_name = "multiple.html" message = "len(View.tables_data) != len(View.tables)" with self.assertRaisesMessage(ImproperlyConfigured, message): View.as_view()(build_request("/")) def test_table_pagination(self): class View(tables.MultiTableMixin, TemplateView): tables = (TableB(Region.objects.all()), TableB(Region.objects.all())) template_name = "multiple.html" table_pagination = {"per_page": 5} response = View.as_view()(build_request("/?table_1-page=3")) tableA, tableB = response.context_data["tables"] self.assertEqual(tableA.page.number, 1) self.assertEqual(tableB.page.number, 3) def test_paginate_by(self): class View(tables.MultiTableMixin, TemplateView): tables = (TableB(Region.objects.all()), TableB(Region.objects.all())) template_name = "multiple.html" paginate_by = 5 response = View.as_view()(build_request("/?table_1-page=3")) tableA, tableB = response.context_data["tables"] self.assertEqual(tableA.page.number, 1) self.assertEqual(tableB.page.number, 3) def test_pass_table_data_to_get_paginate_by(self): class View(tables.MultiTableMixin, TemplateView): template_name = "multiple.html" tables = (TableB, TableB) tables_data = (Region.objects.all(), Region.objects.all()) def get_paginate_by(self, table_data) -> Optional[int]: # Split data into 3 pages return ceil(len(table_data) / 3) response = View.as_view()(build_request()) for table in response.context_data["tables"]: self.assertEqual(table.paginator.num_pages, 3) def test_get_tables_data(self): class View(tables.MultiTableMixin, TemplateView): tables = (TableA, TableB) template_name = "multiple.html" def get_tables_data(self): return [Person.objects.all(), Region.objects.all()] response = View.as_view()(build_request("/")) response.render() html = response.rendered_content self.assertIn("", html) self.assertIn("", html) def test_table_prefix(self): class View(tables.MultiTableMixin, TemplateView): tables = ( TableB(Region.objects.all(), prefix="test_prefix"), TableB(Region.objects.all()), ) template_name = "multiple.html" response = View.as_view()(build_request("/")) tableA = response.context_data["tables"][0] tableB = response.context_data["tables"][1] self.assertEqual("test_prefix", tableA.prefix) self.assertIn("table", tableB.prefix) django-tables2-2.7.5/tests/utils.py000066400000000000000000000026371473544236200172170ustar00rootroot00000000000000from io import StringIO import lxml.etree import lxml.html from django.core.handlers.wsgi import WSGIRequest from django.test.client import FakePayload def parse(html): # We use html instead of etree. Because etree can't parse html entities. return lxml.html.fromstring(html) def attrs(xml): """ Helper function that returns a dict of XML attributes, given an element. """ return lxml.html.fromstring(xml).attrib def build_request(uri="/", user=None): """ Return a fresh HTTP GET / request. This is essentially a heavily cutdown version of Django's `~django.test.client.RequestFactory`. """ path, _, querystring = uri.partition("?") request = WSGIRequest( { "CONTENT_TYPE": "text/html; charset=utf-8", "PATH_INFO": path, "QUERY_STRING": querystring, "REMOTE_ADDR": "127.0.0.1", "REQUEST_METHOD": "GET", "SCRIPT_NAME": "", "SERVER_NAME": "testserver", "SERVER_PORT": "80", "SERVER_PROTOCOL": "HTTP/1.1", "wsgi.version": (1, 0), "wsgi.url_scheme": "http", "wsgi.input": FakePayload(b""), "wsgi.errors": StringIO(), "wsgi.multiprocess": True, "wsgi.multithread": False, "wsgi.run_once": False, } ) if user is not None: request.user = user return request django-tables2-2.7.5/tox.ini000066400000000000000000000027121473544236200156500ustar00rootroot00000000000000[tox] args_are_paths = false envlist = py39-{4.2}, py310{4.2,5.0,5.1,master}, py311{4.2,5.0,5.1,master}, py312{5.0,5.1,master}, ; py313{master}, docs, flake8, isort, [testenv] basepython = py39: python3.9 py310: python3.10 py311: python3.11 py312: python3.12 py313: python3.13 usedevelop = true pip_pre = true setenv = PYTHONPATH={toxinidir} PYTHONWARNINGS=all commands = coverage run --source=django_tables2 manage.py test {posargs} deps = 4.2: Django==4.2.* 5.0: Django==5.0.* 5.1: Django==5.1.* master: https://github.com/django/django/archive/master.tar.gz coverage -r{toxinidir}/requirements/common.pip [testenv:docs] basepython = python3.11 whitelist_externals = make changedir = docs setenv = PYTHONWARNINGS=default commands = make html make spelling deps = -r{toxinidir}/docs/requirements.txt [testenv:flake8] basepython = python3.11 deps = flake8==3.7.9 commands = flake8 [flake8] ignore = E731,W503,E203 exclude = .git,__pycache__,.tox,example/app/migrations max-line-length = 120 [testenv:isort] basepython = python3.11 deps = -r requirements/common.pip isort==5.6.4 commands = isort --diff --check django_tables2 test [isort] multi_line_output = 3 include_trailing_comma = True force_grid_wrap = 0 combine_as_imports = True line_length = 100 skip = migrations known_third_party=django,django_filters,pytest,fudge,lxml,pytz known_first_party=django_tables2
\s*Name\s*\s*Country\s*0{self.expected_results[expected]}
Jan PieterZuid-HollandJan PieterZuid-HollandJan PieterZuid-Holland