pax_global_header00006660000000000000000000000064146056025640014522gustar00rootroot0000000000000052 comment=875c4368f1cec75115e8d1b76e91fc9a9a45c6b0 md-toc-9.0.0/000077500000000000000000000000001460560256400127135ustar00rootroot00000000000000md-toc-9.0.0/.github/000077500000000000000000000000001460560256400142535ustar00rootroot00000000000000md-toc-9.0.0/.github/FUNDING.yml000066400000000000000000000001301460560256400160620ustar00rootroot00000000000000tidelift: pypi/md-toc liberapay: frnmst custom: ["https://www.buymeacoffee.com/frnmst"] md-toc-9.0.0/.gitignore000066400000000000000000000025311460560256400147040ustar00rootroot00000000000000# atheris crash logs crash-* *.md *.MD !README.md !CONTRIBUTING.md !SECURITY.md benchmark.csv Makefile .dockerfile_build_python_dist .requirements-freeze-hashes.txt # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ # Translations *.mo *.pot # Django stuff: *.log .static_storage/ .media/ local_settings.py # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ md-toc-9.0.0/.pre-commit-config.yaml000066400000000000000000000051041460560256400171740ustar00rootroot00000000000000# See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: 'v4.5.0' hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files - id: destroyed-symlinks - id: detect-private-key - id: check-ast - id: check-case-conflict - id: debug-statements # Add <# -*- coding: utf-8 -*-> to the top of python files. - id: fix-encoding-pragma # https://github.com/asottile/pyupgrade/issues/748 # https://github.com/asottile/pyupgrade/issues/89 args: ['--remove'] - id: forbid-submodules - id: check-symlinks - id: check-shebang-scripts-are-executable - id: check-case-conflict - id: check-added-large-files args: ['--maxkb=4096'] - id: destroyed-symlinks - id: double-quote-string-fixer - id: mixed-line-ending args: ['--fix=lf'] - id: requirements-txt-fixer - repo: https://github.com/asottile/pyupgrade rev: 'v3.15.0' hooks: - id: pyupgrade # YAPF and flake8 need to remain pinned. - repo: https://github.com/pre-commit/mirrors-yapf rev: 'v0.32.0' # frozen: v0.32.0 hooks: - id: yapf additional_dependencies: ['toml'] - repo: https://github.com/pycqa/flake8 rev: '6.0.0' # frozen: 6.0.0 hooks: - id: flake8 additional_dependencies: ['flake8-docstrings'] - repo: https://github.com/PyCQA/bandit rev: '1.7.7' hooks: - id: bandit args: ['-c', 'pyproject.toml', '--level', 'LOW'] additional_dependencies: ['bandit[toml]'] - repo: https://github.com/pycqa/isort rev: '5.13.2' hooks: - id: isort - repo: https://codeberg.org/frnmst/md-toc rev: '8.2.3' hooks: - id: md-toc args: ['-p', 'cmark', '-l6'] - repo: https://github.com/pypa/pip-audit rev: 'v2.7.2' hooks: - id: pip-audit args: ['--requirement', '.requirements-freeze-hashes.txt', '--local', '--require-hashes', '--desc', '--aliases', '--no-deps', '--disable-pip', '--strict'] - repo: https://github.com/mgedmin/check-manifest rev: '0.49' hooks: - id: check-manifest - repo: local hooks: - id: unit-tests name: unit tests language: system entry: make test verbose: true always_run: true pass_filenames: false - id: build-docs name: build docs language: system entry: make doc verbose: true always_run: true pass_filenames: false - repo: https://github.com/jorisroovers/gitlint rev: 'v0.19.1' hooks: - id: gitlint md-toc-9.0.0/.pre-commit-hooks.yaml000066400000000000000000000006031460560256400170510ustar00rootroot00000000000000--- # Define plugins (hooks) provided by this repo. # How to test: https://pre-commit.com/#developing-hooks-interactively - id: md-toc name: Update markdown table of contents description: 'Automatically generate and add a table of contents to markdown files' language: python types: [markdown] # as detected by pre-commit with identify-cli entry: md_toc args: [-p, github] md-toc-9.0.0/.project.mk000066400000000000000000000006461460560256400147760ustar00rootroot00000000000000PROJECT_NAME := md-toc PYTHON_MODULE_NAME := md_toc MAKEFILE_SOURCE := https://software.franco.net.eu.org/frnmst/python-makefile/raw/branch/master/Makefile.example DOCKER_BUILD_DIST_SOURCE := https://software.franco.net.eu.org/frnmst/python-makefile/raw/branch/master/.dockerfile_build_python_dist.example bootstrap: curl -o Makefile $(MAKEFILE_SOURCE) curl -o .dockerfile_build_python_dist $(DOCKER_BUILD_DIST_SOURCE) md-toc-9.0.0/CONTRIBUTING.md000066400000000000000000000001141460560256400151400ustar00rootroot00000000000000# Contributing See https://docs.franco.net.eu.org/md-toc/contributing.html md-toc-9.0.0/LICENSE.txt000066400000000000000000001045131460560256400145420ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . md-toc-9.0.0/MANIFEST.in000066400000000000000000000005001460560256400144440ustar00rootroot00000000000000global-include LICENSE.txt global-include README.md global-include CONTRIBUTING.md global-include SECURITY.md recursive-include md_toc/cmark *.py global-exclude *.csv *.txt prune assets prune md_toc/tests prune docs prune .venv prune packages prune asciinema prune .tox exclude *.yml *.yaml exclude Makefile .project.mk md-toc-9.0.0/README.md000066400000000000000000000177021460560256400142010ustar00rootroot00000000000000# Markdown Table Of Contents md-toc logo [![PyPI md-toc version](https://img.shields.io/pypi/v/md-toc.svg)](https://pypi.org/project/md-toc/) [![Debian 12 package](https://repology.org/badge/version-for-repo/debian_12/md-toc.svg)](https://repology.org/project/md-toc/versions) [![nixpkgs unstable package](https://repology.org/badge/version-for-repo/nix_unstable/python:md-toc.svg)](https://repology.org/project/python:md-toc/versions) [![Anaconda.org](https://anaconda.org/conda-forge/md-toc/badges/version.svg)](https://anaconda.org/conda-forge/md-toc) [![Downloads](https://pepy.tech/badge/md-toc)](https://pepy.tech/project/md-toc) [![Dependent repos (via libraries.io)](https://img.shields.io/librariesio/dependent-repos/pypi/md-toc.svg)](https://libraries.io/pypi/md-toc/dependents) [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) [![Buy me a coffee](assets/buy_me_a_coffee.svg)](https://buymeacoff.ee/frnmst) Automatically generate and add an accurate table of contents to markdown files. - [Markdown Table Of Contents](#markdown-table-of-contents) - [Video](#video) - [Description](#description) - [Features](#features) - [Examples](#examples) - [API examples](#api-examples) - [Documentation](#documentation) - [CLI Helps](#cli-helps) - [Extras](#extras) - [HTML output](#html-output) - [License](#license) - [Changelog and trusted source](#changelog-and-trusted-source) - [Support this project](#support-this-project) ## Video [![image](./assets/md-toc_youtube_video_thumbnail.png)](https://www.youtube.com/watch?v=guyVdPNmC0A&t=49s) ## Description The table of contents (a.k.a: TOC) generated by this program is designed to work with several markdown parsers such as the ones used by GitHub and GitLab. Rules for generating the TOC are determined by the selected markdown parser. md-toc aimes infact to be as conformant as possible in respect to each one of them. This was possible by studying the available documentations and by reverse engineering the source codes. GitHub and GitLab have introduced their version of the markdown TOC after md-toc and similar tools were created: - in March 2021 GitHub added an [interactive TOC button](https://github.blog/changelog/2021-04-13-table-of-contents-support-in-markdown-files/) at the top-left of readme files. This system works for markdown and others - GitLab added an [extension](https://docs.gitlab.com/ee/user/markdown.html#table-of-contents) called `Table of contents` to its Gitlab Flavored Mardown ## Features - works offline - edits file in place using a TOC marker (default ``) or output to standard output - maximum heading level selection (1 to 6) - list indentation based on heading, which can optionally be disabled - outputs an ordered or unordered TOC list with list marker selection - creates anchor links to markdown headings by default or a plain list as alternative - checks if heading level is coherent: this avoid creating an erroneous TOC. This feature can be disabled if needed - skip any number lines before generating the TOC - can read content from standard input - handles multiple files at once - selection of newline string - check if there is difference between existing TOC in file and newly generated one - supports GitHub, GitLab, Commonmark, Redcarpet and others - [pre-commit](https://pre-commit.com/) md-toc [hook](https://docs.franco.net.eu.org/md-toc/pre_commit_hook.html) And more! See the [feature comparison table](https://docs.franco.net.eu.org/md-toc/features.html) ## Examples You can use md-toc in your blog, documentation based on markdown, GitHub pages, markdown files in Nextcloud, etc... I use it in [my Jekyll-based blog](https://blog.franco.net.eu.org/) along with its [pre-commit hook](https://software.franco.net.eu.org/frnmst/blog/src/branch/master/.pre-commit-config.yaml). I also use it in most repositories where `README.md` files are present. Most markdown renderers do not provide a way to automatically generate a TOC so md-toc is useful for this purpose. A very common use case is this: ```shell $ cat foo.md # Table of contents # this ## is ## a ### foo #### booo ### foo ## file ## bye # bye $ md_toc --in-place github --header-levels 6 foo.md # or: md_toc -p github foo.md $ cat foo.md # Table of contents - [Table of contents](#table-of-contents) - [this](#this) - [is](#is) - [a](#a) - [foo](#foo) - [booo](#booo) - [foo](#foo-1) - [file](#file) - [bye](#bye) - [bye](#bye-1) # this ## is ## a ### foo #### booo ### foo ## file ## bye # bye ``` ## API examples md-toc has a [public API](https://docs.franco.net.eu.org/md-toc/api.html). This means for example that you can you easily build a TOC within another Python program. The easiest way to build one for a markdown file is: ```python >>> import md_toc >>> f = open('foo.md') >>> print(f.read(), end='') # this ## is ## a ### foo #### booo ### foo ## file ## bye # bye >>> print(md_toc.build_toc('foo.md'), end='') - [this](#this) - [is](#is) - [a](#a) - [foo](#foo) - [boo](#boo) - [foo](#foo-1) - [file](#file) - [bye](#bye) - [bye](#bye-1) ``` You can also write the TOC in place: ```python >>> import md_toc >>> f = open('foo.md') >>> print(f.read(), end='') # Table of contents # this ## is ## a ### foo #### booo ### foo ## file Test ## bye # bye >>> toc = md_toc.build_toc('foo.md') >>> md_toc.write_string_on_file_between_markers('foo.md', toc, '') >>> f = open('foo.md') >>> print(f.read(), end='') # Table of contents - [Table of contents](#table-of-contents) - [this](#this) - [is](#is) - [a](#a) - [foo](#foo) - [boo](#boo) - [foo](#foo-1) - [file](#file) - [bye](#bye) - [bye](#bye-1) # this ## is ## a ### foo #### booo ### foo ## file Test ## bye # bye ``` ## Documentation Please read the [Markdown specification](https://docs.franco.net.eu.org/md-toc/markdown_specification.html) section of the documentation to learn how this program parsers markdown files and builds a correct output. ## CLI Helps ```shell $ md_toc --help $ md_toc cmark --help $ md_toc commonmarker --help $ md_toc github --help $ md_toc gitlab --help $ md_toc goldmark --help $ md_toc redcarpet --help ``` ## Extras ### HTML output If you use [Pandoc](https://pandoc.org/) you can generate an HTML output starting from a markdown file. You can do something like: ```shell pandoc --from=commonmark --to=html -o a.html README.md ``` ## License Copyright (C) 2017-2024 [Franco Masotti](https://blog.franco.net.eu.org/about/#contacts) md-toc is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. md-toc is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with md-toc. If not, see . ## Changelog and trusted source You can check the authenticity of new releases using my public key. Changelogs, instructions, sources and keys can be found at [blog.franco.net.eu.org/software/#md-toc](https://blog.franco.net.eu.org/software/#md-toc). ## Support this project - [Buy Me a Coffee](https://www.buymeacoffee.com/frnmst) - [Liberapay](https://liberapay.com/frnmst) - Bitcoin: `bc1qnkflazapw3hjupawj0lm39dh9xt88s7zal5mwu` - Monero: `84KHWDTd9hbPyGwikk33Qp5GW7o7zRwPb8kJ6u93zs4sNMpDSnM5ZTWVnUp2cudRYNT6rNqctnMQ9NbUewbj7MzCBUcrQEY` - Dogecoin: `DMB5h2GhHiTNW7EcmDnqkYpKs6Da2wK3zP` - Vertcoin: `vtc1qd8n3jvkd2vwrr6cpejkd9wavp4ld6xfu9hkhh0` md-toc-9.0.0/SECURITY.md000066400000000000000000000006611460560256400145070ustar00rootroot00000000000000# Security Policy ## Supported Versions The latest [GIT tagged version of md-toc](https://github.com/frnmst/md-toc/tags) is the only one supported. When a new version is released, that one will be the only one supported. ## Reporting a Vulnerability Use this [Nextcloud form](https://cloud.franco.net.eu.org/apps/forms/s/ozgp2GqH46QMmsE9JPn5aP8B) (preferred) or send me an [email](https://blog.franco.net.eu.org/about/#contacts) md-toc-9.0.0/asciinema/000077500000000000000000000000001460560256400146445ustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_0_0_1.json000066400000000000000000001211511460560256400216740ustar00rootroot00000000000000{ "version": 1, "width": 174, "height": 46, "duration": 96.915121, "command": null, "title": null, "env": { "TERM": "st-256color", "SHELL": "/bin/bash" }, "stdout": [ [ 0.021711, "[vm@parabola-playground md-toc-example]$ " ], [ 0.701525, "c" ], [ 0.175801, "a" ], [ 0.287948, "t" ], [ 0.112077, " " ], [ 0.199954, "f" ], [ 0.135943, "o" ], [ 0.140681, "o.md " ], [ 0.299307, "\r\n" ], [ 0.001273, "# This is an example!! !!\r\n\r\n##tHis is a title\r\n\r\nhello, everything fine?\r\n\r\n# Another title\r\n\r\nSome content\r\n\r\n## YeT aNoThEr TiTlE\r\n\r\n### Yet again, another title\r\n\r\nand yet more\r\ncontent\r\n!\r\n\r\n#### This will not be included in the table of contents...\r\n.\r\n\r\n\r\n\r\n" ], [ 0.00061, "[vm@parabola-playground md-toc-example]$ " ], [ 1.0, "m" ], [ 0.175787, "d" ], [ 0.344011, "_" ], [ 0.280064, "t" ], [ 0.168105, "o" ], [ 0.196961, "c " ], [ 0.642996, "-" ], [ 0.303909, "h" ], [ 0.119967, "\r\n" ], [ 0.242997, "usage: md_toc [-h] [-v] {wtoc} ...\r\n\r\nMarkdown Table Of Contents\r\n\r\npositional arguments:\r\n {wtoc}\r\n wtoc write the table of contents\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -v, --version show program's version number and exit\r\n\r\nReturn values: 0 OK, 1 Error, 2 Invalid command\r\n" ], [ 0.015238, "[vm@parabola-playground md-toc-example]$ " ], [ 1.0, "md_toc -h" ], [ 1.0, "\b\u001b[K" ], [ 0.159757, "\b\u001b[K" ], [ 0.816169, "w" ], [ 0.319837, "t" ], [ 0.224139, "o" ], [ 0.132767, "\u0007" ], [ 0.859283, "c" ], [ 0.196013, "\u0007" ], [ 0.363747, " " ], [ 0.256077, "-" ], [ 0.280066, "h" ], [ 0.215835, "\r\n" ], [ 0.240287, "usage: md_toc wtoc [-h] [-i] [-o] [-t TOC_MARKER] FILE_NAME\r\n\r\npositional arguments:\r\n FILE_NAME the i/o file name\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -i, --in-place overwrite the input file\r\n -o, --ordered write as an ordered list\r\n -t TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for\r\n positioning the table of contents\r\n" ], [ 0.0166, "[vm@parabola-playground md-toc-example]$ " ], [ 0.775344, "md_toc wtoc -h" ], [ 0.807994, "\b\u001b[K" ], [ 0.200027, "\b\u001b[K" ], [ 1.0, "f" ], [ 0.439801, "o" ], [ 0.112034, "o" ], [ 0.173049, ".md " ], [ 0.45907, "\r\n" ], [ 0.245299, "[](TOC)\r\n\r\n- [This is an example!! !!](this-is-an-example)\r\n - [tHis is a title](this-is-a-title)\r\n- [Another title](another-title)\r\n - [YeT aNoThEr TiTlE](yet-another-title)\r\n - [Yet again, another title](yet-again-another-title)\r\n\r\n[](TOC)\r\n" ], [ 0.016554, "[vm@parabola-playground md-toc-example]$ " ], [ 1.0, "md_toc wtoc foo.md " ], [ 1.0, "\b" ], [ 0.23189, "\b" ], [ 0.167936, "\b" ], [ 0.14406, "\b" ], [ 0.175852, "\b" ], [ 0.152064, "\b" ], [ 0.176012, "\b" ], [ 1.0, "\u001b[1@-" ], [ 1.0, "\u001b[1@o" ], [ 0.079807, "\u001b[1@ " ], [ 0.124099, "\r\n" ], [ 0.248641, "[](TOC)\r\n\r\n1. [This is an example!! !!](this-is-an-example)\r\n 1. [tHis is a title](this-is-a-title)\r\n2. [Another title](another-title)\r\n 2. [YeT aNoThEr TiTlE](yet-another-title)\r\n 1. [Yet again, another title](yet-again-another-title)\r\n\r\n[](TOC)\r\n" ], [ 0.016713, "[vm@parabola-playground md-toc-example]$ " ], [ 1.0, "md_toc wtoc -o foo.md " ], [ 0.695849, "\b" ], [ 0.659365, "\b" ], [ 0.040625, "\b" ], [ 0.039654, "\b" ], [ 0.039678, "\b" ], [ 0.039992, "\b" ], [ 0.039936, "\b" ], [ 0.156676, "\b" ], [ 0.280248, "\b\u001b[1P" ], [ 0.808044, "\u001b[1@i" ], [ 0.607745, "\r\n" ], [ 0.247968, "[vm@parabola-playground md-toc-example]$ " ], [ 1.0, "c" ], [ 0.184022, "a" ], [ 0.191886, "t" ], [ 0.176068, " " ], [ 1.0, "f" ], [ 0.212567, "oo.md " ], [ 0.331301, "\r\n" ], [ 0.001258, "# This is an example!! !!\r\n\r\n##tHis is a title\r\n\r\nhello, everything fine?\r\n\r\n# Another title\r\n\r\nSome content\r\n\r\n## YeT aNoThEr TiTlE\r\n\r\n### Yet again, another title\r\n\r\nand yet more\r\ncontent\r\n!\r\n\r\n#### This will not be included in the table of contents...\r\n.\r\n\r\n\r\n\r\n" ], [ 0.000583, "[vm@parabola-playground md-toc-example]$ " ], [ 1.0, "n" ], [ 0.119663, "a" ], [ 0.103988, "n" ], [ 0.2081, "o" ], [ 0.103957, " " ], [ 0.175952, "f" ], [ 0.152043, "o" ], [ 0.141371, "o.md " ], [ 0.47467, "\r\n" ], [ 0.030674, "\u001b)0\u001b[?1049h\u001b[1;46r\u001b[0m\u001b[4l\u001b[?1h\u001b=" ], [ 0.000352, "\u001b[?1h\u001b=" ], [ 0.000254, "\u001b[?1h\u001b=" ], [ 0.000332, "\u001b[?25l" ], [ 0.000672, "\u001b[39;49m\u001b[39;49m\u001b[0m\u001b[H\u001b[2J\u001b[44;80H" ], [ 0.000257, "\u001b(B\u001b[0;7m[ Reading File ]" ], [ 0.000292, "\u001b[0m" ], [ 0.00037, "\u001b[44;79H" ], [ 0.000231, "\u001b(B\u001b[0;7m[ Read 23 lines ]" ], [ 0.000232, "\u001b[0m" ], [ 0.007813, "\u001b[H" ], [ 0.000364, "\u001b(B\u001b[0;7m GNU nano 2.9.1 foo.md \u001b[1;173H" ], [ 0.000312, "\u001b[0m" ], [ 0.000396, "\r\u001b[45d" ], [ 0.000277, "\u001b(B\u001b[0;7m^G\u001b[0m Get Help\u001b[18G" ], [ 0.000274, "\u001b(B\u001b[0;7m^O\u001b[0m Write Out \u001b(B\u001b[0;7m^W\u001b[0m Where Is\u001b[52G" ], [ 0.000241, "\u001b(B\u001b[0;7m^K\u001b[0m Cut Text\u001b[69G" ], [ 0.000209, "\u001b(B\u001b[0;7m^J\u001b[0m Justify\u001b[86G" ], [ 0.000208, "\u001b(B\u001b[0;7m^C\u001b[0m Cur Pos\u001b[103G" ], [ 0.000196, "\u001b(B\u001b[0;7mM-U\u001b[0m Undo\u001b[45;120H" ], [ 0.000216, "\u001b(B\u001b[0;7mM-A\u001b[0m Mark Text \u001b(B\u001b[0;7mM-]\u001b[0m To Bracket \u001b(B\u001b[0;7mM-â–²\u001b[0m Previous\r\u001b[46d" ], [ 0.000196, "\u001b(B\u001b[0;7m^X\u001b[0m Exit\u001b[46;18H" ], [ 0.0002, "\u001b(B\u001b[0;7m^R\u001b[0m Read File \u001b(B\u001b[0;7m^\\\u001b[0m Replace\u001b[52G" ], [ 0.000201, "\u001b(B\u001b[0;7m^U\u001b[0m Uncut Text \u001b(B\u001b[0;7m^T\u001b[0m To Spell\u001b[86G" ], [ 0.000264, "\u001b(B\u001b[0;7m^_\u001b[0m Go To Line \u001b(B\u001b[0;7mM-E\u001b[0m Redo\u001b[46;120H" ], [ 0.000255, "\u001b(B\u001b[0;7mM-6\u001b[0m Copy Text \u001b(B\u001b[0;7mM-W\u001b[0m WhereIs Next \u001b(B\u001b[0;7mM-â–¼\u001b[0m Next\r\u001b[44d" ], [ 0.000992, "\u001b[3d" ], [ 0.000325, "\u001b[39;49m\u001b[36m# This is an example!! !!\r\u001b[5d" ], [ 0.000259, "##tHis is a title\r\u001b[7d" ], [ 0.00024, "\u001b[39;49m\u001b[0mhello, everything fine?\r\u001b[9d" ], [ 0.000238, "\u001b[36m# Another title\r\u001b[11d" ], [ 0.000205, "\u001b[39;49m\u001b[0mSome content\r\u001b[13d" ], [ 0.000216, "\u001b[36m## YeT aNoThEr TiTlE\r\u001b[15d" ], [ 0.000212, "### Yet again, another title\r\u001b[17d" ], [ 0.000208, "\u001b[39;49m\u001b[0mand yet more\r\u001b[18d" ], [ 0.000232, "content\r\u001b[19d" ], [ 0.000216, "!\u001b[21d\b" ], [ 5.5e-05, "\u001b[36m#### This will not be included in the table of contents...\r\u001b[22d" ], [ 4.1e-05, "\u001b[39;49m\u001b[0m.\u001b[3d\b" ], [ 3.3e-05, "\u001b[?12l\u001b[?25h" ], [ 0.719393, "\u001b[?25l" ], [ 9.2e-05, "\u001b[4d" ], [ 4.2e-05, "\u001b[?12l\u001b[?25h" ], [ 0.215588, "\u001b[?25l" ], [ 7.6e-05, "\u001b[5d" ], [ 3.9e-05, "\u001b[?12l\u001b[?25h" ], [ 0.351947, "\u001b[?25l" ], [ 8.3e-05, "\u001b[6d" ], [ 4.2e-05, "\u001b[?12l\u001b[?25h" ], [ 1.0, "\u001b[?25l" ], [ 9.5e-05, "\u001b[A" ], [ 4.2e-05, "\u001b[?12l\u001b[?25h" ], [ 0.175626, "\u001b[?25l" ], [ 7.3e-05, "\u001b[A" ], [ 4e-05, "\u001b[?12l\u001b[?25h" ], [ 0.223876, "\u001b[?25l" ], [ 0.000115, "\u001b[1;165H" ], [ 5.1e-05, "\u001b(B\u001b[0;7mModified" ], [ 3.8e-05, "\u001b[0m" ], [ 0.000866, "\u001b[4;43r\u001b[4;1H" ], [ 0.000207, "\u001bM\u001b[1;46r\u001b[5;1H" ], [ 3.8e-05, "\u001b[?12l\u001b[?25h" ], [ 0.150692, "\u001b[?25l" ], [ 0.000927, "\u001b[4;43r\u001b[4;1H" ], [ 0.000485, "\u001bM\u001b[1;46r\u001b[6;1H" ], [ 0.000283, "\u001b[?12l\u001b[?25h" ], [ 0.206344, "\u001b[?25l" ], [ 8.3e-05, "\u001b[A" ], [ 4.3e-05, "\u001b[?12l\u001b[?25h" ], [ 0.815844, "\u001b[?25l" ], [ 0.000127, "[" ], [ 4.4e-05, "\u001b[?12l\u001b[?25h" ], [ 0.207886, "\u001b[?25l" ], [ 0.000131, "]" ], [ 4.4e-05, "\u001b[?12l\u001b[?25h" ], [ 0.568563, "\u001b[?25l" ], [ 0.000125, "(" ], [ 4.3e-05, "\u001b[?12l\u001b[?25h" ], [ 0.278961, "\u001b[?25l" ], [ 0.000115, "T" ], [ 4.3e-05, "\u001b[?12l\u001b[?25h" ], [ 0.22396, "\u001b[?25l" ], [ 0.000117, "O" ], [ 4.2e-05, "\u001b[?12l\u001b[?25h" ], [ 0.207729, "\u001b[?25l" ], [ 0.000118, "C" ], [ 4.6e-05, "\u001b[?12l\u001b[?25h" ], [ 0.335927, "\u001b[?25l)\u001b[?12l\u001b[?25h" ], [ 0.992046, "\u001b[?25l\r\u001b[A\u001b[?12l\u001b[?25h" ], [ 0.752255, "\u001b[?25l\u001b[45;18H\u001b[26X\u001b[45;44H\u001b(B\u001b[0;7mM-D\u001b[0m DOS Format \u001b[69G \u001b[86G \u001b(B\u001b[0;7mM-A\u001b[0m Append\u001b[103G \u001b[45;120H \u001b(B\u001b[0;7mM-B\u001b[0m Backup File\u001b[K\u001b[46;2H\u001b(B\u001b[0;7mC\u001b[0m Cancel\u001b[46;18H\u001b[26X\u001b[46;44H\u001b(B\u001b[0;7mM-M\u001b[0m Mac Format\u001b[22X\u001b[46;86H \u001b(B\u001b[0;7mM-P\u001b[0m Prepend \u001b[46;120H \u001b(B\u001b[0;7m^T\u001b[0m To Files\u001b[K\r\u001b[44d\u001b(B\u001b[0;7mFile Name to Write: foo.md \u001b[44;1H\u001b[0m\u001b[44;27H\u001b[?12l\u001b[?25h" ], [ 0.224436, "\u001b[?25l\r\u001b[K\u001b[1;173H\u001b[44;79H\u001b(B\u001b[0;7m[ Wrote 25 lines ]\u001b[0m\u001b[1;165H\u001b(B\u001b[0;7m \u001b[0m" ], [ 0.000238, "\u001b[45;18H" ], [ 6.7e-05, "\u001b(B\u001b[0;7m^O\u001b[0m Write Out \u001b(B\u001b[0;7m^W\u001b[0m Where Is \u001b(B\u001b[0;7m^K\u001b[0m Cut Text\u001b[69G" ], [ 5.4e-05, "\u001b(B\u001b[0;7m^J\u001b[0m Justify\u001b[86G" ], [ 5.3e-05, "\u001b(B\u001b[0;7m^C\u001b[0m Cur Pos \u001b[103G" ], [ 5.5e-05, "\u001b(B\u001b[0;7mM-U\u001b[0m Undo\u001b[45;120H" ], [ 7.3e-05, "\u001b(B\u001b[0;7mM-A\u001b[0m Mark Text \u001b(B\u001b[0;7mM-]\u001b[0m To Bracket \u001b(B\u001b[0;7mM-â–²\u001b[0m Previous\u001b[46;2H" ], [ 4.8e-05, "\u001b(B\u001b[0;7mX\u001b[0m Exit \u001b[46;18H" ], [ 6.5e-05, "\u001b(B\u001b[0;7m^R\u001b[0m Read File \u001b(B\u001b[0;7m^\\\u001b[0m Replace \u001b(B\u001b[0;7m^U\u001b[0m Uncut Text \u001b(B\u001b[0;7m^T\u001b[0m To Spell\u001b[86G" ], [ 9.3e-05, "\u001b(B\u001b[0;7m^_\u001b[0m Go To Line \u001b(B\u001b[0;7mM-E\u001b[0m Redo\u001b[46;120H" ], [ 7.3e-05, "\u001b(B\u001b[0;7mM-6\u001b[0m Copy Text \u001b(B\u001b[0;7mM-W\u001b[0m WhereIs Next \u001b(B\u001b[0;7mM-â–¼\u001b[0m Next\r\u001b[44d" ], [ 6.6e-05, "\u001b[4d" ], [ 4.1e-05, "\u001b[?12l\u001b[?25h" ], [ 0.622695, "\u001b[?25l\u001b[44d\u001b[J\u001b[46;174H\u001b[46;1H\u001b[?12l\u001b[?25h\u001b[?1049l\r\u001b[?1l\u001b>" ], [ 0.000298, "[vm@parabola-playground md-toc-example]$ " ], [ 0.951474, "nano foo.md " ], [ 0.175951, "\b\b\b\b\b\b\b\b\b\b\b\b\u001b[1Pcat\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C" ], [ 0.280047, "\b\b\b\b\b\b\b\b\b\b\b\u001b[11@md_toc wtoc -i\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C" ], [ 1.0, "\r\n" ], [ 0.260234, "[vm@parabola-playground md-toc-example]$ " ], [ 0.992157, "c" ], [ 0.159758, "a" ], [ 0.216001, "t" ], [ 0.096057, " " ], [ 0.175952, "f" ], [ 0.244896, "oo.md " ], [ 0.355231, "\r\n" ], [ 0.001349, "# This is an example!! !!\r\n\r\n[](TOC)\r\n\r\n- [This is an example!! !!](this-is-an-example)\r\n - [tHis is a title](this-is-a-title)\r\n- [Another title](another-title)\r\n - [YeT aNoThEr TiTlE](yet-another-title)\r\n - [Yet again, another title](yet-again-another-title)\r\n\r\n[](TOC)\r\n\r\n##tHis is a title\r\n\r\nhello, everything fine?\r\n\r\n# Another title\r\n\r\nSome content\r\n\r\n## YeT aNoThEr TiTlE\r\n\r\n### Yet again, another title\r\n\r\nand yet more\r\ncontent\r\n!\r\n\r\n#### This will not be included in the table of contents...\r\n.\r\n\r\n\r\n\r\n" ], [ 0.00067, "[vm@parabola-playground md-toc-example]$ " ], [ 1.0, "n" ], [ 0.079865, "a" ], [ 0.096004, "n" ], [ 0.200029, "o" ], [ 0.167994, " " ], [ 0.191942, "f" ], [ 0.128021, "o" ], [ 0.20478, "o.md " ], [ 0.451291, "\r\n" ], [ 0.033926, "\u001b)0\u001b[?1049h\u001b[1;46r\u001b[0m\u001b[4l\u001b[?1h\u001b=" ], [ 0.000362, "\u001b[?1h\u001b=" ], [ 0.000265, "\u001b[?1h\u001b=" ], [ 0.000299, "\u001b[?25l" ], [ 0.000633, "\u001b[39;49m\u001b[39;49m\u001b[0m\u001b[H\u001b[2J\u001b[44;80H" ], [ 0.000271, "\u001b(B\u001b[0;7m[ Reading File ]" ], [ 0.000212, "\u001b[0m" ], [ 0.000342, "\u001b[44;79H" ], [ 0.000229, "\u001b(B\u001b[0;7m[ Read 33 lines ]" ], [ 0.000206, "\u001b[0m" ], [ 0.008238, "\u001b[H" ], [ 0.000345, "\u001b(B\u001b[0;7m GNU nano 2.9.1 foo.md \u001b[1;173H" ], [ 0.000252, "\u001b[0m" ], [ 0.000377, "\r\u001b[45d" ], [ 0.000282, "\u001b(B\u001b[0;7m^G\u001b[0m Get Help\u001b[18G" ], [ 0.000249, "\u001b(B\u001b[0;7m^O\u001b[0m Write Out \u001b(B\u001b[0;7m^W\u001b[0m Where Is\u001b[52G" ], [ 0.000313, "\u001b(B\u001b[0;7m^K\u001b[0m Cut Text\u001b[69G" ], [ 0.000271, "\u001b(B\u001b[0;7m^J\u001b[0m Justify\u001b[86G" ], [ 0.000242, "\u001b(B\u001b[0;7m^C\u001b[0m Cur Pos\u001b[103G" ], [ 0.00021, "\u001b(B\u001b[0;7mM-U\u001b[0m Undo\u001b[45;120H" ], [ 0.000264, "\u001b(B\u001b[0;7mM-A\u001b[0m Mark Text \u001b(B\u001b[0;7mM-]\u001b[0m To Bracket \u001b(B\u001b[0;7mM-â–²\u001b[0m Previous\r\u001b[46d" ], [ 0.000309, "\u001b(B\u001b[0;7m^X\u001b[0m Exit\u001b[46;18H" ], [ 0.000303, "\u001b(B\u001b[0;7m^R\u001b[0m Read File \u001b(B\u001b[0;7m^\\\u001b[0m Replace\u001b[52G" ], [ 0.000251, "\u001b(B\u001b[0;7m^U\u001b[0m Uncut Text \u001b(B\u001b[0;7m^T\u001b[0m To Spell\u001b[86G" ], [ 0.000222, "\u001b(B\u001b[0;7m^_\u001b[0m Go To Line \u001b(B\u001b[0;7mM-E\u001b[0m Redo\u001b[46;120H" ], [ 0.000241, "\u001b(B\u001b[0;7mM-6\u001b[0m Copy Text \u001b(B\u001b[0;7mM-W\u001b[0m WhereIs Next \u001b(B\u001b[0;7mM-â–¼\u001b[0m Next\r\u001b[44d" ], [ 0.001037, "\u001b[3d" ], [ 0.000265, "\u001b[39;49m\u001b[36m# This is an example!! !!\r\u001b[5d" ], [ 0.000276, "\u001b[39;49m\u001b[0m[](TOC)\r\u001b[7d" ], [ 0.000242, "- [This is an example!! !!](this-is-an-example)\u001b[8;5H" ], [ 0.000247, "- [tHis is a title](this-is-a-title)\r\u001b[9d" ], [ 0.000216, "- [Another title](another-title)\u001b[10;5H" ], [ 0.000195, "- [YeT aNoThEr TiTlE](yet-another-title)\u001b[11;9H" ], [ 0.000218, "- [Yet again, another title](yet-again-another-title)\r\u001b[13d" ], [ 0.000228, "[](TOC)\r\u001b[15d" ], [ 4.8e-05, "\u001b[36m##tHis is a title\r\u001b[17d" ], [ 0.000231, "\u001b[39;49m\u001b[0mhello, everything fine?\r\u001b[19d" ], [ 4.9e-05, "\u001b[36m# Another title\r\u001b[21d" ], [ 4.4e-05, "\u001b[39;49m\u001b[0mSome content\r\u001b[23d" ], [ 4.6e-05, "\u001b[36m## YeT aNoThEr TiTlE\r\u001b[25d" ], [ 4.3e-05, "### Yet again, another title\r\u001b[27d" ], [ 4.4e-05, "\u001b[39;49m\u001b[0mand yet more\r\u001b[28d" ], [ 4.3e-05, "content\r\u001b[29d" ], [ 4.3e-05, "!\u001b[31d\b" ], [ 4.7e-05, "\u001b[36m#### This will not be included in the table of contents...\r\u001b[32d" ], [ 0.000177, "\u001b[39;49m\u001b[0m.\u001b[3d\b" ], [ 3.5e-05, "\u001b[?12l\u001b[?25h" ], [ 0.675063, "\u001b[?25l" ], [ 8.7e-05, "\u001b[4d" ], [ 4.2e-05, "\u001b[?12l\u001b[?25h" ], [ 0.191763, "\u001b[?25l" ], [ 8.1e-05, "\u001b[5d" ], [ 4.3e-05, "\u001b[?12l\u001b[?25h" ], [ 0.312003, "\u001b[?25l" ], [ 9.3e-05, "\u001b[6d" ], [ 4.4e-05, "\u001b[?12l\u001b[?25h" ], [ 0.175886, "\u001b[?25l" ], [ 9.8e-05, "\u001b[7d" ], [ 4.2e-05, "\u001b[?12l\u001b[?25h" ], [ 1.0, "\u001b[?25l" ], [ 0.000154, "\u001b[1;165H" ], [ 5.5e-05, "\u001b(B\u001b[0;7mModified" ], [ 4e-05, "\u001b[0m" ], [ 0.001053, "\u001b[7;43r\u001b[43;1H" ], [ 0.000203, "\n\u001b[1;46r\u001b[7;1H" ], [ 0.000425, "\u001b[?12l\u001b[?25h" ], [ 0.157964, "\u001b[?25l" ], [ 0.001014, "\u001b[7;43r\u001b[43;1H" ], [ 0.000471, "\n\u001b[1;46r\u001b[7;1H" ], [ 0.000287, "\u001b[?12l\u001b[?25h" ], [ 0.150286, "\u001b[?25l" ], [ 0.001008, "\u001b[7;43r\u001b[43;1H" ], [ 0.00044, "\n\u001b[1;46r\u001b[7;1H" ], [ 0.000307, "\u001b[?12l\u001b[?25h" ], [ 0.574322, "\u001b[?25l" ], [ 0.00127, "\u001b[7;43r\u001b[43;1H" ], [ 0.000575, "\n\u001b[1;46r\u001b[7;1H" ], [ 0.000376, "\u001b[?12l\u001b[?25h" ], [ 0.157676, "\u001b[?25l" ], [ 0.001192, "\u001b[7;43r\u001b[43;1H" ], [ 0.000464, "\n\u001b[1;46r\u001b[7;1H" ], [ 0.000294, "\u001b[?12l\u001b[?25h" ], [ 0.150049, "\u001b[?25l" ], [ 0.001125, "\u001b[6;43r\u001b[43;1H" ], [ 0.000453, "\n\u001b[1;46r\u001b[7;1H" ], [ 0.000293, "\u001b[?12l\u001b[?25h" ], [ 0.150173, "\u001b[?25l" ], [ 0.001225, "\u001b[7;43r\u001b[43;1H" ], [ 0.000499, "\n\u001b[1;46r\u001b[7;1H" ], [ 0.000424, "\u001b[?12l\u001b[?25h" ], [ 0.14983, "\u001b[?25l" ], [ 0.001195, "\u001b[6;43r\u001b[43;1H" ], [ 0.000509, "\n\u001b[1;46r\u001b[7;1H" ], [ 0.000404, "\u001b[?12l\u001b[?25h" ], [ 0.917952, "\u001b[?25l" ], [ 0.000453, "\u001b[45;18H" ], [ 0.000392, "\u001b[26X\u001b[45;44H" ], [ 7.6e-05, "\u001b(B\u001b[0;7mM-D\u001b[0m DOS Format \u001b[69G" ], [ 5.3e-05, " \u001b[86G" ], [ 7e-05, " \u001b(B\u001b[0;7mM-A\u001b[0m Append\u001b[103G" ], [ 7.2e-05, " \u001b[45;120H" ], [ 6.3e-05, " \u001b(B\u001b[0;7mM-B\u001b[0m Backup File\u001b[K\u001b[46;2H" ], [ 4.7e-05, "\u001b(B\u001b[0;7mC\u001b[0m Cancel\u001b[46;18H" ], [ 4.3e-05, "\u001b[26X\u001b[46;44H" ], [ 5.5e-05, "\u001b(B\u001b[0;7mM-M\u001b[0m Mac Format\u001b[22X\u001b[46;86H" ], [ 5.5e-05, " \u001b(B\u001b[0;7mM-P\u001b[0m Prepend \u001b[46;120H" ], [ 5.6e-05, " \u001b(B\u001b[0;7m^T\u001b[0m To Files\u001b[K\r\u001b[44d" ], [ 0.000107, "\u001b(B\u001b[0;7mFile Name to Write: foo.md \u001b[44;1H" ], [ 0.000352, "\u001b[0m" ], [ 5.9e-05, "\u001b[44;27H" ], [ 4e-05, "\u001b[?12l\u001b[?25h" ], [ 0.245917, "\u001b[?25l" ], [ 0.00018, "\r" ], [ 5.4e-05, "\u001b[K\u001b[1;173H" ], [ 0.000501, "\u001b[44;79H" ], [ 0.000179, "\u001b(B\u001b[0;7m[ Wrote 25 lines ]" ], [ 7.4e-05, "\u001b[0m" ], [ 8.8e-05, "\u001b[1;165H" ], [ 0.00058, "\u001b(B\u001b[0;7m " ], [ 4.4e-05, "\u001b[0m" ], [ 0.000197, "\u001b[45;18H" ], [ 8e-05, "\u001b(B\u001b[0;7m^O\u001b[0m Write Out \u001b(B\u001b[0;7m^W\u001b[0m Where Is \u001b(B\u001b[0;7m^K\u001b[0m Cut Text\u001b[69G" ], [ 5.5e-05, "\u001b(B\u001b[0;7m^J\u001b[0m Justify\u001b[86G" ], [ 5.4e-05, "\u001b(B\u001b[0;7m^C\u001b[0m Cur Pos \u001b[103G" ], [ 4.6e-05, "\u001b(B\u001b[0;7mM-U\u001b[0m Undo\u001b[45;120H" ], [ 7e-05, "\u001b(B\u001b[0;7mM-A\u001b[0m Mark Text \u001b(B\u001b[0;7mM-]\u001b[0m To Bracket \u001b(B\u001b[0;7mM-â–²\u001b[0m Previous\u001b[46;2H" ], [ 4.9e-05, "\u001b(B\u001b[0;7mX\u001b[0m Exit \u001b[46;18H" ], [ 6.2e-05, "\u001b(B\u001b[0;7m^R\u001b[0m Read File \u001b(B\u001b[0;7m^\\\u001b[0m Replace \u001b(B\u001b[0;7m^U\u001b[0m Uncut Text \u001b(B\u001b[0;7m^T\u001b[0m To Spell\u001b[86G" ], [ 5e-05, "\u001b(B\u001b[0;7m^_\u001b[0m Go To Line \u001b(B\u001b[0;7mM-E\u001b[0m Redo\u001b[46;120H" ], [ 7.1e-05, "\u001b(B\u001b[0;7mM-6\u001b[0m Copy Text \u001b(B\u001b[0;7mM-W\u001b[0m WhereIs Next \u001b(B\u001b[0;7mM-â–¼\u001b[0m Next\r\u001b[44d" ], [ 5.6e-05, "\u001b[7d" ], [ 4e-05, "\u001b[?12l\u001b[?25h" ], [ 0.50946, "\u001b[?25l" ], [ 0.000176, "\u001b[44d" ], [ 7e-05, "\u001b[J\u001b[46;174H" ], [ 4.7e-05, "\u001b[46;1H\u001b[?12l\u001b[?25h" ], [ 3.8e-05, "\u001b[?1049l\r\u001b[?1l\u001b>" ], [ 0.000932, "[vm@parabola-playground md-toc-example]$ " ], [ 0.382891, "nano foo.md " ], [ 0.351996, "\b\b\b\b\b\b\b\b\b\b\b\b\u001b[1Pcat\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C" ], [ 1.0, "\b\b\b\b\b\b\b\b\b\b\b\u001b[11@md_toc wtoc -i\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C" ], [ 1.0, "\b" ], [ 0.659758, "\b" ], [ 0.039658, "\b" ], [ 0.040607, "\b" ], [ 0.039445, "\b" ], [ 0.040003, "\b" ], [ 0.124395, "\b" ], [ 0.288028, "\b" ], [ 0.280297, "\b\u001b[1P" ], [ 0.399852, "\u001b[1@h" ], [ 0.231806, "\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C" ], [ 0.248007, "\b\u001b[K" ], [ 0.144079, "\b\u001b[K" ], [ 0.143928, "\b\u001b[K" ], [ 0.159962, "\b\u001b[K" ], [ 0.144003, "\b\u001b[K" ], [ 0.151999, "\b\u001b[K" ], [ 0.128092, "\b\u001b[K" ], [ 0.162521, "\r\n" ], [ 0.252692, "usage: md_toc wtoc [-h] [-i] [-o] [-t TOC_MARKER] FILE_NAME\r\n\r\npositional arguments:\r\n FILE_NAME the i/o file name\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -i, --in-place overwrite the input file\r\n -o, --ordered write as an ordered list\r\n -t TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for\r\n positioning the table of contents\r\n" ], [ 0.014831, "[vm@parabola-playground md-toc-example]$ " ], [ 0.481991, "md_toc wtoc -h " ], [ 0.640028, "\b" ], [ 0.176065, "\b" ], [ 0.295819, "\u001b[C" ], [ 0.352225, "\b\u001b[1P \b" ], [ 1.0, "i \b" ], [ 1.0, "\u001b[C \b" ], [ 1.0, "- \b" ], [ 0.335869, "io \b" ], [ 0.535908, "\b\u001b[1P \b" ], [ 0.1359, "\b\u001b[1P \b" ], [ 0.592174, "o \b" ], [ 0.55969, "\r\n" ], [ 0.253784, "usage: md_toc wtoc [-h] [-i] [-o] [-t TOC_MARKER] FILE_NAME\r\n" ], [ 0.000403, "md_toc wtoc: error: the following arguments are required: FILE_NAME\r\n" ], [ 0.017619, "[vm@parabola-playground md-toc-example]$ " ], [ 0.720237, "md_toc wtoc -i -o " ], [ 0.119938, " " ], [ 0.592127, "f" ], [ 0.197618, "oo.md " ], [ 0.31425, "\r\n" ], [ 0.256418, "[vm@parabola-playground md-toc-example]$ " ], [ 0.567639, "md_toc wtoc -i -o foo.md " ], [ 0.151931, "\b\b\b\b\b\b\b\b\u001b[K" ], [ 0.304319, "\b\b\b\b\b\u001b[3Ph\u001b[C" ], [ 0.295809, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\u001b[3Pnano foo.md\u001b[C" ], [ 0.511997, "\r\n" ], [ 0.034358, "\u001b)0\u001b[?1049h\u001b[1;46r\u001b[0m\u001b[4l\u001b[?1h\u001b=" ], [ 0.000387, "\u001b[?1h\u001b=" ], [ 0.000267, "\u001b[?1h\u001b=" ], [ 0.000265, "\u001b[?25l" ], [ 0.000627, "\u001b[39;49m\u001b[39;49m\u001b[0m\u001b[H\u001b[2J\u001b[44;80H" ], [ 0.000258, "\u001b(B\u001b[0;7m[ Reading File ]" ], [ 0.000276, "\u001b[0m" ], [ 0.000356, "\u001b[44;79H" ], [ 0.00025, "\u001b(B\u001b[0;7m[ Read 33 lines ]" ], [ 0.000322, "\u001b[0m" ], [ 0.008222, "\u001b[H" ], [ 0.000365, "\u001b(B\u001b[0;7m GNU nano 2.9.1 foo.md \u001b[1;173H" ], [ 0.000261, "\u001b[0m" ], [ 0.000385, "\r\u001b[45d" ], [ 0.000277, "\u001b(B\u001b[0;7m^G\u001b[0m Get Help\u001b[18G" ], [ 0.000263, "\u001b(B\u001b[0;7m^O\u001b[0m Write Out \u001b(B\u001b[0;7m^W\u001b[0m Where Is\u001b[52G" ], [ 0.000281, "\u001b(B\u001b[0;7m^K\u001b[0m Cut Text\u001b[69G" ], [ 0.000207, "\u001b(B\u001b[0;7m^J\u001b[0m Justify\u001b[86G" ], [ 0.000227, "\u001b(B\u001b[0;7m^C\u001b[0m Cur Pos\u001b[103G" ], [ 0.000226, "\u001b(B\u001b[0;7mM-U\u001b[0m Undo\u001b[45;120H" ], [ 0.000216, "\u001b(B\u001b[0;7mM-A\u001b[0m Mark Text \u001b(B\u001b[0;7mM-]\u001b[0m To Bracket \u001b(B\u001b[0;7mM-â–²\u001b[0m Previous\r\u001b[46d" ], [ 0.000198, "\u001b(B\u001b[0;7m^X\u001b[0m Exit\u001b[46;18H" ], [ 0.000214, "\u001b(B\u001b[0;7m^R\u001b[0m Read File \u001b(B\u001b[0;7m^\\\u001b[0m Replace\u001b[52G" ], [ 0.000203, "\u001b(B\u001b[0;7m^U\u001b[0m Uncut Text \u001b(B\u001b[0;7m^T\u001b[0m To Spell\u001b[86G" ], [ 0.000196, "\u001b(B\u001b[0;7m^_\u001b[0m Go To Line \u001b(B\u001b[0;7mM-E\u001b[0m Redo\u001b[46;120H" ], [ 0.000234, "\u001b(B\u001b[0;7mM-6\u001b[0m Copy Text \u001b(B\u001b[0;7mM-W\u001b[0m WhereIs Next \u001b(B\u001b[0;7mM-â–¼\u001b[0m Next\r\u001b[44d" ], [ 0.000998, "\u001b[3d" ], [ 0.000216, "\u001b[39;49m\u001b[36m# This is an example!! !!\r\u001b[5d" ], [ 0.000202, "\u001b[39;49m\u001b[0m[](TOC)\r\u001b[7d" ], [ 0.000211, "1. [This is an example!! !!](this-is-an-example)\u001b[8;5H" ], [ 0.000208, "1. [tHis is a title](this-is-a-title)\r\u001b[9d" ], [ 0.000214, "2. [Another title](another-title)\u001b[10;5H" ], [ 0.000206, "2. [YeT aNoThEr TiTlE](yet-another-title)\u001b[11;9H" ], [ 0.000199, "1. [Yet again, another title](yet-again-another-title)\r\u001b[13d" ], [ 0.000205, "[](TOC)\r\u001b[15d" ], [ 0.000208, "\u001b[36m##tHis is a title\r\u001b[17d" ], [ 0.000212, "\u001b[39;49m\u001b[0mhello, everything fine?\r\u001b[19d" ], [ 0.000229, "\u001b[36m# Another title\r\u001b[21d" ], [ 0.000225, "\u001b[39;49m\u001b[0mSome content\r\u001b[23d" ], [ 5.1e-05, "\u001b[36m## YeT aNoThEr TiTlE\r\u001b[25d" ], [ 4.4e-05, "### Yet again, another title\r\u001b[27d" ], [ 4.5e-05, "\u001b[39;49m\u001b[0mand yet more\r\u001b[28d" ], [ 4.2e-05, "content\r\u001b[29d" ], [ 4.2e-05, "!\u001b[31d\b" ], [ 4.8e-05, "\u001b[36m#### This will not be included in the table of contents...\r\u001b[32d" ], [ 0.000199, "\u001b[39;49m\u001b[0m.\u001b[3d\b" ], [ 3.6e-05, "\u001b[?12l\u001b[?25h" ], [ 1.0, "\u001b[?25l" ], [ 9.3e-05, "\u001b[4d" ], [ 4.3e-05, "\u001b[?12l\u001b[?25h" ], [ 0.215777, "\u001b[?25l" ], [ 8.4e-05, "\u001b[5d" ], [ 4.3e-05, "\u001b[?12l\u001b[?25h" ], [ 0.359998, "\u001b[?25l" ], [ 8.8e-05, "\u001b[6d" ], [ 4.3e-05, "\u001b[?12l\u001b[?25h" ], [ 0.159863, "\u001b[?25l" ], [ 9.1e-05, "\u001b[7d" ], [ 4.2e-05, "\u001b[?12l\u001b[?25h" ], [ 0.919931, "\u001b[?25l" ], [ 0.000139, "\u001b[1;165H" ], [ 5.2e-05, "\u001b(B\u001b[0;7mModified" ], [ 4e-05, "\u001b[0m" ], [ 0.001053, "\u001b[7;43r\u001b[43;1H" ], [ 0.000201, "\n\u001b[1;46r\u001b[7;1H" ], [ 4e-05, "\u001b[?12l\u001b[?25h" ], [ 1.0, "\u001b[?25l" ], [ 0.001096, "\u001b[6;43r\u001b[6;1H" ], [ 0.000396, "\u001bM\u001b[1;46r\u001b[7;1H" ], [ 0.000444, "1. [This is an example!! !!](this-is-an-example)\r\u001b[8d" ], [ 0.000315, "\u001b[?12l\u001b[?25h" ], [ 1.0, "\u001b[?25l" ], [ 0.000185, "\u001b[44d" ], [ 7e-05, "\u001b(B\u001b[0;7mSave modified buffer? (Answering \"No\" will DISCARD changes.) \u001b[45;1H" ], [ 4.3e-05, " Y\u001b[0m Ye" ], [ 5.6e-05, "s\u001b[K\r\u001b[46d" ], [ 4.8e-05, "\u001b(B\u001b[0;7m N\u001b[0m No \u001b[46;17H" ], [ 5e-05, "\u001b(B\u001b[0;7m^C\u001b[0m Cancel\u001b[K\u001b[44;63H" ], [ 3.9e-05, "\u001b[?12l\u001b[?25h" ], [ 1.0, "\u001b[?25l" ], [ 0.000165, "\r" ], [ 7.1e-05, "\u001b[J\u001b[46;174H" ], [ 4.7e-05, "\u001b[46;1H\u001b[?12l\u001b[?25h" ], [ 3.8e-05, "\u001b[?1049l\r\u001b[?1l\u001b>" ], [ 0.000918, "[vm@parabola-playground md-toc-example]$ " ], [ 1.0, "n" ], [ 0.151829, "a" ], [ 0.079945, "n" ], [ 0.256012, "o" ], [ 0.096045, " " ], [ 0.236486, "foo.md " ], [ 0.128483, "foo.md " ], [ 0.48311, "\b\u001b[K" ], [ 0.659885, "\b\u001b[K" ], [ 0.03978, "\b\u001b[K" ], [ 0.040706, "\b\u001b[K" ], [ 0.039393, "\b\u001b[K" ], [ 0.039976, "\b\u001b[K" ], [ 0.040726, "\b\u001b[K" ], [ 0.039626, "\b\u001b[K" ], [ 0.039629, "\b\u001b[K" ], [ 0.039962, "\b\u001b[K" ], [ 0.040885, "\b\u001b[K" ], [ 0.040632, "\b\u001b[K" ], [ 0.039537, "\b\u001b[K" ], [ 0.039968, "\b\u001b[K" ], [ 0.040678, "\b\u001b[K" ], [ 0.039658, "\b\u001b[K" ], [ 0.039661, "\b\u001b[K" ], [ 0.040669, "\b\u001b[K" ], [ 0.039677, "\b\u001b[K" ], [ 0.039699, "\u0007" ], [ 0.03996, "\u0007" ], [ 0.040013, "\u0007" ], [ 0.611142, "l" ], [ 0.088012, "s" ], [ 0.114815, "\r\n" ], [ 0.000966, "foo.md\r\n" ], [ 0.000572, "[vm@parabola-playground md-toc-example]$ " ], [ 1.0, "c" ], [ 0.152039, "a" ], [ 0.335823, "t" ], [ 0.087956, " " ], [ 0.216073, "f" ], [ 0.14405, "o" ], [ 0.164917, "o.md " ], [ 0.283159, "\r\n" ], [ 0.001352, "# This is an example!! !!\r\n\r\n[](TOC)\r\n\r\n1. [This is an example!! !!](this-is-an-example)\r\n 1. [tHis is a title](this-is-a-title)\r\n2. [Another title](another-title)\r\n 2. [YeT aNoThEr TiTlE](yet-another-title)\r\n 1. [Yet again, another title](yet-again-another-title)\r\n\r\n[](TOC)\r\n\r\n##tHis is a title\r\n\r\nhello, everything fine?\r\n\r\n# Another title\r\n\r\nSome content\r\n\r\n## YeT aNoThEr TiTlE\r\n\r\n### Yet again, another title\r\n\r\nand yet more\r\ncontent\r\n!\r\n\r\n#### This will not be included in the table of contents...\r\n.\r\n\r\n\r\n\r\n" ], [ 0.000744, "[vm@parabola-playground md-toc-example]$ " ], [ 1.0, "m" ], [ 0.18381, "d" ], [ 0.101047, "\u0007" ], [ 0.962982, "_" ], [ 0.280019, "t" ], [ 0.220642, "oc " ], [ 1.0, "-" ], [ 0.39193, "i" ], [ 0.167958, " " ], [ 0.368106, "\b\u001b[K" ], [ 0.151921, "\b\u001b[K" ], [ 0.495965, "\b\u001b[K" ], [ 1.0, "f" ], [ 0.19994, "o" ], [ 0.244678, "o.md " ], [ 0.78733, "\r\n" ], [ 0.247531, "usage: md_toc [-h] [-v] {wtoc} ...\r\n" ], [ 0.000491, "md_toc: error: argument command: invalid choice: 'foo.md' (choose from 'wtoc')\r\n" ], [ 0.016368, "[vm@parabola-playground md-toc-example]$ " ], [ 1.0, "md_toc foo.md " ], [ 0.311727, "\b" ], [ 0.168094, "\b" ], [ 0.660141, "\b" ], [ 0.040658, "\b" ], [ 0.040624, "\b" ], [ 0.039537, "\b" ], [ 0.03999, "\b" ], [ 0.040622, "\b" ], [ 0.674686, "\u001b[C\u001b[1@ \b" ], [ 0.672032, "\u001b[1@w" ], [ 0.599887, "\u001b[1@t" ], [ 0.191891, "\u001b[1@o" ], [ 0.184201, "\u001b[1@c" ], [ 0.535835, "\r\n" ], [ 0.251695, "[](TOC)\r\n\r\n- [This is an example!! !!](this-is-an-example)\r\n - [tHis is a title](this-is-a-title)\r\n- [Another title](another-title)\r\n - [YeT aNoThEr TiTlE](yet-another-title)\r\n - [Yet again, another title](yet-again-another-title)\r\n\r\n[](TOC)\r\n" ], [ 0.014912, "[vm@parabola-playground md-toc-example]$ " ] ] } md-toc-9.0.0/asciinema/md_toc_asciinema_1_0_0.json000066400000000000000000000065601460560256400217020ustar00rootroot00000000000000{ "version": 1, "width": 83, "height": 46, "duration": 28.378759, "command": "./md_toc_asciinema_1_0_0_demo.sh", "title": null, "env": { "TERM": "st-256color", "SHELL": "/bin/bash" }, "stdout": [ [ 0.013629, "Running a demo to show some of md_toc's capabilities...\r\n" ], [ 0.000432, "\r\n" ], [ 2.001283, "$ md_toc -h\r\n" ], [ 0.261431, "usage: md_toc [-h] [-i] [-n] [-o] [-p {standard,github,redcarpet,gitlab}]\r\n [-t TOC_MARKER] [-l HEADER_LEVELS] [-v]\r\n FILE_NAME\r\n\r\nMarkdown Table Of Contents\r\n\r\npositional arguments:\r\n FILE_NAME the I/O file name\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -i, --in-place overwrite the input file\r\n -n, --no-links avoids adding links to corresponding content\r\n -o, --ordered write as an ordered list\r\n -p {standard,github,redcarpet,gitlab}, --parser {standard,github,redcarpet,gitlab}\r\n decide what markdown parser will be used to generate\r\n the links. Defaults to standard\r\n -t TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for\r\n positioning the table of contents\r\n -l HEADER_LEVELS, --header-levels HEADER_LEVELS\r\n set the maximum level of headers to be considered as\r\n " ], [ 3.1e-05, " part of the TOC\r\n -v, --version show program's version number and exit\r\n\r\nReturn values: 0 OK, 1 Error, 2 Invalid command\r\n" ], [ 0.014976, "\r\n" ], [ 5.003232, "$ cat foo.md\r\n" ], [ 0.001457, "# Hi\r\n\r\n[](TOC)\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n" ], [ 0.000547, "\r\n" ], [ 5.001721, "$ md_toc -p github foo.md\r\n" ], [ 0.251735, "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n" ], [ 0.015469, "\r\n" ], [ 5.001438, "$ md_toc -o -p gitlab foo.md\r\n" ], [ 0.252434, "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [Bye bye](#bye-bye)\r\n" ], [ 0.01472, "\r\n" ], [ 5.001544, "$ md_toc -n foo.md\r\n" ], [ 0.245354, "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - Bye bye\r\n" ], [ 0.014583, "\r\n" ], [ 5.001507, "$ Editing the file in-place...\r\n" ], [ 0.000604, "$ md_toc -i -p redcarpet foo.md\r\n" ], [ 0.276076, "$ cat foo.md\r\n" ], [ 0.001687, "# Hi\r\n\r\n[](TOC)\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n\r\n[](TOC)\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n" ] ] } md-toc-9.0.0/asciinema/md_toc_asciinema_1_0_0_demo.sh000077500000000000000000000027001460560256400223420ustar00rootroot00000000000000#!/bin/bash # # md_toc_asciinema_1_0_0_demo.sh # # Copyright (C) 2017-2018 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # printf "Running a demo to show some of md_toc's capabilities...\n" printf "\n" sleep 2 printf "$ md_toc -h\n" md_toc -h printf "\n" sleep 5 cat <<-EOF > foo.md # Hi [](TOC) hey ## How are you? !!! ## fine, thanks ### Bye ## Bye bye EOF printf "$ cat foo.md\n" cat foo.md printf "\n" sleep 5 printf "$ md_toc -p github foo.md\n" md_toc -p github foo.md printf "\n" sleep 5 printf "$ md_toc -o -p gitlab foo.md\n" md_toc -o -p gitlab foo.md printf "\n" sleep 5 printf "$ md_toc -n foo.md\n" md_toc -n foo.md printf "\n" sleep 5 printf "$ Editing the file in-place...\n" printf "$ md_toc -i -p redcarpet foo.md\n" md_toc -i -p redcarpet foo.md printf "$ cat foo.md\n" cat foo.md rm foo.md md-toc-9.0.0/asciinema/md_toc_asciinema_2_0_0.json000066400000000000000000000055531460560256400217040ustar00rootroot00000000000000{"version": 2, "width": 174, "height": 46, "timestamp": 1521395243, "env": {"SHELL": "/bin/bash", "TERM": "st-256color"}} [0.009865, "o", "Running a demo to show some of md_toc's capabilities...\r\n"] [0.010509, "o", "\r\n"] [2.011863, "o", "$ md_toc -h\r\n"] [2.21847, "o", "usage: md_toc [-h] [-i] [-n] [-t TOC_MARKER] [-v]\r\n {github,cmark,redcarpet,gitlab} ... FILE_NAME\r\n\r\nMarkdown Table Of Contents: Automatically generate a compliant table\r\nof contents for a markdown file to improve document readability.\r\n\r\npositional arguments:\r\n FILE_NAME the I/O file name\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -i, --in-place overwrite the input file\r\n -n, --no-links avoids adding links to the corresponding content\r\n -t TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for\r\n positioning the table of contents. Defaults to [](TOC)\r\n -v, --version show program's version number and exit\r\n\r\nmarkdown parser:\r\n {github,cmark,redcarpet,gitlab}\r\n\r\nReturn values: 0 OK, 1 Error, 2 Invalid command\r\n\r\nCopyright (C) 2018 Franco Masotti, frnmst\r\nLicense GPLv3+: GNU GPL version 3 or later \r\nThis is free soft"] [2.218538, "o", "ware: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.\r\n"] [2.233581, "o", "\r\n"] [7.236907, "o", "$ cat foo.md\r\n"] [7.239637, "o", "# Hi\r\n\r\n[](TOC)\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n"] [7.241085, "o", "\r\n"] [12.244051, "o", "$ md_toc github foo.md\r\n"] [12.449938, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n"] [12.46659, "o", "\r\n"] [17.468522, "o", "$ md_toc gitlab -o foo.md\r\n"] [17.673403, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [Bye bye](#bye-bye)\r\n"] [17.689824, "o", "\r\n"] [22.691577, "o", "$ md_toc -n github foo.md\r\n"] [22.898801, "o", "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - Bye bye\r\n"] [22.913503, "o", "\r\n"] [27.915307, "o", "$ Editing the file in-place..."] [27.91691, "o", "\r\n"] [27.917858, "o", "$ md_toc -i redcarpet foo.md\r\n"] [28.137716, "o", "$ cat foo.md\r\n"] [28.139199, "o", "# Hi\r\n\r\n[](TOC)\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n\r\n[](TOC)\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n"] md-toc-9.0.0/asciinema/md_toc_asciinema_2_0_0_demo.sh000077500000000000000000000026741460560256400223550ustar00rootroot00000000000000#!/bin/bash # # md_toc_asciinema_2_0_0_demo.sh # # Copyright (C) 2017-2018 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # printf "Running a demo to show some of md_toc's capabilities...\n" printf "\n" sleep 2 printf "$ md_toc -h\n" md_toc -h printf "\n" sleep 5 cat <<-EOF > foo.md # Hi [](TOC) hey ## How are you? !!! ## fine, thanks ### Bye ## Bye bye EOF printf "$ cat foo.md\n" cat foo.md printf "\n" sleep 5 printf "$ md_toc github foo.md\n" md_toc github foo.md printf "\n" sleep 5 printf "$ md_toc gitlab -o foo.md\n" md_toc gitlab -o foo.md printf "\n" sleep 5 printf "$ md_toc -n github foo.md\n" md_toc -n github foo.md printf "\n" sleep 5 printf "$ Editing the file in-place...\n" printf "$ md_toc -i redcarpet foo.md\n" md_toc -i redcarpet foo.md printf "$ cat foo.md\n" cat foo.md rm foo.md md-toc-9.0.0/asciinema/md_toc_asciinema_3_0_0.json000066400000000000000000000064121460560256400217000ustar00rootroot00000000000000{"version": 2, "width": 83, "height": 46, "timestamp": 1552905547, "env": {"SHELL": "/bin/bash", "TERM": "rxvt-unicode-256color"}} [0.010489, "o", "Running a demo to show some of md_toc's capabilities...\r\n\r\n"] [2.014595, "o", "$ md_toc -h\r\n"] [2.191121, "o", "usage: md_toc [-h] [-p] [-l] [-i] [-m TOC_MARKER] [-v]\r\n [FILE_NAME [FILE_NAME ...]]\r\n {github,cmark,gitlab,commonmarker,redcarpet} ...\r\n\r\nMarkdown Table Of Contents: Automatically generate a compliant table\r\nof contents for a markdown file to improve document readability.\r\n\r\npositional arguments:\r\n FILE_NAME the I/O file name\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -p, --in-place overwrite the input file\r\n -l, --no-links avoids adding links to the corresponding content\r\n -i, --no-indentation avoids adding indentations to the corresponding\r\n content\r\n -m TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for\r\n positioning the table of contents. Defaults to [](TOC)\r\n -v, --version show program's version number and exit\r\n\r\nmarkdown parser:\r\n {github,cmark,gitlab,commonmarker,redcarpet}\r\n\r\nReturn values: 0 OK,"] [2.191225, "o", " 1 Error, 2 Invalid command\r\n\r\nCopyright (C) 2018-2019 Franco Masotti, frnmst\r\nLicense GPLv3+: GNU GPL version 3 or later \r\nThis is free software: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.\r\n"] [2.20289, "o", "\r\n"] [7.20649, "o", "Inspecting the file...\r\n$ cat foo.md\r\n"] [7.207679, "o", "# Hi\r\n\r\n[](TOC)\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n"] [7.20814, "o", "\r\n"] [12.210146, "o", "Run with default options...\r\n$ md_toc foo.md github\r\n"] [12.385408, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n"] [12.396683, "o", "\r\n"] [17.398421, "o", "Ordered list...\r\n$ md_toc foo.md gitlab -o\r\n"] [17.575691, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [Bye bye](#bye-bye)\r\n"] [17.587251, "o", "\r\n"] [22.589181, "o", "No links...\r\n$ md_toc -l foo.md github\r\n"] [22.767776, "o", "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - Bye bye\r\n"] [22.779368, "o", "\r\n"] [27.780919, "o", "No links and no indentation...\r\n$ md_toc -l -i foo.md github\r\n"] [27.95595, "o", "- Hi\r\n- How are you? !!!\r\n- fine, thanks\r\n- Bye\r\n- Bye bye\r\n"] [27.967396, "o", "\r\n"] [32.969054, "o", "Editing the file in-place...\r\n$ md_toc -p foo.md redcarpet\r\n"] [33.160029, "o", "$ cat foo.md\r\n"] [33.160866, "o", "# Hi\r\n\r\n[](TOC)\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n\r\n[](TOC)\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n"] md-toc-9.0.0/asciinema/md_toc_asciinema_3_0_0_demo.sh000077500000000000000000000034241460560256400223500ustar00rootroot00000000000000#!/bin/bash # # md_toc_asciinema_3_0_0_demo.sh # # Copyright (C) 2019 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # # Discover md_toc of this repository not the one installed on the system. export PYTHONPATH='..' printf "Running a demo to show some of md_toc's capabilities...\n" printf "\n" sleep 2 printf "$ md_toc -h\n" md_toc -h printf "\n" sleep 5 cat <<-EOF > foo.md # Hi [](TOC) hey ## How are you? !!! ## fine, thanks ### Bye ## Bye bye EOF printf "Inspecting the file...\n" printf "$ cat foo.md\n" cat foo.md printf "\n" sleep 5 printf "Run with default options...\n" printf "$ md_toc foo.md github\n" md_toc foo.md github printf "\n" sleep 5 printf "Ordered list...\n" printf "$ md_toc foo.md gitlab -o\n" md_toc foo.md gitlab -o printf "\n" sleep 5 printf "No links...\n" printf "$ md_toc -l foo.md github\n" md_toc -l foo.md github printf "\n" sleep 5 printf "No links and no indentation...\n" printf "$ md_toc -l -i foo.md github\n" md_toc -l -i foo.md github printf "\n" sleep 5 printf "Editing the file in-place...\n" printf "$ md_toc -p foo.md redcarpet\n" md_toc -p foo.md redcarpet printf "$ cat foo.md\n" cat foo.md rm foo.md md-toc-9.0.0/asciinema/md_toc_asciinema_3_1_0.json000066400000000000000000000103521460560256400216770ustar00rootroot00000000000000{"version": 2, "width": 83, "height": 46, "timestamp": 1553863945, "env": {"SHELL": "/bin/bash", "TERM": "rxvt-unicode-256color"}} [0.009147, "o", "Running a demo to show some of md_toc's capabilities...\r\n\r\n"] [2.010888, "o", "$ md_toc -h\r\n"] [2.194677, "o", "usage: md_toc [-h] [-p] [-l] [-i] [-m TOC_MARKER] [-v]\r\n [FILE_NAME [FILE_NAME ...]]\r\n {github,cmark,gitlab,commonmarker,redcarpet} ...\r\n\r\nMarkdown Table Of Contents: Automatically generate a compliant table\r\nof contents for a markdown file to improve document readability.\r\n\r\npositional arguments:\r\n FILE_NAME the I/O file name\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -p, --in-place overwrite the input file\r\n -l, --no-links avoids adding links to the corresponding content\r\n -i, --no-indentation avoids adding indentations to the corresponding\r\n content\r\n -m TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for\r\n positioning the table of contents. Defaults to [](TOC)\r\n -v, --version show program's version number and exit\r\n\r\nmarkdown parser:\r\n {github,cmark,gitlab,commonmarker,redcarpet}\r\n\r\nPlease read the docu"] [2.194781, "o", "mentation to understand how each parser works\r\n\r\nReturn values: 0 ok, 1 error, 2 invalid command\r\n\r\nCopyright (C) 2018-2019 Franco Masotti, frnmst\r\nLicense GPLv3+: GNU GPL version 3 or later \r\nThis is free software: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.\r\n"] [2.206379, "o", "\r\n"] [7.209793, "o", "Inspecting the file...\r\n$ cat foo.md\r\n"] [7.211251, "o", "# Hi\r\n\r\n[](TOC)\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] [7.211669, "o", "\r\n"] [12.213709, "o", "Run with default options...\r\n$ md_toc foo.md github\r\n"] [12.398653, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n- [boo](#boo)\r\n"] [12.410272, "o", "\r\n"] [17.411973, "o", "Ordered list...\r\n$ md_toc foo.md gitlab -o\r\n"] [17.599974, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [Bye bye](#bye-bye)\r\n2. [boo](#boo)\r\n"] [17.611696, "o", "\r\n"] [22.613308, "o", "No links...\r\n$ md_toc -l foo.md github\r\n"] [22.796106, "o", "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - Bye bye\r\n- boo\r\n"] [22.808055, "o", "\r\n"] [27.809539, "o", "No links and no indentation...\r\n$ md_toc -l -i foo.md github\r\n"] [27.996843, "o", "- Hi\r\n- How are you? !!!\r\n- fine, thanks\r\n- Bye\r\n- Bye bye\r\n- boo\r\n"] [28.008569, "o", "\r\n"] [33.011449, "o", "Use stdin and ...\r\n$ cat foo.md | md_toc -l -i cmark -u '*'\r\n"] [33.1974, "o", "* Hi\r\n* How are you? !!!\r\n* fine, thanks\r\n* Bye\r\n* Bye bye\r\n* boo\r\n"] [33.209085, "o", "\r\n"] [38.210776, "o", "Editing the file in-place. As you can see, code fence detection still needs to be implemented for redcarpet..\r\n$ md_toc -p foo.md redcarpet\r\n"] [38.407375, "o", "$ cat foo.md\r\n"] [38.408353, "o", "# Hi\r\n\r\n[](TOC)\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n- [This is a code](#this-is-a-code)\r\n- [fence with comments that might represent ATX-style headings](#fence-with-comments-that-might-represent-atx-style-headings)\r\n- [if not properly parsed](#if-not-properly-parsed)\r\n- [boo](#boo)\r\n\r\n[](TOC)\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] md-toc-9.0.0/asciinema/md_toc_asciinema_3_1_0_demo.sh000077500000000000000000000041751460560256400223550ustar00rootroot00000000000000#!/bin/bash # # md_toc_asciinema_3_1_0_demo.sh # # Copyright (C) 2019 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # # Discover md_toc of this repository not the one installed on the system. export PYTHONPATH='..' printf "Running a demo to show some of md_toc's capabilities...\n" printf "\n" sleep 2 printf "$ md_toc -h\n" md_toc -h printf "\n" sleep 5 cat <<-EOF > foo.md # Hi [](TOC) hey ## How are you? !!! ## fine, thanks ### Bye ## Bye bye \`\`\`python # This is a code # fence with comments that might represent ATX-style headings # if not properly parsed \`\`\` bye # boo EOF printf "Inspecting the file...\n" printf "$ cat foo.md\n" cat foo.md printf "\n" sleep 5 printf "Run with default options...\n" printf "$ md_toc foo.md github\n" md_toc foo.md github printf "\n" sleep 5 printf "Ordered list...\n" printf "$ md_toc foo.md gitlab -o\n" md_toc foo.md gitlab -o printf "\n" sleep 5 printf "No links...\n" printf "$ md_toc -l foo.md github\n" md_toc -l foo.md github printf "\n" sleep 5 printf "No links and no indentation...\n" printf "$ md_toc -l -i foo.md github\n" md_toc -l -i foo.md github printf "\n" sleep 5 printf "Use stdin and ...\n" printf "$ cat foo.md | md_toc -l -i cmark -u '*'\n" cat foo.md | md_toc -l -i cmark -u '*' printf "\n" sleep 5 printf "Editing the file in-place. As you can see, code fence \ detection still needs to be implemented for redcarpet..\n" printf "$ md_toc -p foo.md redcarpet\n" md_toc -p foo.md redcarpet printf "$ cat foo.md\n" cat foo.md rm foo.md md-toc-9.0.0/asciinema/md_toc_asciinema_5_0_0.json000066400000000000000000000125741460560256400217100ustar00rootroot00000000000000{"version": 2, "width": 174, "height": 46, "timestamp": 1555259634, "env": {"SHELL": "/bin/bash", "TERM": "rxvt-unicode-256color"}} [0.010556, "o", "Running a demo to show some of md_toc's capabilities...\r\n\r\n"] [1.012344, "o", "$ md_toc -h\r\n"] [1.197964, "o", "usage: md_toc [-h] [-c | -i] [-l] [-m TOC_MARKER] [-p] [-v]\r\n [FILE_NAME [FILE_NAME ...]]\r\n {github,cmark,gitlab,commonmarker,redcarpet} ...\r\n\r\nMarkdown Table Of Contents: Automatically generate a compliant table\r\nof contents for a markdown file to improve document readability.\r\n\r\npositional arguments:\r\n FILE_NAME the I/O file name\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -c, --no-list-coherence\r\n avoids checking for TOC list coherence\r\n -i, --no-indentation avoids adding indentations to the TOC\r\n -l, --no-links avoids adding links to the TOC\r\n -m TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for\r\n positioning the table of contents. Defaults to [](TOC)\r\n -p, --in-place overwrite the input file\r\n -v, --version show program's version number and exit\r\n\r\nmarkdown parser:\r\n {github,cmark,gitlab,commonmarker"] [1.198643, "o", ",redcarpet}\r\n\r\nPlease read the documentation to understand how each parser works\r\n\r\nReturn values: 0 ok, 1 error, 2 invalid command\r\n\r\nCopyright (C) 2018-2019 Franco Masotti, frnmst\r\nLicense GPLv3+: GNU GPL version 3 or later \r\nThis is free software: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.\r\n"] [1.210429, "o", "\r\n"] [2.215499, "o", "Inspecting the file...\r\n$ cat foo.md\r\n"] [2.216698, "o", "# Hi\r\n\r\n[](TOC)\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] [2.21728, "o", "\r\n"] [3.218615, "o", "Run with default options...\r\n$ md_toc foo.md github\r\n"] [3.401344, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n- [boo](#boo)\r\n"] [3.413, "o", "\r\n"] [4.41438, "o", "Ordered list...\r\n$ md_toc foo.md gitlab -o\r\n"] [4.605895, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [Bye bye](#bye-bye)\r\n2. [boo](#boo)\r\n"] [4.617561, "o", "\r\n"] [5.619173, "o", "No links...\r\n$ md_toc -l foo.md github\r\n"] [5.801036, "o", "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - Bye bye\r\n- boo\r\n"] [5.812618, "o", "\r\n"] [6.814147, "o", "No links and no indentation...\r\n$ md_toc -l -i foo.md github\r\n"] [6.998235, "o", "- Hi"] [6.998534, "o", "\r\n"] [6.9986, "o", "- How are you? !!!"] [6.998829, "o", "\r\n"] [6.998944, "o", "- fine, thanks\r\n- Bye\r\n- Bye bye\r\n- boo\r\n"] [7.010849, "o", "\r\n"] [8.012481, "o", "Inspecting the non-coherent file...\r\n$ cat foo_noncoherent.md\r\n"] [8.014395, "o", "# Hi\r\n### boo\r\n"] [8.014576, "o", "\r\n"] [9.01643, "o", "Trying to parse a non coherent markdown file will raise an exception...\r\n$ md_toc foo_noncoherent.md github\r\n"] [9.200414, "o", "Traceback (most recent call last):\r\n File \"/home/vm/md-toc/md_toc/__main__.py\", line 34, in main\r\n result = args.func(args)\r\n File \"/home/vm/md-toc/md_toc/cli.py\", line 67, in write_toc\r\n list_marker=list_marker)\r\n File \"/home/vm/md-toc/md_toc/api.py\", line 288, in build_multiple_tocs\r\n list_marker))\r\n File \"/home/vm/md-toc/md_toc/api.py\", line 213, in build_toc\r\n raise TocDoesNotRenderAsCoherentList\r\nmd_toc.exceptions.TocDoesNotRenderAsCoherentList\r\n"] [9.212276, "o", "\r\n"] [10.213782, "o", "Try to parse a non coherent markdown file without checking for coherence...\r\n$ md_toc -c foo_noncoherent.md github\r\n"] [10.399526, "o", "- [Hi](#hi)\r\n - [boo](#boo)\r\n"] [10.411949, "o", "\r\n"] [11.413447, "o", "Use stdin, no links and no indentation...\r\n$ cat foo.md | md_toc -l -i cmark -u '*'\r\n"] [11.599078, "o", "* Hi\r\n* How are you? !!!\r\n* fine, thanks\r\n* Bye\r\n* Bye bye\r\n* boo\r\n"] [11.611748, "o", "\r\n"] [12.61327, "o", "Editing the file in-place. As you can see, code fence detection still needs to be implemented for redcarpet..\r\n$ md_toc -p foo.md redcarpet\r\n"] [12.867298, "o", "$ cat foo.md\r\n"] [12.867999, "o", "# Hi\r\n\r\n[](TOC)\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n- [This is a code](#this-is-a-code)\r\n- [fence with comments that might represent ATX-style headings](#fence-with-comments-that-might-represent-atx-style-headings)\r\n- [if not properly parsed](#if-not-properly-parsed)\r\n- [boo](#boo)\r\n\r\n[](TOC)\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] md-toc-9.0.0/asciinema/md_toc_asciinema_5_0_0_demo.sh000077500000000000000000000054761460560256400223630ustar00rootroot00000000000000#!/bin/bash # # md_toc_asciinema_5_0_0_demo.sh # # Copyright (C) 2019 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # # Discover md_toc of this repository not the one installed on the system. export PYTHONPATH='..' TIMEOUT=1 printf "Running a demo to show some of md_toc's capabilities...\n" printf "\n" sleep ${TIMEOUT} printf "$ md_toc -h\n" md_toc -h printf "\n" sleep ${TIMEOUT} cat <<-EOF > foo.md # Hi [](TOC) hey ## How are you? !!! ## fine, thanks ### Bye ## Bye bye \`\`\`python # This is a code # fence with comments that might represent ATX-style headings # if not properly parsed \`\`\` bye # boo EOF cat <<-EOF > foo_noncoherent.md # Hi ### boo EOF printf "Inspecting the file...\n" printf "$ cat foo.md\n" cat foo.md printf "\n" sleep ${TIMEOUT} printf "Run with default options...\n" printf "$ md_toc foo.md github\n" md_toc foo.md github printf "\n" sleep ${TIMEOUT} printf "Ordered list...\n" printf "$ md_toc foo.md gitlab -o\n" md_toc foo.md gitlab -o printf "\n" sleep ${TIMEOUT} printf "No links...\n" printf "$ md_toc -l foo.md github\n" md_toc -l foo.md github printf "\n" sleep ${TIMEOUT} printf "No links and no indentation...\n" printf "$ md_toc -l -i foo.md github\n" md_toc -l -i foo.md github printf "\n" sleep ${TIMEOUT} printf "Inspecting the non-coherent file...\n" printf "$ cat foo_noncoherent.md\n" cat foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Trying to parse a non coherent markdown file will raise an exception...\n" printf "$ md_toc foo_noncoherent.md github\n" md_toc foo_noncoherent.md github printf "\n" sleep ${TIMEOUT} printf "Try to parse a non coherent markdown file without checking for coherence...\n" printf "$ md_toc -c foo_noncoherent.md github\n" md_toc -c foo_noncoherent.md github printf "\n" sleep ${TIMEOUT} printf "Use stdin, no links and no indentation...\n" printf "$ cat foo.md | md_toc -l -i cmark -u '*'\n" cat foo.md | md_toc -l -i cmark -u '*' printf "\n" sleep ${TIMEOUT} printf "Editing the file in-place. As you can see, code fence \ detection still needs to be implemented for redcarpet..\n" printf "$ md_toc -p foo.md redcarpet\n" md_toc -p foo.md redcarpet printf "$ cat foo.md\n" cat foo.md rm foo.md foo_noncoherent.md md-toc-9.0.0/asciinema/md_toc_asciinema_6_0_0.json000066400000000000000000000144041460560256400217030ustar00rootroot00000000000000{"version": 2, "width": 83, "height": 46, "timestamp": 1560350339, "env": {"SHELL": "/bin/bash", "TERM": "rxvt-unicode-256color"}} [0.012098, "o", "Running a demo to show some of python -m md_toc's capabilities...\r\n\r\n"] [1.013844, "o", "$ python -m md_toc -h\r\n"] [1.202271, "o", "usage: __main__.py [-h] [-c | -i] [-l] [-m TOC_MARKER] [-p] [-s SKIP_LINES]\r\n [-v]\r\n [FILE_NAME [FILE_NAME ...]]\r\n {github,cmark,gitlab,commonmarker,redcarpet} ...\r\n\r\nMarkdown Table Of Contents: Automatically generate a compliant table\r\nof contents for a markdown file to improve document readability.\r\n\r\npositional arguments:\r\n FILE_NAME the I/O file name\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -c, --no-list-coherence\r\n avoids checking for TOC list coherence\r\n -i, --no-indentation avoids adding indentations to the TOC\r\n -l, --no-links avoids adding links to the TOC\r\n -m TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for\r\n positioning the table of contents. Defaults to \r\n -p, --in-place overwrite the input file\r\n -s SKIP_LINES, --skip-lines SKIP_LINE"] [1.202757, "o", "S\r\n skip parsing of the first selected number of lines.\r\n Defaults to 0, i.e. do not skip any lines\r\n -v, --version show program's version number and exit\r\n\r\nmarkdown parser:\r\n {github,cmark,gitlab,commonmarker,redcarpet}\r\n\r\nPlease read the documentation to understand how each parser works\r\n\r\nReturn values: 0 ok, 1 error, 2 invalid command\r\n\r\nCopyright (C) 2018-2019 Franco Masotti, frnmst\r\nLicense GPLv3+: GNU GPL version 3 or later \r\nThis is free software: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.\r\n"] [1.215144, "o", "\r\n"] [2.2221, "o", "Inspecting the file...\r\n$ cat foo.md\r\n"] [2.22298, "o", "# Hi\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] [2.223663, "o", "\r\n"] [3.224875, "o", "Run with default options...\r\n$ python -m md_toc foo.md github\r\n"] [3.413952, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n- [boo](#boo)\r\n"] [3.426435, "o", "\r\n"] [4.427878, "o", "Ordered list...\r\n$ python -m md_toc foo.md gitlab -o\r\n"] [4.61836, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [Bye bye](#bye-bye)\r\n2. [boo](#boo)\r\n"] [4.631221, "o", "\r\n"] [5.632689, "o", "No links...\r\n$ python -m md_toc -l foo.md github\r\n"] [5.820955, "o", "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - Bye bye\r\n- boo\r\n"] [5.833366, "o", "\r\n"] [6.834851, "o", "No links and no indentation...\r\n$ python -m md_toc -l -i foo.md github\r\n"] [7.017477, "o", "- Hi\r\n- How are you? !!!\r\n- fine, thanks\r\n- Bye\r\n- Bye bye\r\n- boo\r\n"] [7.030133, "o", "\r\n"] [8.031626, "o", "Inspecting the non-coherent file...\r\n$ cat foo_noncoherent.md\r\n"] [8.032911, "o", "# Hi\r\n### boo\r\n"] [8.033147, "o", "\r\n"] [9.034898, "o", "Trying to parse a non coherent markdown file will raise an exception...\r\n$ python -m md_toc foo_noncoherent.md github\r\n"] [9.228868, "o", "Traceback (most recent call last):\r\n File \"/home/vm/md-toc/md_toc/__main__.py\", line 34, in main\r\n result = args.func(args)\r\n File \"/home/vm/md-toc/md_toc/cli.py\", line 63, in write_toc\r\n skip_lines=args.skip_lines)\r\n File \"/home/vm/md-toc/md_toc/api.py\", line 303, in build_multiple_tocs\r\n list_marker, skip_lines))\r\n File \"/home/vm/md-toc/md_toc/api.py\", line 226, in build_toc"] [9.229464, "o", "\r\n raise TocDoesNotRenderAsCoherentList\r\nmd_toc.exceptions.TocDoesNotRenderAsCoherentList\r\n"] [9.242013, "o", "\r\n"] [10.243606, "o", "Try to parse a non coherent markdown file without checking for coherence...\r\n$ python -m md_toc -c foo_noncoherent.md github\r\n"] [10.428045, "o", "- [Hi](#hi)\r\n - [boo](#boo)\r\n"] [10.440762, "o", "\r\n"] [11.442176, "o", "Use stdin, no links and no indentation...\r\n$ cat foo.md | python -m md_toc -l -i cmark -u '*'\r\n"] [11.634328, "o", "* Hi\r\n* How are you? !!!\r\n* fine, thanks\r\n* Bye\r\n* Bye bye\r\n* boo\r\n"] [11.64981, "o", "\r\n"] [12.651345, "o", "Inspecting a file where the first 5 lines need to be skipped...\r\n$ cat foo_skiplines.md\r\n"] [12.65276, "o", "# I want this line to be a comment\r\n#### And this as well\r\n## And this\r\n###### ByeBye\r\n\r\n# Hi\r\n## How\r\n### Are\r\n## You\r\n# Today ?\r\n"] [12.653268, "o", "\r\n"] [13.654713, "o", "Using the skip lines option...\r\n$ python -m md_toc -s 5 foo_skiplines.md github\r\n"] [13.848681, "o", "- [Hi](#hi)\r\n - [How](#how)\r\n - [Are](#are)\r\n - [You](#you)\r\n- [Today ?](#today-)\r\n"] [13.860635, "o", "\r\n"] [14.862582, "o", "Editing the file in-place. As you can see, code fence detection still needs to be implemented for redcarpet...\r\n$ python -m md_toc -p foo.md redcarpet\r\n"] [15.150645, "o", "$ cat foo.md\r\n"] [15.151666, "o", "# Hi\r\n\r\n\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n- [This is a code](#this-is-a-code)\r\n- [fence with comments that might represent ATX-style headings](#fence-with-comments-that-might-represent-atx-style-headings)\r\n- [if not properly parsed](#if-not-properly-parsed)\r\n- [boo](#boo)\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] md-toc-9.0.0/asciinema/md_toc_asciinema_6_0_0_demo.sh000077500000000000000000000070221460560256400223510ustar00rootroot00000000000000#!/bin/bash # # python -m md_toc_asciinema_6_0_0_demo.sh # # Copyright (C) 2019 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # # Discover python -m md_toc of this repository not the one installed on the system. export PYTHONPATH='..' TIMEOUT=1 printf "Running a demo to show some of python -m md_toc's capabilities...\n" printf "\n" sleep ${TIMEOUT} printf "$ python -m md_toc -h\n" python -m md_toc -h printf "\n" sleep ${TIMEOUT} cat <<-EOF > foo.md # Hi hey ## How are you? !!! ## fine, thanks ### Bye ## Bye bye \`\`\`python # This is a code # fence with comments that might represent ATX-style headings # if not properly parsed \`\`\` bye # boo EOF cat <<-EOF > foo_noncoherent.md # Hi ### boo EOF cat <<-EOF > foo_skiplines.md # I want this line to be a comment #### And this as well ## And this ###### ByeBye # Hi ## How ### Are ## You # Today ? EOF printf "Inspecting the file...\n" printf "$ cat foo.md\n" cat foo.md printf "\n" sleep ${TIMEOUT} printf "Run with default options...\n" printf "$ python -m md_toc foo.md github\n" python -m md_toc foo.md github printf "\n" sleep ${TIMEOUT} printf "Ordered list...\n" printf "$ python -m md_toc foo.md gitlab -o\n" python -m md_toc foo.md gitlab -o printf "\n" sleep ${TIMEOUT} printf "No links...\n" printf "$ python -m md_toc -l foo.md github\n" python -m md_toc -l foo.md github printf "\n" sleep ${TIMEOUT} printf "No links and no indentation...\n" printf "$ python -m md_toc -l -i foo.md github\n" python -m md_toc -l -i foo.md github printf "\n" sleep ${TIMEOUT} printf "Inspecting the non-coherent file...\n" printf "$ cat foo_noncoherent.md\n" cat foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Trying to parse a non coherent markdown file will raise an exception...\n" printf "$ python -m md_toc foo_noncoherent.md github\n" python -m md_toc foo_noncoherent.md github printf "\n" sleep ${TIMEOUT} printf "Try to parse a non coherent markdown file without checking for coherence...\n" printf "$ python -m md_toc -c foo_noncoherent.md github\n" python -m md_toc -c foo_noncoherent.md github printf "\n" sleep ${TIMEOUT} printf "Use stdin, no links and no indentation...\n" printf "$ cat foo.md | python -m md_toc -l -i cmark -u '*'\n" cat foo.md | python -m md_toc -l -i cmark -u '*' printf "\n" sleep ${TIMEOUT} printf "Inspecting a file where the first 5 lines need to be skipped...\n" printf "$ cat foo_skiplines.md\n" cat foo_skiplines.md printf "\n" sleep ${TIMEOUT} printf "Using the skip lines option...\n" printf "$ python -m md_toc -s 5 foo_skiplines.md github\n" python -m md_toc -s 5 foo_skiplines.md github printf "\n" sleep ${TIMEOUT} printf "Editing the file in-place. As you can see, code fence \ detection still needs to be implemented for redcarpet...\n" printf "$ python -m md_toc -p foo.md redcarpet\n" python -m md_toc -p foo.md redcarpet printf "$ cat foo.md\n" cat foo.md rm foo.md foo_noncoherent.md foo_skiplines.md md-toc-9.0.0/asciinema/md_toc_asciinema_6_0_1.json000077700000000000000000000000001460560256400267302md_toc_asciinema_6_0_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_6_0_1_demo.sh000077700000000000000000000000001460560256400300422md_toc_asciinema_6_0_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_6_0_2.json000077700000000000000000000000001460560256400267312md_toc_asciinema_6_0_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_6_0_2_demo.sh000077700000000000000000000000001460560256400300432md_toc_asciinema_6_0_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_0.json000066400000000000000000000143741460560256400217120ustar00rootroot00000000000000{"version": 2, "width": 89, "height": 46, "timestamp": 1585155760, "env": {"SHELL": "/bin/bash", "TERM": "rxvt-unicode-256color"}} [0.010153, "o", "Running a demo to show some of python -m md_toc's capabilities...\r\n\r\n"] [1.011397, "o", "$ python -m md_toc -h\r\n"] [1.144886, "o", "usage: __main__.py [-h] [-c | -i] [-l] [-m TOC_MARKER] [-p] [-s SKIP_LINES] [-v]\r\n {github,cmark,gitlab,commonmarker,redcarpet} ...\r\n\r\nMarkdown Table Of Contents: Automatically generate a compliant table\r\nof contents for a markdown file to improve document readability.\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -c, --no-list-coherence\r\n avoids checking for TOC list coherence\r\n -i, --no-indentation avoids adding indentations to the TOC\r\n -l, --no-links avoids adding links to the TOC\r\n -m TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for positioning the\r\n table of contents. Defaults to \r\n -p, --in-place overwrite the input file\r\n -s SKIP_LINES, --skip-lines SKIP_LINES\r\n skip parsing of the first selected number of lines. Defaults to\r\n 0, i.e. do not skip any lines\r\n -v, --version"] [1.144939, "o", " show program's version number and exit\r\n\r\nmarkdown parser:\r\n {github,cmark,gitlab,commonmarker,redcarpet}\r\n --help\r\n\r\nPlease read the documentation to understand how each parser works\r\n\r\nReturn values: 0 ok, 1 error, 2 invalid command\r\n\r\nCopyright (C) 2017-2020 Franco Masotti, frnmst\r\nLicense GPLv3+: GNU GPL version 3 or later \r\nThis is free software: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.\r\n"] [1.153268, "o", "\r\n"] [2.157095, "o", "Inspecting the file...\r\n$ cat foo.md\r\n"] [2.157653, "o", "# Hi\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] [2.157873, "o", "\r\n"] [3.158819, "o", "Run with default options...\r\n$ python -m md_toc github foo.md\r\n"] [3.291031, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n- [boo](#boo)\r\n"] [3.298924, "o", "\r\n"] [4.299849, "o", "Ordered list...\r\n$ python -m md_toc gitlab -o '.' foo.md\r\n"] [4.440878, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [Bye bye](#bye-bye)\r\n2. [boo](#boo)\r\n"] [4.449244, "o", "\r\n"] [5.450176, "o", "No links...\r\n$ python -m md_toc -l github foo.md\r\n"] [5.587205, "o", "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - Bye bye\r\n- boo\r\n"] [5.595605, "o", "\r\n"] [6.596582, "o", "No links and no indentation...\r\n$ python -m md_toc -l -i github foo.md\r\n"] [6.729254, "o", "- Hi\r\n- How are you? !!!\r\n- fine, thanks\r\n- Bye\r\n- Bye bye\r\n- boo\r\n"] [6.737559, "o", "\r\n"] [7.738477, "o", "Inspecting the non-coherent file...\r\n$ cat foo_noncoherent.md\r\n"] [7.739502, "o", "# Hi\r\n### boo\r\n"] [7.739825, "o", "\r\n"] [8.74086, "o", "Trying to parse a non coherent markdown file will raise an exception...\r\n$ python -m md_toc github foo_noncoherent.md\r\n"] [8.877359, "o", "Traceback (most recent call last):\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/__main__.py\", line 34, in main\r\n result = args.func(args)\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/cli.py\", line 54, in write_toc\r\n toc_struct = build_multiple_tocs(\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 335, in build_multiple_tocs\r\n build_toc(filenames[file_id], ordered, no_links, no_indentation,\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 257, in build_toc\r\n raise TocDoesNotRenderAsCoherentList\r\nmd_toc.exceptions.TocDoesNotRenderAsCoherentList\r\n"] [8.886285, "o", "\r\n"] [9.887329, "o", "Try to parse a non coherent markdown file without checking for coherence...\r\n$ python -m md_toc -c github foo_noncoherent.md\r\n"] [10.027086, "o", "- [Hi](#hi)\r\n - [boo](#boo)\r\n"] [10.035279, "o", "\r\n"] [11.036226, "o", "Use stdin, no links and no indentation...\r\n$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\r\n"] [11.163924, "o", "* Hi\r\n* How are you? !!!\r\n* fine, thanks\r\n* Bye\r\n* Bye bye\r\n* boo\r\n"] [11.172106, "o", "\r\n"] [12.173201, "o", "Inspecting a file where the first 5 lines need to be skipped...\r\n$ cat foo_skiplines.md\r\n"] [12.174048, "o", "# I want this line to be a comment\r\n#### And this as well\r\n## And this\r\n###### ByeBye\r\n\r\n# Hi\r\n## How\r\n### Are\r\n## You\r\n# Today ?\r\n"] [12.174162, "o", "\r\n"] [13.175239, "o", "Using the skip lines option...\r\n$ python -m md_toc -s 5 github foo_skiplines.md\r\n"] [13.312227, "o", "- [Hi](#hi)\r\n - [How](#how)\r\n - [Are](#are)\r\n - [You](#you)\r\n- [Today ?](#today-)\r\n"] [13.32088, "o", "\r\n"] [14.322071, "o", "Editing the file in-place. As you can see, code fence detection still needs to be implemented for redcarpet...\r\n$ python -m md_toc -p redcarpet foo.md\r\n"] [14.491408, "o", "$ cat foo.md\r\n"] [14.492049, "o", "# Hi\r\n\r\n\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n- [This is a code](#this-is-a-code)\r\n- [fence with comments that might represent ATX-style headings](#fence-with-comments-that-might-represent-atx-style-headings)\r\n- [if not properly parsed](#if-not-properly-parsed)\r\n- [boo](#boo)\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_0_demo.sh000077500000000000000000000071061460560256400223550ustar00rootroot00000000000000#!/bin/bash # # python -m md_toc_asciinema_7_0_0_demo.sh # # Copyright (C) 2020 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # # Discover python -m md_toc of this repository not the one installed on the system. export PYTHONPATH='..' TIMEOUT=1 alias python='pipenv run python' ### ### printf "Running a demo to show some of python -m md_toc's capabilities...\n" printf "\n" sleep ${TIMEOUT} printf "$ python -m md_toc -h\n" python -m md_toc -h printf "\n" sleep ${TIMEOUT} cat <<-EOF > foo.md # Hi hey ## How are you? !!! ## fine, thanks ### Bye ## Bye bye \`\`\`python # This is a code # fence with comments that might represent ATX-style headings # if not properly parsed \`\`\` bye # boo EOF cat <<-EOF > foo_noncoherent.md # Hi ### boo EOF cat <<-EOF > foo_skiplines.md # I want this line to be a comment #### And this as well ## And this ###### ByeBye # Hi ## How ### Are ## You # Today ? EOF printf "Inspecting the file...\n" printf "$ cat foo.md\n" cat foo.md printf "\n" sleep ${TIMEOUT} printf "Run with default options...\n" printf "$ python -m md_toc github foo.md\n" python -m md_toc github foo.md printf "\n" sleep ${TIMEOUT} printf "Ordered list...\n" printf "$ python -m md_toc gitlab -o '.' foo.md\n" python -m md_toc gitlab -o '.' foo.md printf "\n" sleep ${TIMEOUT} printf "No links...\n" printf "$ python -m md_toc -l github foo.md\n" python -m md_toc -l github foo.md printf "\n" sleep ${TIMEOUT} printf "No links and no indentation...\n" printf "$ python -m md_toc -l -i github foo.md\n" python -m md_toc -l -i github foo.md printf "\n" sleep ${TIMEOUT} printf "Inspecting the non-coherent file...\n" printf "$ cat foo_noncoherent.md\n" cat foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Trying to parse a non coherent markdown file will raise an exception...\n" printf "$ python -m md_toc github foo_noncoherent.md\n" python -m md_toc github foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Try to parse a non coherent markdown file without checking for coherence...\n" printf "$ python -m md_toc -c github foo_noncoherent.md\n" python -m md_toc -c github foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Use stdin, no links and no indentation...\n" printf "$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\n" cat foo.md | python -m md_toc -l -i cmark -u '*' printf "\n" sleep ${TIMEOUT} printf "Inspecting a file where the first 5 lines need to be skipped...\n" printf "$ cat foo_skiplines.md\n" cat foo_skiplines.md printf "\n" sleep ${TIMEOUT} printf "Using the skip lines option...\n" printf "$ python -m md_toc -s 5 github foo_skiplines.md\n" python -m md_toc -s 5 github foo_skiplines.md printf "\n" sleep ${TIMEOUT} printf "Editing the file in-place. As you can see, code fence \ detection still needs to be implemented for redcarpet...\n" printf "$ python -m md_toc -p redcarpet foo.md\n" python -m md_toc -p redcarpet foo.md printf "$ cat foo.md\n" cat foo.md rm foo.md foo_noncoherent.md foo_skiplines.md md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_1.json000077700000000000000000000000001460560256400267322md_toc_asciinema_7_0_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_1_demo.sh000077700000000000000000000000001460560256400300442md_toc_asciinema_7_0_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_2.json000077700000000000000000000000001460560256400267332md_toc_asciinema_7_0_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_2_demo.sh000077700000000000000000000000001460560256400300452md_toc_asciinema_7_0_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_3.json000077700000000000000000000000001460560256400267342md_toc_asciinema_7_0_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_3_demo.sh000077700000000000000000000000001460560256400300462md_toc_asciinema_7_0_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_4.json000077700000000000000000000000001460560256400267352md_toc_asciinema_7_0_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_4_demo.sh000077700000000000000000000000001460560256400300472md_toc_asciinema_7_0_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_5.json000077700000000000000000000000001460560256400267362md_toc_asciinema_7_0_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_7_0_5_demo.sh000077700000000000000000000000001460560256400300502md_toc_asciinema_7_0_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_7_1_0.json000066400000000000000000000151031460560256400217020ustar00rootroot00000000000000{"version": 2, "width": 83, "height": 46, "timestamp": 1611160459, "env": {"SHELL": "/bin/bash", "TERM": "rxvt-unicode-256color"}} [0.006135, "o", "Running a demo to show some of python -m md_toc's capabilities...\r\n\r\n"] [1.007122, "o", "$ python -m md_toc -h\r\n"] [1.205091, "o", "usage: __main__.py [-h] [-c | -i] [-l] [-m TOC_MARKER] [-p] [-s SKIP_LINES] [-v]\r\n {github,cmark,gitlab,commonmarker,redcarpet} ...\r\n\r\nMarkdown Table Of Contents: Automatically generate a compliant table\r\nof contents for a markdown file to improve document readability.\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -c, --no-list-coherence\r\n avoids checking for TOC list coherence\r\n -i, --no-indentation avoids adding indentations to the TOC\r\n -l, --no-links avoids adding links to the TOC\r\n -m TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for positioning\r\n the table of contents. Defaults to \r\n -p, --in-place overwrite the input file\r\n -s SKIP_LINES, --skip-lines SKIP_LINES\r\n skip parsing of the first selected number of lines.\r\n Defaults to 0, i.e. do not skip any lines\r\n -v, --version"] [1.205153, "o", " show program's version number and exit\r\n\r\nmarkdown parser:\r\n {github,cmark,gitlab,commonmarker,redcarpet}\r\n --help\r\n\r\nPlease read the documentation to understand how each parser works\r\n\r\nReturn values: 0 ok, 1 error, 2 invalid command\r\n\r\nCopyright (C) 2017-2021 Franco Masotti, frnmst\r\nLicense GPLv3+: GNU GPL version 3 or later \r\nThis is free software: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.\r\n"] [1.22023, "o", "\r\n"] [2.223355, "o", "Inspecting the file...\r\n$ cat foo.md\r\n"] [2.223963, "o", "# Hi\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] [2.224005, "o", "\r\n"] [3.224999, "o", "Run with default options...\r\n$ python -m md_toc github foo.md\r\n"] [3.439828, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n- [boo](#boo)\r\n"] [3.457865, "o", "\r\n"] [4.458982, "o", "Ordered list...\r\n$ python -m md_toc gitlab -o '.' foo.md\r\n"] [4.648557, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [Bye bye](#bye-bye)\r\n2. [boo](#boo)\r\n"] [4.664706, "o", "\r\n"] [5.665772, "o", "Constant ordered list...\r\n$ python -m md_toc github -c -o '.' foo.md\r\n"] [5.87788, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 1. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 1. [Bye bye](#bye-bye)\r\n1. [boo](#boo)\r\n"] [5.894791, "o", "\r\n"] [6.895756, "o", "No links...\r\n$ python -m md_toc -l github foo.md\r\n"] [7.10896, "o", "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - Bye bye\r\n- boo\r\n"] [7.123939, "o", "\r\n"] [8.125197, "o", "No links and no indentation...\r\n$ python -m md_toc -l -i github foo.md\r\n"] [8.331182, "o", "- Hi\r\n- How are you? !!!\r\n- fine, thanks\r\n- Bye\r\n- Bye bye\r\n- boo\r\n"] [8.346253, "o", "\r\n"] [9.347239, "o", "Inspecting the non-coherent file...\r\n$ cat foo_noncoherent.md\r\n"] [9.347893, "o", "# Hi\r\n### boo\r\n"] [9.348052, "o", "\r\n"] [10.350585, "o", "Trying to parse a non coherent markdown file will raise an exception...\r\n$ python -m md_toc github foo_noncoherent.md\r\n"] [10.560376, "o", "Traceback (most recent call last):\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/__main__.py\", line 34, in main\r\n result = args.func(args)\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/cli.py\", line 55, in write_toc\r\n toc_struct = build_multiple_tocs(\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 367, in build_multiple_tocs\r\n build_toc(filenames[file_id], ordered, no_links, no_indentation,\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 276, in build_toc\r\n raise TocDoesNotRenderAsCoherentList\r\nmd_toc.exceptions.TocDoesNotRenderAsCoherentList\r\n"] [10.576715, "o", "\r\n"] [11.578079, "o", "Try to parse a non coherent markdown file without checking for coherence...\r\n$ python -m md_toc -c github foo_noncoherent.md\r\n"] [11.801014, "o", "- [Hi](#hi)\r\n - [boo](#boo)\r\n"] [11.81771, "o", "\r\n"] [12.818955, "o", "Use stdin, no links and no indentation...\r\n$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\r\n"] [13.029036, "o", "* Hi\r\n* How are you? !!!\r\n* fine, thanks\r\n* Bye\r\n* Bye bye\r\n* boo\r\n"] [13.044662, "o", "\r\n"] [14.045578, "o", "Inspecting a file where the first 5 lines need to be skipped...\r\n$ cat foo_skiplines.md\r\n"] [14.046162, "o", "# I want this line to be a comment\r\n#### And this as well\r\n## And this\r\n###### ByeBye\r\n\r\n# Hi\r\n## How\r\n### Are\r\n## You\r\n# Today ?\r\n"] [14.046329, "o", "\r\n"] [15.04761, "o", "Using the skip lines option...\r\n$ python -m md_toc -s 5 github foo_skiplines.md\r\n"] [15.244163, "o", "- [Hi](#hi)\r\n - [How](#how)\r\n - [Are](#are)\r\n - [You](#you)\r\n- [Today ?](#today-)\r\n"] [15.259783, "o", "\r\n"] [16.260795, "o", "Editing the file in-place. As you can see, code fence detection still needs to be implemented for redcarpet...\r\n$ python -m md_toc -p redcarpet foo.md\r\n"] [16.488959, "o", "$ cat foo.md\r\n"] [16.489472, "o", "# Hi\r\n\r\n\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [Bye bye](#bye-bye)\r\n- [This is a code](#this-is-a-code)\r\n- [fence with comments that might represent ATX-style headings](#fence-with-comments-that-might-represent-atx-style-headings)\r\n- [if not properly parsed](#if-not-properly-parsed)\r\n- [boo](#boo)\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## Bye bye\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] md-toc-9.0.0/asciinema/md_toc_asciinema_7_1_0_demo.sh000077500000000000000000000073471460560256400223650ustar00rootroot00000000000000#!/bin/bash # # python -m md_toc_asciinema_7_1_0_demo.sh # # Copyright (C) 2021 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # # Discover python -m md_toc of this repository not the one installed on the system. export PYTHONPATH='..' TIMEOUT=1 alias python='pipenv run python' ### ### printf "Running a demo to show some of python -m md_toc's capabilities...\n" printf "\n" sleep ${TIMEOUT} printf "$ python -m md_toc -h\n" python -m md_toc -h printf "\n" sleep ${TIMEOUT} cat <<-EOF > foo.md # Hi hey ## How are you? !!! ## fine, thanks ### Bye ## Bye bye \`\`\`python # This is a code # fence with comments that might represent ATX-style headings # if not properly parsed \`\`\` bye # boo EOF cat <<-EOF > foo_noncoherent.md # Hi ### boo EOF cat <<-EOF > foo_skiplines.md # I want this line to be a comment #### And this as well ## And this ###### ByeBye # Hi ## How ### Are ## You # Today ? EOF printf "Inspecting the file...\n" printf "$ cat foo.md\n" cat foo.md printf "\n" sleep ${TIMEOUT} printf "Run with default options...\n" printf "$ python -m md_toc github foo.md\n" python -m md_toc github foo.md printf "\n" sleep ${TIMEOUT} printf "Ordered list...\n" printf "$ python -m md_toc gitlab -o '.' foo.md\n" python -m md_toc gitlab -o '.' foo.md printf "\n" sleep ${TIMEOUT} printf "Constant ordered list...\n" printf "$ python -m md_toc github -c -o '.' foo.md\n" python -m md_toc github -c -o '.' foo.md printf "\n" sleep ${TIMEOUT} printf "No links...\n" printf "$ python -m md_toc -l github foo.md\n" python -m md_toc -l github foo.md printf "\n" sleep ${TIMEOUT} printf "No links and no indentation...\n" printf "$ python -m md_toc -l -i github foo.md\n" python -m md_toc -l -i github foo.md printf "\n" sleep ${TIMEOUT} printf "Inspecting the non-coherent file...\n" printf "$ cat foo_noncoherent.md\n" cat foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Trying to parse a non coherent markdown file will raise an exception...\n" printf "$ python -m md_toc github foo_noncoherent.md\n" python -m md_toc github foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Try to parse a non coherent markdown file without checking for coherence...\n" printf "$ python -m md_toc -c github foo_noncoherent.md\n" python -m md_toc -c github foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Use stdin, no links and no indentation...\n" printf "$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\n" cat foo.md | python -m md_toc -l -i cmark -u '*' printf "\n" sleep ${TIMEOUT} printf "Inspecting a file where the first 5 lines need to be skipped...\n" printf "$ cat foo_skiplines.md\n" cat foo_skiplines.md printf "\n" sleep ${TIMEOUT} printf "Using the skip lines option...\n" printf "$ python -m md_toc -s 5 github foo_skiplines.md\n" python -m md_toc -s 5 github foo_skiplines.md printf "\n" sleep ${TIMEOUT} printf "Editing the file in-place. As you can see, code fence \ detection still needs to be implemented for redcarpet...\n" printf "$ python -m md_toc -p redcarpet foo.md\n" python -m md_toc -p redcarpet foo.md printf "$ cat foo.md\n" cat foo.md rm foo.md foo_noncoherent.md foo_skiplines.md md-toc-9.0.0/asciinema/md_toc_asciinema_7_2_0.json000066400000000000000000000152611460560256400217100ustar00rootroot00000000000000{"version": 2, "width": 83, "height": 46, "timestamp": 1618065590, "env": {"SHELL": "/bin/bash", "TERM": "rxvt-unicode-256color"}} [0.006035, "o", "Running a demo to show some of python -m md_toc's capabilities...\r\n\r\n"] [1.007006, "o", "$ python -m md_toc -h\r\n"] [1.221794, "o", "usage: __main__.py [-h] [-c | -i] [-l] [-m TOC_MARKER] [-p] [-s SKIP_LINES] [-v]\r\n {github,cmark,gitlab,commonmarker,redcarpet} ...\r\n\r\nMarkdown Table Of Contents: Automatically generate a compliant table\r\nof contents for a markdown file to improve document readability.\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -c, --no-list-coherence\r\n avoids checking for TOC list coherence\r\n -i, --no-indentation avoids adding indentations to the TOC\r\n -l, --no-links avoids adding links to the TOC\r\n -m TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for positioning\r\n the table of contents. Defaults to \r\n -p, --in-place overwrite the input file\r\n -s SKIP_LINES, --skip-lines SKIP_LINES\r\n skip parsing of the first selected number of lines.\r\n Defaults to 0, i.e. do not skip any lines\r\n -v, --version"] [1.22192, "o", " show program's version number and exit\r\n\r\nmarkdown parser:\r\n {github,cmark,gitlab,commonmarker,redcarpet}\r\n --help\r\n\r\nPlease read the documentation to understand how each parser works\r\n\r\nReturn values: 0 ok, 1 error, 2 invalid command\r\n\r\nCopyright (C) 2017-2021 Franco Masotti, frnmst\r\nLicense GPLv3+: GNU GPL version 3 or later \r\nThis is free software: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.\r\n"] [1.24011, "o", "\r\n"] [2.247683, "o", "Inspecting the file...\r\n$ cat foo.md\r\n"] [2.249411, "o", "# Hi\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## __Bye bye__ *bye*\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] [2.250153, "o", "\r\n"] [3.252191, "o", "Run with default options...\r\n$ python -m md_toc github foo.md\r\n"] [3.489595, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ *bye*](#bye-bye-bye)\r\n- [boo](#boo)\r\n"] [3.50685, "o", "\r\n"] [4.507947, "o", "Ordered list...\r\n$ python -m md_toc gitlab -o '.' foo.md\r\n"] [4.735371, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [__Bye bye__ *bye*](#bye-bye-bye)\r\n2. [boo](#boo)\r\n"] [4.752882, "o", "\r\n"] [5.753784, "o", "Constant ordered list...\r\n$ python -m md_toc github -c -o '.' foo.md\r\n"] [5.964008, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 1. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 1. [__Bye bye__ *bye*](#bye-bye-bye)\r\n1. [boo](#boo)\r\n"] [5.979297, "o", "\r\n"] [6.980371, "o", "No links...\r\n$ python -m md_toc -l github foo.md\r\n"] [7.199988, "o", "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - __Bye bye__ *bye*\r\n- boo\r\n"] [7.217292, "o", "\r\n"] [8.218866, "o", "No links and no indentation...\r\n$ python -m md_toc -l -i github foo.md\r\n"] [8.438493, "o", "- Hi\r\n- How are you? !!!\r\n- fine, thanks\r\n- Bye\r\n- __Bye bye__ *bye*\r\n- boo\r\n"] [8.455101, "o", "\r\n"] [9.456421, "o", "Inspecting the non-coherent file...\r\n$ cat foo_noncoherent.md\r\n"] [9.458095, "o", "# Hi\r\n### boo\r\n"] [9.458364, "o", "\r\n"] [10.46083, "o", "Trying to parse a non coherent markdown file will raise an exception...\r\n$ python -m md_toc github foo_noncoherent.md\r\n"] [10.681219, "o", "Traceback (most recent call last):\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/__main__.py\", line 34, in main\r\n result = args.func(args)\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/cli.py\", line 55, in write_toc\r\n toc_struct = build_multiple_tocs(\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 367, in build_multiple_tocs\r\n build_toc(filenames[file_id], ordered, no_links, no_indentation,\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 276, in build_toc\r\n raise TocDoesNotRenderAsCoherentList\r\nmd_toc.exceptions.TocDoesNotRenderAsCoherentList\r\n"] [10.702213, "o", "\r\n"] [11.703501, "o", "Try to parse a non coherent markdown file without checking for coherence...\r\n$ python -m md_toc -c github foo_noncoherent.md\r\n"] [11.923728, "o", "- [Hi](#hi)\r\n - [boo](#boo)\r\n"] [11.944064, "o", "\r\n"] [12.945164, "o", "Use stdin, no links and no indentation...\r\n$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\r\n"] [13.15915, "o", "* Hi\r\n* How are you? !!!\r\n* fine, thanks\r\n* Bye\r\n* __Bye bye__ *bye*\r\n* boo\r\n"] [13.175019, "o", "\r\n"] [14.176371, "o", "Inspecting a file where the first 5 lines need to be skipped...\r\n$ cat foo_skiplines.md\r\n"] [14.177968, "o", "# I want this line to be a comment\r\n#### And this as well\r\n## And this\r\n###### ByeBye\r\n\r\n# Hi\r\n## How\r\n### Are\r\n## You\r\n# Today ?\r\n"] [14.178236, "o", "\r\n"] [15.180343, "o", "Using the skip lines option...\r\n$ python -m md_toc -s 5 github foo_skiplines.md\r\n"] [15.406022, "o", "- [Hi](#hi)\r\n - [How](#how)\r\n - [Are](#are)\r\n - [You](#you)\r\n- [Today ?](#today-)\r\n"] [15.422074, "o", "\r\n"] [16.422969, "o", "Editing the file in-place. As you can see, code fence detection still needs to be implemented for redcarpet...\r\n$ python -m md_toc -p redcarpet foo.md\r\n"] [16.640376, "o", "$ cat foo.md\r\n"] [16.640929, "o", "# Hi\r\n\r\n\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ *bye*](#__bye-bye__-bye)\r\n- [This is a code](#this-is-a-code)\r\n- [fence with comments that might represent ATX-style headings](#fence-with-comments-that-might-represent-atx-style-headings)\r\n- [if not properly parsed](#if-not-properly-parsed)\r\n- [boo](#boo)\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## __Bye bye__ *bye*\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n"] md-toc-9.0.0/asciinema/md_toc_asciinema_7_2_0_demo.sh000077500000000000000000000073611460560256400223620ustar00rootroot00000000000000#!/bin/bash # # python -m md_toc_asciinema_7_1_0_demo.sh # # Copyright (C) 2021 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # # Discover python -m md_toc of this repository not the one installed on the system. export PYTHONPATH='..' TIMEOUT=1 alias python='pipenv run python' ### ### printf "Running a demo to show some of python -m md_toc's capabilities...\n" printf "\n" sleep ${TIMEOUT} printf "$ python -m md_toc -h\n" python -m md_toc -h printf "\n" sleep ${TIMEOUT} cat <<-EOF > foo.md # Hi hey ## How are you? !!! ## fine, thanks ### Bye ## __Bye bye__ *bye* \`\`\`python # This is a code # fence with comments that might represent ATX-style headings # if not properly parsed \`\`\` bye # boo EOF cat <<-EOF > foo_noncoherent.md # Hi ### boo EOF cat <<-EOF > foo_skiplines.md # I want this line to be a comment #### And this as well ## And this ###### ByeBye # Hi ## How ### Are ## You # Today ? EOF printf "Inspecting the file...\n" printf "$ cat foo.md\n" cat foo.md printf "\n" sleep ${TIMEOUT} printf "Run with default options...\n" printf "$ python -m md_toc github foo.md\n" python -m md_toc github foo.md printf "\n" sleep ${TIMEOUT} printf "Ordered list...\n" printf "$ python -m md_toc gitlab -o '.' foo.md\n" python -m md_toc gitlab -o '.' foo.md printf "\n" sleep ${TIMEOUT} printf "Constant ordered list...\n" printf "$ python -m md_toc github -c -o '.' foo.md\n" python -m md_toc github -c -o '.' foo.md printf "\n" sleep ${TIMEOUT} printf "No links...\n" printf "$ python -m md_toc -l github foo.md\n" python -m md_toc -l github foo.md printf "\n" sleep ${TIMEOUT} printf "No links and no indentation...\n" printf "$ python -m md_toc -l -i github foo.md\n" python -m md_toc -l -i github foo.md printf "\n" sleep ${TIMEOUT} printf "Inspecting the non-coherent file...\n" printf "$ cat foo_noncoherent.md\n" cat foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Trying to parse a non coherent markdown file will raise an exception...\n" printf "$ python -m md_toc github foo_noncoherent.md\n" python -m md_toc github foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Try to parse a non coherent markdown file without checking for coherence...\n" printf "$ python -m md_toc -c github foo_noncoherent.md\n" python -m md_toc -c github foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Use stdin, no links and no indentation...\n" printf "$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\n" cat foo.md | python -m md_toc -l -i cmark -u '*' printf "\n" sleep ${TIMEOUT} printf "Inspecting a file where the first 5 lines need to be skipped...\n" printf "$ cat foo_skiplines.md\n" cat foo_skiplines.md printf "\n" sleep ${TIMEOUT} printf "Using the skip lines option...\n" printf "$ python -m md_toc -s 5 github foo_skiplines.md\n" python -m md_toc -s 5 github foo_skiplines.md printf "\n" sleep ${TIMEOUT} printf "Editing the file in-place. As you can see, code fence \ detection still needs to be implemented for redcarpet...\n" printf "$ python -m md_toc -p redcarpet foo.md\n" python -m md_toc -p redcarpet foo.md printf "$ cat foo.md\n" cat foo.md rm foo.md foo_noncoherent.md foo_skiplines.md md-toc-9.0.0/asciinema/md_toc_asciinema_8_0_0.json000066400000000000000000000205141460560256400217040ustar00rootroot00000000000000{"version": 2, "width": 83, "height": 46, "timestamp": 1622221077, "env": {"SHELL": "/bin/bash", "TERM": "rxvt-unicode-256color"}} [0.008049, "o", "Running a demo to show some of python -m md_toc's capabilities...\r\n\r\n"] [1.015122, "o", "$ python -m md_toc -h\r\n"] [1.356915, "o", "usage: __main__.py [-h] [-c | -i] [-l] [-m TOC_MARKER] [-n NEWLINE_STRING]\r\n [-p] [-s SKIP_LINES] [-v]\r\n {github,commonmarker,gitlab,cmark,goldmark,redcarpet} ...\r\n\r\nMarkdown Table Of Contents: Automatically generate a compliant table\r\nof contents for a markdown file to improve document readability.\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -c, --no-list-coherence\r\n avoids checking for TOC list coherence\r\n -i, --no-indentation avoids adding indentations to the TOC\r\n -l, --no-links avoids adding links to the TOC\r\n -m TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for\r\n positioning the table of contents. Defaults to \r\n -n NEWLINE_STRING, --newline-string NEWLINE_STRING\r\n the string used to separate the lines of the TOC. Use\r\n quotes to delimit t"] [1.357019, "o", "he string. Defaults to '\\n'\r\n -p, --in-place overwrite the input file\r\n -s SKIP_LINES, --skip-lines SKIP_LINES\r\n skip parsing of the first selected number of lines.\r\n Defaults to 0, i.e. do not skip any lines\r\n -v, --version show program's version number and exit\r\n\r\nmarkdown parser:\r\n {github,commonmarker,gitlab,cmark,goldmark,redcarpet}\r\n --help\r\n\r\nPlease read the documentation to understand how each parser works\r\n\r\nReturn values: 0 ok, 1 error, 2 invalid command\r\n\r\nCopyright (C) 2017-2021 Franco Masotti, frnmst\r\nLicense GPLv3+: GNU GPL version 3 or later \r\nThis is free software: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.\r\n"] [1.449011, "o", "\r\n"] [2.457318, "o", "Inspecting the file...\r\n$ cat foo.md\r\n"] [2.458509, "o", "# Hi\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## __Bye bye__ **bye***\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n\r\n# a string with lots of spaces.\r\n"] [2.459323, "o", "\r\n"] [3.467115, "o", "Run with default options...\r\n$ python -m md_toc github foo.md\r\n"] [3.932094, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ **bye***](#bye-bye-bye)\r\n- [boo](#boo)\r\n- [a string with lots of spaces.](#a-string--------------with----------------lots-of-spaces)\r\n"] [4.018819, "o", "\r\n"] [5.020617, "o", "Ordered list...\r\n$ python -m md_toc gitlab -o '.' foo.md\r\n"] [5.43953, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [__Bye bye__ **bye***](#bye-bye-bye)\r\n2. [boo](#boo)\r\n3. [a string with lots of spaces.](#a-string-with-lots-of-spaces)\r\n"] [5.499317, "o", "\r\n"] [6.501616, "o", "Constant ordered list...\r\n$ python -m md_toc github -c -o '.' foo.md\r\n"] [6.897007, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 1. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 1. [__Bye bye__ **bye***](#bye-bye-bye)\r\n1. [boo](#boo)\r\n1. [a string with lots of spaces.](#a-string--------------with----------------lots-of-spaces)\r\n"] [6.963218, "o", "\r\n"] [7.965299, "o", "No links...\r\n$ python -m md_toc -l github foo.md\r\n"] [8.342501, "o", "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - __Bye bye__ **bye***\r\n- boo\r\n- a string with lots of spaces.\r\n"] [8.419095, "o", "\r\n"] [9.421119, "o", "No links and no indentation...\r\n$ python -m md_toc -l -i github foo.md\r\n"] [9.951343, "o", "- Hi\r\n- How are you? !!!\r\n- fine, thanks\r\n- Bye\r\n- __Bye bye__ **bye***\r\n- boo\r\n- a string with lots of spaces.\r\n"] [10.052129, "o", "\r\n"] [11.053621, "o", "Inspecting the non-coherent file...\r\n$ cat foo_noncoherent.md\r\n"] [11.055057, "o", "# Hi\r\n### boo\r\n"] [11.055449, "o", "\r\n"] [12.05725, "o", "Trying to parse a non coherent markdown file will raise an exception...\r\n$ python -m md_toc github foo_noncoherent.md\r\n"] [12.513556, "o", "Traceback (most recent call last):\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/__main__.py\", line 34, in main\r\n result = args.func(args)\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/cli.py\", line 73, in write_toc\r\n newline_string=newline_string)\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 842, in build_multiple_tocs\r\n list_marker, skip_lines, constant_ordered_list, newline_string))\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 748, in build_toc\r\n raise TocDoesNotRenderAsCoherentList\r\nmd_toc.exceptions.TocDoesNotRenderAsCoherentList\r\n"] [12.598876, "o", "\r\n"] [13.601292, "o", "Try to parse a non coherent markdown file without checking for coherence...\r\n$ python -m md_toc -c github foo_noncoherent.md\r\n"] [13.957473, "o", "- [Hi](#hi)\r\n - [boo](#boo)\r\n"] [14.008105, "o", "\r\n"] [15.008974, "o", "Use stdin, no links and no indentation...\r\n$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\r\n"] [15.429645, "o", "* Hi\r\n* How are you? !!!\r\n* fine, thanks\r\n* Bye\r\n* __Bye bye__ **bye***\r\n* boo\r\n* a string with lots of spaces.\r\n"] [15.514497, "o", "\r\n"] [16.516463, "o", "Inspecting a file where the first 5 lines need to be skipped...\r\n$ cat foo_skiplines.md\r\n# I want this line to be a comment\r\n#### And this as well\r\n## And this\r\n###### ByeBye\r\n\r\n# Hi\r\n## How\r\n### Are\r\n## You\r\n# Today ?\r\n"] [16.519302, "o", "\r\n"] [17.519869, "o", "Using the skip lines option...\r\n$ python -m md_toc -s 5 github foo_skiplines.md\r\n"] [17.935135, "o", "- [Hi](#hi)\r\n - [How](#how)\r\n - [Are](#are)\r\n - [You](#you)\r\n- [Today ?](#today-)\r\n"] [18.007388, "o", "\r\n"] [19.009973, "o", "Showing Gitlab's removal of consecutive dashes in the link destination...\r\n$ python -m md_toc gitlab -l 6 foo.md\r\n"] [19.41085, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ **bye***](#bye-bye-bye)\r\n- [boo](#boo)\r\n- [a string with lots of spaces.](#a-string-with-lots-of-spaces)\r\n"] [19.483543, "o", "\r\n"] [20.486416, "o", "Editing the file in-place. As you can see, code fence detection still needs to be implemented for redcarpet...\r\n$ python -m md_toc -p redcarpet foo.md\r\n"] [21.00377, "o", "$ cat foo.md\r\n"] [21.007926, "o", "# Hi\r\n\r\n\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ **bye***](#small>__bye-bye__-bye-small>)\r\n- [This is a code](#this-is-a-code)\r\n- [fence with comments that might represent ATX-style headings](#fence-with-comments-that-might-represent-atx-style-headings)\r\n- [if not properly parsed](#if-not-properly-parsed)\r\n- [boo](#boo)\r\n- [a string with lots of spaces.](#a-string-with-lots-of-spaces)\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## __Bye bye__ **bye***\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n\r\n# a string with lots of spaces.\r\n"] md-toc-9.0.0/asciinema/md_toc_asciinema_8_0_0_demo.sh000077500000000000000000000100101460560256400223420ustar00rootroot00000000000000#!/bin/bash # # python -m md_toc_asciinema_7_1_0_demo.sh # # Copyright (C) 2021 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # # Discover python -m md_toc of this repository not the one installed on the system. export PYTHONPATH='..' TIMEOUT=1 alias python='pipenv run python' ### ### printf "Running a demo to show some of python -m md_toc's capabilities...\n" printf "\n" sleep ${TIMEOUT} printf "$ python -m md_toc -h\n" python -m md_toc -h printf "\n" sleep ${TIMEOUT} cat <<-EOF > foo.md # Hi hey ## How are you? !!! ## fine, thanks ### Bye ## __Bye bye__ **bye*** \`\`\`python # This is a code # fence with comments that might represent ATX-style headings # if not properly parsed \`\`\` bye # boo # a string with lots of spaces. EOF cat <<-EOF > foo_noncoherent.md # Hi ### boo EOF cat <<-EOF > foo_skiplines.md # I want this line to be a comment #### And this as well ## And this ###### ByeBye # Hi ## How ### Are ## You # Today ? EOF printf "Inspecting the file...\n" printf "$ cat foo.md\n" cat foo.md printf "\n" sleep ${TIMEOUT} printf "Run with default options...\n" printf "$ python -m md_toc github foo.md\n" python -m md_toc github foo.md printf "\n" sleep ${TIMEOUT} printf "Ordered list...\n" printf "$ python -m md_toc gitlab -o '.' foo.md\n" python -m md_toc gitlab -o '.' foo.md printf "\n" sleep ${TIMEOUT} printf "Constant ordered list...\n" printf "$ python -m md_toc github -c -o '.' foo.md\n" python -m md_toc github -c -o '.' foo.md printf "\n" sleep ${TIMEOUT} printf "No links...\n" printf "$ python -m md_toc -l github foo.md\n" python -m md_toc -l github foo.md printf "\n" sleep ${TIMEOUT} printf "No links and no indentation...\n" printf "$ python -m md_toc -l -i github foo.md\n" python -m md_toc -l -i github foo.md printf "\n" sleep ${TIMEOUT} printf "Inspecting the non-coherent file...\n" printf "$ cat foo_noncoherent.md\n" cat foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Trying to parse a non coherent markdown file will raise an exception...\n" printf "$ python -m md_toc github foo_noncoherent.md\n" python -m md_toc github foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Try to parse a non coherent markdown file without checking for coherence...\n" printf "$ python -m md_toc -c github foo_noncoherent.md\n" python -m md_toc -c github foo_noncoherent.md printf "\n" sleep ${TIMEOUT} printf "Use stdin, no links and no indentation...\n" printf "$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\n" cat foo.md | python -m md_toc -l -i cmark -u '*' printf "\n" sleep ${TIMEOUT} printf "Inspecting a file where the first 5 lines need to be skipped...\n" printf "$ cat foo_skiplines.md\n" cat foo_skiplines.md printf "\n" sleep ${TIMEOUT} printf "Using the skip lines option...\n" printf "$ python -m md_toc -s 5 github foo_skiplines.md\n" python -m md_toc -s 5 github foo_skiplines.md printf "\n" sleep ${TIMEOUT} printf "Showing Gitlab's removal of consecutive dashes in the link destination...\n" printf "$ python -m md_toc gitlab -l 6 foo.md\n" python -m md_toc gitlab -l 6 foo.md printf "\n" sleep ${TIMEOUT} printf "Editing the file in-place. As you can see, code fence \ detection still needs to be implemented for redcarpet...\n" printf "$ python -m md_toc -p redcarpet foo.md\n" python -m md_toc -p redcarpet foo.md printf "$ cat foo.md\n" cat foo.md rm foo.md foo_noncoherent.md foo_skiplines.md md-toc-9.0.0/asciinema/md_toc_asciinema_8_0_1.json000077700000000000000000000000001460560256400267342md_toc_asciinema_8_0_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_0_1_demo.sh000077700000000000000000000000001460560256400300462md_toc_asciinema_8_0_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_0.json000066400000000000000000000234021460560256400217040ustar00rootroot00000000000000{"version": 2, "width": 83, "height": 46, "timestamp": 1638223392, "env": {"SHELL": "/bin/bash", "TERM": "rxvt-unicode-256color"}} [0.002946, "o", "Running a demo to show some of python -m md_toc's capabilities...\r\n\r\n"] [1.004213, "o", "$ python -m md_toc -h\r\n"] [1.216159, "o", "usage: __main__.py [-h] [-c | -i] [-l] [-m TOC_MARKER] [-n NEWLINE_STRING] [-p]\r\n [-s SKIP_LINES] [-v]\r\n {github,commonmarker,gitlab,cmark,goldmark,redcarpet} ...\r\n\r\nMarkdown Table Of Contents: Automatically generate a compliant table\r\nof contents for a markdown file to improve document readability.\r\n\r\noptional arguments:\r\n -h, --help show this help message and exit\r\n -c, --no-list-coherence\r\n avoids checking for TOC list coherence\r\n -i, --no-indentation avoids adding indentations to the TOC\r\n -l, --no-links avoids adding links to the TOC\r\n -m TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for positioning\r\n the table of contents. Defaults to \r\n -n NEWLINE_STRING, --newline-string NEWLINE_STRING\r\n the string used to separate the lines of the TOC. Use\r\n quotes to delimit the string. If you output i"] [1.21623, "o", "n place all\r\n the newlines of the input file will be replaced with this\r\n value. Defaults to '\\n'\r\n -p, --in-place overwrite the input file\r\n -s SKIP_LINES, --skip-lines SKIP_LINES\r\n skip parsing of the first selected number of lines.\r\n Defaults to 0, i.e. do not skip any lines\r\n -v, --version show program's version number and exit\r\n\r\nmarkdown parser:\r\n {github,commonmarker,gitlab,cmark,goldmark,redcarpet}\r\n --help\r\n\r\nPlease read the documentation to understand how each parser works\r\n\r\nReturn values: 0 ok, 1 error, 2 invalid command\r\n\r\nCopyright (C) 2017-2021 Franco Masotti, frnmst\r\nLicense GPLv3+: GNU GPL version 3 or later \r\nThis is free software: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.\r\n"] [1.231495, "o", "\r\n~~~~\r\n"] [2.23527, "o", "1. inspecting the file...\r\n$ cat foo.md\r\n"] [2.236755, "o", "# Hi\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## __Bye bye__ **bye***\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n\r\n# a string with lots of spaces.\r\n"] [2.237414, "o", "\r\n~~~~\r\n"] [3.2415, "o", "2. run with default options...\r\n$ python -m md_toc github foo.md\r\n"] [3.479479, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ **bye***](#bye-bye-bye)\r\n- [boo](#boo)\r\n- [a string with lots of spaces.](#a-string--------------with----------------lots-of-spaces)\r\n"] [3.494502, "o", "\r\n~~~~\r\n"] [4.496332, "o", "3. ordered list...\r\n$ python -m md_toc gitlab -o '.' foo.md\r\n"] [4.695467, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [__Bye bye__ **bye***](#bye-bye-bye)\r\n2. [boo](#boo)\r\n3. [a string with lots of spaces.](#a-string-with-lots-of-spaces)\r\n"] [4.709769, "o", "\r\n~~~~\r\n"] [5.712839, "o", "4. constant ordered list...\r\n$ python -m md_toc github -c -o '.' foo.md\r\n"] [5.929494, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 1. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 1. [__Bye bye__ **bye***](#bye-bye-bye)\r\n1. [boo](#boo)\r\n1. [a string with lots of spaces.](#a-string--------------with----------------lots-of-spaces)\r\n"] [5.943946, "o", "\r\n~~~~\r\n"] [6.945834, "o", "5. no links...\r\n$ python -m md_toc -l github foo.md\r\n"] [7.175309, "o", "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - __Bye bye__ **bye***\r\n- boo\r\n- a string with lots of spaces.\r\n"] [7.19285, "o", "\r\n~~~~\r\n"] [8.194795, "o", "6. no links and no indentation...\r\n$ python -m md_toc -l -i github foo.md\r\n"] [8.392153, "o", "- Hi\r\n- How are you? !!!\r\n- fine, thanks\r\n- Bye\r\n- __Bye bye__ **bye***\r\n- boo\r\n- a string with lots of spaces.\r\n"] [8.405631, "o", "\r\n~~~~\r\n"] [9.408707, "o", "7. inspecting the non-coherent file...\r\n$ cat foo_noncoherent.md\r\n"] [9.409761, "o", "# Hi\r\n### boo\r\n"] [9.410067, "o", "\r\n~~~~\r\n"] [10.412979, "o", "8. trying to parse a non coherent markdown file will raise an exception...\r\n$ python -m md_toc github foo_noncoherent.md\r\n"] [10.647775, "o", "Traceback (most recent call last):\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/__main__.py\", line 35, in main\r\n result = args.func(args)\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/cli.py\", line 64, in write_toc\r\n toc_struct = build_multiple_tocs(\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 378, in build_multiple_tocs\r\n build_toc(filenames[file_id], ordered, no_links, no_indentation,\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 283, in build_toc\r\n raise TocDoesNotRenderAsCoherentList\r\nmd_toc.exceptions.TocDoesNotRenderAsCoherentList\r\n"] [10.664687, "o", "\r\n~~~~\r\n"] [11.667053, "o", "9. try to parse a non coherent markdown file without checking for coherence...\r\n$ python -m md_toc -c github foo_noncoherent.md\r\n"] [11.877614, "o", "- [Hi](#hi)\r\n - [boo](#boo)\r\n"] [11.891481, "o", "\r\n~~~~\r\n"] [12.892696, "o", "10. use stdin, no links and no indentation...\r\n$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\r\n"] [13.105842, "o", "* Hi\r\n* How are you? !!!\r\n* fine, thanks\r\n* Bye\r\n* __Bye bye__ **bye***\r\n* boo\r\n* a string with lots of spaces.\r\n"] [13.121329, "o", "\r\n~~~~\r\n"] [14.123426, "o", "11. inspecting a file where the first 5 lines need to be skipped...\r\n$ cat foo_skiplines.md\r\n"] [14.124117, "o", "# I want this line to be a comment\r\n#### And this as well\r\n## And this\r\n###### ByeBye\r\n\r\n# Hi\r\n## How\r\n### Are\r\n## You\r\n# Today ?\r\n"] [14.124462, "o", "\r\n~~~~\r\n"] [15.12656, "o", "12. using the skip lines option...\r\n$ python -m md_toc -s 5 github foo_skiplines.md\r\n"] [15.335789, "o", "- [Hi](#hi)\r\n - [How](#how)\r\n - [Are](#are)\r\n - [You](#you)\r\n- [Today ?](#today-)\r\n"] [15.350826, "o", "\r\n~~~~\r\n"] [16.353282, "o", "13. showing GitLab's removal of consecutive dashes in the link destination...\r\n$ python -m md_toc gitlab -l 6 foo.md\r\n"] [16.570979, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ **bye***](#bye-bye-bye)\r\n- [boo](#boo)\r\n- [a string with lots of spaces.](#a-string-with-lots-of-spaces)\r\n"] [16.585488, "o", "\r\n~~~~\r\n"] [17.587717, "o", "14. editing file in place with a custom newline...\r\n$ python -m md_toc --newline '\\r\\n' -p gitlab -l 6 foo.md\r\n"] [17.88186, "o", "\r\n$ cat --show-nonprinting --show-ends foo.md\r\n"] [17.882617, "o", "# Hi^M$\r\n^M$\r\n^M$\r\n^M$\r\n- [Hi](#hi)^M^M$\r\n - [How are you? !!!](#how-are-you-)^M^M$\r\n - [fine, thanks](#fine-thanks)^M^M$\r\n - [Bye](#bye)^M^M$\r\n - [__Bye bye__ **bye***](#bye-bye-bye)^M^M$\r\n- [boo](#boo)^M^M$\r\n- [a string with lots of spaces.](#a-string-with-lots-of-spaces)^M$\r\n^M$\r\n^M$\r\n^M$\r\nhey^M$\r\n^M$\r\n## How are you? !!!^M$\r\n^M$\r\n## fine, thanks^M$\r\n^M$\r\n### Bye^M$\r\n^M$\r\n## __Bye bye__ **bye***^M$\r\n^M$\r\n```python^M$\r\n# This is a code^M$\r\n# fence with comments that might represent ATX-style headings^M$\r\n# if not properly parsed^M$\r\n```^M$\r\n^M$\r\nbye^M$\r\n^M$\r\n# boo^M$\r\n^M$\r\n# a string with lots of spaces.^M$\r\n"] [17.882932, "o", "\r\n~~~~\r\n"] [18.885713, "o", "15. editing the file in-place. As you can see, code fence detection still needs to be implemented for redcarpet..."] [18.885907, "o", "\r\n"] [18.886082, "o", "$ python -m md_toc -p redcarpet foo.md"] [18.886268, "o", "\r\n"] [19.176672, "o", "$ cat foo.md"] [19.176844, "o", "\r\n"] [19.177659, "o", "# Hi\r\n\r\n\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ **bye***](#small>__bye-bye__-bye-small>)\r\n- [This is a code](#this-is-a-code)\r\n- [fence with comments that might represent ATX-style headings](#fence-with-comments-that-might-represent-atx-style-headings)\r\n- [if not properly parsed](#if-not-properly-parsed)\r\n- [boo](#boo)\r\n- [a string with lots of spaces.](#a-string-with-lots-of-spaces)\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## __Bye bye__ **bye***\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n\r\n# a string with lots of spaces.\r\n"] md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_0_demo.sh000077500000000000000000000112521460560256400223540ustar00rootroot00000000000000#!/bin/bash # # python -m md_toc_asciinema_8_1_0_demo.sh # # Copyright (C) 2021 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # # Discover python -m md_toc of this repository not the one installed on the system. export PYTHONPATH='..' TIMEOUT=1 gen_foo() { cat <<-EOF > foo.md # Hi hey ## How are you? !!! ## fine, thanks ### Bye ## __Bye bye__ **bye*** \`\`\`python # This is a code # fence with comments that might represent ATX-style headings # if not properly parsed \`\`\` bye # boo # a string with lots of spaces. EOF } gen_foo_noncoherent() { cat <<-EOF > foo_noncoherent.md # Hi ### boo EOF } gen_foo_skiplines() { cat <<-EOF > foo_skiplines.md # I want this line to be a comment #### And this as well ## And this ###### ByeBye # Hi ## How ### Are ## You # Today ? EOF } ### ### printf "Running a demo to show some of python -m md_toc's capabilities...\n" printf "\n" sleep ${TIMEOUT} printf "$ python -m md_toc -h\n" python -m md_toc -h printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "1. inspecting the file...\n" printf "$ cat foo.md\n" cat foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "2. run with default options...\n" printf "$ python -m md_toc github foo.md\n" python -m md_toc github foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "3. ordered list...\n" printf "$ python -m md_toc gitlab -o '.' foo.md\n" python -m md_toc gitlab -o '.' foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "4. constant ordered list...\n" printf "$ python -m md_toc github -c -o '.' foo.md\n" python -m md_toc github -c -o '.' foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "5. no links...\n" printf "$ python -m md_toc -l github foo.md\n" python -m md_toc -l github foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "6. no links and no indentation...\n" printf "$ python -m md_toc -l -i github foo.md\n" python -m md_toc -l -i github foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo_noncoherent printf "7. inspecting the non-coherent file...\n" printf "$ cat foo_noncoherent.md\n" cat foo_noncoherent.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo_noncoherent printf "8. trying to parse a non coherent markdown file will raise an exception...\n" printf "$ python -m md_toc github foo_noncoherent.md\n" python -m md_toc github foo_noncoherent.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo_noncoherent printf "9. try to parse a non coherent markdown file without checking for coherence...\n" printf "$ python -m md_toc -c github foo_noncoherent.md\n" python -m md_toc -c github foo_noncoherent.md printf "\n~~~~\n" sleep ${TIMEOUT} printf "10. use stdin, no links and no indentation...\n" printf "$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\n" cat foo.md | python -m md_toc -l -i cmark -u '*' printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo_skiplines printf "11. inspecting a file where the first 5 lines need to be skipped...\n" printf "$ cat foo_skiplines.md\n" cat foo_skiplines.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo_skiplines printf "12. using the skip lines option...\n" printf "$ python -m md_toc -s 5 github foo_skiplines.md\n" python -m md_toc -s 5 github foo_skiplines.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "13. showing GitLab's removal of consecutive dashes in the link destination...\n" printf "$ python -m md_toc gitlab -l 6 foo.md\n" python -m md_toc gitlab -l 6 foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "14. editing file in place with a custom newline...\n" printf "$ python -m md_toc --newline '\\\r\\\n' -p gitlab -l 6 foo.md\n" python -m md_toc --newline '\r\n' -p gitlab -l 6 foo.md printf "\n" printf "$ cat --show-nonprinting --show-ends foo.md\n" cat --show-nonprinting --show-ends foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "15. editing the file in-place. As you can see, code fence \ detection still needs to be implemented for redcarpet...\n" printf "$ python -m md_toc -p redcarpet foo.md\n" python -m md_toc -p redcarpet foo.md printf "$ cat foo.md\n" cat foo.md rm foo.md foo_noncoherent.md foo_skiplines.md md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_1.json000077700000000000000000000000001460560256400267362md_toc_asciinema_8_1_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_1_demo.sh000077700000000000000000000000001460560256400300502md_toc_asciinema_8_1_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_2.json000077700000000000000000000000001460560256400267372md_toc_asciinema_8_1_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_2_demo.sh000077700000000000000000000000001460560256400300512md_toc_asciinema_8_1_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_3.json000077700000000000000000000000001460560256400267402md_toc_asciinema_8_1_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_3_demo.sh000077700000000000000000000000001460560256400300522md_toc_asciinema_8_1_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_4.json000077700000000000000000000000001460560256400267412md_toc_asciinema_8_1_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_4_demo.sh000077700000000000000000000000001460560256400300532md_toc_asciinema_8_1_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_5.json000077700000000000000000000000001460560256400267422md_toc_asciinema_8_1_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_5_demo.sh000077700000000000000000000000001460560256400300542md_toc_asciinema_8_1_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_6.json000077700000000000000000000000001460560256400267432md_toc_asciinema_8_1_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_6_demo.sh000077700000000000000000000000001460560256400300552md_toc_asciinema_8_1_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_7.json000077700000000000000000000000001460560256400267442md_toc_asciinema_8_1_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_7_demo.sh000077700000000000000000000000001460560256400300562md_toc_asciinema_8_1_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_8.json000077700000000000000000000000001460560256400267452md_toc_asciinema_8_1_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_8_demo.sh000077700000000000000000000000001460560256400300572md_toc_asciinema_8_1_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_9.json000077700000000000000000000000001460560256400267462md_toc_asciinema_8_1_0.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_1_9_demo.sh000077700000000000000000000000001460560256400300602md_toc_asciinema_8_1_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_2_0_demo.json000066400000000000000000000261311460560256400227130ustar00rootroot00000000000000{"version": 2, "width": 147, "height": 41, "timestamp": 1691509712, "env": {"SHELL": "/bin/bash", "TERM": "rxvt-unicode-256color"}} [0.003171, "o", "Running a demo to show some of python -m md_toc's capabilities...\r\n\r\n"] [1.004212, "o", "$ python -m md_toc -h\r\n"] [1.110495, "o", "usage: __main__.py [-h] [-c | -i] [-d] [-l] [-m TOC_MARKER] [-n NEWLINE_STRING] [-p] [-s SKIP_LINES] [-v]\r\n {github,commonmarker,gitlab,cmark,goldmark,redcarpet} ...\r\n\r\nMarkdown Table Of Contents: Automatically generate a compliant table\r\nof contents for a markdown file to improve document readability.\r\n\r\noptions:\r\n -h, --help show this help message and exit\r\n -c, --no-list-coherence\r\n avoids checking for TOC list coherence\r\n -i, --no-indentation avoids adding indentations to the TOC\r\n -d, --diff returns 128 if the newly generated TOC differs from the one already existing in the file\r\n -l, --no-links avoids adding links to the TOC\r\n -m TOC_MARKER, --toc-marker TOC_MARKER\r\n set the string to be used as the marker for positioning the table of contents. Put this value between single quotes.\r\n Defaults to ''\r\n -n NEWLINE_STRING, --newline-string NEWLINE_STRING\r\n the string used to separate the lines of the TOC. Use single quotes to delimit the string. If you output in place all the\r\n newlines of the input file will be replaced with this value. Defaults to '\\n'\r\n -p, --in-place overwrite the input file\r\n -s SKIP_LINES, --skip-lines SKIP_LINES\r\n skip parsing of the first selected number of lines. Defaults to 0, i.e. do not skip any lines\r\n -v, --version show program's version number and exit\r\n\r\nmarkdown parser:\r\n {github,commonmarker,gitlab,cmark,goldmark,redcarpet}\r\n --help\r\n\r\nPlease read the documentation to understand how each parser works\r\n\r\nReturn values: 0 ok, 1 error, 2 invalid command, 128 TOC differs from the one in the file (see --diff option)\r\n\r\nCopyright (C) 2017-2023 Franco Masotti, frnmst\r\nLicense GPLv3+: GNU GPL version 3 or later \r\nThis is free software: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.\r\n"] [1.130052, "o", "\r\n~~~~\r\n"] [2.132961, "o", "1. inspecting the file...\r\n$ cat foo.md\r\n"] [2.133859, "o", "# Hi\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## __Bye bye__ **bye***\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n\r\n# a string with lots of spaces.\r\n"] [2.13415, "o", "\r\n~~~~\r\n"] [3.136129, "o", "2. run with default options...\r\n$ python -m md_toc github foo.md\r\n"] [3.241206, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ **bye***](#bye-bye-bye)\r\n- [boo](#boo)\r\n- [a string with lots of spaces.](#a-string--------------with----------------lots-of-spaces)\r\n"] [3.263165, "o", "\r\n~~~~\r\n"] [4.265376, "o", "3. ordered list...\r\n$ python -m md_toc gitlab -o '.' foo.md\r\n"] [4.374239, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-)\r\n 2. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 3. [__Bye bye__ **bye***](#bye-bye-bye)\r\n2. [boo](#boo)\r\n3. [a string with lots of spaces.](#a-string-with-lots-of-spaces)\r\n"] [4.390588, "o", "\r\n~~~~\r\n"] [5.392815, "o", "4. constant ordered list...\r\n$ python -m md_toc github -c -o '.' foo.md\r\n"] [5.502382, "o", "1. [Hi](#hi)\r\n 1. [How are you? !!!](#how-are-you-----------)\r\n 1. [fine, thanks](#fine-thanks)\r\n 1. [Bye](#bye)\r\n 1. [__Bye bye__ **bye***](#bye-bye-bye)\r\n1. [boo](#boo)\r\n1. [a string with lots of spaces.](#a-string--------------with----------------lots-of-spaces)\r\n"] [5.522723, "o", "\r\n~~~~\r\n"] [6.525053, "o", "5. no links...\r\n$ python -m md_toc -l github foo.md\r\n"] [6.634906, "o", "- Hi\r\n - How are you? !!!\r\n - fine, thanks\r\n - Bye\r\n - __Bye bye__ **bye***\r\n- boo\r\n- a string with lots of spaces.\r\n"] [6.65035, "o", "\r\n~~~~\r\n"] [7.652633, "o", "6. no links and no indentation...\r\n$ python -m md_toc -l -i github foo.md\r\n"] [7.759164, "o", "- Hi\r\n- How are you? !!!\r\n- fine, thanks\r\n- Bye\r\n- __Bye bye__ **bye***\r\n- boo\r\n- a string with lots of spaces.\r\n"] [7.774487, "o", "\r\n~~~~\r\n"] [8.776603, "o", "7. inspecting the non-coherent file...\r\n$ cat foo_noncoherent.md\r\n"] [8.777219, "o", "# Hi\r\n### boo\r\n"] [8.777375, "o", "\r\n~~~~\r\n"] [9.779351, "o", "8. trying to parse a non coherent markdown file will raise an exception...\r\n$ python -m md_toc github foo_noncoherent.md\r\n"] [9.887258, "o", "Traceback (most recent call last):\r\n"] [9.888371, "o", " File \"/home/vm/dev/personal/stable/md-toc/md_toc/__main__.py\", line 36, in main\r\n result = args.func(args)\r\n ^^^^^^^^^^^^^^^\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/cli.py\", line 79, in write_toc\r\n toc_struct = build_multiple_tocs(\r\n ^^^^^^^^^^^^^^^^^^^^\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 482, in build_multiple_tocs"] [9.888556, "o", "\r\n toc_struct: list = [\r\n ^\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 483, in \r\n build_toc(\r\n File \"/home/vm/dev/personal/stable/md-toc/md_toc/api.py\", line 370, in build_toc\r\n raise TocDoesNotRenderAsCoherentList\r\nmd_toc.exceptions.TocDoesNotRenderAsCoherentList\r\n"] [9.904954, "o", "\r\n~~~~\r\n"] [10.907169, "o", "9. try to parse a non coherent markdown file without checking for coherence...\r\n$ python -m md_toc -c github foo_noncoherent.md\r\n"] [11.013827, "o", "- [Hi](#hi)\r\n - [boo](#boo)\r\n"] [11.035609, "o", "\r\n~~~~\r\n"] [12.036881, "o", "10. use stdin, no links and no indentation...\r\n$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\r\n"] [12.144446, "o", "* Hi\r\n* How are you? !!!\r\n* fine, thanks\r\n* Bye\r\n* __Bye bye__ **bye***\r\n* boo\r\n* a string with lots of spaces.\r\n"] [12.160201, "o", "\r\n~~~~\r\n"] [13.162281, "o", "11. inspecting a file where the first 5 lines need to be skipped...\r\n$ cat foo_skiplines.md\r\n"] [13.163072, "o", "# I want this line to be a comment\r\n#### And this as well\r\n## And this\r\n###### ByeBye\r\n\r\n# Hi\r\n## How\r\n### Are\r\n## You\r\n# Today ?\r\n"] [13.163221, "o", "\r\n~~~~\r\n"] [14.165522, "o", "12. using the skip lines option...\r\n$ python -m md_toc -s 5 github foo_skiplines.md\r\n"] [14.274055, "o", "- [Hi](#hi)\r\n - [How](#how)\r\n - [Are](#are)\r\n - [You](#you)\r\n- [Today ?](#today-)\r\n"] [14.292361, "o", "\r\n~~~~\r\n"] [15.29451, "o", "13. showing GitLab's removal of consecutive dashes in the link destination...\r\n$ python -m md_toc gitlab -l 6 foo.md\r\n"] [15.404072, "o", "- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ **bye***](#bye-bye-bye)\r\n- [boo](#boo)\r\n- [a string with lots of spaces.](#a-string-with-lots-of-spaces)\r\n"] [15.419992, "o", "\r\n~~~~\r\n"] [16.422107, "o", "14. editing file in place with a custom newline...\r\n$ python -m md_toc --newline '\\r\\n' -p gitlab -l 6 foo.md\r\n"] [16.610443, "o", "\r\n$ cat --show-nonprinting --show-ends foo.md\r\n"] [16.611294, "o", "# Hi^M$\r\n^M$\r\n^M^M$\r\n^M^M$\r\n- [Hi](#hi)^M^M$\r\n - [How are you? !!!](#how-are-you-)^M^M$\r\n - [fine, thanks](#fine-thanks)^M^M$\r\n - [Bye](#bye)^M^M$\r\n - [__Bye bye__ **bye***](#bye-bye-bye)^M^M$\r\n- [boo](#boo)^M^M$\r\n- [a string with lots of spaces.](#a-string-with-lots-of-spaces)^M^M$\r\n^M^M$\r\n^M^M$\r\n^M$\r\nhey^M$\r\n^M$\r\n## How are you? !!!^M$\r\n^M$\r\n## fine, thanks^M$\r\n^M$\r\n### Bye^M$\r\n^M$\r\n## __Bye bye__ **bye***^M$\r\n^M$\r\n```python^M$\r\n# This is a code^M$\r\n# fence with comments that might represent ATX-style headings^M$\r\n# if not properly parsed^M$\r\n```^M$\r\n^M$\r\nbye^M$\r\n^M$\r\n# boo^M$\r\n^M$\r\n# a string with lots of spaces.^M$\r\n"] [16.611495, "o", "\r\n~~~~\r\n"] [17.61361, "o", "15. editing the file in-place. As you can see, code fence detection still needs to be implemented for redcarpet...\r\n$ python -m md_toc -p redcarpet foo.md\r\n"] [17.765212, "o", "$ cat foo.md\r\n"] [17.766148, "o", "# Hi\r\n\r\n\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ **bye***](#small>__bye-bye__-bye-small>)\r\n- [This is a code](#this-is-a-code)\r\n- [fence with comments that might represent ATX-style headings](#fence-with-comments-that-might-represent-atx-style-headings)\r\n- [if not properly parsed](#if-not-properly-parsed)\r\n- [boo](#boo)\r\n- [a string with lots of spaces.](#a-string-with-lots-of-spaces)\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## __Bye bye__ **bye***\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n\r\n# a string with lots of spaces.\r\n"] [17.766623, "o", "\r\n~~~~\r\n"] [18.768502, "o", "16. TOC diff. Check if the existing TOC in the file is different from\r\nthe newly generated one. In this case they are different so the return value is 128. 0 is returned if they are equal...\r\n$ python -m md_toc -p --diff github -l 6 foo_with_toc.md\r\n"] [18.916994, "o", "$ echo ${?}\r\n128\r\n$ cat foo_with_toc.md\r\n"] [18.917963, "o", "# Hi\r\n\r\n\r\n\r\n- [Hi](#hi)\r\n - [How are you? !!!](#how-are-you-----------)\r\n - [fine, thanks](#fine-thanks)\r\n - [Bye](#bye)\r\n - [__Bye bye__ **bye***](#bye-bye-bye)\r\n- [boo](#boo)\r\n- [a string with lots of spaces.](#a-string--------------with----------------lots-of-spaces)\r\n\r\n\r\n\r\nhey\r\n\r\n## How are you? !!!\r\n\r\n## fine, thanks\r\n\r\n### Bye\r\n\r\n## __Bye bye__ **bye***\r\n\r\n```python\r\n# This is a code\r\n# fence with comments that might represent ATX-style headings\r\n# if not properly parsed\r\n```\r\n\r\nbye\r\n\r\n# boo\r\n\r\n# a string with lots of spaces.\r\n"] md-toc-9.0.0/asciinema/md_toc_asciinema_8_2_0_demo.sh000077500000000000000000000136071460560256400223630ustar00rootroot00000000000000#!/bin/bash # # python -m md_toc_asciinema_8_2_0_demo.sh # # Copyright (C) 2023 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # # Discover python -m md_toc of this repository not the one installed on the system. export PYTHONPATH='..' TIMEOUT=1 gen_foo() { cat <<-EOF > foo.md # Hi hey ## How are you? !!! ## fine, thanks ### Bye ## __Bye bye__ **bye*** \`\`\`python # This is a code # fence with comments that might represent ATX-style headings # if not properly parsed \`\`\` bye # boo # a string with lots of spaces. EOF } gen_foo_noncoherent() { cat <<-EOF > foo_noncoherent.md # Hi ### boo EOF } gen_foo_skiplines() { cat <<-EOF > foo_skiplines.md # I want this line to be a comment #### And this as well ## And this ###### ByeBye # Hi ## How ### Are ## You # Today ? EOF } gen_foo_with_toc() { cat <<-EOF > foo_with_toc.md # Hi - [Hi](#hi) - [How are you? !!!](#how-are-you-----------) - [fine, thanks](#fine-thanks) - [A DIFFERING LINE SO WE CAN CHECK THE DIFF FEATURE](#bye) - [__Bye bye__ **bye***](#bye-bye-bye) - [boo](#boo) - [a string with lots of spaces.](#a-string--------------with----------------lots-of-spaces) hey ## How are you? !!! ## fine, thanks ### Bye ## __Bye bye__ **bye*** \`\`\`python # This is a code # fence with comments that might represent ATX-style headings # if not properly parsed \`\`\` bye # boo # a string with lots of spaces. EOF } ### ### printf "Running a demo to show some of python -m md_toc's capabilities...\n" printf "\n" sleep ${TIMEOUT} printf "$ python -m md_toc -h\n" python -m md_toc -h printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "1. inspecting the file...\n" printf "$ cat foo.md\n" cat foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "2. run with default options...\n" printf "$ python -m md_toc github foo.md\n" python -m md_toc github foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "3. ordered list...\n" printf "$ python -m md_toc gitlab -o '.' foo.md\n" python -m md_toc gitlab -o '.' foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "4. constant ordered list...\n" printf "$ python -m md_toc github -c -o '.' foo.md\n" python -m md_toc github -c -o '.' foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "5. no links...\n" printf "$ python -m md_toc -l github foo.md\n" python -m md_toc -l github foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "6. no links and no indentation...\n" printf "$ python -m md_toc -l -i github foo.md\n" python -m md_toc -l -i github foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo_noncoherent printf "7. inspecting the non-coherent file...\n" printf "$ cat foo_noncoherent.md\n" cat foo_noncoherent.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo_noncoherent printf "8. trying to parse a non coherent markdown file will raise an exception...\n" printf "$ python -m md_toc github foo_noncoherent.md\n" python -m md_toc github foo_noncoherent.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo_noncoherent printf "9. try to parse a non coherent markdown file without checking for coherence...\n" printf "$ python -m md_toc -c github foo_noncoherent.md\n" python -m md_toc -c github foo_noncoherent.md printf "\n~~~~\n" sleep ${TIMEOUT} printf "10. use stdin, no links and no indentation...\n" printf "$ cat foo.md | python -m md_toc -l -i - cmark -u '*'\n" cat foo.md | python -m md_toc -l -i cmark -u '*' printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo_skiplines printf "11. inspecting a file where the first 5 lines need to be skipped...\n" printf "$ cat foo_skiplines.md\n" cat foo_skiplines.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo_skiplines printf "12. using the skip lines option...\n" printf "$ python -m md_toc -s 5 github foo_skiplines.md\n" python -m md_toc -s 5 github foo_skiplines.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "13. showing GitLab's removal of consecutive dashes in the link destination...\n" printf "$ python -m md_toc gitlab -l 6 foo.md\n" python -m md_toc gitlab -l 6 foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "14. editing file in place with a custom newline...\n" printf "$ python -m md_toc --newline '\\\r\\\n' -p gitlab -l 6 foo.md\n" python -m md_toc --newline '\r\n' -p gitlab -l 6 foo.md printf "\n" printf "$ cat --show-nonprinting --show-ends foo.md\n" cat --show-nonprinting --show-ends foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo printf "15. editing the file in-place. As you can see, code fence \ detection still needs to be implemented for redcarpet...\n" printf "$ python -m md_toc -p redcarpet foo.md\n" python -m md_toc -p redcarpet foo.md printf "$ cat foo.md\n" cat foo.md printf "\n~~~~\n" sleep ${TIMEOUT} gen_foo_with_toc printf "16. TOC diff. Check if the existing TOC in the file is different from the newly generated one. In this case they are different so the return value \ is 128. 0 is returned if they are equal...\n" printf "$ python -m md_toc -p --diff github -l 6 foo_with_toc.md\n" python -m md_toc -p --diff github -l 6 foo_with_toc.md retval=${?} printf "$ echo \${?}\n" echo ${retval} printf "$ cat foo_with_toc.md\n" cat foo_with_toc.md rm foo.md foo_noncoherent.md foo_skiplines.md foo_with_toc.md md-toc-9.0.0/asciinema/md_toc_asciinema_8_2_1_demo.json000077700000000000000000000000001460560256400307502md_toc_asciinema_8_2_0_demo.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_2_1_demo.sh000077700000000000000000000000001460560256400300522md_toc_asciinema_8_2_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_2_2_demo.json000077700000000000000000000000001460560256400307512md_toc_asciinema_8_2_0_demo.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_2_2_demo.sh000077700000000000000000000000001460560256400300532md_toc_asciinema_8_2_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_2_3_demo.json000077700000000000000000000000001460560256400307522md_toc_asciinema_8_2_0_demo.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_8_2_3_demo.sh000077700000000000000000000000001460560256400300542md_toc_asciinema_8_2_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_9_0_0_demo.json000077700000000000000000000000001460560256400307462md_toc_asciinema_8_2_0_demo.jsonustar00rootroot00000000000000md-toc-9.0.0/asciinema/md_toc_asciinema_9_0_0_demo.sh000077700000000000000000000000001460560256400300502md_toc_asciinema_8_2_0_demo.shustar00rootroot00000000000000md-toc-9.0.0/assets/000077500000000000000000000000001460560256400142155ustar00rootroot00000000000000md-toc-9.0.0/assets/buy_me_a_coffee.svg000066400000000000000000000017051460560256400200300ustar00rootroot00000000000000 buybuyme a coffeeme a coffee md-toc-9.0.0/assets/md-toc_logo.png000066400000000000000000001607431460560256400171410ustar00rootroot00000000000000‰PNG  IHDRv1h‰±„iCCPICC profile(‘}‘=HÃ@Å_S¥"+Šˆd¨N¤Š8jŠP!Ô ­:˜\úM’GÁµààÇbÕÁÅYWWAüqtrRt‘ÿ×ZÄxpÜw÷wï¡VbšÕ1hºm&ã11Y¯býATf–1'I xޝ{øøzáYÞçþ=jÖb€O$že†ioOoÚç}â+È*ñ9ñ¸I$~äºâòç|ƒž2SÉyⱘoc¥YÁÔˆ§ˆÃª¦S¾vYå¼ÅY+UXóžü…Á¬¾²ÌušÃˆcK BAE”`#B«NŠ…$íÇ<üC ¿D.…\E0r,  rÃþ¿»µr“Q7):_çcìõªã|;Nýð?WzË_®3Ÿ¤W[ZøèÝ.®[š²\îƒO†lÊ ÉOSÈå€÷3ú¦ Ðw t¯¹½5÷qú¤¨«Ä ppŒå){ÝãÝ]í½ý{¦ÙßÃrÇÌ>™bKGDÙÙÙñ˜ pHYs.#.#x¥?vtIMEæ 5ÉtEXtCommentCreated with GIMPW IDATxÚì½y´\gu'ú;U§†;ÏÒ$ùj°eËØØÂ `c0x€©i™H¿~ÉJ²Hú…ðÒy é•Î#ét'+½’°’0X€d;N'!Âd‹àQXò [³®¤«;5óþ¨:ç~µk_™twºûݵ´$Ý[·êœï|ß~û·;À?ó¯O~ò“×äóù·‹Åë‹ÅâÎB¡0šÏçû …BG>ŸÏçr¹l&“ â8FÇ€äßQ!ŽcA€ ™L&ýwǨÕj‚Ùl6ýYòûÉû@E¨V«¨Õjéû&?ÏårÈçóÈf³é{GQ„Z­†jµŠ(Š ýŒL&¨Õjéû$ßwžÜCrÝÒ{Iþ¸×š¼ž{{=Éû$'ו|fò宓{É{Öj5T*DQ„0 ‘ËåÍf›Ö6Y÷èúÄqŒl6‹0 ‘ÉdÒÏKÖ½—äºèóáÞ—>kðé~£(B¥RA¹\F­VKŸgr-ÉkÜgä~–ûÞîºDQÔ´§èµDQ”î©ä^“õ£{•>CúÌ3™LËþ­ÕjéÞNÞ/ù¢ëj}¹gÈÝgÉz¹ïáîA÷Þ¹ûO®Ñ=§É}$kšünǨV«Mç‰{žîµ$ïëÞ·»¾ÔN$¯¢(®ÕjµR©T®T*kåry¾T*¯T*/W*•§ªÕêWÞ÷¾÷þçl7ƒn´ÿþ» …»;;;÷uwwo/ n|÷иÆH;`tó¹†ÌÝ$É×$Û=XîëÝËîä=Ìô5®±u׃¾†:2÷À¹FÁ}­{xè{SÃNxòž™La¶Ü?½'j’×%‡®is‡KïǽꬩQs »ûY‰qM”»î~ kO gØ]CÂR×P'÷JŸ/½÷3“?Òþ¥NžîɰÓóáîêÐÜ=CîÌrçÉ}­ë8éšÓ=C×X»ÎÁHg‡:n A€µµµÕÅÅÅã«««‡ÖÖÖ¾ðÞ÷¾÷‹ÿ¿a'_üñîîî÷õõõÝÐÑÑÑïn(ºÙh'Eîáv#Îør‡ÊÝtî÷èfv8uî£ÆÁ݈œáv# ÎÀs‘F²ZôÂEáôÜks77wMôþ%ÇJ#Vw=ÝßuFÊîµs†Ý}®ôQG‘D‚ÔHr†œþÛunî—k¬i@!­ý5”Ô(rä8©r÷ë¸8§Â*ÔÁÒ3ÁÝÝkÜú-//ÏÏÏÏgqqñ¾ù‘ù‹ÿm ûþýû¯ëííýðððð===C\„K7±r!IÅ“çv7ÕsŸ‘6gÈè÷8ÇÁÁ:Ü&¡€Föô³è“¢/š]p–~>÷t-hFC#-Ér•:zpéïI˜û\ÎðÓhœbê`¤¿¹Lˆ[Wº>–a×ö‡xЈf^œÓ×Þ›}îyÐ=ÏVôÞéyáÎ 3Òl€s Ü> kéîW+ ¢‹wrkç^ÏâââÌÜÜÜß/--}ôýïÿÓÿ[öÏ}îs÷ö÷÷pttôõaf¹H‹nX˘sÑ\±S\Ñ=X‰‚ MÑ]Ã%Áœaw±Nêè¡¡›„‹˜­È‚f\TËýž¯qä®×] ɰ»÷æF“ †N ÷YܽqÆUsBnTGßGsÀ A1\'­›”r{Êu¦4ðà2A²â¾Ül†3Î’£¥_nÄ>ºR!Õ„hýIr°Úsà쇔áIÎÔý êÄ9ûãd*µééé\XXøƒ{ï½÷sÿKöýû÷¿g``àCccc×kžXZhnpÿ×}n‘ÍÅÉ¥4”éaç çèÁ•Œ»}q…! ‡æ ÷š¸?ãà& WÑHØ]_.Bä"- צ÷Kẇ$¨‚:qÎñqE_ óÖ ]k7cÔ"Z÷ç.V/ý.÷9\_3`ܳq³hÍðq…dz­\ I3¸šƒÕàŠ‘ö’»O¸úŽdS¤gÇ9«‹/>5;;û»÷Þ{ïþÿ% ûý÷ßóÀÀÀGÇÆÆÞÇqÀ¥ÆÜaà` šrrØ dÔ8Ãʱb¸ÈÓ¯)C‘fØ¥j=gܵÔPrp’qÑ"u)e–¢VÉÑh™‹º¤„eØ9ãÀ±n8F Ý{”ÁZÑZ+Xsk+9F.Št!7*w³AÎésF†+ªr ®Àëf@É}¸År®ÞBIÚ9ÓØ8Vö¤vn¿Ðú-DsY3e%iu.ˆlü;žššzdvvöÃï}ï{ûŸÖ°?üðßû—ÉæàR$©€Æ8·jÁ Ô¨s87Wäâ¢qÉøIÙ„gÐkæð})CÑ ŽÞÇEßÖ{q©¹Åžàœ‰K‡£N²X(EN+hIkÂÑU¹âŸä$¹¨”î mͤ¶ãIÅ:Z æ˜/Úóà ˜t­9BgØ)ƒ…ƒ¹‚¯eØ%H‰Ë6,&d$=ôÊ9[wOk%u îŸõõõÕóçÏþ]ïz×OþOeØ÷ïßÿÞ‘‘‘lÚ´érºY9ÃÈEU\*ÆÑ§¤÷”Œ%¹ /.¶1w`¹(ŸÃó,ƒAqz)“°=tm5Fƒ…pЉ{½”ò'aßÜ!q_ËADR/·NÒá–Œºæà(Œ#ÁþMi”\@#íe©HŸ¼¯kØ5:)Í,)\&Yê@麺ŸG5¤\&ÁÕž¬zw­ÔÑuÑà]©öc1mèµOOO¿4==ý‘{ï½÷³ÿì ûC=ôç?Z,‹\2.œa—Š+R„ÃÑþ8¬Téf¢i;WØÓ§qÎƬˆš3´÷\Ãz]Ž.zi1Èr¤\‘™(‹ò'9vnÏH0‚Ä1§×ë[“¡{ƒ;¸¾J¡F+Ê–X”Ñ£ñø9çï>oÊçØ4DÇ9Í ºðGPp.pRæ§7)èr?;q0I£œ[ÏÐôú${ÀÕ Üë_[[[?wîÜg~ðð§ÿYöûî»ïÊáááoÙ²åféPIÑuÐÆaVN#7—)“t;ÒÈŠ| eÜõr6ÚÔ!v+=w7‡TÕÖ˜Fpœ!“(g.ÊÝ× ëÖh‡$å­s‘WÀåzè½&Ïœû]ˉÐuà29©°Ë9j«èfÑê¸@DÂ×¹¨›8ýSÂÛ5ÇÉš5òפÆ9E˰skűÀ5ìR?ŠTGú¸3Ä=—䙜={ö±™™™üÄOüÄóÿö8ûO½Ü;66ö‰±±±k%Ùb·P8ƒ´®2iCj 5ìbeRZ%E1’€Ä|±¨‰œ¡Ö`')B”žw=4{Ñ6;ÇÉ·ØKÜ’ž!÷|4ž³D}• +y¡ÑJ¥×P#Ã9D, !®%Õ¥¬z…}kx²tÍîÚs˜tïÚÞôܤHÙʈ8ê±Õœ&Ù )ó * ôººº¶f2™{îºë® >øàsÿà ûþÍøøøoNX,®$5…H¿Ï9 Ž/+9ÎèJ´;j(hñÑ·ùCŠÚ¹Wº¸:„ô9CFK'­æÍøHé¹V¿ÐŒŒïáòe-ùh¥H÷«ñØ5ì^2fœ1ÖÖ”f Rw¤çÌEÄR`C3SZ_‘ÖQêWáê&i@£…úàîɵkÆX µ5Ôj)Úõ@ggç@>Ÿó]wÝø[[·nýPOOÏ€…9køGÒ°3íajъƶ6¬ÔNû¿VÝ·$´ÈÛbŠhx­”Þkï)è|>SÚœq°&—»Ñ†9Kz!\ô©eÔ0IQ>׎.­µLʧ{™»W­ùˆž7ˆ‹sRÍЇeã›-[\k¯iP©U_ÐŽå„´<š…‹ÅÎb±xãwÞÙ}ðàÁøïnØ|ðÁߘ˜ø¹®®®+rÕ"qª( ]«EœÁ±”Ý4,Qò´víÉH“…UJ\dú@3î!õÁ¯Û1â¾–Ëð´Ÿs f„÷Yº6IçFrnt(i‰ºiQü´#– {Úù¡k®V†£Áܳ¦°“Àøfƒ>Y›Ô?C ¸·ÑÀƒ6ÍA€|>_Èçó{ïºë®|ù¿›aà~«aÔ»}d´¸C¯ytÎÒÃÊ)ÉIø¥v½>ÆKzˆš×øÐÛG£}rŽHr4ÒýqSiM4¬Rs–>Ì'AŸ­Ö±ìs­Ôš:oʰ¢¡ÔYªÕˆ¸h[û#e¥Z€f¬5HTË‚µg(у¹{Ö(¦–2¤uæ4ª/qïãÓCÀ Ú{s™gøç‹Åâkïºë®®üÃsÃÞÀÔ?ÔÙÙÙã{`8γ‹h É‹KMÜ!ôá®sÅÍÓszVÝ@ãÔ[ˆvT+Eƒ®8øC*VS&ƒ¦ ¢eg.m5wiïÇáÎ>ED‰ä[x¶“†Ûj)¾TÜÕœ¸Åæ’ £‰AåÖ@sèKE¢šJ, Z’´dèúIgœ«X™­”Ñ·»/-ØH#qäóù|GGÇuwÞygåÀßúofØ?ÿùÏß;>>þÛS—Š—¾1Ú¡–¢ K[Šd|¢!O´à­è*­× cb¸{ãØ”Êå ¹)'W´²)î98.+ÐèlÚë}˜Ô$ÓŽq—Rw – Ú£Ím¿×ø÷’H§x);­‚®ôŒ¹uµ°iMÞA ²¨Q×öOK Ô¬â³o†cÕ’ÏÌçó…\.·÷Î;ïÏ+ßõôq ÖÐß=dÕ'$j§iHûœkãÔ*µöw‹¦§1—´7ý\Z7à"mN½R{VVG·ä¨,o8RËàiÐ¥áìˉ6cZtÒä«X,vØ{Çw|顇ºäc¯C_Ã>44ôñááá‹€âé’«†qYÕd.ª’´Y8ÃÅ1o$a}ª÷:i‚“dèhõã<»EM.r•ºz%ÃËq´“‰4RmGë~F˜n#•ûì|šÊ8gK÷‡V@KîGÊ"©T¶/´!áKc”Œžû\éÞ·&qãÝ}dI:Kft]é=¸°½v7ê–‚ÎÑYu9®ã™Ö©\¨‰}M&špÙ‰¶÷¹ë ðåÙrk:<<¼£\.À-ÿdûƒ>øçÛ¶mû~Ž‹J£AIÜŠZ5üJc°øFéZ·ª³h“š4½‹›LŠIZAú<¤´QêØÓ†H´ÆØ¢f ºá¢$®;T{ŽWÜJ‡% e òát~¤¬ŽÓ8± ­cš“häc9CD §•ÑhçQRH•>“4“ΆC[Ù'×ì6¥Q WOÒj%í¿µÚ!½gN¶BjNìééÙzçwnýÜç>÷ð÷lØ?ó™Ï¼wË–-¿–ËåBNäɪF»ÆFãÀJE3wCX”6 ›µðX‹Á"= ‹¦¨Í/•4¯-¦‡fH¹lC*Èiº7ÚàjÀ4Ê'7Êr,‡ƒˆ,\Ú:tÚIi͉Hï!~I¹Q‚í´‚º¾«Á¢Öžçö'çœ$-nXˆä<¥z5„4Sæè¤ iR׬}Úªid ŠbøI:;;÷ÜqÇÇ4C e”\ÈAAÔZÂYV¦ÑncŸÕÇ"5¡YQn>/ÝoÒ}qYUÕ‚5)Т¿+ñì9Ùrι‹Å+n¿ýö¯=ðÀ§Û‚b>ÚÙÙÙi±"8|T;$’NÓhÍE¾1¾iÖÐ+Ò‘Ò:‹v&Eéœq§Ð‚ôû–`×¼D ·¤zÉe]’ÁL¾ç±¤æ$mœ[ò*à®Û'E–² u%‰%6Q;ôLÍ9´C gO‚д‹ûwÿXS´¨s•˜.íd|œ£‘d¿)SŒË>i×'ó‘ óA¤l1ŸÚ—ÍZs¬=›\S±Xìü(€[½#öûï¿ÿ=;vìøE8zíUN«¬[K<`ŸÔWR=´6°ÕP`)J¶®Î»–êi®Üè:¬™‹ì´4U:d¬Æ±`¸÷¦CL¸,€ªJÍVô™J DR&bœé=I‡Êò$z®Ñå"w >äÄXÅeK•c…ø²~,=.8Ò Q.x®G2ÊRr Ü0n ~£ëáN:³ŠÔ>Åmmî„$ëMBWW×¶Ûn»í…ƒ~×+bïïïÿ5êÔ¸» Í9µˆÅb?HBò`5I‚n4ޝE“FÀQoò3·³Kw9ã$‹¬t^£4éÝÝ8ãFËÂ$ •l’œ 78‚+ºr,$­8kÑû¸(›oéÓÈãS»Ñ-‡ ûÈåúðñ¹óËEÜù¦Ì&mJÌ$-+ñ)@ÓóÊÕJ$Y ‰üà£7¯OŽ<Àían0ŠÒ JsØÂs?`¿±ßwß}÷nß¾ýç%.·5R+rÌ 漬VÌ”°kI£º(‡3\Tînp)h,Î0Hãù¬¢¦DaÓW –Œ<Ç|áhp>Œ¦­b#“œ‘T`õ34c­õ8hÝ´œ‘—~Ç¢ÖI‘µN¼æ}¨{Ö@—?®Í І½[ÅMMiÔ ú’Q€tBšåµéPVW;w¾ÜŒ•«aIµ;nÿwwwÝvÛmÏS¹ÁÖ?(‰òH‘‡4cÔè¥& Ép“F|Y'Òøºä}] Q2¶›zmŸ6wn#Y ŸA®­á¹Ö!ˆ¢Ùl¶¥Ó•+ÒJ©·á&ëoq³ÛXÌÁ9Z¦á#—L£4®óÓ·ˆ©ÍÐtÏ —ê['p7¦f=Ö°.bÖ®ÛXÓ>稙?ÜÍ$ wiOkŒ5À 9UZà´Šá\ñÜÎ]Ú(7È[j tßw``àƒ>'FìŸþô§¯›œœüÍl6›áXR‘ÂWâS£)rÍ->Q©ï÷8n­–zY³(5,WJ#©A—&ÒûÈÝrŽÃw,¡vˆ´HÃj9÷ù,‰Öª9© F‹`µæ+º¡VlÖ 4ëÀk”JN£D’«å²"©~£É5hAœ›™ºQ¸D•Õä¬ Ã›—²#-Ê·$³¥ÇGTNÊ6´š×;B÷‹ä8ÇO#÷ÎÎα[o½õáxà<±÷õõ}8 ìæÙ8~¹–ºjÝ\e^”¬Që|ŠKô!pÞÒõ˜RDÉ͘tšêqS<˜Ó4q —Ô©*Ýç«q¬Ò é^}šÃ¸F޾åÓè¡1z,¸Âe°X‘”4*5¾j–œ±´ä§9=±9ŽiaeÒó–‚ Nü‹;'n¡]ʲ¥¡(ôúµ¡á¾÷î«ó#é>–j?®=WIà>7b— $?Ïf³ÙžžžxkØï° †OTÈEdVë³Vô°°Cí rŸCÅŒ¸k’hM\û²6àÃ7•ºµŠºô-î±4Më0pL Çi”9Ÿ¢°TL—àI¾—@P’b¥†Ãr…q®°žD]VaÑgô#g”4Úž»O¹®JIz˜+Ê»&Gx&8ù0Ï4¸‘‹f}ºÇ%¸ObeY,² \©ƒ£ 7 Í¬Ò ¼ÜÁB1_øÂ~|bbâ½n¥5ÙXs*%¬ÓWÓÅŠZ´¡ØRóf8-\×Ò¡çàZ§ ‘Iªv®IkŠ’Ro+²±ÒY-ò´­€êÓ@£ RC›t$ íÈåJd‹WíúѺ¦}dµiæ`I/KA‚¦ÚÙÎ0’ĹjÖ­KŽKË.µáâ\#-s³Q} × '÷üíC3N¾ …Bçí·ß~âÀÏ4Eì½½½ïÓ¨\\tå³é,‘$.½âŠ.–af—j£ï|06NaÐÅ»8IU+=§ë!iKHÆWêFõɤ8£*5Àh…'ŸÍD&EÊVC —ah*”Rd¬ñ奯<‹]e1&¬ç){©æ 1µh™“9æX@šr#x—ñ¡Ñi¶ÄcÉÁjg†£¶;¯ÁèCmNRsH ߆á2@íÓâ)GutaÝäµ===ïðM†½¿¿ÿŸïC/£t?j¥"”er˜WtÔ¢NŸ\joqï¹hG’»µh[’·w»5ʤÄÒ°œ6gH9ÃÀATR µSKÍK”×ÀíÌÕ6ZTkÁsV­C|ò‰ê-êý=Z¿à©¨ ”´'h}Hc&¹†Ëý¾ÖaÌ9@k@‹{­ò”¢u‰KßKsëÆ]‘ biZvG;í­¦+‰†oh‚böïß×ÄÄÄOûn4™VjÝ‘lÃ{àR'©/Û£‘&MO\«jKÎAÒ<—6 6Ògƒq‘µ¡iBXí•¶"v_ãª'‰û­9éKÆÕÒc猤f˜¥YŸäbÉZKEOMŠV2”\ããxÚù¾¯ªgrÎ4UIN†Ysðœ·ôß­ÉMÔXºQ\àÂL´³›ËZ¤ì›ËPòù|ñ¶Ûn;tàÀcaŸywâa¬iôRÕŸn·9‡¦š°—æQIKí0ûÐ&¸[l.ê‘ô´¹ ¾ä•µl‚cH馤@ieb”E¤¤(ÒŠ¼¤ÉP4û³†X­T<×~¦9>Ž`Qàè^ó%#øDŽ}¥EuÎ=‹ënuºRÕGÍ1ûÎ ¥Y>ÇÉ—ž”]Q£.I’h…ü¤:¤$â&1c8¦ Ÿ@ggç»|1lügŸÆæ¸–>ÅN $Žòc]°6Ñ‚à+Ú´ ¾p½§4îÎGKÄŠ‚9J”ñø_öÆ-f’øP,©qÔtäiäb) ZkI›…4è‰>Gʨñf®e…ZSbUXºô\ÐÄÁÓZ†ªí7Ÿá¾Zô‹‰Í'íŸâ3‡õ[µ ŸþŽ%–àïœÃ§Ñ¹Ï r sïèèØ—bì===Û¹’œÎµ®Ëy?ŸÆMSD+æúÌ)ô#%}km0†O¡OZOßaÁLãfBR”Ì3íàÐM*¥®>MlR4ʉU¹ø*7Œ€¾—¯6NÐJÏ¥{цûŒ×ãžNj֌–VTä ™% ,½¯Ô §e¿¾†R+ˆr|zéìSCîî ®+Ø'è±hsÏív¯Û*ÞKTf®ØÝ××·ÂO}êS×tvvvZ5)-Š9nÑKÓŽñ5Î~ìýÒû ŽK-H[|‹­ Y˜Vskg ¥ìÉ—£Ä¥óÖhМhÔtdQ©¥;Ú|Ä|ËÁ$_Åb±ó¾ûî»&,‹ou'ÓKò±œQR¾Ä;e³Ù¦i÷d" yI,)"“ WÜÂ$×gqÍ5Z§›ji´BŸ íf$n£qIÆL*‚ûD™Zež~¶¤¥/uîÚÚ™ ¤q}ž«E5“˜3>P›ÔÙè3ÀBûâ¨uœsÔ4]|›Ä¤b½¥[ÔåVË’$C-Q„},©±QÁ“ ?®¨M‚ó™ˆå~võk5ž0 ß‹Åë%ãÃánÜ‚pgTÀ†Óuæ€{_ÉàQ¸Bbžh:-’d¦D·t+Ùtñ%Mv­bnEj’T)—ªS‰] —óÁy5J%‡SûHHÍFœî´t| ªÆb‘˜Òà ý~­©Ï*†ûd<\ä-­™Æ4³£kÁ5X¬1 zi'+òaY¿ïSŸÒ²cIC­„rÐç¨ ðñÁõ›šÏç¯óùüNŸJº6ƒ”c5p4ž¯¥¥,©¬iجoåSÈ‘"xÁwsr:êšáäRPºy¬¨‰{Omp0—IpÎD£)jtFúšfô}F jr¤gìSP” .°±êUV¤(M“îW2f›K2JÚøºï•i;ŸFÃv¦­YFßÊ´ÝsÊEÝ\ ËõáHìk8ÇÐJör.—ÛæóùQ©ù@’Ë %Q«ÛfËÑÙ8à ‰DI‚f´| Ž.hyv)u£ò©ÜüR‰¿¬5%QƒÊEhœ–‡Ï&ö‰®¬L‚[wŽç«e>Ó|~nÝs»=íàÌ\´eulk´9iÄ"×Tä[cÒ8ÐZÝÅ¢'·SžÔ IDAT9[ì7Uã³·i7&·Æ¾û… Z$*³´6ÒÈI* (ÁCܳs÷pö5—ˆaökÊuڡ࢞İk^×Ò6¡ÐŽ{~¦ñD%åHÎ9H]w–‘&>ù²,|˜RÝÀb×p°”4wÖ§€+-WýÇ¢ ÆimÿR³Åæ®Òy¹ƒl9@É9Kõ%+¥Öh­>{[Ú‹ÄÉÐÎg ÚÓàP«ìÁkp…¡s¢tÖL‹Á£Á€’R®+¤FU-y nF.—ës¹\‡öð¥Í趘sÅitž„¹jÄ} £³'e‹b&náÉÚƒô5,Yƒc,êWH³6(W¸Ò 'M(®ãnLΉY-üÚœI ÿå8öÔHmîšÓõaŒhé¸&Œg5ØXpmóÄÓBR¯Mgk§ùPj²ò (©²§5ÊÏÍ“è¹\­Š;£#Æý]úŒr¹\G˜ËåòÚ„ßXŠÊ5iUi@­Ö=fAÚašøˆ2iÌ.Ât£?®‚/%}Z¹¹ ¼ÖÕ×NŠ+ÁmÖçp±&¢&­¥IZ´ô 9ê¤ö9V4îÓlÆ$V—ªuŸRq• L,í»O0ä#hç I¹ÜÁe>°_‚"pçÑ"„H™£5iKÃã9€[¬—xòVáù0 ìKS¤éº6¼š›žâ¦D!i^ø4ôXƒ«%xŠØ-cfE]f,i­KÙŠOWŸ‘ZFB[?|YÚšCÕô‡èÆö1|ÚúkÙ€ÕÈ£õ%øtÖr‡Ñ7 Óœ·õ”¿o‰jYÅk‰`­¿/óÅg¢’ÿYS»$9ɘk¶Ã:ûZ†Å9 Fæ‚YÉnR–w&s¹\6 ‚ Љ†Ys^Äu’ñ— ŽûYt³ú`ÇZ„$Í?ôIOµSRª³>߇ΦÁ=¾£×¬)9íðë}ŠŒíŒÉ“ÒOÎ0Z¸6§øé;Æ3mÞ³Œ€hWHw³EÖàlûDí’³´œ£ÍkÙ5¢Ïê%ñ ^|ÙrZܲcšÔ4½W·%É*4gl©Z&û"“ÉaµZçòiÅ-’ÐZ“5yná¥A×®aÄ>ÕxŸèΚíK¯” ¸oÕÞ‡à{À¸{×è}ÆÅ¡­¦/Ô~+ˆJÙ W|÷8ÛaÉøB_ÜánǺÑ'ç<´"%ýj&Ûa!i˜=·×´BµÁXkKY)í Q±$,:j›¤^k×ãûåòáÃJ¥‚0 [¦£kX!g<%­gJËã4›¥l èÕ*bjs}š$q&ÎpX#õ¬†/)#ð@ÃiíXQŒ&`éäS‡NSNN@K3N>ŽG2&¾™¯Açö !Ô²[kºgpµ¹”Zç{ïíhÃX[|kàI; '}áû¬58Ñ"x´ûÚ U_x‹>Ó0iý÷iÝÖ0fmV¦ë¡Ü‚e¸%q{‹F¨‰iia»€k˜j6y5N¥ÈkêòÅDµ1lRjéS€¤{Á*šúHÝjx¸Öi=wŸ1ƒíÀW\˜¤â.;C‚š¬HíÕè'ù^¯¯q—X–L´3XöF«ÕY×ì#­Y}³jßa+Öù”j”îkB_|H‹–$x‚ã…»ªsB«åí¤ËÐ:>íÓZáÉrtÚlXºvV‘Jê2õáÀ¿œ±V„[µšÏ|£«°ÞεJ*¤µ–øäZ¡ÙG[‰ÛSî~çš©ô…ÕõÚN–s¾2Ã>÷¬ÕÔ,ãièÓ"yŸ½`1âÀuvKצ=#‰I$qM1“[‹0QaôQÔ <\‘‡jŠøB;>Ñ‚ed%Mg‹a`®Êî;YGð¹÷ Ø³ÔBmm.mÓIÑ–/Ýͧ6Ãe8RãA]çÓÐã‹u[ŽÇª‡øÓ5FrÀš°U;Ð’vVÚ(^¬E龌¤v!$«©HË}Â,ÈVj´“‚Z+מ©ÕYüöW³)¸”íòMÍ}RA-Rç4¢­‰ëž­¥Ìíàv”-¤E>iÇàTøˆ%i#ßÃÈ/ öá 2ôjðK ðiþÐFúµ,Ê,ýŒ~Ñ´Ü}5ƒ´Ú€™¶[ŸÐdG$UV­+ÓgjwoÜP Ëö‘ªö¡Cú7¬ëw!r®VÈ!$É5†\:­±C|4˜5ÌÙ*–ú0)¤)ÎÒ6¤„¡rJÓªoGëÄ2\>Q¾ŸÝ«Æ:ÐZzé[´Ó²Æv5e¬.\£K¥¨}²Ÿõôp®¥Üœ~ˆõ|5)ß„jçÎPÐ8í–“å¤-¹ ŸH\Ëà5HÕ’Bο/íVÒ¾Ò20«ûYÚšd¯û¹!'F£á¾ÆÀ¿m§*î;Í—E¡ .I÷@ UVÙ·BŠž­TL3P´³OqÒ"`+­å²®ãTŠdÝ5•¨·V*ì›’sÌÍiºæ\a{µáØ–£ñ Rh cÉôRµdÚ"bÍа|θsrÜïh{W½óé‘`hK [£eâÔ¸sõ+í܆RÅ2Â>F_ •K‡4*–UðÚòi0¡ÑŠ%e¥‘¾Î§Ý”X{¾r¸RêìS{ðÉ2h»¿Ô°Ã¾×ai„øÔ@|ê ß +ÆWáÒ·ðés¶,‚€Vا_Úìwל§æÈ5'§Õ$á5I©’+Øú*Yúž}M„c¹H h’Ä2=_!×ìcMµ÷¤§š1p#L:ɧó1|\´àý•à©yÆÒà6'Ö/­›/”ÓndeE'R„%ETšAóu‚¾ì(‰à«ñ­E]®lªAõ•Ê•„¬$¨ÍwÏúìek [»’Éí 8×5sgÊ'«•²Hë-øDƒi¤ÀbSÑ,•*>J„nŸº6#ä`MŸÁêÜÒÄäÛú¤j³‰¶Ci“®ß è éX˜*½—ÇìB–3”"A˰ùSµ9¡À‚˜¤©C¾U“‘Úë]¯áÛ’Qפ!´úF;Ã_¬â¯eü­µ÷…$4ï“MhV¬uo'{•ž?§Éâ:fiî‚íS6“´Ø9£nÁ©®à2X©Ã6äÀzÚØ" áða©wŸÖ} ߢ­ã–å£d§af†nÉ#XJˆÒ š5Ð÷ò•ÍõÑÚö¶a9~ÍÑkÐô1|˜Ü:Òµ¦ŸA šÞ¿/œ¦Qÿ4á:kÖ¨TÀÓž…[oº§ÕÌèı2\ê\9(Ïb¨H?Ó®ÉB4| êÖ}ZÃO´aHº›Õ‚]¤¢…”&Ñ×h³EµÉñÚ¢jx¸4¾­ܲ‘+‰¾çÃðÝÚÃ׿údKídU¾8·Å“·œ‡„ÓºŒ4ßVÚ?šÃq-е"¬4©žòöµÂç|$v™¥¸ÈOµ¢³4ùKzÎ õ—B¦Ò «{ÜgßkI;]œRW»=Yð"'wNíÕ_b5,JâŠ4Ãoâ±Ktmz6MI5F‰f³J›Uر$¤I0Ò}ù1})‚ÒðªÌ×þïÛ´`AR¼£}ß'b— ÝØ>Å9ý ­»‘F<Œ¥AUÖ€Úk`S¹€ÉÊŒ|#tmŠOGµ$ŸëÛ¨æbÊgñÖ}œšY¡]˜¬l‘Þ«±ƒ“èÕö8ýwòúÐ}³d’Ä¥”Ø/ÔpÑã.ß᳜ѳ8Æš÷“†K4FŸˆIá²Ú¬¥Íù´Ë–к1µ MƒÖÚ©9hŸ+ɳº_år•J¥éÙ‹EKŽ¢+++Mß/ ÈårfQ‘BŽœ²ŠŽV`c |á0Xß!,Ò}IN\›I M@â¾Ü‰j.,˜4Ü$Â0layHMB’A³êC¾uî5ɵÓÎÉ}¸sG¹¬®!'Ü{î^µÂn‹VŒ+Ê%U§ièOçfjFO<9¤I*G7@Ç(‹lç·ù¥™£Ò"®­­a}}]T“€®®.är¹¶ds­–PµZE©Tjú½b±ˆ0 ŨB+ùе;xAºw ²´s$5X[[Ã×þ4fžx¥2ÍCÛwãÖû ôööЇwvvüçŸÂ¥Ù ½À¦»·ÜrK“нV®EžË,9-$Ëè­¬¬´Ôƒ(bÑs%•D ’ »ëĺ»»Y(ÁGí09³kkk˜››Ãìì,æææPž?‹hö8jK/_D¦oÙÞ1d‡v kp@__r¹(zàÛ ¥)¡Z¢`î^¬V«¨T*X^^Æââ"æææ0??Òì)Dó§­L#^™Av`2}ÈtmBØ=„ÁÁAô÷÷£··ÝÝÝÈçóÐ:ú¥è\*¢Ò¨Ÿã·Ó¯Žrsñ@ ãÖŠ¥Z„Ç1Êå2žxâ \š¾ˆh} qy (-#*¯ .¯ *-!F»ïüר¾}»9÷Qâ—j«««øêÃ÷aöÛ`µ”¼@¼ñïá^`Ïû»víu0´‡»–äøœ|U*>|'øuÌ.Õ/g¨Øþÿ…={ö öf‡Jéš+øò„}ÒÂvê3À]ëååeÌ|ça¼|ˆb §ŒŒ¿Ùl–í0Nöîüü<¦.Up~¾þýM½Àå}}¦ü©eÐlŒêùеN T­VÃÜÜÿØÏ`f¾Š Ùw’· AúmЋoº¯K¾ílaçúëßZ~Pÿ`d0‡·ü›ûÐ××§g¹ì¼T*áÒ¥K8qâŸ}Åj¨Ö€ZTªõgÅ@˜9†LæÂì7f€0t¡=7bèê;099‰4š§Ù‘›‹fß>ç¤R©`ii §NÂÙ—žÁì·>‹¥5 ˜jqý^¢ˆ¢úß½3sÈÏ ³d2Àr˜Çé°€ ÐƒÜä-˜Ø}ÆÇÇÑßß\.טY "+ãõ©Å…4ýÔ& iŒípÐ «T*MZâqTE×€¸†1P^Æñ—aÛ¶mÈçó*#…S*sÇŽaq X)Õ÷Úp7¿®ÉžÊ6üF1.#dò ª'Å©sOãt±Å]·ãÊ+¯ÄèèhlHÉ>:FÔ¹™†=‰Ò“&Q”âP– \$±+ö}2YÄAÈ„é¬?ðå#‰Å×Ý€M›6±….05µZ 'OžÄÂJ#` кÓZ÷¾ºèZç ›Rrl¬¢ZŸ=ßèZ‹N5—ÅɦЄOg¯„#º‡ïÒ¥K(לM›Ôû­Õj˜?u8ý½0 ŒlîCooo ~Îw‰u£Ihë“}Œ‡lõ™oLÒ)ýn pÞ߇B×%Š"¬®®âÈ‘#8ÿåÿ„§–°°¶átÕûrÿÛøð(®0ke`fy#ËÏíÁäm?†;w¢X,š’26õ#5ÐÒ}­¬¬`ýðCX^¯¯3ä÷|j(|§æøô2ød'í Áà,d,txDêšqOaÏâ‹‹‹8ôè×qîïÿgçg ZšEt|È…@&hPñ†!b RÖËÀjyØÆ1pqXxâæO|³ß÷!¼îu¯Kñ®–¤±K(×Ý…öž|òIœù«âô PªÔ¯É]×0têן”c’Û(æëÿó!‚82Y “Æ©Ôʘ?uO­®âöÛoGGGGÓ~—˜„–2ªÕ{út%jj}Ò¤o-ºL7yºèÉFŒ›6Ú…¡tÕUlñðµÂVE8{ö,νü2Ö+)åj󦕰¥i.µl[R ®œB;8º4èÖg ·V8Õt´¬ˆ{N>ø»ä$VWWQ:| uÆ]E øš@WW—$øúùù› Á‰-)×ÇÑÃG 8Í4£Ã!ÂLµF¢@j+paÞ±ûq=òÍÀæ¾ hÀ}OöšI¶:†‹O}3ËðâzäüÊ òàï¢TúÜrËÑÓÓcªÊjn×1=öÈ7pîKŒÓ3­÷PÈ}Àø–!®ûlCOOOJb Ðn©TÂÒÒ.]º„µ—¿he¦°ÆqÝ:¤6;Â=Iaßg~½×d?‡¯•Š“\ÃF±Šlx¹L™®aÔJËõ…Žk¨>„ééw ³³³E;…z;­ZEÖ××qâùg°´¶qPºóÀî+†ñÜó—Rl0&E2®Àš¼£Í_äôqDÃÀ+ŠÒhn¾»6!:'­ÇŠ4¬‚—ûÞIÁñü\#ªk¼áÉ«Q(Z29×°OOO£\Ýø½\¶_5bg>ÝÀšãÁÛ~ù³- 4nX ÇJ»xñ"¾ò‡¿„ó û#@ݘ¾óÿþ³¦â' 8ìßýwww·©åaÿÚL/6Œpã¥ù˜FßúÓ¸êÚ022Òdé,ÜZ­†mÛ¶¡zíµ˜ºåûqìɯc걃8=Ô’à*ÎÌÁßþ!…9¼ñoL£]+kå‚$‹{ì›_Ãé¿ûÎ͵õM}À–ënÆøÞ{°mÛ6tvv6YHºÌª(а~à ˜™™ÁñãDZúò# û ˆåÞ­¯Æ'À“C‰½¢)ýùll‹—¼K5~'È {ò,,œÙ` Ô*8yò$¶lÙÒBO㨎RZEfgg1óÈISó0 \±½ …kߎWþ¤©èSÏ â–ÎCn ‡Is…‹šé ùŽÃã6¿ ùYYmåÖôiލûÿZ­†ÙÙÙ4£ B`Ó¦M( éž ïU­V1×€o‚|³¹èïïgªvX|"}ï1“ɤôLZh—œ¯»×Ö×בʹ²g ÐÝÝžž5 ÔÄþ8XËÍš:„Óßøü†QoDA]Eàª+G1y÷/a×®]Èçó,“Œc@‹Eìܹãããøîä5(~á#8v±ÎBA²9=„óq¸¯×]w]J¦ž™t×××ñÄOàì—[z.¶Û¾ÿWqõÕW§Y†|Ð3Â0Dww7ººº066†¹«®Â+¯¼‚…“O#Îd½np5*ißQ¾?Ýk¡¿ÓJµt^jrâ«Çâ³Ù´4ްỗXij6lβŒ›¯¯¯áä-@©oÖê:jó§pîܹ´™É*&pȵµ5\|öK)o==Àö»ê"ÁF ]hI;žF'îZ§3¡ŽÓŽ^s FFF6{ÐL㢫äàÐ(Ýýã¶Ms”kþjZ_ÁÁhQ±Æ¸°ôê}ç0j™ƒÄÓ·Œ:×–íâ± ¸x®•7¢«ÞÉë›—ÍÌ̤ðM’j7véPQcÎý_[oÍ ¶³†R½Hª¬ú"îìJôæJ¥‚#GŽàô /¢TÝØ—ÙØ2Œ¿ý—°uëV±Ö¡1¨Ýˆã¹\—_~96ßùAŒô48+ëÀÔãã•W^AµZ,ýS­VqäÈœyîé4óK2â-ƒÀÄÝ¿‚íÛ··@<Ú~MζԸגÍfÑÕÕ…Ë.»,eú̃–tè¹u£ÁZEuÃîVl¹…¢ƒ$\PÛМŠjµF;Wˆë¥M›6!èFŒq Dµ J‡bnnN¼6ÛY©TðòË/caµÞ —D~#×¾)ü‘÷Æú%ƒêsÉ(h‘Xë}PöBàÕ8bá>™÷ }¥é3–†nKN‘®C" PrjÙ šœ1·g«Õ*fΫö¸Ž¯wЂmS#Î=?ÎIÓ×Xk¦é[ÁgPêc g–Ö‘ wIázþ„™•æ@£#Œíû>\uÕU( jÍG “‚ÂB¡€×¾öµØrÕU(æ68å€K‹Àéïü5Ö××ÅšÝCsss˜ùÆczÉ© 4à¹Í·¾»wïni„’l¡æÜµºL‚Áû@–ÔNp¡«aÃ9ç(ŠV*•”Žä;HZjnK„'Žš »›(‹è½ê­˜ìDú󨼆“'Obdd¤IÀIó˜É¿±úäg°TÚ0–CÝÀÄÄßI‰ ÃîvâRMßAµ’„±4Œ^Œ%ö/¼¤§4LÄG‡ÛWRXÒˆã¥R årÙlº*•J¸pæ•zQ;ágêÏeii©iø€ûy‹‹‹X?|«åh}àŠP«Õ°°° r€3™Lª¤Ý'§Ìi ¤Úƒq¼fVNˆú%ù ŸìÏ}m‚KŸ>»ÔÄUÏõBô•7Ýîîn/0ɰ»?w¥gûûû±íMïÃùÿ-ÎÎm8•R¸øì7qþMÿ»ví26+• Ž?޳§çê÷Ð(æe`¬س÷¦4¸ã:ž5r‚ûLÜ-hr{_rÜB"Ñ«-’FÇÍzìŒÂo£›)Š+áB.Û¡þ^@×KA ŸÏcÛ¶m˜?”GÔ¡‰j ˜}îË(]sMSÑÌ’ä¬Õjuæìlº9»ŠÀÀÍïG¿È. „) òðJÒ"¤Vß±kFXÕ%w|Ç ¶cÐ9iÁV*<ú裘ýÊ`yMÐ/I0ãX^¯ÿIîöÂ<ðå?øä2Í–±C½«Ö€™åúßêíßG}gÙ rs¹ãÃ9Üô³б±1UzA«yHëé“vsïÏfwY›´/4Ã@÷j©TÂ¥§ÿKëÍïßU6ÝønŒµD¡Ö`iÉàÓŸår9LNNâôå»1óÔ (5 ”(fW€ã/Åää$ …+’|n©TÂŧÿókÍL¼Þ`øÍÿ6mÚÔ”ýIRÏœE…µ&)MÑU²'Öt+NÌ-í™Ðà ‹£_“™u $MF/ŠGµú’ÊŒŒ ·e/ÊǾR‡c" |ü1œ?ÿ^lß¾­.sÅ…R©„³G5ušöËv_‹\.çdÍ'´á³^>Ã4êz D#Ö4©iJ2ò–q÷5êÓ$í6-¯àì Z ¨}gl¢˜šãY!.UÕ]Àõæ—e'cãT¶ªQEŒ–µTÛ‚ª(+†H!`Ÿ®V8ÅS®nããPÜg–©™™\8úMµ è.[÷¼-–r{‡c~P~=·æÝÝÝØûCè>úÿ¢´¼qŸËëÀÚ?~+7ßš2希9‘¢˜>r(­Ñ¤6 ع»Þ ñjú/,v”öü¹¬ŸS±””Y©¢¦‹å7©>r†]êjâ~ή$"¯V«é¿iá1Žkˆ£ âcw¨££cWÝ„L¦îMk1°º¼|쥴ˆêØÅì•öBÛ±£££-bg±@³Š`´hA î}ÓBµ‰»HÚREÜŠ&5(É‚h¬¢¸û¹?A‹¨F+ÃÃ.&0‚X¢‡pj'ßËgkÇQ|%_üPÂþÅÉ6åR3ÄÖÐÜv–Óø'®ø˜^»bƒÍk$ &ÍžÇÜ/ûʵxL¸×†£HÌ Ë9h ´ÇÏÔb¤3*]÷9•J3g^J!Ô(æ€üUïDgg§92Ord­”6L `dr; ¹æL®\E]¹³RÏi­VÃÌÙcu&L¼ñ§r»ïN‹¾Ü3M„aȲÛ|˜fôþ,†“Åg·ˆ-t…œ7‡!ÑŸsPÜБšˆˆkõˆ¹Ø_û/±vèÏ€JQ ¬/Îáܹsh)žÑˆ¥T*aᙿL£õLP×€¸ì²ËZuU¸(Žàc\uZã™jVj!Á~å=,ìž+¨IC5í«óRάl±[‡¥Õf}q7r®64Q’È”ÂX‰Ø%£H( }‘<¯f¸(4®Iš%î=,--aæè·PjèútäüUï@>ŸG­Vkß’ÏšÅòZ Ô;NÇÆÆ`±¾,‡¨µ‰[¯ƒ¦ò(f\hí¹Ð2+ÉQsÁJªÅBŒâ`O®§”}Q˜V2àÖô¯ä3RG‚†N{ÐLitƒJ×)p¢aèM™.Úž–f>p´o.;ÓºiÇ9g¸á9Zq¶Eõ–òË}ÒBmbõ2›Ü¸ˆÌÆ ¨Ï>eR¡PÀÄž‘Í)#`µ¼òÂá&a°äš.]º„ég¾šF|ù,°i÷k±yóæÚ‹iÇvZ¦Ñ¸$ý–²f@&õæ¬æ/š]µKiôexhEU‰ÿÌQOÝý%ýqwffË÷¸ÎbØÒÔqÌNÏŸ?ß„©väëò¾nAQ¢†I†ÔǨ[òǼ¢QGÍV}fŸXr–ŽMZóh|@‚é‡ 3°­eŠæ¬4ʨ¦óä>¯úçµÖ\Ãέ'‡d (ö¦{Âg\¡$àçR:)ê¡iñÀ#xäÎ84jö9”Œ„Vé¶pbÎøµxæ Sfɶ ]—Éd011C–.!Šê°üê¡cᆛ›D¡jµNœ8…ÕÄ®"0ôÚ)LìœD ©é%Ží‡"Ý»Tе°vJglnw;…PéÚé°fÏ䦸XNÀ]¯äó%N·S°#_Ç×¥g—ÉdP©T°vü±T½³˜FvìNñõv‹ÂVIkгº=9ØÄG¯_~C—w¶œKw%û¯ÑÜ©4ûò¤Ú©Õqã3[‚ š±(ª®-Qp¾Ay™ÅÄ¥³Á­ëPh“súÉ繃{$̧X«)J¦Yr^Ok{Ö<•‰A& ‘É È†@&qéžž÷¾ý]Oia8wî\Ó ­®®bùÉû±R®¿&Ûà1oÛ¶ÕÅF?hí<åœm¾²4¤hªå籞Þk bVW$ÅC“è@‹¤-4·Ü` ZãX&¥R Ë/~µþüЃ·í²´6"½••Ì>(å,à |c,ù:-mmµˆÞ'ð}vf-&æ »D³´öFj¤È%G10»Ô:ÜÜG‹ÿÕ|%Ÿ‘@*±î6°rNq1™³Ð”ÙÕ€hq*uN>Ê¥Z6'ÙAÍKC^¸ÏKÖš“_ö—cÜ3¢Ôª•YF€Ê´¤ŠAA&‚lègDã†!vî܉ ¦×R¸øÜ×Q.—S,ljj S'6ø·]E`è ?„ÁÁAïÎ1ÍsRó¤>©¶zà”Õj<ÑD†\ªž›Zj:ÝíR99\ií7  O¤Ï¯˜ W½£…žF¯i~~+k™V>ÆÆÇYîºT\Ôº@5Ü\ª-Xò¾z3ZÄxBŠ\¥B›ƒÔ ¦Óå¬%ø»F¬°¤ |劓"h“ewšÏ¤`-p2AóZE10³§«íJkHòÔLjÕé$ȆÐó 4š:O9¹Ii ‹dP4J\¥o ²ÎdëF>› ‚™L›7oÆÑmû?ú(ÊÕúá]:ò÷˜žþattt R©àÔÑ'šÔ»‹À–+_Ÿ2#¤¢N;Q„4 ؇ÁÕ(Z €Íù°s|ƺ†Šƒ$5:_º+¥“ÆqŒååe»vÇ—;wK¾žË=½ýXYYA©Tb V•JgΜIé‘.-oyy¹©ØGÓçb±˜ AYWÒsÕ`5®%5ºX˜µ+Ç(0ÔD®¡ß+‹ÈfZ±(n¥r†‰ƒ,ê-wMq7S/ëɇHÉœ,‹i /©4ǪÕ:Ò…E|jˆZƪ\-¨p³æ¤»” *$Q‚êâ8næ±sF]’³´°[éA¥ïdêÑz‚µÇ¡JI+‹yͨžx³ÎÂåuàøñãÅìì,Vž¸?UñˇÀØÖ͘˜˜hzÜrÍ…éçnTÊݯå¹è?Ó2ïeúäðeëø8‰Ñ¼îÂ… 8ô±éÙ [¬vÙëU`neãÞçV€'ïûmÍ·R@“×Ä1°ViÖé%àòËÈeÐRŒN¾úºMw~7ß|sSÓš¯d*WK±æÎJÆNK±5|=Zé1¾tX©Ö:::IÔ›ý'm ÒýP…X)’MœýªQs´ž¡­“"=K—[±ÖZ\‡l©Ì®F㥠}îR Ç9ÀĈû<î=,I“”äӼ‰x ¸Ž¹‘Ýâ)2u£ŽLÄ5¶‘%ùw.—Ãe—]† ùõS¼^ŸüöìÁÉ“'±°º1}¼;ô¼þýèééi©htÇ€@häFßIÎÛP–Ó”"-w“HF@3ÜV¤ªÑ"µØpþRSó <Ì÷Þ4âÙåf'@µ^èÄ©ä}ÏÏsë¸ñïÅu ¯Tj¢Ìi0yQÊ"—©Z™À«¤áÕãã܃ @WWF¶Œãüü¹&¦Q5fggQ«ÕD©Ï\©0I×¢Z­böü©º™S~Êç€pû­- “î=wvvbxb SóSÍ÷P«Ï@¥E`_ЦDððÁÔÝÏHš7¥A<šÃãÖ’Ú¸L»L KA¢µ™{goDï‚ñIþDášw§ž¸‹çNâ•W^ÁÌS£RÝHÅû»€;v4:×2*d0ÖEj8ÐÄ}´õŠ¢+++8{öl‹ô°otíþ»R©`jj ëëë*=ϧH$É•RyUËàÓ žFhØPØ´drc[ÖZõöü½Ø¡¶Sè£Î]uHé®–2ªE ÔŠ§n±Ÿ›}Ùn½¨CùoIU“¯J˜¿pr£ iÔ4º³].—Q~ùë)K*ù“Ïý£—±Ò¸M÷°ë­(’Þúr¸0u•J…‘Eº¨õ:‰PÀÕ^¨ú¥8hEjø±Zƒ¹7j×pp›6“iðØ…–æ»Ýzŵ(æQy±>äöÂWÿçŸå*0ÔS/ºmzí›1::šŽ:Ó´@6Œù†n·½Ik"¥Ÿ‹‹‹xä›ßÀñË8zôhŠKES·xJx¹\ƳÏ>‹ï|üçñøãcuuU¥EIüm_ƒ.5ÎH±u œâ&C+†ß*Y}7°ŠÑm%»Î=·‡9ÖŠeÜ›¢6ðƒH¤÷ÑN’Èd2˜Ø…|¶ù3Ö«@ùè_cmmMeóø ­âpµZÅÜÜ.?–ö¤$Ï/ŸC*æ'ÁeÙlƒ»#pÌzX?ò_±¶¶&vÊKÝØ>¥O_‚f}?Ošn—üJ˜ž/æG pZ±‰ß êcÄÿ.UÅË›vaõô1Ôju|úÔL Á@gØ|íiaÌ岎ÑÞà )߈ÎýwEX^^Æ·ù:Nñc˜ZV?û«ˆÞó[سgOª½¡i•»·T*á»ßý.^Øÿë8> LÌþ¢è—qã7¦ì‘v r‹‚x9hNÛpÙl›‡Bd2Õ–âúc¢:4²â¾óÙº“N b&*¯ÅÀ……F±5h ®.ÖGŸeŠI c_R!¹v êÒúùÈýJl‡v>—4"È_pu2ksÇØ´i:‹@f¹¾ÎA#Ú=wæΞ=›öHX²ñÖðøjµŠãÇc™LÚÊÀØp>eºqb[‰aß¼y3º‹õZMR_¯OœÄôô4zzzÚšé«.}ž¡å $ÄCã»Kïr#ìƒ-ávRE]ú9ÕFH¢Á(“²Y –Eœ Eê˜ûÕÑÑî×þ V¦þVk4¦#ëaË–-)þìw6Šý‹§VœÞ{¹\Æ·¿ýmœù»áì\ýZ_<`ÿ¯Žqw§ ¹Ã$Ü÷]__ÇáÇñüþÿ/_¨¿×é óð@¡ðkxÃÞÐ4îË'*õº4¼OúJØLoù¥OµTüÝ=·²²‚'¿¼G©‹x…Y`ÏŽ"^ÿ?LùÍÎÎâKÿéçêø} tuWßõÜpÛÝM ’RäÔÑÑÑ$j¥ý5XF+–ZNÁ§`G ý±Ò KÓ0á‚׸ô÷÷cÓ•¯Ã…ÇŸ@Í©{¯¬§ŽÂÎ;‘ËåLÆ‹+JÁÊÊ æžz0%CÀaºoø tvvª,¥0 122‚ѫ߀‹}»Þ¼Ö8SËëÀñc/à²Ë.kbÌiŠUãö>Wìl—¥ÙNL,½žØj¾¼•”v6A™,bdÀ¸"æ0 199‰ù\]Z ©Ôuׇný©&Ýhiã¥iª'®ÛΰYÎñ%,†Ä¸GŸý5?úï±gÏž ¾7)&×™õû7ŒzrÈ£f³æå­ˆÃêjÔ(—Ùl}}}¬aO°ÆJ¥‚ê‰GÒa y ãšÂððp:Ež~Õj5ÌÌÌ4ãª!0´íJ ¤›c!ÑhIÙZg@Û/œ‚÷¸Ì—ÝGBÁY­³Ø:Ò^îèèÀеw£÷™'š&)-—€éÇ`ê†Û±}ûöWéìj´=º_+• Nž<‰©c/n ØhDë]Àä®+‘ÉdXí ÊŒ¹îè{êÛMCC–ÖÙ¯þ .½v/&&&Ä‚¥DÏå äÒÞ±ÖÚʰ-ÈS2ì©jOñWnÄ™Té¶ðÈf§‘`°rнŽ0 ±iÓ&®¸£?K6d¸ìŠkšº%xˆkã·®_Ò›‘0ÒŽŽìÛ·ÛÞþÓë߀¢8v8ò™‹£G¢Z­²ê}IGæ³Ï>ÛbÔ3°uØúÎ_ÂÞ½{Ó{¶ô,¤ï£Óîpo,p‘Çââ"¦g«M Fƒ[®H¢pN5ÈQ®6Óà†‡‡Õf¦væYú6»ÉwªŽåDµÙ§‘îûtÚrÅà0 ±}ûvl-6ÁZQœŸž?ôwXYYñjTô³è:ì……œ~ä3˜[n®7sÀ¦k߈͛7›Ï'›œœÄø–¸ Ã(ÎÍß}â[X__gkbî¹ÑH“E³¥– -ÀJ­\á>ý,­{É5ê´ÚoµÊjé=mЉãÎBnæ`¡PÀøµoAoo†ºáž:ü²¹èݹ£££ª£i9DIk6aP/l=®k,‰Xoºím¸ìíÀfjÜ/G>ó«8räJ¥RËA]__¯õý¿Îõ-÷|7ÝtºººØ aÍ딨y §ñóÛiÓvw~~>¥¤l ›• fO?Ÿþ^&6÷#ª!uGk .U“ÅÀÏék­(¦)£ZBdM]¨ûŽO“"Ê$ÓBÿm¿€M=Í,œÕ2pî[ã¹çž«ëž3,++”œ`©TÂ3Ï<ƒ3G¢TÙš’É›ú€m7lè>YØuèïïÇЭ?ƒ‘žs7à˜©¯~GM›®4yÚ¯¢9`ÎöQ=&Ú (ÙŽÙäój)çÕišeuYjxl}jRÔ`KÄ"ã†zÆ0 qõÕWcÇŽO²b<®œ¯¦GM)pM2£ÁȪVS)ÓÞÞ^ì{Ó[ë3^¿ôÉ´èÅÀKç•¿øuŒ_~9VLqµÌ=yS/½ˆ©ù Ç“ €‰`âî_HºÕ)ª9¶vè~tƒjðƒÆ*IþŽ¢3g_F¥Ö8ÀQxî€ .õ,•J(=÷PZ\ë*…k¸º‘&KÑý ÁíÀ.\äÇ ÷°FÊÑsãs¨}¯MƒÖèk“s¶tÅ.Ì=},Y‹bàì0ô¥ßÇÀÀ¿ÃÖ­[[ÚûÕs/襔Ëe¼øâ‹8ÿw€é¥f§Õ]Æö½ “““âô Îa†aˆ«®º sW_‡¹CO§zBQT¯O ü×ßFgçoaûöí)ƒŽsô>ƒR¸{­ÕjXYYÁ… °sçκÒH+¾äI`1‚:C)qM‡¦ÖZ”§q¾ã8FEõÑxQ ˆk"/š«‡aˆžžôöö¢¿¿ýýýèëëCOOOKg—W×,¡Þù…4} nx@?nzóÛ1ùöŸÀhÿÆ`æ$5<üÔK)7èeß}ªnÔÝH}|ØzÏÏãæ›oIG•i«-mŠ\;U~瑼¦\.£üüß`­qß9`è†w¥çâ8ÆÂ¦gÊiWd>¶îçqZ·EÝåFåiND©iÆhÜøôÿLÇ.Óì¸{h``[ßüSl®ýÔjÀK¯,àù/~ gÏžMñn_†]Çõõu¼øâ‹8ñÐoâÄt«üαWÞxgSÐæó¼‚ @oo/vÜú£Ø2Ô Y4ÎR¥<÷ò žè£8vìJ¥’8«VÚïRq: >NŸ>Ç{ /½ôÒ†îg@¨aó’½M÷’ H8†“i©,Õß(šF,nd¥ôü„ùæõˆgãe¨,F‰„¡%°ÌÍo¾“oûñ&X&nplkñµ/j|/v„Æ€-wþ öÝx;ïQ¢áQÜPëG!CyÐLã~¾¸¸ˆKS3¨Õ8jèÛv-;TÚ½¶ÙÙÙT½¯'ÎZÚ¯I¡rF’®·h3;µîdM‘žƒôÿàéŸ>2ÁR §4šì•L&ƒË/¿£oû9lîÛF+eàù§âÈÁ—^z)m–Ó˜b”žD´O?ý4žýÔ‡qøxLlߌßo`||¼©ëÛR—tû_vìØmïúlj¦Â®W€ÃG.à©Oü_øÎw¾ƒ………Ô[Ì%î«V«¡\.×¥4Â3_ºå©çWÖ¼†Åû<;Ÿq™¡$Òeq–}9=Ôͧ†8®qÔ±ksÊ$xÈ>cÍÚ™pCïGÒÙ°0ål6‹Ü|ûÝõÏÿÒÁù… ž­K ¬cÔo¼åM-R¶VqÏ•[µœ¨&r¥‰µ#‹\ÓììlS–’kh†qÁ¥s'R᯾HÛÌ9ö §.iAˆÚ}Q9U©3—›†c1i¸kLß#ØØ'A,Ó%µlƒÓkq :…6÷Ýxj‹Sˆ¿v—–Ò¶¬”€ÃGÎcþâ‡0uû¿Â•×Þ€M›65Õåè³L†–—Ëeœ={/=ù L=v§gš« €-ƒÀØ]¿ˆ«¯¾:}¶šº¡ìuttà†nÀúÊÏ úâŸàììÆ™+WëD†¥ý¿ùënÁ–Þ­[·¢³³³®éÔÔèþO>¯Z­¢T*affÇÇÚ±¯qŒ W¨®AFeYiòÚ~å†tAPŸyJ5a|›r8ê¤yÐÒÉÕW+ˆk Š(zR4*é;XÜzþÖ7U´FlRÁËwÐgPSãGÀ—?U7îO©ÏÀX?°íîŸÅMo¼­ ¦ð¡¢ú¼Æ¢eiœ-È ê“ΞJ[Æ5|½¥Õüù¿IBgÚ{ºººÒßã"~_LZ“h ]¸t$›ëü5Öo¹Õ9…SGµP›oëCjà¡Ä¸÷÷÷cß[¿"d¿ù .,lár8~˜~èÏpñ«†¡7ü0Fv^‡¡¡!tvv¦}‰A_^^Æôô4.=ÿ.>õ÷˜Yª3]Úr&¨õ-wÿŠ3O?Š“C(^÷ŒŽoAoo/ŠÅ"r¹\ê°¢(B¹\Æúú:–––0==Õ—Emþ‚lˆL®™\€B] Ë“XbA½Úžmâ±K†™²S|G;q©770nDëq qýÿnõWzhާá–\!ªåúAš‚ImÂð5Úa¹ñö{êóËŸÂùù–\O¶aÔ/»ûÿÄMo¼ ÝÝÝMkª9RNcß§ le"ÒüÌvE¡J¥ÊÏÿmj  !0xÍ[Ò{äŠDµZ óóó˜>³!PUÌ}“{›ZÌ¥ŽHßzWgðÑAâý|¤_¹!Ýlâü;¸ wn¬ †s~”Íf188ˆßöÃÈv"ÿ•ãÜ\]T+1Œ‹kuŽøù/D><ˆ\X7ÐMtÉDþ· ”ÊÀjÅÉV¯+æ4Þïû¼îu¯K!G)SÖ Ïô÷ööâ·¾ßéìFñ¯~gf구¨Ñ½VÖæé¥t½òG8×…”*™ÜG1¿±îÙ°Þdd²ÂbãïFÓ[&D„@ drEoˆY ¬´¬¾wè#õj1\´‚+ÝôîX©8ªµ â8BUS..ŠãDŽ4Mh.Ý ˆA[ _ãérÆÖ½&7­ëïïÇM·ßS_‹¯|f£EÞÔßþÓØwËm¬ÁÓêîFà:‰-8B2vm ‹p_›ð•§OH™  kòõ)»Ž¤1iucÜ-²¤é¿ÝN’F•ž3ţݢ¾”-ZJ†[„>ã¦1Ž- ¬¿Gñ”αK<à ƒƒƒ¸õŽ{ðÝÑmùÒïáø©%̯: sq]½´˜°¨ôròšLlê¶îÙƒí·ÿ$víÚ•¸†ÂÆ IDATvR»ëïÛ I?twwãÆoĉÑßÇÀ7?‡³Ï<ŽKËhj†ªÖêRÐér87ѵÑÔŒÎB Å| A6d#È:v$²!z'¯Çž={RyhßìÌ=»–q§Ï7¤‘Ÿ/÷XJ´¦æ¿chÌVŒj@˰k¹¬5apº„Ç5¤‚ÖÔÍ*ªµã)Æè¾ooo/n¼ýu,æ«÷ãÂBýû›û€moÿn¼õ­MìékìÉ1ºÅUÉ´Ã0ñ‘Bu  «¥ƒ6 4ÇlI®±Z­âüÔ9”+ί·cƒ¿NYAZ&*­•¥o/͹ôµçƒ§‹û;n5šÚëÇ­­¯cÎf³èêêÂÞ½{qaâ÷QxêÛ¸øÕ?ÅÌbßîvx&0gÔ[™m0šz‹À–±"úný\{íµèïïO% ¬½nÕâ8X¦££W\qÆÆ>ˆ^û.<öiL;–jE-"k¬m:ì’ „wÞŠÝ»wc||<5ê>ȇô<¤ „ ÒÂD‚ÓݤRÄ) iTŽïžÍf7RÉLAÂBÓñiÑRmÀ­«‰œËå°u¤Þ±š| ÷Õ£:@Ã÷ÀÒÃÏqé)<ÕÛÛ‹}oy'dÐwè3@ ¾éØ÷¦·¦z)œ“äQKΑjÉkÓÑ9v 0ÿ§’´Èµ´´„Ñ £Ñý?4C¿(—([VÎ=ƒ‘¾ºA/æÁ×½³i¬è•Ã˵½£éïK§Ý©ò¾AÕÈ`APi2.ù¦IQ¾X®¥®Qyóù<ÆÆÆ08x.¼æz¼òÊ+X|úLŸxk¥ºv{-ªC.Q\ÿw˜­gWÙl݉‡Ù:?}ÓÕû0øš·cÇŽJ1yqVðáÞK†èïïÇÞ½{±|Å8uêξø4f¿‹kõkï,Ô£÷zZ¿¯0Û ËdryÙ‚|rÛß„‰Ý{1>>޾¾>äóùÝx™¥ h‘ìXrFÜÙ Á¡C‡b­Í•v7•£ ·ªÎQÝ’Ÿ—J¥´ÛËýJ8ªÜàŠ!º×¬Q15£Ÿ4E¬­­µ8‹¤"N Œ[Ó¤4þ+m\q×:Y ®>èŠzi‘5w-ߥ†[¢Cº†Ãˆ%ÙVw¯T*•z·-iFï•:¡Dr5¹Æ|>ßI×Á9;.Š¢{8¹ÉÙk5 ÷÷-*›ÕÕj5¬®®²P.¿›KÏ9È”²¼¤¡çR‡r²6•JÕjkkk˜ŸŸÇÜÜæææP™?ƒÚìqÔ¦¯\@¶o 2}ÈîDÇÀ(100€þþþúÄ£|žuPÜÚ.Vm‘’l°R©`yysss˜Åüü<*s§/œA´|ñê%dwÖï£k¹ÞMé}ôôô »»;á\é>Nª¤äôœßþö·cIØŠÃιô]2Ƀwž`讑¦ÆGâýºŸç.gð´Ê¿Ö@#1}¸èÑêJã‚4øÀ}(š.37—V„A¶4Ià ™Ü`©öqßçœ{ \4#5­i’¸RwŸ=s{˜òÒ%§{ÝÉýH…tZ¡A©5C؆ }6—RpmÛî…Q#"ѤéMÔ ºEXwÚ ÇÖ¤||hR>l¥¾\ÍÁ ۉÙn¤lÕ%,C­]›Eù`·ZT¥mtŸNS fH²I®–B!+•÷Õ’2…vd|Ç!Z’íÎø•Å4v‡Oov=š”g4¹ZWËÒœW;gQªçqSä´ëôÙûÒy ¹“/’çŒ6'ö%rš²¸Ý¨ZwžÄᦠc$ hç0ú’¹ˆ:®ˆÂáxí Ð.]ÇÇwnøñ«Ië%'ÆÝ¥’úŠðÅÏ}ºn}Oûd:Öþõq€>™¢›ºsB{\‚fØ- j B‘ì Ø8†–ˆã²7Ö½fqx½‡#“ ľ#¹u¥HI£ËRNõÉbC ·ãx·Öt-ºóÕhöyhÔKú)>šáZñÓ»öÅ©w¦%ΉJ)‡Çúl4‹Áâ]·‹‘šÚü‘¤”Jø£O±‰ 6|Œ‰/ÅÇYÖÒûK0«­ãêí ãÖŠŽ_ŸƒÁ´Ï±´o8ãhÍdµ²@‹`¡A5Ô!h½-RÖa5.Ð kµËŸæ”[ ÅLáÆ‰ù¾ÄÁàÛjÛQÖ£ÙgH·ópì© *¥¨¾ìíúh¹H´LIžØÇØYÌ—vjÖ¨>.­Ö mÿÛNÒN€à‹j“¥¸½`Ñã,…VD¶z|¢Q­ˆë£"i ÙùÖÄÜëá(¾’­ÐΛ¶Ÿ$ÈÙµ«tÝ¥ tÚ`(4“X\³‹{|ºº¸ƒH‡øF½ÔûiÅ ‰n‰0µS‘ÞÓ'båHÔ!)5j¼uŸˆYÂ?µgûOÁ°°”} v>ÙŠö‰9D×Úç€kElMËHƒ=$]"Ÿf1Ú¹éÓ‰ÍA7kÄ’Ö A­K\Ê´lE*hk#Û9óVÆ$9iîz\d!›Í¶À0Ü~IºŒrYWH»ù4fŒë %A{‹^'m s¤P¢_rчÅIÕ bÖ°né!J‘-mæÞG+ŽhXš:VfãÓŒ%}®Ä£µÕkkÀA0¯†êgÍšôiÒUkÝÛÖ~¤‡8¡£Zu‹v©€–Ú§Ooˆö\¬b¾&[¡Ù$Ÿßñº$è˪Ÿh“Æ,ÇA¡EÚ a5úqYV“a×  «0çFñ¯&¢ãZ¸)û@Šj5O-Á7¾Æb/XÃ%™\nÆ¥Öí©1<|xº>øœUóÁò}º‡}ŠÙÜÄ.߉CV¦ )ZÁ“ú.4éÍÁør­µna‰)äR;}÷š%áMe2$‰dé\J“³|²AKJB b|;µ¢»U«³²j,üž£ä6 ÚÐÒqZµÕ¢Fm¸7¸ƒc·Xt) jÐ`í rmö¾†V‹¼¤¨e.•S‹š|ºb9ÜÏ'íôIU-‘Æö)n¶s/§ß‡ó­á¹\dÈô¼¼Zzª6­^ZGk –qÆÏ‚x8‡hi¥àË*Hû8VÎh¤O§´ý[Aå¸%…U£.É3´hç»MéŽK$®/wÛjóå6%Q+âÒû¢××ÎÈ4-"òÁ4­k¶ `¯Úg}%¶…öyšTªoteE«4Ó±eX°X;Y޵”.ªáÍ=Uê^õÉj^M!‘F–œ‡O¦Di;‘±æ¬Úeqµ­®Mó±Ö¥cF©R¥ôÞ’ó©#Iº[¡+M+ᛜì¨ô`856îâ­(Jó²íচå¸÷VjO£1Ÿ‚’¶‰¬¡Ý>"S>QœÄ¯µEQM«]Û¤Úuû`ªZMħxjÍæ•"3‰ï3ì]c;Ð{âĦ(y@ƒ¬³B§Æi÷e XÏœòêÛé ð=ëk«ѼvŠñìÓÎõÉÜ÷¦òåÜï†R÷UòNBÕ•¨”'$Ùðøÿª{÷ MÓ«>ì|_¿3=3ÝsÙ™ÝÙÙEZqIˆ,ÁÜƈ" JI¬#áJUªœJª\qQ.'ˆ娨r œT*ƈ›Ñ¥PBÅXQYD* ʲÁ6  ÚíìÌììÌtO÷wËÝoÏÓ§—óô CZ5¥™íîï{¿÷}žóœó;¿óûåÅŸƒ±Ë"™îtf¸Ž¸Ú¸JµÑeô•el…" –ʰ•¬€¢¾V&*U0­@nUõÎ]ÆÎ(jî@¨ö\j·¿Ëtñó@ÒÈ PÕƒ‚Å*S½ê~U©¬•*ÆS7l¦ø êTؼÛ[•J¤†shGIPÓGMïå" z±’­RNg‡ù¶¼iqþ¬o]iøô˜q³›Œ†¢\P¨*;ŒÐáâ=ôQ†» å9TÅ ú¼^5“Ee•î~Uz$•ž»?H_¥²Ñ+UgoEë0{ö™˜ w¥reS¾U"†“BQÒãV*¦´b\­phG> « ³ÿ> ©]p™ÿÒoÉ}E¯BñVUPRüu÷ ]³°§¹§4*ƒN®éXYÔ±%”}"A§žÒÑaý=Oå5ÙgW8¬›µÈ·òyK‚]O»Nо«²g”Ç€«¾ªû¡r8¸C\]sµbªè©Y6…`³ÊÏÌ'TQ hVàDSýh_:Ýý É´×D%2vãü/+t„Qç÷o;¾=Ô·Ê‚@SîL;£RÀ;Îfª4Õ*%%Cf>¬êù ×ú|g]’ÜPªjvô@"Î~°BÝm7GnÚ»&'ƒÆ*L¾™uæxã5ÄãÊ'÷¬U¸}†JÓØõIߣ‰¶•>r˜B~Ĭ‡† 4¹‹z2'϶£‰ v,—Ë<öÜìT¥zñjc¢¨4îœT§Z•2¾ÒÜPâþètuŸÁM˜U2'×ÐTå6ÈV™¯{Îm%Ws¯°3”aEùQéÓ¨{¨Ø7ëg›¿ª¹ï‚3Ê+Wø§"Ë¡Íèz«¸JÉUò.9btRôÜкí©*Ñ0šÚã®ÙZMžçóùa£ #(]VR*xÄi™¸Œ½Š»2ßÑJ¯p[Uƒ  {á¦+ƒ>ÕW§]êY¼êÀw ©Êô- l•¦WEk¿×€]'“PÁ²‡¢°ìjω‰ÿ¡uã^ŸA!Õ±þž=Ÿ«¬*ݲbÀîf²oNÚ”(¢£"»=öX?t±XìöŸQF½,Pjj¨7Êôf£àöõœVrq™®kTTe|+'«’R‹A&Uý E—Dº vs› ZaTªŠÀœÊ:ÙÚA=%DÇCpI¥à HeÎÑkGˆ$#¥¸"Y«EÕðS=#V]T²pWe*`fœÎ Ñs²Y2Cþ®îs¨Ä‰9‡e¸ yï è"QÀ©d'ïB@y@VäX7'{ÈiOœccŒõT>,8V}‡ÕÁîæ ÜÚd‰©ëƒ1™ãª·B{Ÿ[8jha‘Q›½ÂEuSpy\]ÓEaº Ö Dè`a UUž²áq´8w½Û‡‚Ê|·ù+bU(¨÷pûÝ"Gf⮑¬šš¬ƒ£0U6”3¤È»³³³Ùì@®97µ>¨û¢6‡ ‚étzÄVNÔóÏ- ÚéVV^¬ UI†Ô©š•N+¥*íªD’ü¡œwÔÆͪ¶t¹$WlÕPæÊmo—`(˜®ÂˆQkB¹U ;'¢’ –Á@,IƒöY*z¤2öf #,T¡TGÌPIOn޻ת@Õ¬Â8hž* +µáPóD1V«UìîîÆoÿöoÇÍÏÇbûÅXí܉Õì^¬vîF̶"æ÷cµ{/V³û±ZÎ÷þ¾\Ätóјž¾“Sçbºq1ÖÎ^‰õ͇âìÙ³±¹¹gΜ‰³gÏÆÉ“'~XÕÆVUµ Ã’?ÉjFḯŒÓ«ä‡U3+ólf‚¸ÅŠ‘”Rå Ãà§UâôæÓÃU,H.C=&»QiØ»uRU¬d´Î†R=Ev‡efɹìšQ2Q"—Õ8TæY¦$Ù³/Š9‹¹$åˆçi›£Lˆ• PxÖr¹ŒÝÝÝX.æ«E¬–óˆÅ,b9XÎcµØÕb7–³íˆÅn,w·ãþnÄÚýÏÄýÙgb:‰ØÚ‰X›DL§ÓIÄdqb-âÌ×ãä+ÿã¸ò_>úh\¸p!Ö×ׂ|ÎÚ®„3’PZÔ.¸çEªŒ*ž*Ód>ôy+Æ™ ô0‰ré*¢JÓKõÔïdj¯k.²CYú© áì s#ÚUÓ rdÂ1Ü$3ƒƒª©cy©uÍ UåVô‡*6™ NÌ,½¬f™¯­M†«°»¯¬JPNM½¸Î1š[­–±ÿZËy¬V±˜í/bµ˜í¿VĽûûMDìÌ"b¼¼UÄx%/~îzœú£4Dœßˆ¸üµoŠ/úò¯‰'žx"N:Emýz+õ܆fY{•Ïñ`ÄêjBüÝÖøWÑ3+å}—®Œ²»Ç qx¸ÆS} 0Ib—Õ#%Ò S)òSЭF¥sذ; Ý>i«A†¸¦«“ÃVÏVö:F4qEçß" ¬BP Ÿè0ЇÉ7µr“Ðb˙ʡÁåž«åŸcµØÝÿKŠØ/žþ?}Íæ{îDÄÍ»Ͻïñ¹¿3}íwÄ—|õ·Æã?~Ág e…õp\Û?†—U)d• س‘ªŸÓeNË›Í Íš¬€§ŸŠ_^=*M7Õ¼u= çŪž_…íRÁš™n·¢ W*oǪdã(€¶~®U³”€´U”cÏ(Ü]}_Qt‘#›*R} %¡D¤¥¨·¿7(ྊ¿*‰–ÒwhA\\ó;k'þäÄ~6¼œÅéaëÃ^ö~qrx­Ï³EÄÎüð9°\Eܹqo'âú¯þ“¸õ‘~Ûߌ׼æ5qöìÙ#ׄšŠí•Lô8ºà #¯â°= RQ¡îŒóÆBÆÞ®Œw›´¢«âî…¢¯õ°eªS¸ž©vè@ušHyŒŠb)cG)é !œª)ƒ2ÕüI¥7ÑãËš¹é•Ä¢¢^™+¯ -·>C¯µ• $Œye#bµ\Äd±ZÎc2â?ø¶¿—.]‚¸o £Ìf³ØÝÝíííxá…â¹g>;ÿæŸÆð©ÏÄ݈;ÛûÆ*bww"þðZÄÝÿãïÅîóÿY¼æ›þÓxøá‡éÔžz¸ ¥ž§2â³£÷s¢ZÕæ;¼ÔAJNm’ãÀùs²lßéå5Ѝv.8© Ã6¸k6¶Á£ÇJ¯ÄTºÚÇPˆ3ƨ%µ…‹• D…©£²j‡N0°]qdâbU~®ž,x ÛÛjg´o†ÊMeˆ›§9Ý ˆÉd/¸7?söìÙ¸pᜰŗòF[,1{å+cëk¿!®_¿ŸøƒÅõýoñÜí½l}¹Ú?DVϾ±û¡÷Æj¾_ýWŸŽ‹/!÷»KÕ •ôŽ‹˜'Ìô¡G[•üyq©‘eVv¢bã¯bì•ÏålÎÜáÅf ˜o/ÃÚQÅzœÞˆÓù©ô/\sXA9.ƒ¬^Ê`UÖ±J”ÆOÅ€‘*ë¯ÒÀfž°Nÿ AÙ¨¿’anôì†|Ò´¡+%˜R£+K'“ILVËXíGܽ×[mòI„²Âabmm-Nž<ç΋'žx">÷Ô«âßþ‹dz~g|æÆ¸g¹Š¸q7búÏ%¦Ç7ü•ÿä–q¤=¡ìÏm¥v©¸ÙBpAÔ•¬JÚ”•²=Ò«UVƒÊ + ƒj²à^U|l™^,@:J¨“˜n³S'¦„ù¶«ª1–¬¨ª6 xÜéÖI/,\éW8葉í¬ý_5¢G¢N•w¨”ïÙTAéo#}ŒÃN/ûßã·Hb-†q:u*žx≸té;ã__ýâ8ùÞO|îpæ~ýNÄéý\üëǾ0^ûÚׯúúú±T•É¢Œ*ͪ^S(Ô0ÍqX, "¨LÜ)>zÅŒØáòVrYiEð.W@8Ó]§²ès “êºUMÀGDè™ÜVgµÖSÄAÈ=Jš•ëmã2î`ëŠeânŽ"#*ùþ,—˘:=·@Τ8×GoÐÞ‡ã¶PZkkk±±±¯~õ«ã•oþÑøÂG÷øîÑ4l?{+â¹üñüóÏ¡/Gƒ]}VÇÜ@%kþÌŠiåù¡®z~ž?ÇÅ^¹€Â‹‘vuõ~°„}6æ©äüGu¤~ªü œn¸ÓUGŸG½¯rúê­„˜ÈUÏZ­ì3Öãc}¯\ŠšC•®¾SE¬©Í;wUÌHª–l=ãèÇ© ªßhS°àå`1–±º*…ÙQVÈŠ¯X%#ó^¬ª•¢Ã¤ÇPUÅU¹Æòs6 ®ÉÌæ/òp»V$ÙÂtÞL4™LbŠ2Žq$ßá«Jn’5AþìC/qp§P’é+)ÚöëÔ©SñªW½*žøšoÓ'?”›w#îýÚÇÍ›7hÓ0F„¯>&%''³´YaÎÕP…jÐæ¬ÜÁhS´š>éÜN4 =.8.ËwßU#(ÙhŸKÎä«UDŸä80cæ8ŸÏásc“‚«÷K}?,”¬z*S=N…YeºÏÌ8%=Ý~~el^Õ³?ÈØÙ8{~\¾ L¡"´up3§Ó†³ÇkÏŒå$ân|û»ñНú–xö·ÿilï>ÈÚ—«ˆ?y>âSŸúT\¼x1Nœ8!'ê´wA•†ªa#è¬!­J;6p¤$ƒ]S’iÓ8§+fj\ѯB-¹TtR‚b,»dbOêù°†¦”«b•Œ·ê±›¯›aÕwCFËÎW¹ ]¨}ˆž;J’z*N×éÂMì½3{ŠÅ :0­œRùteY@/ž«=¼{µŠX’Ì1 Ôä0 ñ²—½,®¼ú›býDóùbOwæú¿ýØÙÙ¡˜£‚ TÀv‡\%{qYlOgŸáÔ3FüZV‘ôö”b¡¢\ºû«ü#™g€šîÑùίë¬óýG˜tO§«žÜ$4Ëè+*žk^,‡þ¨~Qµ/£˜dì+¯ot¤ÓÓê©Ðkæ{ȨӬ¢Ÿª ¢\ûÆÊ•<7+ÎéÚ^x®p«]{·Xó"\__G¿ì/Ç™õÿ·µqã#ïÛ·oÇ|>·Í&DzPUx)ˆƒ=TµQØfÌ %лñz$ÔýS¹ªíŽ’X檂zÅhFA|ޤšï®¡ªž³:ÆŸC0-šgÉÁœ56UÒ€ôQÜÌÀqUˆ©J4AÄÔ´F±KÅNöó(N Ù篷bºÕSkÌÚUÐfï¡2µ¼ðÖÖÖâÊ•+±¹qãÎ9šED¼¸qóæÍ¸témÆôˆý3¸A Un²ÓÏÈe½‚Ë*N= Q…!ÅHgÞ®© »!îsÛÆ5¯Ðýr:ê,ظ Ò}Wö;™ltíÊz­’´³Õ†¢úÕ3sF,LÎÉ›ôÄ7u ´ßËþ³™`’âri]ÚÿGÍt4Í*µñq‘e•J–Õé`æ9™îE×É”'ôoÅP ÆÉdçÏŸËO~a|îö'b{öà5æËˆÛso…Ð IDAT·oX:³èŠY-öQÀF¸¦6q2ËvQ³²—q‚/ ÒJf âvÏ>Ce¸DáÍJ0Œe£•χ*!d‰Æ®Á¬6Zåš•º¤“P­¸ÖäM zÈw–A/ ºë0ª ~v/X/¨ÒpwÙ:ƒ$ªöŠÞ¬û]©®‚|ìÚGcóyW+Ò*‡œ%)9Yic\Ö¹RÏS´ï©e@0 ;ñÛî®S¹so‡"ëÚ±œÓ ¦¹ß?Ï©S§bmz(aÙ<âögÿøÜ¦¢[VpmD·«¨æ Â6Në¾¢0üjÉ^³{­2‡3!¤ÊT!ºß•Fˬ+®ZN{¾2šÎpaG­El¯¾zUnYñòóW„«èÁ¨{\I†*­Ó¸qÆ#¨‰ŸY-öô¨RÁfÔz“’E€¾jÎÑ >y0qº÷Ïi—rZÏ ÌSµè7gó㽪ӣ¢ÌÂѰÇh]ÖrŠ}î{Ž•P ²§TÐGîýT`œ d Eꢴ¾ ^5‹w¦è¨©«¼[• Sþî^öJ[T’tЫÑ«Æ,9Ëgë2ÿ,K`Ô~Èòì2­öÀ¤ý‹hHÊA¼•/ÍœÝðÕj1Y‹Õd_+f‡pýª:acâÐC6Í5Çp0ŠrzWX,3 W¬™žl½Wè©Ç£‡³­˜h£ft5(T¬ç­9(U|4Ýä°«*°¤Ët{v•Y+iUm(-×srÕÿ`’nÒ1ÚTÌ1e˜ñ½[wÊ;€õŸ9(1¶¦¯!¸&Éh·Zîø1ƒGcÝ.ëRåmþ,ËåFõU`ãf7åÇ2:¥ñšjì!"îjvVqM>F™bp™ƒSÔæG›3ó¶+”-µ¡Yö‰Ì¸SÅ•ð½Ö|¨ïTÑu¯B¬à´KªŸËIÆöÀ°RïUIXãÕ]“ª”Q…ØÃÂSž­m’°¶¶FýhqOrzdÝ3.ÿÚÚÚ^󹿍ŒM›ffúýC¿“X®V{}tT¤ÿžLY 5Œ?¿×L9ú½C ·s¢G=RÇî¿o™ ;Uõ<T­ªGn¶Þ»w/æóùh 5QÉ{êÔ©pºF(),žÅbÛÛÛŒ&ÕLd2ËgΜ9rO—ËelmmÅ|>·º>,ÛÞØØˆ'N ´ã}ÍbR9ä†Ýx666b†#ëxkkëHc±9Ø”æøº¬ßPɘ™¢fo•èf!ªÓ¥¬‚sr™”1RA™ÌÊð•I „br¯ðTdJ­0'DO:$é;bµ6D,Ö"¦'d&È‚kæ²ßÙÙÙ‰Åê°%ß©!âÂ|ñ!*XUÃ5ŽÝtŽ>.óTð H¯ƒ¹Ü0VFU)0"b{{;>øŽ‰ç?þ±Ø™“…Ó¤ÁÆF»ÄISI]<7‰/ÿÿ)žxâ 8Åê̽lÑ^ëööv|ð§8®ÿÑÇb¾È*•wñÀC`üÖ¥ “xÕ[ÿA¼üå/?”‘mmmÅßñ¶xþ>zôuc_/ ½Ýþ _>?‰W½õ'ãÉ'Ÿ<䥹µµµ½=è­"Or?ð"ïí8u}ñü$þÂüýxòÉ'™­­­øÐþóš/|Þö™µ¯w趬öïÃ[~âà> µÌž­«Iù ÎëÑ·ÛƒËM¬"xˆ­ÇüÙØ\ #V q>öý6ÞŽŸypfª|¬ð§¥ÖÈtˆÉt-VÓiÄrZšnSÍQÇ1].—q÷îÝC›s{sRk|)-½Øð‹Qï)'UãŒU¢˜ÃG™U_^@¨´…0̽ëñÌ­ˆÝ9ˆ•Üó8hl/VñÊ}-‡+åLÕì>˜?¸{=>‹®3öUóßÇoÍ—«øòý ‘“„xÝ6°³ ±X®âˈ¬ôr|ÝÙ¡eÝÆâ½‚$³å*¾l=diµõ|<óÂþõ®lIUóÕáë­T°H^ºM$UcÑÁlJ[EýòòekÎõÅgîý¹ˆƒn[Í©Áaånû2ĵµµ=ó†Qºw2˜nžV[ØiŠ®g±XÄÖóŸ>²¹†iÄôÂËe÷Úé"÷0w¾]i°U”¨«c˜‹<;¼Uæ„ÇÚÈfï*m³ÒX„Ÿ§pM«àÈuº*ªÍªW+,{’r]è禓xÀkîét:=üû“‡YÎÎ'¤¢QÓÄ(6dÁ¬6VTðm†28ìšíY¤ûâŒP\UÑV¨zP~±î³çŠ|@MC ,Е!÷{±¯E¬ˆÕbˆULŽ@+,ÐV)cù{;;;±ó‰_û³Ã7gm-âÂCÊìÝÄ­cÞ¨Ó›ñµ{ý)]£±R)¨Íæ¨pЉtô …hÆ¡9)v6&6¨Kìkñ ¸îBïç X­V{™9‰º“ ?AVàsºÖU-˜çÏ5M~v’­Ä!7)Ü 7E2ëʵ¹Ù‚ª†9‚$ÕÚb½&&‹‘X\LËLµ‰;þÎP™–T¦‡Ï%?¥WM¦˜à*¦-Â΄8±XÄ­[·âúg>3öóçÏKã  z”ãÄâ†rXÆ0ÍgÇD¨ðšÛ –_ƒë½¶éÆ#ñøÃ×c6ç8ð$ÓñG.ŸÉ*¤Raó°{9Ncmãr<þÐõ˜/ÇõözÆ`6Iùò…ÃëÿÐ0Ù™Kqõ¡ë{=€U£j`|ÏËç±Aõd2‰éæåxüáçö0vpxNDré„fÄ“ËqõÂs1_øÃ,߇Kç÷<ÔüòEE¬ ´,[wÐdÏŒëÛ©Iߘ•ÈœJJY"ƒðþÉdò€î˜ Jƒ:”÷Žc·'×j:ÄdmˆX ¥Œ²Ò€Dv±Xĵk×âÞý´&gOG\¸p¡tó*%_uQ¨Š Où:˜† +åç‘UæŽbä0ªÙø;gΜ‰ozëèÝçr;ë|£êpssó€eÁðKǰqÏäÌ™3ñ—ßò¶V »– ;µ¯±±±q(¸/—ËX__¿ôýÿíÁÞÊø;z¯¶ù7™LbssóHÞ»¯ÿý+†ñÕÕA7^o{/OŸ>ÿÑüpÌf³#‚vÊ ßËöy¡g–ƒ6ÊN+Ã=hï¡Iz7„ƪÆ¿wD›¥÷zK;·5Æ÷PéÊè<¬3[zB]ë=ÏÓµ˜L¦±Ú–M€šŸÊGTáO;;;qý£ŒíÝÃÙË™“¿òÛãüùóÇé½+ýh'äïèyh(qú™Òc•ï¯pÿj DŠ}Œ¹sæÌ$‘Ï.bX½µÏÊïY›››0ûC$!Ïë|mm-666(œÄšcíP`Ë.igccãÈÞPÕ& èÀÚÜÜ<âz”÷'ê©T÷$ðʇ³ÓöQX rûÄ­ª¾rS¦@•ýÈš¸ã¿‡žÍ1.ÒÚ`åN«ÉÊ”ƒSìàßkÖ®Keåh ¿ÿô§?ŸûW¿; _ß8qåK¿&Nž<)GôUó–ý›À³`¥ô¥™òÚ¬®ÜÃØ©VH°ˆU¨ g ׊<:ÜzÙ^•ßSúø®ªcX<Ë|«IAl Hi¤TD* Ÿ•Ï tŸÐ:`p’Û=û\UóØ•áè¨Ï¦&_Ù3ÏÏi`áj¯ŭYpË.!è˜N§±ØÇÙ'¦¼¯6¼`Ù;wâ“¿óþ¸qç‘õˆí>öÈ©xüñÇ!ŠQ ^¨œW•{ÍtBÚò}Ž^Y[u=JiMÑ9æ«’,¨WšÑU¯ÖŠA‡š'¨d°èwÜà:0Ä—¡T¶¿¥´Âöª|n×ôVr¹/'Wó9ñ¬:±CZù+DÞëy~ÔmU9´§Ýb±(¥üìÔo\¬ ÕW«X …Ó¡“kµZÅýû÷ãw÷wãOþß[»‡AoDœùšÿ<Ξ= ›:*K«K¶ˆsƒ [±L£r8ì_¹ÑWD³*™M%Q5Ù•C%“UAŸi¥£@Î,CW™óS­4Ê#1sÐ~v…’MvúêÎ!Ë]î7¨ Ø+]]©b˜lƒƒ~TÏÏ­gÖWT‡NFAÆû5ä.~EN“©?"Å2V¢¾àˆ ø9ç¦2Û þ±},>ýþX\{áWxÃZÄË^öp<õÔS£ÚUW•i¸ÌNEîô8¬×,ªHH¨Ãɰž  Çàm 'Ñ‹ŒSÜô,j~õ@.®1çªt¿Õ$dEÆ æ ¾B¹«ø¶zW5]mTª” #U®­2UŽdDL@ºN¬ßˆÖŸ{6¹ï“ŸÁ˜¨ȵ50òi˜¤¥žL&«%%ÂVL±ÑÁ2ŸÏãÞ½{ñ{¿÷{ñ‰ÿýˆO>·åï¿ÏZD<þPÄcßü7âÒ¥KGBW uU.{BÖ)^Và&£ÜËŸ¯|¾j gn]•>€;ÜXfî**þË·Jpsë5cÛ@Peÿ8¡5Ôøc™fî™U§@UŸ¨‡Åä /Ô¡_¹Î¬}ϪeÕû@ˆ‚O[ˆ%Kµäƒ­i¤‚ÛÞË¡ ¾HÕŒe›c`Gš cÁÿÁï-í)ÇDÛ$ÝÙÙ‰Ï}îsñû¿ókñ̇ßr³™ê‹=zã£ç#ÿæ·ÆSO=ëëëPme.P"ùbWR+uÍžLܱdTPÏ+é« Ggúàd'T…ãøý=f/U œ« œázÎæŽÓÈe“Åì°QdçµÊî“‚Õ³Ë×”I øOáÙN]”í%ÀÖ“°P^íg}F2 ª¾òzÉ={n«ÕêÑÑÑä"{$ö`g{ƒ«“¿?j#Œª|Ï=÷\|ü~?®ðϽqwçð¨ötqùlÄ˾þõñUñ/ÅéÓ§aIìÊÄJuQÅ!+Av°ÊéãWt«"g T \ôÞN\LaÙ WðUÕpAÝë*$÷T*r¾.«vГPГ‡Pʇ•¦¶Ë´+ö‚ªšV’=†'m&Ué 5Ã3Ù‹ Ÿ9†N~½|H mJÏœBØåÓÆeµ0ÓY-#VóˆIĽ{÷y’f)Òñßóùda¬{ÃIóøè¯½7V;wc9Û‰Õl;b±«ùvÄ|'V‹Y¬»±ZÌb9›Ç*"îïF,Ëý¿£í½›N"6OE\y(âê·ÿ7ñ_ñÃ"*Cp¸32t~©ãÁ(—ƒÜ¨Ý.ž‘…¦D™À?WRº¡Î¹ÓíA̦úP#Z%ŒZÙã¿é›ú¬®cA¼¢¦ª0ïê!uØŠ­kõŒ;D½ Ô®SÙ;cTeŽ¿ª ÿœQg–_ñhL&{tÇêCcS‘nÄ7ŸØX~¬öþ·\D¬‹Ýˆå|/¨Ï·b5߉ÕîVÄb7–»Ûqwïµ·÷ÿÿÞЇÞñ˜Æ^@ä|Äå×~güû_ý-ñØcʼn'Ž wª+ý˜ê ÜËò¨4ß*ÜkÇÔ@Á¿2©yœiO‡C"Ôtë¡·±lÈeÊΖÐIW°cõYUÖ®šãŒvÊ`ǰª2Ø¡À´ŠªûÄ6*˜WÕjÕÂ`N…Ë£Á“®Ù`—H˜Y«FFOÀ¦åÂX.öƒú2¢ÁbµÜ›ïEðÕb^Êö†iĉµˆGÎF\ØŒ¸øuß_ôÔWÅÕ«W¡j"EK¦¡£„g¢ãʨ†N¿œ-dË¥ z(Ñ…ÊΪÆ"* ì­ö{œIWÌ«¢UNî‚1ôÀKú_Ïœ îJ¢»Çß¶')©ØøU '7¨ÖS­*š´²¹DƒOZ1Õny Ù a&‡EÉ‚Wû‹o9Õr¾À—óY|DÄb÷N¾;ß Þ;³ˆõˆµÉžQÆÉ!bãÊ“qòKþJ<öò/ŽG}4Ο?  2?%[‹ÜOÔw›¥2èp<†1ºLÙ•Þ¬WÀ‚ºóêdÁBá“HK±ªW•ÃÊ«#vƒ—*kÉA0h°Hy(† Z¿Ùh¥"Ÿ­˜Z• ¤ª Z9Ì™ïÀKM±!“QrœÉ‰öø³í÷ÔœËV sÝV˜á¸p˜UÛ\E_v/mˆéCëý?«ØwX:y&V‹yL×7ãÌúùØX?“ÍGbºy9Ö7ŽóçÏÇÙ³gccc#Ο?ëëë1 Å–UÈ=p´™©§ æ2L> ´€z3ÀÜ uúòLpI1k¤‡z=Ucój EÏ£u¢©Ü¯êÏ0qTʳL¸GøUàn²ºÇÆQUŒ¨éWi@2\Yù ³Ê®"Æ×£ÈŠ*ªŠ *ƒæòÞ@L™ì\Æžé¡ÀÞƒÿµ’9pŽFÅ™‡™?øÉ“'ãÕ¯~uìîîa»dL>HòBÛ?•ì¼YS 'S¨² \†Í̪½¹ÅÑéV„ݵÿ½¾pØ®;ˆ*‡ j²±{©î© Àh=æ^ÄqÁŒ5å藕Âe¸Š‚ZeÑ C… æ9rEo–®a÷Ìž­®Ÿ­ÓLYuÓ³ŽuŠ„Ri\O&“¡a`¿ãn¯¯¯Çúúú¡¬H‰Y±Á `3D¡Xí è&Õd˜ë®»ŒÊ ¡ Ù^¶XZô<0†œÐ+ÙIO`Wì Wò« ²S­ˆ€¡Š(ÿqŠ£®tw½ÒqÁ¼§Øžtû_U¢ŒåžAOï¢×3 ÂSÁ˜U ÚR°to¿Dí-§eìÊI™j°‹myíƒË]ú*þ« !Ç[UÍõÞ*ãh¿Zìj)‡;'¨¯:ï.ƒ<FBŠt®™[ÉV*Bih£W¸Ç]~5ÎýR*/÷³=MZ—…ö4uG­ƒ³*Á»‚s£J±2œå¬9•4±ûùJ8ïCäÿàÖWåú´‹®KI13ÖÐÐÞ|„ïdÀ^ábc`g™+3& ÃÑXPg¸Ë~r7žÉ ¸fcã‚©*¡4À”?Úôl˜,+ÆUÆûÙÆÉ5—!9ïÔžLFaÞ5I5X§Ö‹@×Ëz2Õì¿w06Hå>3H÷ŤÚÿÆðsÕ ®âê r,'Weªÿ®|O•$ƒì‡«†v¼ÕáU :pÙ˜Z YÓ½gré WŽˆš?jÑUyÑîÀÉAÖVùýFŒúL¦´Âh`™„m«¨/V ·ª¹1²Fce0rtbJ×;pÉÓÓg¬¯Ò»ÀXÉÚÜņ‡\CÑ­ê0;¼Ýîz[¨ª®6œÝ¿•„0ZÏÊSv`ÙbåäÊMNÑfBÞ—UüT•Á¨2¨8SlÜ{2ø æÞ>`ÆÏГÿe8q…—_É„z™D,Ëî1©`¨¬pp€Ó¦©và¨ä¤"Ýê2v•VEâT€GëNAtÕá(wÐäÆ¿ƒn+ÒŠª)ÞË cs1}_%ÆUX°ý½a±X@ ×ÔÈAMH¡²Lì:lL-ZvmÛ#ªFš+§J–çNm6¬’3á6#F{O4òRlò*âoì@pŽ.3íá#øÀ5Å*`GgCN‰Ïõ4seV…ªPEWÒRp£m*3f75ŠÐ£¨B¯ìMÀ*ì¬ÊÚÏ„”}/—ËCpgÖ‘ $ËXó1ÛV1 çŒ9öhi¸2¬Ò´ù|%4ÌäDϪ u eLVÑìœ f%Cí màP›’9¨;5Fxz0ˆ±Tu­a÷A6ÌÈ™Q+ò·U¶£ "§2Õ„­ÚãU+§*[¥+¹ Õݻ㪤²Á%]î WÏJ=‡±G†Ù)b†áñ]8‚6PPAx&,˜N§1 CŒUƒ’uE<ÔT°¸XfÈ4’A 'hÄÊ2W‚÷Lê1‡,W±TJlåÕZýR”C5Òí6›3Ð@4RY¸{‹$Ú½ƒô‹ÐÀ ÀJ„Ù¨±j³Âòq,«ŠÐÏæ{[Ù8¬Çt»rõÊ;0sê6¨£9&³í®qµZíiÅ䎵ã]fSD]düVÄ$`/WYÓ…ešJߘá‹UlK5;zˆ¢ 2»B5Є¤® Z©Xk5+Õ•k>Vıê\tª}” ö®˜-ûFfÇÇÍp+’LiUÜÕÃÊÍAô6ÀÝ¡åà¶*MYQ‚’·FÜÀc>Ø À(Ñ8ì ÿFZÊ R~Ÿ,cb f¹áYḛ†Q•BÇøãúPÁ×-('ÍÉ6Quƒqæ$ãXÕÎÜ›˜ß(Âü™ÍX¯dsî `ûÀAmŠê™Kïãö+*ëÕeœmæ^ ÒJ­ìtïs¡ P÷,{!¡ª3B5Ø«OvX´ñdDST¯­MzÛû4 ’ (<©]ßdSX¨ìUª~Õ@å)Rq0ÕI¯dìUhÁÁJþÕoÖ¸T™$“ÙíaeA‡SfN¨=£ÕU>V½1£UÙ( -ƒoz-7˜ÕCIfxµ#T'h•q¸‚ÐŽËb«H ¸þNµâ¨VðhÎÁ9Ò)Ôbèy@ŒË^{í¥ðTºË=XƒƒËÒ’wl‹žÑs× D|v× D Ae…èó³C'_G6å¨f‰îðɦÍë¾J¸"KQÉÍÏix0Ú)ÁW,'(o× N¨«Ž{Ý[%T(ÑŠä„à˜èª’ªj½•rŽ“ì=YbD¿X|B÷r襹¡KÖ2–×ͿۈÕñw5 Àš®¬Ëö•é-{ØŠ¿þRŒ¯Ùçï}½ªæ4úY÷|ÙP•ÂŒÿÿ¥Ê¨ŒUMæJC­‹Ê´³¢Cºµ•“& Æ$Azß=¼õJ”ïWÕÛ ÃÇ­HŸ 1±¼&—TÒN tŒ«Cµé§ô)\àC‡@Ëh>Ûó© \•âÌXe• Èž1W ˆŽç²´Þ¡%;Àpdup¹f/¤Ô#ëôiØðN/¤Ò#ƒÀ®õŸI ¬˜Tl…ãžÔvÒ»’´GâV»»Yu8!•J'ËP1驈TEÆ*#EñqÄú«VEˆ530‡Žãdê$cÁ5ÇÖk(¾*ºÆ¬ºßŒs¬c{â*N°Â{Ù¢‚’Ò9¯²$ª¶`Ž—‹6£û]Çêp:óGÍ7¥äM–¥V{å * ƒ&äÆÞËIçº ´7ic¤ãé·É3™pU³2ªf~ÄÎt¦*ोH-ä鿇˜8K´»šCöpk;ÎX7 ‚w¸¦jœUÆ—ƒ»æÃ¡gðÄá¤,S­ÈŸWÖÀiï ’‰ˆ©ËFÐTLÔCiq¥›Óþþ(%­Æå+cü F8N£œ•ð• ‹ÍŠ8“ˆVû=·wÚ@¬¦€+Cu@)#–ŠI…À(ÝJÌõÅVqõ”Gÿ8ó®™¶xUŒÈá»HÑe©=_Îͨj?WùœÊ¸*7‹6*3êîµsAJMÚ±¬Y%¬Rd’ªÕ»bäCõbqIåðF´àÏÇ:UZ/L×ÿ¸Fë½A½Ç ÖÍ+ »^Ú­Òâ©B«h¨‹í1EkTëMïWu•̬]–Úsú±à^}° ¢`¸Ãã«l‘JC¦ÊŸ)›ŽìÁ²i_—ÇùÇt'ìUiþ¡gÆÄær#LñìÇM5fíU³õê¤-¶RÊøœ²¢i•+îâ ß>﫞†fE#^eÎhm)/ÖüyÇû§d®ÌÖJVW¦²Y‚âú8ëF’­ÞM+À’›Jïýÿ ‚c°ljÜ€-Žý.ºQLíQ5¿AInrSÑQ–™qN´QØtX….õR¬½ØÀY/ª¢Ç\‡Ð†R&".co=X³²mÊ·ËÁd"s‡›:äÙõ¶ãäÕd¦šõªª³Êa³((ɱ¨RihW³YV9õèÛ8²„j;KB7K“Ÿsi@U‚̆*væþ; à­Ë6+mFl3ãS•l³2h€Tóˆ®òìdêF+x¥ºþ”? IDAT 19|M•ô¬¡V…]TÐr”“aFM!·ù+G;—Á™ìÒ6hå,*Y¬Ú¤9;Ëë°_(ÓðãT‘L£¾Í.‘ÃKð…žÞJ¼˜Jb¥"©TæÙvRU£hæÃõYÚï-‹#Y¾\‰•¡–µ1 f惘¿7ööûΪí8^“ì›*¬1UÉîªã©*lœ >¹Àp–€3àîѺV¦«î2Def­c¬2­0%Ôì†b °Ä$¿¯RzUf=ÚýêÐDº;U3‘Šdª¸ÔàSE—%l¨E ]>pQ‚:°‡Õ{ÓÛ`„d˜Fxk¶"²ð×qš'íÃgöRlḉòPÙZ1BpMàJ0BZ+G4šx­ÈÞækQt3—£ûÖfjIm’ê¿‘Â'kÞ£gäèŠU â tæhxJ"B?T¤ssW—{îî@r84ƒAU%Ž>/êw¡„}~'KÁT•Ñ9:xÑTê!V âk+nk6ØÈ7(—oíiž/@ &U Ô¢©þWnª+]ÙÍw4ÍŠ¨TÆA&§¨¡4ª.9k:²Eí¨„kkk‡˜>oÕårkkkp~¡š¸LU¡9 TÙ±‰Z7¸S©ºœØ2‚pUs›©¶‡éø¬Ðº¬È{¸þ…’TèÞr‡=R˜­ˆöUšé¨áêz Ú/lžVG9ÃVMÅv(A•B=‚XU;-Æ,`l‡Š?%£!V5«]3¦ú»îÞ86 Ìã1+/2Æ «R²°Q•Šˆè<¥‡‚;ºîŒ“;A4UIU²þ*ƒÁ{ÎP¼*\‘fÕ”")°õ‚´ÆYŸÅUT=0ZÛlNAeïèóäyšv¹½æ’Dç¦ Fø×쫘3ºáø`‡a8$Õf…m3Be˜*ÓeØ?ë¦÷á¢mX„‰WKF6UWÍ,*îRU‚e-μ)}¢nÖ,tf†¯*ö–jz3(ª*ë[Éò]2ÑÛ\¯òÁ+~¢Õu¯z3 *©L<ç„õ ˜€bi±‰A2¬_Ű•¾_ÞnŠ}ö,[¼Ò{}8„'±†«cj°ÌZQ+¡ÿr™x–+v¸¸ lì}%Œ•o•{Í5 U:4í'º¿•,FMâ!¨‚É@ôÈ´Vô@z"J>ÔDm¥ÙʲxÆDªÌ¨ pvM=2qqür•ÀõTÕ¬öU6qa‰‚²Ø XNLœ‰Œz¨×#;ÊÀ3eŒe®àWxtµDQY‚ƒ5ÔÂeF¾ªœcU…b†(3æ±Z "U½õy* Lu=¹Þ>ÿŠ. î¹TŒwÐÚm›dy¢µÇ€\%@n°ÇÏ•IýV±eöÜ6Ž‚(Ù:©©1LÙ¹©¦²ÒŽwÕtoBPeÝ Y¹&1¢ë²ç;äR–Jmàcì TŠ#:Pû»í0ò³ÃÝ‚Êlt@°C•ù!:ÚcU·Æe­Š˘>UMf­¦ÕÏ´=*T<WU}n ÍJ ‡1—ÕUZ[N©ô•*"yŒ€džH—£ª:IGÿt}<×`É/3ŒÏûJ%7ãëŽN—Mv[L1'ræ†:èm6§®¹PÉ”ªMÇJ§½²aò"®Xñ¡†›^E›¸íQŒÓžÕ¬­¨’ÉI0…Å¥‘ã §Ÿâ¤h+ÐE¥q©\²T²R©h\s½7ëì5|VAU™Ø«¹õ^n^¤Úœì™ÉppWSPbÁ½*m¬DµÀ”‚ée%T¦Ñ±ÌVQ*‹ÙA%î&U^Wa†ÇitöâùÌ…‡AR.€;åIÇÍV4Ee.^i,;%G6ÕȲiU‘¡FWuÚlòò¸ÍI¶/«Á’86IYϯô/*,®¢Fk:cÙȲbµçàã‘J[iòçÃ^I· ZUi2ÇáAm~¼•ôd'Ï‚ UNö*ÎéJ¸J6Ô QTšQUh‡mìŠÏ'¡¯ŽB³ÑiPìä°p–A)¾6¢9²ê€  T ¶Re{¥bìq$r}ìU¦çÆô{¿ãG=¦–YUU ˆèsWxâU]ûŠ/´ê%¸A6†&0Øg1îj7¿ ¼ÙÁ+n`qE™øWÕ73w¥UêÊ8׸©šWöð˜QAÏç©j‘¨ÃDA'lVÝC6°¦¬Jö¢Ôñœ+Žz ÖÀTtM5x䪆\™¸êÃõ-Ô^bAÒí·\¥£ûÀL/”´wûYQßHU{jß:{óöÎÓ¸©õL ÈÜÿ<ÖþN†ÀѺ>Ø+'Cû÷1p"q›jd«e…áW *¥»÷V^ž-%Ö•¿ «w2®•Ì®:ˆ–×#’b‡k V~W=;d¢ôvª\ûå`¥º‰ES ä PÙÛª:rÍêÊ<’“vχõŒòŒR·²Ìn'E|st¢µZë•QêŒ'Uéf JªdU¼›=X'qªNì—¢¬¿QÉòb¨|îöû·ƒÆXÒÐ.úLñtýL;½ T0ëöPÍ#çjÈ ›Šcϰ^î19.¿ªˆ{«Î1=µ‡•ˆ‚ž¼ÃœÈÔàW…ªª©Š‘º‚ïrB—¯q„ÐÛŒ¿=<w± vJŽ73O 2§Ç¢2 ç“È $ôy™¯ kܲÏTmÀ±F™Â£îÇšP¬LV#ùj²CéVô Ì4„ ¯´ëb¼w9¸÷ê ¡A%µ€Ô1£Ã Îõ0hªöƒÊp¾BɬV2 뮨/ÃÐ*ª´h=T¹½^¹ÍI€’ÉpƒŠ™=ØòVIó@ŸK5\)Òî1+o@ÛTUM%¤Œ²v&Óê!ÕN·¢UM‹Yª"€tëK¹ÍR€ƒ®zš‘yq÷ZË¡’²ª+ä×Tsv}jm(¿Êã\_E&¹"qíaD/F –éöˆé¡;K‚P¦`¼Š·±³[¬>#¥3sÜçízR¨JE÷"Ã;‡{ex…ùˆ¶r qb5Õ&DÎhY£‰)L¶ÌŸ*¤£J%… :yWe¢ š>ˆBVáî+‰CwŽHj%¥˜!*çû®XI¹ÉÌäO™ë“ueˆ=Ÿì€sÛG¶Ï\c¿ÂîȰ,JXß eÖìp¬ô°ªrÉ•¦nï°cØU÷„3GW׆A]v€±«Zq;W 9»²;–€Ãª‘Ë’ È :øÄ²üžlÅÑ­”1ƒã>»Î¿b´(( Rý(óãž/T2W昹Ku]Vbáw•Â˦fQfËàv€1ÊÁ.í„‘DˆÊTãÕ¡ÂÈUâÈ`YäuʈLÙÑiE9£uG7‰ˆ2f¦wÌä\ö¦Ê©ìªe9ËÊm³7 õJ¸¢M¨2ꌻ)XAÁ5íçG#Ð*»¬âãÈU««¸f&£>2Ȫǻ•°=¢\ ủ fìú=ÎÜÃ3¨ì«þUUË¥"U=*÷ÙUã•ßWï¯ø÷,ev„Žñƒ*KTa,«FÁb4œF0©Êj:óbØU³gmm ’õ¼— Uk?”á:¾j/à*[¬LÉóRŸ³úŒòÏ:¾9»ŸÊxÁmx,zû4ê <Ž ¬Òew+Æžê"l΀5Ò+û­†b‰`†ÈXršl¯U®§bYˆ¨½mÜdr,yÊA}mm ö(ô⌓HÚ“"Sð*8°bÖT\ˆÔ)_‘tU­gÜÞvn¬ýÞ˜iWôÐ{môTdš+*h"ýŸJ©Â6H¢XöWš‡n¨L%î3ºl»ÇÑÈÁmÌ@UδÁíQE¼p½§¸ƒ–ùèªØ y/3 5t3’B%lÕŽ=3·2öªÝVuä»mV2ùZ†§£ÆÞq¸°U©\F1ì±™S¤§QËôD²Q‰jèVð7•9˜Çn³&"“‘¨èÚTš²UhDLÅ­–A«&™«`܈x.û]• U©`ŸéË* ¨dÝ¢sŽf(¨#î¹ÓÎApò˜»aµF‹ªáŠhÃhÈ \C%{Ræ®Ù‘;íhã±Å¢Êö*á)Uì®Z VºüÕæZ„Ç¡™1Y[F/t<|…ùª,¨’é¡þr³¯ôXª Û ΂’;pm”%½Yƒ=Ð=eê÷ä™=úD.`º â‰{oãJEA·.™«š~ Ü^Ñqѽ\v3N9øºsÀq§™¢H‡‹ì3¬É« žêdfÞèu…We·²~s÷Ï9ã(5Œä´Ý=¬‚i¢ûX9ÏÏá«=,˜Šó“빸C\5']e€¨qùµ2Õ³‡3ÚŠÛRÏ}TH:XYu¯²a…}«÷Rð—Ú»í5¶ÓãnŸ"èÑÅÙ#²½,sh›¦løÆzÙ/5gcª±Z7XóµB]ê)~Ø8Çiº!ØZ‘ͯÅìùXOâ8}fæðÞö«Bf¹¢*ƒ·]¶·‘©‚~e Lú€U!hhPáÜû`òÕ$„Q(ÌŘxí{£ÍqÜÖXeÕãžÄöäÊkNÍQ ­Ž ÓÞhÕÉÕ ¾UC܆¨dJjÄ^ñõóõf‰ã±Qixôbì.UÐu©a T6«ËÚ‘ XH.²z–W#%»êuÕª­Âåf§r¨©žBæQ»!ÅŠœõøœPpW•²:,FÄ€%¬/å‚¿3ª’&*PÒjû%LŠ£öm_cX,”ÔN•Ž¿8nŠ6ȵA=?¦n"Ó÷pÙi¥<®Ò«ÐÍ?_+¼Óóšmö­!صVd‰eZE¤Ë±töO…I ¼R+MÜÜýÏ.0VheŽ©”_Ó•Â.02s‹îamäêÍIT{3 î ÖîpÌp¹ÇÂ’K„4døÂ5™{D+p›-A÷ =·–¦?R‰l¿´y•È„Ç@—UЏ Ëê‚v†jÓ"Hñ‡<ã&Ò* JÆÆšnùÙU[oÖ›ƒ:Ë`Û Æ™D(X#— ö©@ ~b‰„«Y$¯÷Lû­ìUE¹˜d¶R˜Ì?ÓëcZ±qëÅ×Uÿ¡…%”Úlev¢ˆKE³ŸÅguÉ<€ÔVŒæxØ™ƒŽ{ Ǫt¥{¿ç††rF‚)ZcVò«‡06ŽT÷½ªçšEj`ˆÙ§¡À®‚NUyÑ©zæ,úóå‘É\î]ÞQĪS©*û­&(ª@ÔÉê^PÊ™ŒšYáú»ûÂ$ŠäÀ˜OJC}Në°Á,'Üדxå牨Ò9IF*¡Œbëö/›èÎÌ•µhS³¡VUÇ‹+4¸^á)7ˆ„DË\Æ4LT€b-•2ÚÉÞ¢l Å¢{0au`£C¦ •ÌÚÕ@Ã\T£¼ýÝ­­­˜Ïçòž²µ¹Z­bcc#†a(Á‚®úcxïb±ˆííí#§lß ÀpêÔ©#Áp¹\ƽ{÷b6›Ñ¬Ð™Âœ9sJ%oooò>fë—Á!gΜ‰aŽÀ;÷îÝ;4“yù¹ç86>/upä;ÿÝ—´ ¬Ãz| †vª–ã}ªÙ +‘3ÀV¡*óÃQ¡Ž_eè Xœ`ÁÆ9¤»ŒE±²³R¥÷€ÊFe!ÅKñešöèY²ÄÃq·¶¶âƒ?ý¶xþãù~kh«æÿ÷~çÁßÛW»ta¯zËOÄË_þòCð¥hΣûµµµú™‰ÿØÁuºƒÍÅMÆkœ<øÞÅó“ø où‰xâ‰'ªËÕj÷îÝ‹ÿûýp\ÿ£Ælñàe¦Ñ^ö ¼GDÄ¥ó“xÕ[2¾à ¾àÐzÜÞÞŽ½ãGîë*_ðäð}„÷õ­ÿ ^ö²—𬝷}^ãí¯w|¯ñŽÿ}²ÿº_ñƒÿóÁëVØ“Yn¸½¯íGÏ÷w’^3ö¯÷•³Ù‘l|¹\Æòîs÷AÅÓUóz“ýû2_®b>Ÿ—5…X$'µˆÜ¡èŒ=Mv¦7Ï`§i58‡¤\ò¶ 2ÔLe‹[]%]«nºƒ8Ø´$c!¸ò©3WôDVZW”Þ*¬!×ðE,$禎pôêtîqï¡‚ŠØµÁŸmÓq!rPŸÔ¡¤žF!:ü$U4]Ë„>ã£ÔC¦æ9i"¾‡ÞGöeV{òáÍ-_EĤ‰Â+TTè‘­"V“ÃQ~à›œX*:¡šØuh€Ú·(ðW½„[ż¡}ð¨ÉÔžÐÊá†e"= w½x¬‚2•/´ÌÂP î6.ê;F°ŠƒWªj”èz˜+U•í€z™zæ^G5%+s™Ö× Ò-Wã†ß9 YP¯Èó2¬¿¢2™LÞ{"²óÊAH¡€Ú¹vä—¬ü„ü8OWæ{ˆƒg•´ñ™ÆþÞöÑG¼ÒÿSØvUJ@%JùÐPÕ®ëåÏ68Çaè4"â­#>æBï ŽŠD3˜p2Án¯’|u\n¦œY9 *2ƬÙUÍ U¦Ž&åÐ:QòÃíë³ì1eQÆHP½¡–%´\.c²q)®>t= e‚=ÆŒ®]M˜:¶Z;kkk±vö‘xâ¡ë{ØrÓ˜ 6izÏ–qmŸßÚæåxü¡ë1[àÏxèuW‡qüË¿î!ƒ“Ëqõ¡çb¾Äñ}•ñ’æ¯Ïc׫étÓ½ë/¿ØŠ½~óy.ÇÆ+ÕÛ·J¯LÈTzv(c?K?ö±­T aÓ†h`…q³ÑpËÞhÿ^!í»Fi qA:&5IX¥L9ͧ.¨¡Øu° ì8½N‚×ÑÑófF'y¨aóã XÖVÕBníŸíímè%Ù~–qÂ17µV«Õö†³éSϽþÈŠÙÚÚ:Ä2Q%úï§OŸ>Âɬ ´^Кm¯qd™ä}}ïÞ½<»?å÷ïk{ÏÆû0ŸÏm_ƒUvgΜ9$m›I¬Ÿ§\™Òsoûxcb×^K›$äŠù˜²A64[4þÎàÊT³ŒŠ±,òÅ3Cf…+UM˜S‰jÄ)ÜÞ5)zÜÜ•ŽI~0•RÛ5ë†e•1y—U Ý?Ô8s°\ÏÁ5nª³gÏJ½Žñ@i›„¨ÑÃŽB×§ ©µµµØÜÜ„k:Od:H-oøÍÍM)ÃsÖ,o_cccÃ¢å ™%`øð0ÄÙ³geŸ¦¢ìéàÚÜmå r£¸÷:XTI[3سŠZ´kvPà •¸RŽ8¨¥ÇVËm('ºå ”ªyse÷ÙŠ^·s(bµÂ1¯êN+OžTíe)hÎU@ŒaT‘?PåïqìÙÜFgYµv\/PW•¡¡³Š9 ÛÛJw¨:ñªà·žf{â¨8hõ$T¹:S?¯ú‚ ÷Gð ÄÔ¨.£ÇµßÏ¢Y®¤B^y}¶¥­:Y+A\Q£Üâp‹ \¨LT÷A !¹ûå‚ÒEwŸ»¢£`2cõ0oTÓ‹õJªAß%꺫Ž@Õ÷íÑJRM¹<µ\1šp¢d=Ò¼•Ÿë}=9ÒAK„Ä^WUh*1b°£›¨Í0 ìš]Z]µ[\õ‚DŽGí$*W]B  ΨÅ]U}«–lÇÉÖî‹&a«A§"ÿ‹ 1ÔsF­ž*=‰|UqÑj¶§ ¢UpuÐMå sÌ(ö; óg}vŸPÃÝMG1™HTÂŒ¸’¢œõÐ Ë”Òöê` 'd†¦2¿p‡–R“Sî,ªÔ;<†N}•AT8ë꾡,Ôõ+Ôa£h{®é¤ ÝaYÎbð^ïä%3—P‰OE©•ñÐ+ÎGL´ŽQ™{R¥É[ÙN¨°âShIû³ƒÒFZÆn€§Çm¨mše~¬‚PsJ‰è†"oÂWTÝØqUZ·j°üR(`ì@Èïé(ª¨zªLC2˜õcÔa&-•ÊJ1'ªÏ«ú^,gHÆ}%¾†„Î*‡1«ZØA‚àBµwœÎxuÍTSÔ¸f3;½ïã TØ!«Ð…<-‹šùÏÏtX,«½Oì†gYœbJ°, 5a•!3Ê6ªš •ò*w²3Ñi!Wøí w®° ²Z¦rr°+§Ù†È¯²„ªU\ex¦¢JÉ(t(¸;œ_‰8ø­ …CWƒ™jà³ÊÓ#g1XÍfQBMC/T{ÕŸc“µŽ1W™‰`¯YYÓãZ,Gˆ¼ïz€È «Õj5Ìf³EìO *,Œ(äM¯0uÅ®aSšÕ`q\žèØgž½ƒ(œð‘5,(`ªùÞ³æX¥ò`MÖÈS£æÕæžÛ,á-fæ­¤ÛÏÖŠ”UûílÜÕ½Ræ.. ©¦r•¿ÍѮT¤,[®VÀ•k¬Î¸9VxÿÚs‚¯3†}Æù|¾f³Ùî‰'$Åë„»ryÎ4®ÛSÓmn´‰X%Põ»TÙjjôˆP)¶NµñX¥t)Í|¶±Ý=vŽXlÙ)U:ãñÊt›UYŸ¡û¦„Ü¡¢‚‚ÄØ#×Ãêr² jÌl… –úÙ^w-u¸*ÖT¹Á<9ÇC°ÊHñdÕ›)ÈŸ}>Ÿïóù|;"δq.À£ ”›‡Za%¸ó!l…ê+'¾Ê(óJ@¿Ç s£7T8ûÊ‘EU¬![ÑTq‡†3Úp™F•ÉR}¶•,<3*zFîY©\ɲ™vbi(ýt 8<­7EÜ+žVIjŽÃìbÁÕ1ÏcÅ V9TÀÝ tÿÙ 3ú`ñe¹\Æîîîö°X,^˜N§Gy´X²4$ \å¨SsIDATí0Sû§u$A™ î.qô#§²§pÊ–›«š½ÕŠ¡š5T^Á=,3d|óJ†¥ô\°CAH}>g¦®ÈÇÞ\R,cL±Ly²"¦‹ÂéYÿ¥õôd‰„“`ý°žyµN¬ò,Ú¬¶‹²@‡©TÅY±ít°µ ¼­©ö¹¨F8J¸vww_æóùµétúEq„3Ô M™¼3ฬՠȇTu¢5_W+»À6²=ï1ì`YžÚhd±+z„¼Üáäf*ÙYÅþÌ ™Uð{U%8ã—vS²ÉMU¢ûàèmò€DúTul5=0Š—ÊÀbS¸jäY W…¡Ví÷°^Û£nú:_ši»˜\¥0þ™Íf׆ÙlöñÉdòu9£`¿È>´Ól`²µ*›pl›žÌœuëÑÃWj*¶ò¥LÁѵç@]áG÷T ìž*%¼ c)êªóMÅ{enl.ùšöôBTÜÃæi ÎFTœ÷ ¾}\HKõœ#˜2éa™y…u㈊'_"«$[ ÊaÁ‘òa›{ž-Áéµ?»³³óñaggç#ñýŠÿšÍY3”¢øï Ö`*³TzÜUòõ+޾3ëîCÜB©¿ &k©#Ü­m"z"+‘+ÍÙã2ÔbåWf(¨;úf»®Û{9®Õ<‘ë2YöÕÚW„7°¦¸â(ù`œx¤Öªˆg!UñºƒÐyWb@åZ+MÖsÕ.³ÞChˆDSý—öß»»»f³Ù¯"˜‚5óM°æ'ÕðS7ŒyjªÌ3í¶¯é8³J€êTgY{µá¤0{^Šg\½ÏÊT¥GyÓe§jÊ´gUT?Y̰ó*«ˆU},à1>¾£«àá²Ma0‹ÛnzÛíg½ÉÖš Ò˜·G Øù£²õŒ†;•Q5àÙzÉãÿïììüê$"â7ó7ï>}úŒRék³Ç¶dI‹EÌçóC?Ë2=”-¨a |ƒ‘£Ó´QY)³’k³ºŠ}`E‰ÁÊRج£®µ´5&.„ªV6#½ÄõE‡`EïQûPµ¤š]ù¹e¨ÁIU<ŸÁiU†‘¢çž”¤A |ÔR‡=‚F+†Û¨Rb×Ë`Çö90·¶ÌQ°Z·Ê$¦’˜´½5ÄVAëiþ {4”¬­­i¼O§Ó8qâìsdVßt:û÷ïo½æ5¯Ù""nß¾ýÉS§N=U¡)û2€P¶_Áò«š±Çu»GYïx³ÕgptÉ^ú#;¨;¥·lf‡ÔqÅ›XC¬çˉ¿±òSý÷^¡*;Íà%¥Úk”Á*dÕÎ2±ÌŽaÜ.‹dAð¸Ï\5Z+Fw"o˜û›úÊ÷/gæè>±¤–Tëí¹»wï~2"ö”îÞ½û[—/_~ªÂXÈMVG?dC"êf37&ûTz†®¦¾G_VÕ¤S*ÃæXóZ5)’ÅÈIÃÝ»wßqð¿þë¿~ëìÙ³vÇ6äø³y( ñp[®<•mq-•]»&ÊH'm2d‚q¼ŒÃ£2QEÑâÈÆ&(P"û­üU#`%#̲od¾’)X¨ìDÙxÆ.™ÑsÞ<ÌèÃù›öÐD3ÉAjŠ"›{+jÈL=*þͳ¨¢lM¨Ù”8rÿª¨9V 5–¸T,5+ £Š;³-dωݛlhÓî“ñûkkk1 ÑϞ×J,Ñ´óøs[[[/|ý×ýC{DÄ‹/¾ø;çÎû–6@³²ÝH¤5ƒð»ÊIËÊ#—™góVY®¡VÅôÝkäÌ77ùrã™´íóh7­ƒÛòýÊVÔ”t=³T*¸LÕS*z 2`¦Ãd™ljOÆÙÞC$ÔÚAºª Ù«'ĨŠṲ̀R-õLzV{8ª’è©1ÃYj2Ø QªY3%%è}ÛC1aÌ}ëÖ­ßßï °ß½{÷ç'“É·äL9³(*¥k¥,fS‰Ò©ÐívZñ0TL7•È •¡‰Þª~»þbA7Ï% *ˆmÜœ%¨©>Äì@¬–‘!6“ÛÌnÀU™(vd…Y³¾ ƒ'+{E±˜üRé)*jEÃÝ »„AT=êÀBÏqÇU3’%.€£J¡Ê'—ùd(ø÷Å_üùƒ×k/êÃþðó›››‹ÅèŸa´8Fx%Ó‚òCl!š*†I ßN·0ÙT' "®á©´¥Ñ{ n:‚pÚk†á+A6H/SQ¦¨ÄÀ*ÍK'¡€‚ ì ºr™2˼Ü÷³ž=ƒÒS+ñÙDk%`©†œZAz@ŠŒÖ4‚™re¼_-l£àQwää‘=6|æX.(Éd𦒱fšîU10ÄÑ£ßc¬#ÖÐvkæZ£F.³°«è#õ°?Ða¢¤¡ÕD®;˜²>KÖÉaÒ Uf«`Øp)dD ±!Ê ´][3¡ª¬’âU1$·nÝú@û³‡ûíÛ·ß~õêÕ7L§Ó5ôpÐ"AÎGíwÒ¢NŸº"vÄn03¢@‹G56{¦ÙgB M—å²&µ£}© QWÕ†DdƒfY>Úˆ¹úr»‚ìÔçf‡Gu€Š¹^1 ¾’]W–¬¹ÂZ{JšØ±D”oAåó¹)Rô9P%Å4T?5)«‡«›Ÿ`z>ì Ap#ƒ‚ÛžZëý¼X,wîÜyû¡ëÍà}ï{ßÿóØc} êt£©¸år³ÙŒ:Žä`„¦¸TÖÈðb¤‡Á2Ë\n3æ‚3óvÃQJ×ű<4–½mC›¹¸#Ê(\ɬ˜3*à0v…ãŸg©a×TD[7L<,Ãa¹äÎ,Ÿ|_ÛŒÝ#J5™cFÍ 0øeóèæŸ¡Y4]‰Ö »Ä:A{jÌ´Û¤++Ï2)ü´Sa߈u‚î-š˜GLûˆÝ‚¼,ÆýŽàÁö3å*òÙgŸýÍ×½îu‘fìûpÌ_½zõjAU_{¹«ÛŠ,¡Æ_eÚQ© ö–¸¨*@Ù1SUs”*g\«²Š|9ö»OJv±N*†",¬0 *lg¢à-•y£F˜b‰°>ÓVbP‡Óga„‡;hÁIU" ¸ãÁ¦øî*É{žõxÐ:vÕa¾vÕ{sÄ ×e¾zO– X†B¨*"îÛ·oÿø‘ŸA¿øþ÷¿ÿ_\¹råÕ9h!˜V†eµÇfz «dŠaø9ƒb'fe’R•–yƒ™Íøà[.wþì(CÈôº6ST4FÕ—@r¯hºÎÝ}°Œ»‡fÇyfÍV©"XW‡†“@É¢³€­pw%±û›5r³=Ï… ˜­ÿ Š¬ýj3ê6)A™'Z_Œ˜÷Œ“]FÕ‰ºÏª¿”uX éåŠB%bí=k+õ\]åÊèúõëùÖoýÖ¯ÌÏl@áæÍ›?öè£þã xÊÎueŠÖ–ƒ;29‹0ÑåEÈ*€žÀÁ*¶P˜E™2¥fY¨Ó¤W:Ý `‹½bÉWùb}ˆüŘJ(0õè±Éh{0= %žÇ®GQ^–­ ¿EUe¼Ì2P}å@׎¶ghAÑ<Õlƒ²¨tF.ŠÅƲh•$¹)_T»¬»ý¾‚†„¬øêæÍ›?_Ÿ½ñûÞ÷¾_»zõê7äl1Ÿ¤ãø;šÖËåGw"öŒn‰®‰)²‡†Ì¶Õp ì¹aå¥2†@|VUâ:H§:,•{è½,è•U12D#dX7âØ³ J5æ\qÝ•Õ;,ѵVõŽ7¾ño9V`ˆøÀ>ð—.]ú’PLJž5œsÙ”7+šhkƒ›ºjo¦’Ç­ˆøç2/_·z@¨ÆôçÑ´šƒW——m|$˜ÆÄ²TMú²Ï©¯ª¹Ä%4ø£ ¼FÔ{æ€Ä†EX€æ0rÅ ÌIÚ)ÍüÌUÕ‰(w¬ù™ƒöø:mÂÒRÙ!Ì’¨6Qs´J•Ñ#Ò€:ðr?Àaçíýg}ãÁ×’J±©œôŠš±pý¶Ì8kÉׯ_ÿÃ×½îuÿžŠÛƒ ìׯ_ÛÆÆÆO­¯¯ŸRô¨€ÑŹFËP•^Lþùvb–qjÛëë™Æ¬˜äÅQið9Í7<ÓŽæ;+8åA:¾––„ÖbšyèËÝ_挔+…l~¤›€á¾ ¯X2HŒˆ·ÌwùóV(…j|ÝeLÔj¹\Ɖ'¨|‡âà·û°ÝwˆÝÁ` E²˜’å2ÓSÅ\qƒ“ÍDÁd”XMµ‰Ìýû÷ïß¼yóm.n—( ï}ï{ÿá+^ñŠl/4SûÚž7S.Us¦™O&%‘ŠäcÙX.;u³–ÊØuOq‡Q™É´"ÔbÃ7jäFgˆ²I†e·Ï_ñ™L+órTU‰RTt>ÆÉFA0¯ã æû—«ƒLAl©kìY¡õ£&yä“©±íZË2eæ8l?#­%$3öÙ(]«ÚÔv>r¥2Êãà !IED`±(ï§£ú”jÆ¡Ý'må÷ÉO~ò§^ÿú×ÿõÏK`ßgÉüó«W¯~m»ÉòI®ºéh³U==Uã Q% ‘åƒsÙæ ZfM7· ؉1j”ÄNZ[[;¯wÙ zv-[ùˆ¢ ž¡uÐÁÅtÍÃõ *• ù`jt´k•mz'f¨XYL.I´÷v>ŸCšÃÇQ0Ë•“3h{pí~Í=äÕ€ÖSÆþÑLÁx]c`G¯ T—X9†j*³ž•’Až¨Ï>ûìo¼îu¯ûºJ¼ªýùçŸÿÁ'NüÊ¥K—¾Pasùa·™N.CòâUÖa¬ bå»*›Že„LFS‡(¾±ã㪒Ye:ÊAhoÉL·b™2“ÑE¦',{­È8á&5X¦†‚”!:TT`È NcŒ-õr@kµ»•ë}†!Õ:W¼mô³-•ûo –ËîC ®uìxÐÁªômØ*p¦¨ÉXH¬!ÎâE»žÆõsãÆOܼyó«ñºKfîñŸ¾zõêOž;wîË42ÃD±ÚÓ•mD´P•5Xû2ß6³&ÜÍEÍÖãuLÕ `=4ÐÁ8Ö*ÃF]û¶‘„BÅZ@cÛ|ÎM<Ä A?k²²9‚*—Ù%Å6ÊSáÙŽGíXho´û"W)Ù-WJm¿¥M¨Ð$cn+±=­äßi×\î·d( 5|Eùæ÷fÓÍ÷3Ô›ckUHÏÆ12 ÖÞ¯öµÇ?/¾øâóÏ<óÌßxúé§ßUÕCO`ÿîïþîw½ë]ïzbmmí¿ÛØØ8—³Tþ3¿A%ê‚ ó}D§Ó©F›µ2z›ÊØŒ«Ë2ȶ ®dñŒ×¼Kçõ'XvÁ¤_ó3GÆà*cGÏ@ý2,ßM£¡:Õ€ÏÕiž§PÔ:÷ÅÜ—üÇàÖ¨FP–’¹eÔW›"r@ìêù2¡1GAEÉÕ(d¦ôcÐpC %ŸlÿWÈ÷îÝ{ñÚµkoï êÝûøõž÷¼çG¯^½ú_mnnn"ªƒ>XfÃXkd™R¦À!Þr›aŽÙ7›Þê`h!Ê–P ¨‹mÀÉŸ/ÃríºmG DTÈöÚÄ¡hЬQ©¬åÍUjù¾°Ï…z`lâz2™Äýû÷ï>ûì³ÿËÞð†¿uœø|ìÀ¾ÜôÊ•+ÿåÙ³gÏ¡iK†Y"=E“b4<æ'¨¬ÑÛ¡ ¦{G›,ëe(ÍÅf8·¢>±,Á(³b6oÈ”Y "ª™ÓáFWAV±#4„îÇXr6®Ø¹:AZæ¨IÆäŽ]MÊ2q)4Y›ƒ(ƒF2&_á…³û’©¨­fûø;'Nœ8RE8!2T0‹c69S Æ4BP. þ9æäûï(ÙèÚÞÞ~ñÚµk?ñÆ7¾ñï76¿¤Àñîw¿ûo^¹rå‡.\¸p 1]”‘ƒQÓRMËålŽ‘ýQVß ÉÙ¶Ÿñ“³ÃeÀY 6Øï ËÆóßݵ;åH6AɬάX,ùó3sÅçGþ‘ Ce™˸*:ç¬ùŠX1*Pý;µØ> Æ VÉýU æç‘+¹¶‚é†Bd Pe^ãzY(#VÍpÖ”UÕ!ƒ{ÛÀŽ’E&ˆˆ¸}ûöóÏ=÷ÜÛŸ~úé¿÷RâòKì#[æ‘Gù»—.]úB6öï¤fÙ¡ÖtÉx*ÂüX0i3¹—f¢Jy’qd¦ÉøÀ*ËV"UJŸÝä;‰ZŽU[9X0Š˺XÐÊü~Ä$bY°Ì)9ªé@tmJ³ÄQV_Ü%=NyÁ'¬/†z:,¡@‰k“g4Îú lnçuÜbJö‘9PÆž™¶¿¨›L&ñüóÏâúõëû{¿÷{ßõRcòç%°GDüìÏþì—^¼xñ§®\¹òµ,+e‹Ùe€m`oƒªêú·e´âY3•=´¥/ÓµÎY㬣À®šÉ9À²`­š‹NÇ]yAæÍÌ6ˆš`uÞ­9P à€T@sÓ•Õ(È“Ïnqë™nŒâʳ틊a4¢ð©Œ¸…PPEÃ2MEçeet89¯Xt€!|š­óŠA‹‚ó̆š»jº1ÛØtíڵ߸qãÆ¾å-où7Ÿxüy ì cæ>öØcßsúôéS®9Å2vÕÄÊÝÕxDJ;CéØ8£sÑT"+ßk±iØ¡Êè©H )o:ê,Ó»aI‚2RwnC(°¢Þƒ”ØAŽú9ø¡ŸaëÃUÀ,9SJ¨‚Q•šäÍýåçоÏöööýgŸ}öž~úé¿þùŒÃŸ÷À¾Ÿ½¿ùÒ¥Ko»|ùò—0ž¦ˆA4¨ŠiKiæ°ƒˆ*—ƒ‚ân·‡ jÔV„„˜l€j ªÒ’•õ9X"JYËíe} –Y!¸D~ýÜÏýÜÓçÏŸÿ¯/_¾üÚaÖØ~†;./Ld?‡ ÄyÏcÝŒ¡ #FqÌ_Y¾´}U (ú›âg±¡ü!»ª%KT–£ ]ÈX°gcàú,ÂR+L–-²ÉW”£»ö¿+ÆjƳþ‚2ÖF<~ÖÜg=%V£Ÿ%Bèð@Ijò#¶ R mÉèÍ3¨ÊT=ˆŠ“Tb¼-®]»öÛ·nÝúñ^­—ÿßöñëg~æg¾âܹs?ôðÃËùóç/ªL ¢(°3½öl‚4DZæ‚Or`gV–Ì Ël8’+ 5dÄšŠÎš+—Øma*æ\ÅØŒ.Öj…¨~‚›ÄeBa(@Ù †Ìÿ”õ€ÚßG×¶ÁÏÊ{ĪAØ3ÂËÍ>týˆË8û¨ÍÁ?ûçêZMp¢LßA©¹ªÌ¯‹¦œÛÏÈ, 3œ”8“ÌA¿¨R¸sçÎ[·n}àÅ_|û›ßüæùg_ÿÌ{ûõÎw¾ó¯={ö{ÏŸ?ÿš nBR©>æ`Ïš%(co±´àrÀbüi—Ý1½•¶×À2°ã؈µ éääÏãX0L®U¨!ÆdXðFý„y3VJ®¢˜ÉG^/h3çÏ…2vTmªy v}ˆ¾‰ÞŸ5´Y&Î2êœèäY¶²CIös½´ÏÆ!ÁŠ Y› e¥ s¶¬æUÊz0¬Ç6¾þ;w^xá…~çÎ;?ÿôÓOÿÌŸuLýsØÿm§OŸ~ãÆÆÆxîܹWlllœA¥:z0nzm$äÊÎìôΞ›‘yÃä2±]¸m@s]a(ËïS~Í, •¯•i¸0&‰íbb](Ce2¼Š ެÜÐø8êé´‡2kêfªûlˆ‡²zg?¨’V¡0»FôœÐ=AZ-L1Áqªÿ‘÷W»‡G6j¶æßm«çñgf³Ù‘}ˆö–‚‘›S{Oïß¿¿uçÎOnmmýÖ½{÷Þó¦7½éÿúóGÿÜvè¿|}}ý›×××_}òäÉ/:qâÄ•'N\†áô0 '‡aX†aÂ(T¬ÔfBHYA1cò¡2ì,BÕBE³Ùì`áeåA¤„‡pE¤+Ã`…1x£†rûVNTê£ 5¨QÖ¥¼RQA+¬ÂÈ8=z¿ö^¶UZÕé(߯q€AmÓÚ …ex!7òt€&hQ°Ê÷A‹ˆj¬ö•Òaqž¦ãûg¦–šhïÓ0 ‡’–ù|~p8Œ}ü9Ä­Ï à|>_íîî.vvvvg³Ùö|>a6›]›ÍfŸÍf™Íf¿úæ7¿ù÷þ<ÇÍÿ:””ƒö8éIEND®B`‚md-toc-9.0.0/assets/md-toc_youtube_video_thumbnail.png000066400000000000000000004721531460560256400231270ustar00rootroot00000000000000‰PNG  IHDRUàMgG) IDATx^ì½{”åÙUßwnݺ·ÞïWWõsz d¡8 $!I0‹$˜…"Y(€p¼ø 'YIœÀZÆ„8„G0 Y¶ à$x-²$KX6,Œ,‰‘Æ’fúÝõ~¿ëÖ­ºù|ö¹¿ê;=ÝSÕ£nf$êŽZ]]÷ÞßïüÎÙgïïÞû»÷)ýþ?Occc-ímm¥ÎÎŽÔßד‡úÒôô|ªV:Óöv=mn®§KϧTJigg/5ûüÜHµÚnèK%~>88L{{µÔÑÑ—k½isc;µ·WÓà`Gj¯¤å•ùÔÕUI‡‡)UÚÛù\5-.ͧ¶R[êëJûu®±_Jkk›it´#Õë»iie!•Û{ÓÈèhN³ó Ü«”vwvS¥êµÓöÖvªV;ø\{º~ýrzå+Omå¶´º²œú[¹4˜–—6Ò6ßéïéa7ROO5Ž ¥J%qÏÝÉsô§¹¹¹ÔÝÝ“ö÷·ŸïïMmmio·–RjK‹‹k©‘¶{;ÏRMû{ûidd0•Ûêq­™éµtîÜ…tp¸—ökÛi~î€çŽgÜ«í¥ÎŽ.æo“quñ™ÝİS£Þž–WSojï8LÃ6î‘ÒÖæfêêîO•.æi9žÛûîîîpï.ž¹šv÷vSgµ/mmí±F;Œy?§ÕµÆÝÎü·¥®Îjâö÷êfnÚ‘™Žx†Í ž«“{ìÅú1|·?djff&>3<4šº:zR'ò²¹±ÊÚ"x|n}c?--¯§¡áÉ´²´›FÇû‘ÁžyH=½}iey•û6ÒàÀ!×BΪicc‹9OÈÀ>ÏÑÁ³×b*ÈÖ•Ë7Óù©‹!«==]iuu5õö±˜%ÆÖ8ˆ{wt²&ÝÈU…9>d=ÛÓêú æ–u<àºì‘®î®ÔÓÝÍxöb—WWX¯n~NÈG^ƒÍ­Í47?—.=2ÅúµñûrèDªikg1öE£Á=j¥TÛ­³†ìÆ;22ÊsÎs¯Á…Zíkõ²wºÙ[iâÌkxÈü줙[Èc/ª¬#6ƒƒýÐ!î‰áŸe¹a½¹|{µ;]¹r“½ÓüT4<8˜Ö×—Yï2ßÛAF»˜‹ ²_ymo/³>åØÇm̯2ÞÆBn²ŸÛY߯z ™j+³Gj®s;×ÚeÎYNtÝ(r¾ÆÏ® óÃ~ÚB?+CƒCieeƒõ+3w›|–yDNëÜdø ÷(¡§æ¸>ë¾Ë:[]\ ½±½½:X9íëCWÇøÊ|FÙ)#‰ùíEçÌ"!ã~Æ{vu ¤2ƒïíëB'n°ÎûèN®ÉØ}†Æa)ÆŠ†G&·R7zuqaýÍZpmçf¿–ªñôµ½‹¾eOmÅœrß}dgcc'Ææ:x½ímÖ‡½å<.//§Jg?rWE·7ÒîÖß]M#cg³~až:ª=̉ãhçÞãèÃÅ46Ööi²³Í5wÖÒ™3cü{¹©óê¡S6da8Õöê¡“ŠÚ½çK;×Ñ¡}ÚÂ. poö-:¦†ž@Ç®­m¤¾Þ!>Yf–t,ëxx¸ÏXÔÕèïeh)•XÿÞÞN~§MéŠç=8¨3§~féøS©¶3^dƒ÷ÚÚJ©Œ¡9lÔY¿zê`=ý»ÜVIkèÉ^l·Ïí|MMN±—ÖâÞLS¬·vz/ÍÏ/²gÆ!ud9t§²u€.µí³¶Ø ìø66¥\ÁÞóèÊÎõk׸GÛ®=ä2ö;óRnk¤ ÷éîíEf°k¬óÞ.k;Æs‚-º:{¯{ÈÚc¿÷¶S7ºÖW…=ºËgÝoUt‹{¢½ýÙXæ^%ž}„yd¿qÿëïg‡Øƒ{µZêë/sñÉ÷íæïÝ_û(ÞÖsŸñõ°”û:£¯o]²Â=:˜ž—9®q-?ã³k“™‰ØÓ+«K©¿Ì/§§§Óùó¦::¶ÌdutõÇZn¢oÚxþr™ï4:YÿõÀ [[ë`ŠÐùÞÃq¹ÿ•Oex]¥Z\Xf™ï.o|Èï° ØíúÁ*¶½Šw{Ký¸¥-b]”³2P§0ö ö{«³«3®áhQã1nÇšçßò3È9Q>œOíŠcð}±þE~ZüœÇáißë©ôÏç?ol¡„ë‚uœ*³Â e½¾¾…R›OO¼æUaûzÓ­[7éЩ*—K<@›rM)HÙK]UÁwà·J}k{šÏUCÚcnÎññ‘tE]E± ób´ãØõ¦ë7®ñ”#¿îD öÙ̇±ÁVVV¸@c¥2>>ÊD9¹, Bå380ÆsTÇã©1Æ.Ƹ »0»¿_O;r¿ÏBp72< ßÓ¨GAT1š‚Ú^ž½ ¾³»:•€·QŒkã°†±šcl8ü¾çlm¥Î8¸/ÀÇÅÙU*:£(Œ‹û¥›YƒZIÓ³Ó(ˆQ Às`ᾇ̋²…’Ao„qÒ”x& J´ƒ Wx\x©Ìäé,ONN ëü®2g^ËuÖ¡PgP˜€Š{ph ”«HJk•£¤ÙÐè{íé™™ ƺÁY@‘Åz/ Ôb9ÓyÖ€n¬áðåy]__g>êáè)¼›«ibb„ñ ºø=Nö×éU¹ ¶ºÂ8Õ÷™;ê6ʹÜÎÂÁÁ6Î,ßQæÜhY¾Ú1ÎΫ†O'jl¼#ÀÄÆc ç¸c©¢Ô™êP~Y£]À§ŽÝù “Ìÿvô©©3ŒuEXMóý©³“Õl›{\oÃH‚9W)öõ ¬ÖÒ:Ƥî©çúûq*˜;•mgW²¶{ 0ÈwÆÇ1²ÈÍÌô óÝ›ê8‹áFÚCa³&Èèr.˜îuŒ |ϲÙa‭¯±/Ûq¼qꔇ••é˜+çDg±¿ÆÞìd]U€—](ÖÁµ[YRÖX¨ÅwwW# ²-7È:ÅeM`XBvÜ{°Q4ÊB¹‚óÆzo0OÃz•æüü<€G %ï|ÕpÊÛPFÎY_?F“€À#\â}äYg¯²ËçP䌥¶§¡D¿t³¿z»¹×ó=žª2Ÿ»¨µ…»›3œrþTJ,-ßÄÁlÇÇØé"pÀX:ü¤’ ggíH]=*Æmä°çç/¦Ïþ³»Á8gÙ÷8¤èt×_½& ¸é€T«eî;Æ}aa1?Ž´yår#MâÌ.ÍÍ„^ÖØùG€×É÷»ûv«^‚a GJCÖÝcp Çþë$ â#‡‰œ„Qây•ÿœØý}Éöý82½;€Æ B7À$œ sHV CއθykÀÄEt•u*Cý1ÏÛ|Þ½^ãû FF]+d§¬¬ëÐé´°_u,w‘ž˜/Çã^×8+LJ‡dì•2*8€ÄxØsµ½=ƪžiÄ:oooÄúô(êÄJ œ¶©–‡è·^ù{þO}(0]ÇLÇ>.Ôtžt~\«p°:újŠØ ýì4¨mÆrÀøö˜î¿tzß8™šÄÙ ëd¢3*^—=©ƒ¦¾¸°úʱW;Ôóþ2¢ ÖawG;bð¥D ýÀš hJTr/H6Ð8==Ë3ÄO;ÍÒ¡R/µq}K:Cas–öÀ2;Á×k‚ Ë:Žˆr=Ø=õȶ€àV,QgøÒÆë̺þ­Ó­×5Èê÷{Лü­.#t°݇®ÃòöP¾µ¹²‚—Ág:›YÇL¹ì 'ayy1ö¥stŽD žûÑç¾ ›Èq…÷ëuöE'àz'¬422Ì÷;‘kåå o‚Ô ÀÊñlCŒÆàè{ýõõmð {½¯Ã?:†YÝÏ8¤·'ö°k"¾˜›[LâhK»q¼n•9w?+Ã:²”ÕK8|Ò ²'0 êX‰Vyšr8£ãcgxÏ€+zuGÇÝJp€-8O9RÞÛyíÐöøì¹s=¾ð…a'{ V/Ìߊ€tÇxòÌxº5}ƒ;T°o“áì‹_Ôîë*vdqq1œƒ•Ú²-Ÿâ÷¯2é~ÞÓYÂ>8Ü%øx–IÕêDöõ†]ß#È쾟8®ïÖ6X <544Äy-p ß›H3³óà AGêì· ó圌vÏ¥ÁpÖ×u è .ÖñplêeI|äõç°mê½èH>Dé mƒ[µQ:6¨ä´êlxO“èT¾ã=™kƒ«}õ]•ueËľm×i:&1íD8;÷eÇæö+û::àþQâ«:¾ßtüŒÚÉàªXÏï³øuB~‰9އ¨òkêÍÂ9Q7–ù~ž«ìðû]ý•b8+ÌCØ~Å4dGgÑR§(cêÍëA ÖS§T»+n GчC/9¯¾¯<¸_7Ùß‘¸1Ë¡n::µ¸ŽÁLõ{Nh“Bw²f…sÙÐT¾X·À“Gc¶ßÚ°b>É…¾Î/¯UÏNØoÿ“ïn8ðvyЍŽó?6Z?`oƒÍ5ÐvR6ñØ5˜1¡dö6Fjä —ïwûˆê–4DHŒD¦‡h³JÀè–‘…H¡š83›Ue¡ð刪Š_ïRe„Äcm”rQÀ‚ŽCˆ«¡˜œ\ö ÂiDÁH‹øC©¹q6pyä@Æ ÜЇü{EêŒÍTc’²Ÿ½R7ÝŽÕ ÙÁïÀ€À¢À(—ëÙÕ $ŠåõT U<à]@ò:Qš `ÖŒGŽ˜å˜]äÚ=Ý*)¯Ó‰Sr5þÖ) ï@¶‰âì&lÔbƒQãßε޵ãëíÓ`•#ƒ(È j}ëÖ4‘ý³(Â홂ˆï)dfü®Ì!ŽßöŽNb{žK&³?O°qûêí특RžyæZdת8ÆF ªk¥Ž§Îò NЯJð³Ÿýlzã›ÞHdÿr(áQ€ŽÀ@C>;ÃϾ<¢©Cy•²Ù2^¥Â­Q0úmh#däjp¨™#ÚˆBÛ¯oÄæÝÙîĘkL÷0F½#ª¾·O”–hXçÐHÑÈd­JDÙêíÁ Ù›GÁ2?š N”ëîü9®³ ƒ_ÅÆzêóO¥G}<Ö|ye‘hö8÷GŽDõqÚ+DwvqNù¾QÖ©ÉK|îfD¨U.]Èv­¦kK³(íÑщ˜W£€Ê}M²F”:+Mž•¨éäľ3”ZO÷XÖWç™"à8àFPuê]S³‹‚O¼²ªÓ 3èþ&°µ½ŽåںƠ?öW• èîNF/]ÃŒÙ$2{¸LyrïÖYcªŽŽ÷ÃÐj@6pl#ÀÁ²ÁÈz… Sg0ŒâkpÜ#>³Ùøˆº³¿W–‰þ¶™‘Páào¯¦Ç¿Áõhg½Gñ°ÃÛ1jØtr÷QêU²¥êŽn€˜ÎÜù gCÏÖ:©óóË."¸Ä÷Ô f·‡Ø?ó8fŽ[™ÖèÐ2 ð,µLGã“éÖõ[ÈÖ^8ã6Ô¹í€#×»ŽPÜ|VeOðíZUÐOkkµ­Ã@ˆyOŸòIÐe,ãŽÙYîÛx yç 9N¬³ÖóÐŒÀz_ÐÏø)®egg_)vÌæwÈ;ÙSŒ­­ÄØ0ê]DY½¶{~cŒŒ m €T@Ùl@¦@GÝ™ïFÓNmFd\GkçZýåF¶—õ1ðØlœr®×ÈÂõ²¯’]ŒËLöÔÀ¤Qðn N”ûÙµ–‘`„Ú@ßYZqfþz±+Ûî°¹îýž1g*Ávœês/$Ó±0*«s¥C–\E2¹êS³VýØ7÷û>ïuë\¢/ b,ÃŽ0èÉ-Ò.²79I€]¡Þ¨ ë:`GõÏ­ -áPÖ ztG w‘€W¹Âzb«Õ»² †vŽ>=n¶"S 0mOÝ(áÍÍýp~uÍŽ ŽšýÒÙÔ)Æe|¾Æ¢^~æf8‚Ó lKò· ðHž™<ƒÙd쌧™åtO¹¦‚0¯å^pŽs†Ÿ=ƒ.3:.@2¸ÓÛ38Åì±ÏW|~i&ˆiÁ*N± €ú!h™@A7ël0Ì´XA{¶LÖY  ;6®uôº2¸ãÀ¬¾ÎBfTôÅšéÀŠ•Ì®D¶Éý¼rç>4蕳<â \uä¶6À"N•cW(ÏFÛ•µÎNÀ=xÍ÷ÜwÚÀì¢Ù§d¬“l˜{Q9Ý« ±Ïd9W‡‡±«è¬mdv“÷†GÆÓ›dðν<‚ëk ée/#Ø\¤ðo±Å%@3ÞÌOçÁÉi®Eð›=e0w aP§½·Šs ´&ƒÏºÌóÉ2PžÜ#››{YÀ/Úvƒ›ìql«Æž×ŒDU§VÙJ³vfíÝ:âêÔ.dB›}íÚ<öþRèTy8{®¡ýÊÁ'Y¿*Yð>]'("Àv]Ügê?Üœ­&@Vœ]쵯SÎY*qO3³Ðñp޽>w =‘} ì-ò¦[ŒþŒý®‚+˜~¿È5Q>y¯œñØGË+æH@ýgðÏŸ|É… "0e†ÊLYøF9p_C.33Áqd‡Î½œ³WÎ]¶4ø[|¬ä`ú9¯]«È˜‘á:ñŒÌƒºÑÏùïȺš bÈ:³fÞ+m³3h00ϯN™ûCY60 ½QŒIŸ¤ù¸ù;áqÕÈò™¹"ìže⟹é zŸ"cåçÅå®UÌ+ÿv¿»oM ©zwÃ(Vå¯2<@x¤ƒí ÐS•011Fjv)"ŸFÞ¸Á@'#E=(§µµUŒ4dºF²REåÔ˜ ¼Wé?F±·¶  hÂy(;½d¥4è¦ôUŽ.ÈÂâf{¯¿r™‡Â£!Ñ‘ÝŘûp>T’ Ù4Ó¨3F•à‹‚Ý…ñŒŽõ¥kׯ„30ˆÂŸt¡\™ß.@Úš™(6÷ÌìlºpŠ#“+­î€û©/|P=VçBRêá_#r|áÜx¤ùÈøÅZ ƒ„NÖ€ED˜LÁ>o7ŠÝôûnd`ˆ‚@14« 8wCkð1yéu¯ySúüç>9#±nب/]ˆ{©B!bˆ¾*¦s禈Þ#@½~ÝŸBÑÊ€l·”7•Ÿksëæ\dÞ\s7ó RP·‚bÜÇ)‡F¸0rÅum¤9Ô¡Ki”Í~™¹vu:½êU¯$Ž­úB\KE3 %ržLÖÊêBzË[Þ’._~šõ0ÛXcÞÖƒR¤Bt¼* º9ÐA[]Ý gNj_›” JWʈ1ööLÄÚ‰nvîÖ­+<£1¨‹c£Zf2já„ F´{pÛEÄ5ƒc)^d;p8:ZÎiC€%Sú ÔçÅȦkhÎÈjßTDÖ›ùèèˆHžk7]UGJ $½°'¢„QºyãVDXÆ¡RÎÏO‡áÛDþK äÔÎ0ß…ŠZÍ j{«ÄX ¹í,¥Qæq™JP%Œæ 4V Hô HË2Ê·Ì>"ëÁšdÍÌÍÍ_ΑÜ~©MPúæúëpIsÏI‰°J§J ` å§7š®au¿/-ÏǾVnRìWÚÔ a4näˆìÙY©¾_¾øÅ§b¯·’¸UPę̀3t"×#r¯Ñå¹w‘×u¨e:ÁHýê-@U  Øè¡@ß½>Ð? SçO»ã’>¥±Tñïì­„ž¨ãP©gºÉ˜_¸ø |–µ=äý[‡‰íF ۠żìågX0 d¥ªénïjÁ lÙÆn‚ÞÛL…1ïgô½ú‹©‰‚3•¾žß,™ÏÇ<èÊp ÇÂgÑù,//0׬'‹*Ïa$YšŸLTu"¡ãhêu€|7†‚5Õ±T‘ xëЄ‰‡¬®Ê"À—™hÁ£öæÇÆà‚ëd ÈùTJµÑ4袳®MÒqîëþÍà )–÷4»Ð·¤{ zÇÚ¡°o^¥ ­ {È‚)KèVébÒÕ´“^[ 5†.ªÕ¤OU´2~Ô óØ ¤±~î›:zS§S¥Ãa–ÊŒÏ.N…ÁŒŒ@Ö°«{PÝ800Šî2à u àºËº &uH{z†šö«NwÌ92fVÝ`“ûP(ýÕ«/F¡˜j'3}R*)‘rì„ôo©¼›ÒúXS³žÛÀ(j9Vç¢Cggçš4 æx(²a ŠºÊùˆ 8ó¦qvtw¢ÃÈü¸f¨ej8/’p fÝ§Ò¬Ìø ôýÙÀãÑó­mäC‡ßsþ}6³§™¤£›)0Ð5qf*€ÄÜÜr‘Cußéí°àÈ¢‡ ª \ƒ"+`CŒ¼;ÃJG™#®²1È“cb±ß·¶rÔ¹ƒÀáµ+·˜»Ép`­{ìù¾ÓA&qìaB'±T†.Ì8¥òèPKŸ3 ëHùS'6˜33¿‚öepŠs0L` ‚®8¡ÚÐ%À{_´N)VàŸSÀ¥ÝÑéšÏ*ˆ’A2¿p {C GǵS§êh9÷q l!¥ß ƒvE: Ðî}£ê:3:°f–µuÃè÷¼ëÓG!ôûË5qÏ«7gfæâùï °!2D ‡_} mV¦Y)ŸÕ,‰¯æRÙ›`¥!€-¨NuDK¤)ODPU´·«ÓγƒkÌb÷Üp¿l£C]WiôwÈ`߀A4Š-8˨÷*à"ƒTÃ#”Mq6“í3.¡×Ü3fXf©Ûä™GÒðø£é‹Ÿû42=8I¦Hv×`Ì68k›û5 § H»lAo&°¦¿»«þ4î0Û¨ƒ^ »-`6sÖF¶qks%…U¢þÐYo꼈OBvt xÆ›×q_~!p…×6Sêܹ¦™Æ`¸ž²~²³êú:§°rtÚ sí„rdæJÝÐCà@Æ‘ëW §œû²W ž;^u‹Ù.YB®­ï{ývdÆkêÌïr½n©ÆèÏìX°ÛtØ÷‘™Š½/¾4!NàŸ)v¾òm¡º_›NB–®üÙœyÉNDœø¥&›±M  À北הêÖ@çFÖ·ùЬ™Ž&ÈqN>Å68« ››ñQïí 8ÔSÙÁ4‹í¼zOëÞð¥í ;ûi4w?àÍ4éBd;ªÅKlÒD¾ë8ŠÑ7•Œ<â=¼u£%:a#€÷-<Ê*Âsáü…ôÅ/}‰‘u`²× vô°Á1²:S›(sæOšJ÷aõJõÜÝlüAQtð]yÐN²Š‘K6ÿ­›*NCA€²!S ù *NPfˆ„Ž5ÊZ0A–iw£‘³d¡h#ð¯‚𺽽N¤ˆ¨l¿µSfˆ`Öˆ¶ÙÊѲ2ÑñÑp6††¤ÈuA™šúo ¸ÌðŒSpåÊÕ(éÙÇh bL1%²Î-@rS±«0ùƒ”™ mÝ— ÀHï> VãQ$ev&ôÅÇ¿ÀµJÙ¨¡Ê\zàyv03{.y'ã?$7PÍÈ;Ñ5³':#¦mÁHލ®²–nv·“l75#fdtÊuÊfg¥Y_`} AÙÁ1aîtCè³g/R¯õ%Œ,††ù=„çZ^n>ÁÇüü eÌXΟÓÁ›gœd¹0˜FÚVVç pöñ3Q?æÄ¶°x%ïéØâŒHt‡ËRW}¦3gPÄ;æÝˆ>H!°Îfxø<`g #]‚l_mÏZ@¢´‚1"åÖœ˜î—6‹~ŒÌ‘k ¢E‡ÛèZ2Ã…<Œ¸I”Oň“E¿‚êÀ¡[Þ†"ÿãP'Ã¥‘SΰR$ÆÎŒ ·8« ÍÌY3yælðáâ  _¸p)"æf—Œài`ϳлŒ"=Úß•ÂeÍô 8é:fgÏž‡qbœkýƒà× IµŽï \õåE‚ŽÇüÂLPƒÎ’=Ýà[úTÇ92z´ÆøËd®{-õÁÍ[WÓEj5ç ½šwŸI­ìÂy8pýšœvêêЕƒ€‡…9@ QZ×öæ­/1–Ùè‹AÕ¸WÚ3^d½Zj‰'“9:jÄÀ•¤mcÔ¶Óä¹nÖ»?hfºÆG ª³a£ì]1 –0ð##gr”cjù²•Õ[‘µU©\&»gSƒ3gÎ`ôˆœã05ZŒQ8n}DƒzŽk×f(“v7}¦ìh<¥|áÒàôÌÎÎFvhfî騵q3«¨Ìx™YYÇ8Œ_Å®Ff‡9à~Ÿ‡â¨AË@¢ŒÌ.á|=FVe Ç챈Àža¯­Í“-Þˆú£……Yhãü~)äÄŒÓ ²qµ2l @«ƒbäÌäÙÕ‹P/4°A[ÛŽZ‚ · @¬¿!8ˆ(Qפg‘}{‰íÎ:÷PÙíŒ&ê•NÞÖ–õ4(a¿h¥#Í3?£dÌ OMž§>ójzì1(d´w¶Ý¯®=0<8Hä9Z_µ™™Cô™56ÊÐQÕ©ª²¿'øÍ]t(¢h Ê>lêxKI0KÌîŽfUÆQé$gÀ"hÏÖäPß‚Žñwû¬eP_O‚ε5 ©ûƒºfÈ{ 6gq8RKÎQ¾‹– x¡Q ~nêÑ ¡Z´ MKà-Öx<¨ÎR’eLO/]2GDiÌÂœŠµ  u3P¶4µÝl·õŸQK„Üôx€êNpÀµT?às,ÊS2¨ý´©’2hÐÕ:X³™:j±Ð3Ö^ª×|’•Î':àÚÕ¹Ø :*6ŽŠÈ9öTJ›5€fyÜ;‚B©À¾rC çl52—«lC˜©ŽÙoöN•˜ÙT¯oýž©=œ#ë{¥Y[[¬žÒÙYZB7a£´% ‹3asÇг°‰ú{qFƒy¾cYÏZÖˆ)GX‡Y"²FM ´vÀìVn@e¡¸È{K÷Muço>šÈ¸.k+°*` ˜ñÕA™€.©Ó«®’õ.¦3›´¼¬¾µLE<§n`¤FÂù¨—Ñfæ g©£7ˆ6É”X€ÛÈ£NZb¿;w&ìfPªÁoÛ;Ò”uÆ ,D]F…ÍÛT2=®\6H®—ޱu1¤ —¤Éš©ŠúHN¥™!63èúê¤î£ó øŠ+uŽxĬ½ûQF‚øÍï™± ¦ƒN„Ž*WÈÈ-•Ïš '§Êgô™n;@Ù+‚…ÓÕFð);M~ o”Ãij:Xì·ž¹hFQ8 Åwlø¡ãæºêÔ©;Ôá¾¢ʦ ŒÏ  I¯£íuä{dgÌõ’fíºîY~ÄÞÌ{2Ûêph¬Éb‡xM³â–\ÿ¥o`)2ŽOÎxå T®÷Þ i•fv}é9÷™í–T÷l4½°N è8}#]ÐûÖÅú‘µÊtÈÈ–±w£A›NÕïüÓw6\|9*Êj~T *"Á‰`ÎI7z&% 6*‹Ÿ ÐLÙˆ4Ò4ŽXZ´¶g!2DF6ì®d”Ì"t© òí¾²ºj6§ÅKa*‘Ö ‚1Ø^v‘ÆÖNn#£F’g‚Zäd×vqÒˆRH²°”Z‡2b\˱u@9p,Fñv]Á™2³¦ó#°xÅ[—ú¥È ÆM€±S[Šè“ ëFØPõË-ðÙ7R˜yñÑŸ›½…Ä1%2~õÊ|ŒI*XjkcS3³sE['Ô‹£`tGåäµü[g3‡*ãÒÊ-"ºÒÑ´pú0Á¤Ýqä» D5æãDõ§€» Ìù¥pØ|†je æ\?NóÝ5js¤¨9Þ 2_?ày‚Fh žÜ~:9ìt®ûí8Hftl M°cmm€ºQ§¶'×Z^¡ñþ~®ÏrLQ@ʵΟ‡j‰lÔÌnØY,MN˜èèZÜïç{hÀ¡ÑY Ž«j—¿w~œ¯-£ƒfºŒ2—ÊЗ‘!¢—[pórv'5«lMÏ#H˵2ÒM©#aÜî©Á‘}i_8:넜õ›‰tж6‚Í®]ãg!‚ 4”Uk1Í’š9ÆùÖÑ´›¨ÑÝ t¦5îOõ¼™N’Ÿh”ø†Gxn ¯ÝWRTð™±ê\«« :Â:›3A/6Ú>?GÆ¿g=Ag0À4;Á~‡ÙË*ó‰“4}Kðmctà@ÇÎù0HÓÑi Fy·‘ –Õë6Ï!ª 1ëî3šÍŒ.OQÜ‹!b_¥€fÍ s.h2c žƒ§›3Ú/l4„þ²ÑJt3$pe`MZÙW¦kÉ‚ÈZý#p’Bš3J9Ó%£¾È­r#˜=´c?+Ê­ó«‘¶A‹”¡ÞÐÙf;#3f­‡àÐgÈ…ä‚2³$Êmî<«]2c½„ ê¦#ÜNÙ7ÁÄ&6&"¦¨çAÖ-š95A€Ù6%ƒ4¸¿zÎ ¤¶Wý«,vTÈ^³·»q$kì•U¶ #ôàÈLK¯¢þ…}ª]ÌÕ8¹èeï—íNÆÍ}L7ó®òhæ²ÄÞ1Ó#t…Ì„z4‚*Ø|*‚NžE7\ À=4LV`YÒOvê&Tr í±ý}ÀöJmN;[i¦=0PP5Sùá<˜u3[dÍ!Ž]í ©èÔšŠœôUåÖµˆîgÉÁ5X:5$ƒ’ëdÜ2¸•J,HÍT6×_pm6PÚ¤z"g©\SëòÚÒ M¦&/F@uúµÆîŽA;bý .Ü­Wêî ÇÑgkÃÞ˜ è`½ J¨Ç¥×™=Vï ìb |6š9aëû¸÷áA%äYY¦+ªkç¾T벚bë×–wp(¥ì ¶v8(6®±¨<è[ØB¨·f<¢ss Þô Nu¬­©kèS;O»Pþ¬C;€n¿-pÜädÎv[]AÉÊ™5ôz B–°R fÙEÕ³vRÊõ¾õhøeTGdÌ­ã0hRÛßN·näš=k®ßôeƒÕl:~é°°aìÚ¢c!:M;²¼Hà’ÀºÙi›m¸¿Ì ÚT‹gïè0YÛŽã‹^r,ÙnyO»U³^‘…Íké¾w¿V‘!u²/ƒsfXÍØ™­ èn##×g„>Gñ|–Sð¥Éš},œ®\;h#6k¥rþY/d¶Èæ„£¡ n2*"kƒÜgŠ\f…ù~áäøyE1¹èl÷“ÎUôÌN©óðŠÆfÙYkÒ¥â¸fÇ'gxŠFûR¥[k{óYÅr™X4´À™òÙ£+¨û+×BªëtœÌçúÈ쀆SÕt¨"[×ͽ|EI‘trä½@‘¹æü½ÏåÏ:ùE½XÑU1kAƒÍt–^Ñûí?nÈ]ØbVp`[ œ=2.,ÍŽÍkѳÜÏ¢XÒH/릆Ŵº]«V–àäUƒ6 S±‚¢ÍŒ£âDº™ · Ò-δ§Qýñ ;Ïd.¶À> ù!·%HíD{3‚½½ekóÕ—Q+Áy z‘4¿·ÇÖ®*£"F1/ž‹Bt7–™! ±]·r¡¬0ÌîH?¢NÅ£Ãe›ÐI"]O>ùïÃÐŒÆ'ì”dg9è#Òl1JDÊ€Âo´M'ǨŽí¦6XØkíÔò’‹L¹Ý¤|c‹U][ WÈҘݳ¨¾'Ñ@Î×ÌÌ­ô5¯|U€>B £í¶£@þ6F¹»ÇZi$û(X¢¨ÖPй éÆïî5ªa±´í`p¨(œ8r6ÑlàêU(U8KÔ€ Jœ4@ùø[´5„¨u2‚£‹/ò})9 º9^Ž(¡œö-2ZCÔq|ú3ÿ.½òåÅz $L²öB!‘ì±3Êoä2êO^ö22TWx~À7²èœNߢm1w—®HÖ½ h¢­1mgÍlìáÐݺ9Ã8ÏEôÒpÕ™IëBl‹igG ‚¥>ÑY`g=ÇÂ"4S"õò–«Kk„Œ ™_¢î§“µ¡uû"ue ¢¹Ù›i‚`À€/A—Ì`ø¥m2¤-Jºõ`€ÊÂÒ-"õJ:N\GÑ^Vþq?TÑnÖðêUi2(i"`R.ûh'¬S5}k&Gh'ížG4~YÐj¦e ½ò¯‚‚q9œû[7i,@ ßèÈŠOg£ 7“-ÑÀÎ{…Âq³:+Ð u.]üK‘9ÝÁÈnÒrZEãÛAÅè^PTjû{Ê‘´%Ô¸YༀW–ÍfŽ£yŒÅ¼€5‡”‰Ü•m?:vãä)³‚åÙLŸ‘{¤Ù©®Žy^ã¶Nï.Ž6ÓB¤2r}"¾¶ã×xÉ© J”?#Ý¨ÕØË‚„2û¬£ ÀÚy&ºUî!{:~6ö8Dùê¨H‹l»³ÔXNŒ=FEçu„sdòvQä‚"õ—ÅñFt­y·p# Î5â:Bme ô¥Äy „µ`Örø>y¨·´­ÞÚÎ-ï{imTÒ¬²ê¼Û*Þ–Êê ÞXÓ"°Tæ×àäë<šµq%ôŽ†ÐŽvRW×§‘˳¬1ÔÍ ×Í뇞ÐÙ²ÉÚWàxPw9¶)׉T×íȰu¶2Zp ^w3ð:?Ûd'¤ÈjtüžÎ£mþÕ¯“4 ˆ®WQ#“» ??Jæe6ö®Ì+ë+sa|n;-èZ:²>$rvŠõ¼ÙàDÇ!jZ8vE| ºÂqÃÁÓAÓ S7jm””³‰FžíÌ •à£þVGø9ÐUèØ‰yÍ¡N ÒàÏÐAQ½g³w ²ÎmÚ+ÖvŒŒS'õ{ÚBé‚"I›4Ø oœýkO ,£'œGŸÙ,“ΫÀ^Ú´ÀLn¾Ýö¬],ìSÃapk‚€‹Ù, ~Ðe>®’IV§ðŒ$¬kŒÞ¹[¬ÙÉ ÆhwBegˆ¹[cíu$œ¯ìÌûìwƒ®ÇçÏ=t"å`fö2rd \®ï’¢'>±CžÍOÌ€@PŽWÉšªçÝw:pÚ8©æê/×0ƒ_ëØu$3¸4[e0¹ÏN‚͆Ø ëO èè”Hƒ¶æ;7·ÈNž5—‚siÕãcãÐV9f‚àŒ÷ŒæLÈm©ŒlIeM¿cÔÎLµœárd}W®¿\‹@ø(A:÷†ã—Î/å¿—L¨µ·fˆÕ‡î?ªCŽ4©Ñ õð@Çˬ¦ôÈ|DCQ£ãÒÓ&ÖâFc t¹6Øà«²ô^'ƒ‚Ö ÚU0ꬭãwfQuâeh# Žk«ÌòÚI\³¹nËû,gαåê‘(oÀÙ\‡I¤]ŒLÜo•爾e‘½ä¾(a“}D1Àh&„l.2¤Sc ´W~O]#ÅS6R4U³{žµ‰Q•\« ðsæGÌž>ؽW›ž2j&dâ£:’Åù{õkîTk½¦D8;Ñšq*JmÌ ç°0Y¯N•, ›|H¡ËõNÍÔOnæÁç,ɉýæmT™ÓÜ0F{ T¤Ö"•r!?©¾f›kdßšÙ+ïãÚiïüNÔ(zïœÔŠ€RPuŠšß‰(<Ÿ Œ‚öè> ‡ÑàŒY°æ¾):ýé,F»ôÇ,×°å1ºßŽšZüãôZ0ÙX³–ÊlŠ2O€…ÍF€šMzìå‹r™žž¡èx<¢õ¹™‘QèP=¤¹RSzùnÆiºÀEZ–è«há±Â%'Õh&·,¤Æ6”>4F¿¶OjœAO`¬íø#Ý%· ÆèÒÕkbÛŠÚè‘£P´¬¢ææ¯PS MîÈGÄ|pAÁÙ³´A~`ÿxœ§„¾Šˆð<…»Fü‡†mžañ¶ç&àøÄ9;RgtÚ.¸›Ñè…ôÔˆÙI-ÎL š®$Ï>iD›\38 ×?:»5©‚žÉ¦Vž†‡§"»d†O‰´0ifÐm“=?O” ¤ŒNL¨Wy_C§"Ñ!rhG¯µ•í  õ3_ÒÇvæÛ$?þøùmÒ*è#òfz´êy¦’]õžyæKQˤ£×Ëõz8OþÙÓŒI#xHã»™ ©‚}ç{tôL’8ÃGC™™­‘WëæràÝ÷Ò 4a´¥›YÛŒ³tül>·&·WÕñYÚ Ø‡Ù7ŠÞ‡®2#gÝÎ DãfTS9ÌÀ;+tkiVV)°Ffæ¨ìZG”kÛ4ìfÆìV)uŠ/µ9FÞ¢9"u:ö"Ù¾ý=d†¢}k¯âL›øœÏÌœã,µsæÚ*IÕ¥$Q÷¾{Rj®Ñ`†(™%ZÇïìÞˆ3¤¡ŽÖÿÐÈmÛ››• “ž™W'\cññ¹"£ßÁ»àË3Ú²æü,|ß¡NÒ5‹È/TkNOcȸ{Ôú? ¯:ØçÖ›ÕÐNù¶S­GDøü¶—®nM‡gÑ™;Ü·ÖÄó|n²NŒÙvÖ¬‘`Ç£7Ú¨ÙRŸ<²nȱÚqûèz ¤¸4Ð^CÏ • m‡s#í[¤aÆIìÈ©Su û£œ¨p.¤t¯LûÊvʨºÙéh[€ÃèZfŒqí°4ÐŽØA哃èµh¤LLÈ•€Eð#Ø50è³åHw¦ÁÆÍî×qŠÍòí좇èÜšè’ÙCwV›˜¨7´Ý;Ì”ú]3fLÜ—6Mù"ÝÊëOPãº4?Ã}³3ì\¸æEÅ3››µpzcß5ë;tFGOŸÚF£(Ûõ‡cWP¡ì,«A‡Ö öÓæ‚g0ɱs ŸF¦­eÊÝ=­Û5«à2»ÈŸ™¯`gàô ùP¶sw:»QZ» ãÙvÞÌ"GÃÆi&ÓãL‹ÏaS$AY>Él°ö}'j -‡Ø#(¢|G0¬¯ïG¦Gš¦ 'jx‘ƒ ’sª§ ½ò|‘…pý¢~$7Ä·D€§ŽK‘YiWïFGAÙ¹·ë¨rÆ%×ïFï>§®Œóìš”ÀÜa»˜M%6ôOnjÆz4³ÐfúuxÕeaÃhÒ ídiâ"ŽÍ±sfÎb†SÅßê¾¢i‡ch2™ì Ú\-l612Cü;'p°‰Ì‰x  æŒP~¯•ªX°! ŠcœÇÃ4õç8mñw®M ‡2›ç¦3•é‡E=ZÌIÜÏó­lûŸ©Ù·¹ÝÔ#æ<ô^îҚϴÊÝãû½÷5VWvIߎ°h8dªòÌ®‘ë©© œ¡t-¸Ñ™J+h䫟x‚õôôåÏÈ”omw-’Œ¶Ç*w½ÁÜ\Bá“Ò!¸Ñ€ x‡¢˜Å†Ö$¸ñy :«Ùe)€¤á…@«Ø õýn’V D¥¬7é§‘…¹eäw'iåÂØÙ­ÎâÙÙÍsZr—Ž| ˜›aN²\ñ-6j™M]jc¡Ûiqm œ1yfµ;yáösî†ÙX(+NkS>õ§ŸcC¯û™£׈D½þõ¯gãÍe]/×0nMˆYXÓÞ:ƒ6µ0Q5jËäÆo“á™$¯¶£ªFÓ¯òÈ+àdsPPL2f›{‹Ú×£¡‹½žAj¹nܼNÑÙÅùøìpò‰¾{¾S; Di”öÒ#—p@n ëf…Fbþ—öÓYöÌ™Ð2…æ†6žg'H›,Ø–G95ñŠô'ò¯Ò#ž ÃbWγÔIzÞ˜™A£ª‚Aëœ)žS%H˜ÉИúÜà{õê|DuÌ= Oƒ‘{Zä×÷m{l´Ñ# ¬ãHOœ‹Â€@Çßš@÷¼g´… ÎEËümÁ‰Žš{0Šº‰,J“üÆ9rÌ»óÓK–Înžýœ­r@]MLÜsžY#è3k%5Òµ(·û,d®Ñ-ó³¹¼TȲÖ=e'N}Fs©ŸØú~¢²ÖQ±¶zQGÄúʈò5v4êY13 ³wÒZƒêwëvw4äÖƒmD„ûÂ룖¤š%Rß8÷a8#C/ê‘ÍH¤èš ³a÷9mƒÀAçC'ÄZê  ‚‰ë×®‡ÃðÈ#‡A3X©Z«$˜Gg¢Ols¯£n ” U”y#øcȸúGð"¸`¢! QèQ(“{»ÖŸ x30€3™åÉ ¦ÁY:÷‘—+Š}õóÖ’ù·aÈzÚçôUÎÖ3£ë¹pÚ†ééë‘ T†Ô-žï'ÐŽ£ ÐÔµ’,Μ™;ë˜ hDÍ9×”ÆçÜ™W︼¶:ÖãG ¸Gd-è¨F‹ju;òmÐÖº%žÂÛ¶³×/\x„êe~ÖqiàÔ,²­.”å óG~’ïÙôËæ%8÷ÅØ£¹©Y%önÐ(iÁo)’ÔzQ‘a†•c±uŠCãÍ\/ðœ.† M¤ç[º!>•%”÷i>KÕ.±Îyà˜Lê¢9Ø+j ø|b+¦p>كΩ,éàÊhΞ;fÛm]&2XìõR>.@=‘yà*®½ š >g5³sÓmæ5:`g‡£pˆ³“‘³.ÚuÜ>FYáPèTçL­]"3µµpÖ¼vT´žÊWÌ·-ΈG‡ Ùj¹k·¯|”PÎ.)31f[qÅáÉ:zÙ9)šbøÜ!3lmjtøËEƒj?7»Z³Ô|åó£²ýõÁò_¹¡F¦ º¼îLÏ»ÛËël‡5)‡Ú4m„ÙÚÈ|›5Ó 'Ü µå¹“tøf’Z^¥ßÿgïiX€mG7 Ex‰,€8GkQžíáðªŒ"%LT. FÐmdÂèö(´ ³6s°ŽÊ¢uÔ縙¯L‰ñÖÈOÍ—ù¦6°ÐÀ¹˜*{ÎH›Qþm¢vMñ¡œ< ]¥%º¸ò="*Q'À‡Ug^y>WËT«Š@Ó1гȹÑlgÙÕm‡šjáô%¨ì@€ãRôâäfÏó@`ÝÀ¦y¥µ¸0fïâ HæH‡ËȹΈ Kgp޳ Œ$Uñ|©.ò`s´Ý(™l#ttƺ 3:/ÍPÙÈE}F¤XQS¦§˜¶ÄÑ$ëVY·åÊQ㲄#<55Ù4£¤~Æø¹žÏ¯bRÉ߸NÏU±ÛK“NbdE*ëäµìn£²/Ò¿ªá°¸^çlfîʃÅúØNS  £6åJÀÝN—(T ­Áw܉èD†cÔV– ’eìE9énmÍ’º„ãz=ŠÁ¥µñ²[ŸgéLêð‡¡…’ LGËÚ¨4õŸùÚFH»zÜêÖÞY³„ à¼hì¢6“c<œ±. ÇC™r å©{n’QA¦ 3x¼±Í\Je°-g>ÄSelA®ÑKt¸¼Dæm:6«Ž{Š^j)Œ¾Š<"Óåf˜lpâþ2R¥1”JèØTö>—ιÊÌ(¤58FÍ;ƒR¡¬Cˆ/ñ·´2•]6~ÖBz†J áÍi×h•s²Ì€êD»Gå«ôm{/ÅI®¸µv3Š–ÆLœÆÓšE ±†Ãè»G"nROrCσúÇïm8£R(u/h¹™^$‹É³õ±žv/ Îí¿ÍðXìÙ=f&la݆kq;àÓã~‚Nè|ÛpFбü{ ¡÷3 V&Kß1“h]¦vAó(èÖÙ"×z  ¾cWR³Ð¹½vîd¤ŒyæF´"—’"ˆlR‘Tü‚b)ª>·4 ë$,äwoÚŽU¥Áñoõž¢ÑQ qfŸ•vffKÇaœ`Bî‚d¤ƒ©q4I±Ÿä0΀ºÍLÑëk€ÄšÕ‰ñ3RcžÃA¹3ZNÝH¬‘Ãè˜IgCѨ“Oé'eiÏÔ_šíˆfDùÕ>dͳʢ•º¾X¸’ôd­XÇç÷ì*õÑ™És`ÈÆó<ÑæØC ='IýûÓ¬·kàÙG‚]¸aHÏiñ0y y0z>+):L!ËAÇ`',Û@7©#ÍPZÇ`äÛW4=àùÝ{ÁV¥«¾×±Èç-9ßRaŒ¨º¶EÁu䢯œŒò2׎_™)Z›G–ÎH/Ù¯lØÑ ÒhšÑÖˆ~âd)¹¸Y¢€6Jg-·+Î…ÑvCÕ°K©‘ ¢Óhêöpγƒ)-M:8¶[f Óìž 3Æ8$S%¥8·aŽØxÌ¡Yj÷Ï-‰h0{)c.e}˜¡”Œ¬£7¤Jm+!ÊuÑkAP>B :y²ŽÔ*ÝL¡Ã܇øëXÅQ ¡»ÜWqZt»,·ÉâÎCœsÇ:¹wÝ3q0r†d1WêC)lf†r«äœrXÚ3»£Y¡s,Ðëêø›uɇñZ'§ÎéˆÆ63dúÜ«fgl°[óQϘ U>£ ß_£™…x¬í’TEW[“ï«èÚ˜©þRb­Q› 9÷¬Cñ®M› æ¸>kkÅòyfâ· ´uv²ö@òØ–¤À0É6ô>ãs=”[u…õfjmIÆ‘9ë!¾ËǪãÈZ¡Ó£®ù):ßås÷lŒ“ÏYD#íQ¦åùïÜê;gLüL´ýnÒî<”7;Ï>ü7;,…C’)„árðëŒQ´QÒÚ¥Õej_ø3êG³ƒ¤ÎŠë¨W›NNÑá01—·éhdš\Þ›q ¿¿³O${°8¯¨o:ú”ö-×<éH)ÿjÊØ{;«#ßÈK„‡˜œ|?ÿ/gXãž¶Ï.îtô`ŪŸsF­øu³^’ßåË›y´+lnŸ^èï¢«YƒÂ™³NLÌü§Ÿø;•å-¨y3⺡lÙÉìsO=@^ýê'xXê„ÈÒä}”ÑÁ‡I2òPe“Ô ¡MÖÍ„ŒRx{ýB͆Ì?_E!=ʾ¢‹ ÀbUaœ Â#hL&*§ g§bª\åÜÑp>4ð’k$Þ¨ÝëôÀ³@Üí%(sÍ$‘„Ýɵ4»& ¬—ºƒ8Ì×Ô¤› Ó˜8™E{ñòç~gÔÝ”wq ·Š/žƒÍà<ø_€ikÀ"l48?[?Z–KÕ³XYcà†ñÔz; ÎÑ,#wB„ÒIæizšg”(ŽÇëæÎ,ç5G_ŠhŠræ&TTÐ?¬o¿|v*N­ÅÜŒçÉÎÆ¥0¼8×'GÞ8$ÏäMF´ÀíxäÒ%œ$ZÈò§GDn±ŠÑïs/T(”ƒÞ„Ô­¢]wq„€Ï츽‡QcçËF‹:ÌŽ0Ò±·hÚùÌ—Ú@d0Œ…€Bãew»Ì_·C£mu.ÒÔ!S–œ73Æ‘ÀàsÔÀÂBnœ[çƒC½Goîr¨ØDQ:sí=Õ¥žLoæMJ¯Ž›ú@åfí‹>³ ¹ë„…鈢☬85.!û™³®Í4 ŽGÃ2ÊøœqÎón¤0‘“±Šn>캌Nô¶ÅIð*ã Þ¹[—Ù/£ûF—qÒ<üš¹ìaN|®8q¥"Ö-G;Q6Þ5å>.:)ëÝDºa 4š^Ç.xð±ÃæÔpаÔ?ÊÆ80‹FÀýØäÏFÎzL»SI¡ÉçæIgæ<ÀhÐ#m%wP ä>mž)'ÅY¶Í²ig‘}›ÚMµ¦p‘N­Ùá"#×L]€æE§â€¬5±˜p²¸5(‘ùÿyïG'6 ts ­ œ<Ð5Ór¤2ËEÛÆ0Gc3€鈽Qè&[ë ¦sA;ú7‚DùA‹‚ãÐÅügQv,TsüKé+‚õ9#­ÐïàDÆ%¢Ý9X ñõúq¸j“z£àÇ!±!®ÑmšÅHWq.rN¢ÿOÒ| £¿ò± Ž+õÏÛ‘ $ä¢lí‡Åæ¹¶ÎÆ@Rœ³ÒÔÝÅ…ŠÈ¯Ïö‚ý’í’aÞìZù’Ö%èTi#ó?è)\¹sœwþ» ¸Å™uQ”níŸÁJk=òDû]›ˆ|»§›Aˆç»n3PŸÏÏ9æeíxaÔTßi‹³­ÓÈ@“ì!Ày›g.öSÞ×fSòùMÉLráÒ bu¾Z¨CÎßÑžVLb_I÷”I#Íõäf‰â¸@QSl';d@=æ= àí\»Þ‡×†€GVÂûÛ '¸˜*Xé,Ÿ‚åŒHóyM›NCÓé{îÔÜ’v«®nÖ¥ N~t÷¥Ï˜Gü‚þŽ@Ô€åzÈb}üj?ç)¹ãwü10nã *3~O|gVƒg1Çâ§ÈIõ ]—p#lŽÎ«`s]LtÉEKýÞä°úìäXó %~1™³ž…Næ¦#ì~ úÏ£M-îå}ÕEʩ㠟´R³Nv tbšÀ>?`œ‚t4½Êi¶TaVâ•ï•ÿÇ dykÎQddrÄñD ï&uSÌ뎤1_<¯÷Ý6DœÖO.q0âN÷a ËÇàiZ_…MˆoßåÚÅûw»këïîõýç~/Ï]<}KVê¸ë½{ÔÙ–Þt›y·ï?Ûvˆyzæ¤Û〬å×1£ÎVÝàš{.ÇBˆŠfHAíF63®ÌZ1(Šø•ïltÒQNC+ˆê጖u:öeγÑZ<Óú3N˜æ™#들Ñè8(`ZT«Ò¨TÆÓüÀ/p L•t"Êv Ê™?o„6§ù·¼2ëÒBG`þ['£˜We6{t7VîWƒ íXãåÏè ÞË8ùÐ9*º)Rðw~6œq¿|/ …Ý$)H­{¨)yoþ˜=‹ƒ[¬Ï‘îã í ÎÌí-˜# Ù›7­›OŒÎ5`Y/LšéÐ|3Ù|l d ÀË?¿Ê÷ƒÊÊ–ìEŒ~¹«Åܶ(…¦DË×(€Ë‚„ib$(©vÍçjܰÜ9áwÇæûÎ]áÍ7ïe§2ŸOŸÓCJó8¢Ûlñ¬¡$n_£X'±Fèãæçnߣu Ï• xÎ;õVñ±–Åõz9bž£4Ž¢8ˆ5æWC#ðRŸ(l*ÜBVò#åñ5u\O6™ç¸ ¤ægt.œÍ¤WsÍ LU8“álè«£}–÷Mh¢¦,;O53T1/¸OS÷rYÌí‘SÕr£•¦X*×ûÞ«¹ö·!v±K[GX¬MSñ*t[8 Í7‚–¥8¯]ñ§ù¥b\ê(¿â^w¹Ž‰¹nÚ¶ÐIÚ­£ë˜1Í~ä=[æ´y·¸CÞ9pfFÇ÷bh~×}wô½¬ òžÎº>ìsìsF¡“òŸgíÅæ&ÊŸÍ2ê½üwל‹âÏs•¥)ÏoËšïß–íüSÏ=#VvA\+°«ãuX…n*n¿÷ųèd5t粆’yöÙd*²³|D¯{›¯eNNþcsâÑ^È` Tìü{ßÙu¼ÝÁ¯é 1Ùïø–'Ò·ò§qHqeÔóÊ sÿÁN£<ʤ€Ž~dÏ£±R¼Ï¸Xç»ô_ÿk3ãç#J`ôxaq‡ˆÖ¢œqbŠõÖu[ABëÀ0K;i‡«kof‘¹'¾//Ëë&úËŸRi4}ÿ÷ÿÃà¶ÖàÀÚ©L:eè0ž5œ#þý’êR(ÙP4n:ÅD뚬€ò¦3bPd Z”¢…cÜ&"·ènS[,Z«òh•Óz© M)õóÙ4R{7¥äÕŽTPËm3¸~ö«Øµ-#hÙ$ϺÕqRÙ*wVë÷[¡x঎Þ9!­ßmù¸áÄ£ÞsO„:l^"¶5—UPŽ” ‚ü™ü,ZƒfnïÑ«åÙ›6ó9Cmýø³Ÿ·µÝèóD9î6¿­ëv—É)•_UqÞXùÃ1¦{ÍWsmZlÄsE©˜ŸãôÐã¼óó­Ïv¯÷Ž»ÇòRÈØÑï]?]˜Ö¨X1ßw\ü®sÝT˜-'Ç+Å$ø!:g¶"ïÜb1Šß4€¾Ç÷c¤Çí±PÎx½¨¯#Áz!ãhÙw•‹b¿ÜaÀZÕcèÑŒv<¨É¸s?ßuïÜl_Öš²aô^{ö­‰º IDATnSý¬ùS‘ùÇ(Y˼÷LGÏAD¨Èª?­y#éWâuŒR;ñ-î˜jíŽ`æ Ëß÷.,Ìó½®q§n;ú·š!—|ù/÷U34ÓúüÏ+¼éЂ†L9kÚüâ—;¯ñ¬^«Õ>ó´w5dÍï!|®ÞB‹¾/ÆZÜî~lJ1¤ÇI‚Öµ¢¾,¿4¶îÕ& ¥v´ÞóNüTìí»­Ikôì$ÂP\›¿[-^ñë8Ìöù0W~ŠVÿé®ðê~·ÒÑÒ5¿ø¼ä$Ïùø™VñxÏðŽoy]ú¶w¼¬¹¥f¢¢þ6u¸ÖlFFh¢f:oCŠL·”*[úào|WcpÆ P‹¬§š™¹4ÀÑ1:zpÛ:Üz¨#ƒðÔ-ÀÞƒ‡_£ûWq8ïP?mM):שµ&& ¥ïû¾Ÿ£ã E©qFAš1¶¡ô…/<ó“Ó¶!šn/$Ä,˜-sJ9¯4²ˆ8Øàwã=òð¦?}å#·}Zå#G"nï©{­u!Ô÷’çüþQÌ½Ø¢ÇøÏÅN¿ó®Åïï|ÿ…h“¯é-´e«£ÒL5çõùŸâ¸yifžÇ¼–º7vy>âÁÏy1¾ã°Ôƒ¿óKáŠE¼´K‹åy¾á¹DÏ'ÇíûB,Ÿµéó/óW ³—cÇϹ™X¸ß±V¬ùáãDù¡/ǽtÒq7nø]·Æ=&»ˆáòc'ëÙ¨á$C;n^ïXŸïºGc=îæŠÅq7¿ãÏš“ÅܹNpßøH!›-ŸÖpZuîI¯ùçù¹cœõãDåN|’5õ3/äºe^[ôÊ2z·­z/ŽùÝS-Ïz?så\¹åY/ôi±­è?œæ⺭ú¢I/lýÕ½Æz75ÓºW‹ù¹çZùFSáßm¼­c?‰n¿sÌ÷!Wwûèq&íaìÌçò}j¶‡1¼tÍV¥û,á>º~+ºorÒ;Þþµé[¿ùkñ-8Ö„4½Ž’ý–©§“‘']Ó~ Ö{FR}èœ)øQÛûÿþßßטš¼'Ž/Ó…fêíyqnìrb#‰ÒáHÔúx~@›0OK˜—‚§ß¨w¥ëׯp¸êkcß´q¢üýÀÿA .+iRùÙùëÞ”Þþ-oO?÷óÿK\KyAéÈé×ì™Á’’ç˃ƒôG4½ë]ï >üÍë7Òÿø?ü÷éßüñ¿Òô£±EOÊŸÍ©esK*‡\ôû<ëÃ{ÅÛÇ â"'„}H ¾Ò/£@‰âYŠ)_=Ûö+}•NÇßjìý¹Õ©º‹œg@ïœÐ¿È¢~¿su?Âx’ym?z,ìþ/p`÷ëÔÝÏÜž~öÅ™]ŸT$N"ƒ/ΓœÞõ%9­‚õÕ.<þlµïÇ?³<”o}ÛëÒ·¼ík©'õxëªr³‰1Ñ úŸMôlže-º4بïµá?G©Ão~à{LÙNVê]½,&Ùcžé”ÏÎ٦ź-yíì6L{O;9y ¢K‰¢]Š”{èNö£?ôËd¡ ÿá }Û·¾#ý­û‰té‘ é#ÿâÒëýéßüÑ“¹{¯8̬ٺ±Eë´o~ó7¦'hÛn1çüȤ÷¾ç=é_~üãcÖg™ÑòŒ‚(pmv±‹XôÕÇ jmù9?uªòÖ7bÚJ£óßw‰þ?äQœ^þtN6(â»UãžìËÏýÔñ:ü…^ù+ë{'‰'}ª2¯/ö^ìûŸtnO?÷âÏÀIdå…ìÿÉNGð¢Î@«£ñÕŽÃZŸµ˜ô‚'zïEЩzÛ[^›ÞòOôYó:sƒ"Îxæ çæsµ¬…ë&ñcMŠ>¥ZŠây¤¥ö[ïᜪMZxãTÝÀ©§—?~¶y@¦Ú~·‹–Ì«œ™@ç/Ú=ïqPž­N_ù5¯ Ô4ìxF§)ÎÄéèÙM?ùcŸLÛß½û]ïN?õSÿ à¦Ógü\¿~‡l&}×÷ü )O¯·‡ Þ­B°…æ0<4š>ñ‡ŸÀ±ú¡ô‰4ê¢cÿÙµÇBÞ×íësÛrRrO>ù$½.DÇ”ç{ÝîHs2-uâÅ=/Ÿ §OrÝ“äÉ^ÔÝxâ›·RÚ,‚<ÙóßYSuâÛ=°žtœ÷wC rO²þ÷Ï&º¿q|%|úN^ÆŸ÷˜s!÷ós ïoL9û~ò=pWÿÊùô³ë%¿üqÝ OºgôýCJî>ßS=Ð’®æNdV¾ü©~þ+¼¼¥‡ýLéúEÃã.RY)˜=^–þêÁ8Ç­áKóý‡µ®/ͧ}¡£òx•·~ÓkÓ7}ë¡òÙýØ„MnkMã¢þÍcZ<"Ân™‰g¶Ê—%=¢ô¡ßý¯W®ÜˆÓ¹8[äâ¬îÎÜÂõìÙsq º¼¾:­)=(̃v·ó<…|bx;uXœÞÍ¡“¶hìâ´ûwýõ_KuŠóƒ¿Eûë¹ôK¿ô‹éð}é'~üo§ID½|õ&]Ç<÷„sVr‚g½<œg©Ù^sjr*}øÃÀ5~§êcá‚éTé\yÆÎÿù‹¿”Þø¦7Dè.ÚAò?™þËx_󼕻‡~ŽªVètá“´‘,”ÞÉ·µcÉɾñçý©p bžžkI[AK8’ñYÊ“„Üžý$Ç û›×“ÍÒI¯yRwû®Íu5r¡¹\wXÇ;泌­[Nöh'úT~®ûGHÇ­Õ‰n*jKäSüîY´ øxã_ÌÖñwð¹ZkŽÿÆ‹õ‰“îB Ü/ø{PÏuÿ{ðö“×û™ƒ»Ö5Ýå!O.+Ù?é8égOú¹Xׇpÿµî/µë<¬¹:Éu£‚üd±ºVá?l=L=|?áè—Új?w<·õÐÉìæIÖõáíAY_¹ýA¾îG_ÞÏgãƒëIŸ»# ¾é_ƒSõD8Uvèö‚…ãLJÏŒÖÍñ§hˆç{#œÅ¨ßTúȇ~²amÒââlš8Ï@#ÞS³gš rØç›7p¦˜Ù¹ÛUŽÜðz‹­pŽ[×/gð'ëÃ4æ'}žƒiøsd0·¬}ñ^sm_¼§z)Üù¥{)ÌÒ‹ ^Èó5ï—ãuñÃÔÃR·¾•}ðßùÊ‘•‡¹®~^_LÁì8UOÄŸÎÏÌY©Hx4ÿ|y0¸íüíõ`”{ züÎëö©ÐØÚÚÁqº‚'¶Oó‰N·6K”Û½örÒ÷…Yããi~a)j§úúâPÀ¡¡AnLës@I?™*ßãÀîô¾÷ý¨JzüñW¥ßø_‹v„#Ð ÿWU|ôcJŸêz´X&~E‹Êb} ß‚ßg纔Ο;Ÿ~ÿC~èÿfúø'>Që¦<äð oxCúßþ+þ\ú×ÿê_§'?÷¹ô±~4m®o:%4®ÈÎWîÍÿ\(<Í„~ò°NªçÛFÅüæ,Õ_t§êá¯ããý)º‡iN6Ö_éçó² ÇêþæïÞŸ.ÎyP×»ÿë<̵½ÿÑ|5}ã«$>¬Õ9™xXw¿¿ë~5ï—ã×áaê᯾ýò•#+s]ïoôÓÇËêI¯tŸÓ©zË›ÉTáTµ·oFþ8Ô]Ç©€³@[ë¬tžì`羬©ŠßÑ¢ô+¿øŸ6ÆÇǣ˂»:9Ùzã‹dž‹–À½±±’q¦úÒ•+ÿ>:ïéñôôô’ËéºÞÞ>Ϋ‚ÊWMïü뿘je~7H×vúîïþ.­¿ú:9ñý;ÿ“¿’nÜXã``¾ÛtªîäþïŸ;‹SõûæPáLø‡°§½{Dã~ûÛß–ÞñŽ¿’þê·gzüe¦§ž|*½éëßÄsù$óƒCRxºbüO~ä‘ÿÖ}êT½TVâ¥?Ž›ó]dôLýEßz>_*×ûjÕÙ/•ù=Ç‹3KŸî—g=‹»>¬u}qŸê~î~GÞ}é­6ª ®ªTZ _!Z¦ÛÝg%g¢¬“¶yE;MòìüG£Š=ýQÕO-ÕýWÿZc{{'MLŒç4ŽJµÝlT_´L_]ÙŠ.ûp ¿æk^I³‰ghb±Í9UCAá3À«7§§¶KkóíÝÞôîwþ249O¹>L=úHúáù›éïþÔßMÿßï~¬ÒŸ¤þ¡ÿ6œœðS¤ ßÁ~;ò_x_§êÃþ*ü¾ õ9N=B»ËµáMî׬KqÚñ›ßüæôË¿ôKéï|'Y­Ç¼Ë,RxGU“›œ³*'q«NÎõ¿kq´œ f|ô£KïïýOé·~냹u!û÷ßøÚôÄk^“~ï÷>DýT#}Çw|gú™Ÿù™ôÞ÷¾GìÃyÂÓÌi»#÷éè†ò¬žé$Þ®KwÒëåe>â/î§Ÿ8Ó8Ó8Ó8Ó8û›“`wÓ4ßôæW¥oÿö¿œ6Ön¥qMÝôhX “¸gTI –žÉ"<©p_ô)ð®râ¦Ùôê7ÿÉ÷6jd˜ü²Ùª……Ù4?;ÎÒððgDõqÈî-ÑGÓµ«W##555•–9Ÿj‡€¹½ìaܬÆÁTÕÎ ´Rÿ…´³_M?ýÓ?}Ûø-\«}p õGôGé=ï}gÚXߎs¦ò©à¹~Ë×íºœÂÑ¡m:ßÿþ÷~ú‰¿ýc|§ž>òÑßç`য|åcégögÓùóC•΄íéø@úùŸÿù´¶¶–ãjz©ÏíB’kNêdaNªûöÓOŸÎÀé œÎÀé œÎÀé œÎÀé œÎÀØ“`w=‘·¾å‰ôÍdªV#SµG“ˆhBAÊò¨£L¾G”‘ɪ×éÝ@*«çëBÿûÍú_4ìÁ~è!Vd«èØA;ój'i¯õÕôŠW¼: õE¦jmm=mÑ)ÐC°<¼w‹ã£d¹l5XN£4³˜]jKëGÿ÷´¾Ù¿°=}ó7cúº7} %Þ˜~åW~§ê©ÙZG*Z¦ß#Ktû÷9eÇozó[yÀö´Š³ôo?õÇ1÷Uœ(¹Žßð|#`ÏÿÏÞyÀGUg_üLÍLz!$Ы ˆ TŠˆ€ {ÅÞ]ëº6\{/èZV±aEAì{GQštHH!½ÏLf2ÿs“ w÷¿®ÙûøÄ$3¯žyñó¾Ÿsï¹().Åw?|k.Žmc­!–CeR[ß›™M¯Põÿqë>UU@PTU@PTßOöBÕ#`ß}zsþm€]¨©®5Ù‡‹U{„(s2©,ûk5k$nmSnS#Ûk N{½.:OD 6!-El/'<7‡ü†«Îþ)îÔOGkKþVS_˜Í’@f _0Ôdz®’“’aʤ}vÁËÅý¹h=5¡WÏ>8êÈ™¸ñ¯sLý¡œ€\\/±ÕLb‹dN¤TOéÌŠ$˜DÀˆýT<—`ÈÚ.RÏØDZŒ€m8Y_R:ZS½d?¶CUä¨j_ ^{>³Ïv–¶œÁïw§é‘UU@PTU@PTª@{žÝMùßþýhÞÈðßFƒ ~Ÿ´8yøM¢ pK„+Ä™‚³ed×kb¢_´×cB-èTöx9O…+×ÔV³|ðFŰg©–%uQYY‹’’RƧ§°ÊßRÀà ã=„±ÆbÏP B¸Y¡XœxÂã&50À¾*iÐ;hà`–í½g€‡‰ïü/cÛe©=R>h-m¡ÊŠ!ÙF Êšÿ±Ù$¨ÂÆ¡\Á"@e ìgW½R-pe¾Eb”·G*ÿòÕžæ·CU½‹õ²TU@PTU@PTßQö<»›ò¿‘ý™þןÄQgÎVŒ$×K¸"И±L‘ῲ?;Ëýšš&ÁÜ„çI; &ÛC÷¶;|Ló‹7Øl¬äìö û â"é,ãÛVZF‹A|\ ]­bþÌXsA”¡4ödmÞ¼îèœzÚbsBâD…­.ð%H$%‘„ºZ²8øsÄ °Š,Û¡ªåws)ò6‡™¤> ÀÚÆyì.Úã— ÊÚæ÷.ÿûï4=´*  ¨ª€*  ¨ª€*ÐAh/TÄžªYh·Õ™§ °ŠP´ÔúE‚)ZRù¾Œl’}Ë?y]fçÚî¹}|8%Íkú£RRSÑØBVfãÔ«Ihöž–…bºUÅ%%ì±ê‹†Æ*nÝŒ„x/:uÊ0;“&.)tD%⤞ۯ þØíÖ&² ,¦ó538ÂÜôII}¢|ŠÿèTYtV¸DÄ©‡Ê°§[”\ÜvHÛÝ=ÒVäö–ëíêƒ1Qí;•ûµwôþÕËRTU@PTU@P~wÚ U£„1£÷ ’Ô˜Þ) é‹,Ò3Åv(æNH‰_lL<üÒ+F”ËåF]-!Lú­Ä«ûî˜ÎÌJ$<1í‚Y>Þ(:PM>ÄÄF›þ'gNÅrÔ¦M›Í qBì¡J`pE=šh‘Ey¢Ì€àz¿Óˆ4åA£Ÿ0Eç«9ÌT@†a„Ø÷fÚ_d€V¤lÏÆž¦¶ezV“pJ[Vi[þgçP_s™Lß%âvµ”²Ð@ ¿~©µ©u¸p aFö`õ\í:bÝz5rÞ‘E™Ñe¶m9Ø® ëw¿£ôTU@PTU@PTÿ1Ú UT£™þ ”› y®—Š:q d¸o#ÇIIâŸËÅA¿d#™M%¨â^·—šó²°í`eRÛ8‹ŒŒ <þøã8âˆ# X™ð ²]8Wÿc÷¯^®*  ¨ª€*  ¨ª€*ð»+Ð^¨uàŒ5ˆ Ukžå#azFq1pÂïóÃÍqSZáâ{AVÞIõTé €I žmÞãG…SYöW__2¹GIæ[%%ij¤ÏOûËg A *Ä辘˜df¦ ²ª±±±(È/"Pݺuã°àj$¥öÀqÇÞ…ò*Içs¡Wï^X±ü;L™r8Þ~ëM‘Ñž48±˜NšœŒdÁ E™DÁKfL9HU6Æ©›ÚE‚—©]d …¬)ë‰ëäÀâÛo¿=ztÑGeæa5·8G» öã—ìÅãò Àë ’Ô‹œ7S!I¶’¦3A+'ÏÁ„dp#“îÁ7E³õë×3„#Å4³ý¦À¿ßýÓPTU@PTU@P:¶톪ƒúcìÁ{Àíô13"Ê ù5lÐR'úR¡ÖÔæûÑ®êë}ÆÁ’\ ÇMÙž~â¨p”›é}üWI‡ª9@|B,B¤³ÄÄ”••">>ÁUxF!NU×nY„/7Ý­J ®EïJO•€…'&S§ß„””d¤etÆÀ1þ³¸â²K°xñp;b°zõZÔª Ô8I{¹]{ ..ŽîS~üi©;ž;ËmHLJA·®Ý V±LqóæMˆ!1¥óiiñ8ûì3 p¹¸è¢‹ ä‰ÃöÓO«¸ýN7KK>…Uv†_øÕÖÔ™°„„¬^µ¹Ýº$°bÍz¸¹rÌÈìį’A–I®\Éõr»Ò‘ tì;R¯NPTU@PTU@øƒ)Ð^¨;v >x Íœz¬'挘4¦ÝÇ ø ®p‘›$ÔO²d‘ö&3ÍéÙyG…+ÊËëgÜ#¿¯‘ƒ€}æ5/Ã*jjjèÐ¬ç“Æ­ôŒZalÚ¸99½y@«¯ˆêÍñ8眿£kn¼²àU44У{7ÀGG'èoÆØ1cQ¼­€'Æi³OÃÙg^Bêóš÷Ôý¸åÖÛè …ˆÂÁ\wÎ7Ø‘©Æ~ÂØégÌÆgŸ~mÀê®;ïÅÄÃÆâRLáºuLß×Ç}‚³Î:—ìLU¼z1øM .×|÷Ý÷`ÔèÑÊK+±äÃ0nìh„™€xÈ„‰Œ”/CŸ¾}¸Þ]ÈÉÍfHG ¾_º Ç{,z÷éE­2®™@™è£A°¿8=]U@PTU@PT§@{¡jüøÁ8ðÀ>äƒÚ–ö¤HEœ”JH…”zØ‚ ´§™É€Â. |Ÿ•n‹œ–„?q€ÜhЀtŸJ t4pîÔàÁ{à‹/¾dUŠŠ‹ÍΥ̮¬¬É)Iœ<œŒe?~l)'Ý«­Eu8댅Ìpwg§+¡ë­7^ÇìÙ§cÉ’x”l"OÙ0 _<ÿÒ‹¸ðüëñý÷?`È!˜ûð8ñÄcðáG_›ö©'ža)žË8PþFì1$ÅEÅX·¶˜ïÚé¦%³Ì‰9s®7I„'œp ]$))´£¶¶6BP;,ÛçT T¹Êñäã'øåá‚ /ÄܹcÅŠ1çºk0ÿågñêË/áÚëo%¤}ˆÂme8û¼3 bɘûÀCØgŸ}xüDÓO%ºx%ÏžZýUîÎÔ RTU@PTU@Pþ ´ªFê‡èEs‰€Ä*:Ë ±EHÚ¤ôÏ´'±µIfâJy ,ÄÑ€òÑtjb© í­Å'„%D"!¦VÐ/DÖŒîÝrM•“dVTľ)ºBÕ5µz÷éÎþ©2’Y!·¦æ"dpû&:=Å%!œÎÇtyl<@=òzcËé?üp¼¾x‘ŠÍ_ ¸ÙT÷Ýw;zöŒÛnØ‹ýpÚ©ÇÓí©ÅqÇKõÐÜû±ß~£ðüóÏãÛï¾¥µ[ó €r1²0¤cu7]³l̘1%4¤)½ëEff1èÝeÃSO=eú¹fΜ…×_Ëü¹þ,Zø Ö¯Y'Ÿ~|øfL›ŠE,atpè׸CÆbÞO±ô0ÃPª|h‘†¶ˆYÚF[üAî>=MU@PTU@PTU (Ð~¨ê‹Ñ£û3¯!2à·%@Ũo`Ÿõ¶šC’¯À™UæßfJ¥LPøÈöáûdžKK+L²žÃáBËß22ÒÌ ªq°ªÑ%«7òó7qÖ:Ù9‰ÎVJŠË8ߪ™¿ÇHªo¨…¿ÙŽË/þ A7á(À¹V}ØCµ ‡M:ŒPõºñža–G‹£™Mgiû\)j=÷ì<üÉ ‘:gfá¼ þ„Aƒa¯á{¡Ž3²N:éX¼ùÆÇ܃D«Ë—wÞyòrsqøaš|d* IšhY„4Ã-)}+(ñnF#Λ÷¤Iœ9óh¼ñút;Ã_o¾‰Çx›7âo=7Þ|0ËV,7 ãáuýŒž={³K¬ÂÈ€0¡×í € UàïQ/APTU@PTUà¨@{¡j {ª¿§)ÿ³Æ;Éå £øÙ%A}2—ÊÏA ó{£dHp3Cì19õ}úöÄG}„n¹=ác-e³4É‰Š½ÖºìÎ%ûÞ•zʪ€*  ¨ª€*  ¨ªÀHöBÕè1Y…¶=aˆ)bIŸ“†“I§a#Õ{õl°’ŸÅ­²Ó ’ò?3ÃvÑÂIa?Kþ’“2PXX„¤ø$@<ïá IDAT®dÒEˆål4fV½@ qL¬­)â «Dî „5?o øta_R€¶WÄ~jv&ã¤ãŸf´ û•<`Š^o¦ä­ÂÑGOÃÂל„ì"ŸÆ±³ŽÅe—]‰Y3OÂÊÕËX,çÃá“Ų–"ŸarMGL9?þ¸ë6lBlpûw£w¯=v1ŽCä4ãú\{Í_0räF·O"äUFˆ©M韓 d\tö¾7.¼èB&ø˜U¢¢¼xòÉ'XN`üñxå•×è@­4=ZoÐY««­Ái§ÏƇ ¯xeÁK¸òŠky\ûû#8jÆ,$%§ªêM  ”û ˜ê¢ ¨ª€*  ¨ª€*  ¨¿¯í…ªꇃÇ1RÝÕhÆ5‰1#aw2ì×*”6ù>‘D@±d§Ñâã +'Û‹/Œ ''§r`n˜åzœKÅÌu‡åmÑTW—ÓîŠjq„šMĺ?Pƒø¸d¦ä‘äBNö]…x@¢Y'öXЖ„3g?Zò]2BO æ>ô 1È¢¸ˆŽ¥Ù§ÉÞ+F´GŰêvL˜0š•Ï­R6|%3’}& ‹ÊM™ÝÕ¾“›ÁrÃzF¹çš$Âk¯½‹ÙûÔLGJ@Fzœqn»í~cÈm¬Xµšt|Lãa¢<<õô3\o_Œ{V­\aÊcb¼xì±ÇØÀI'ÌÆ ìÝZ¾üGÜxã xíÕX¿n-Ë/ÄØ±cXbx;Ïs3Ý5´ûî3 i™&C&+K—&ÿý¾TU@PTU@PTU ] ´ªÆ2#FöBlL³yž±­G*Ú„E$”O)ü¬józ£ø¢Ý„ó [ˆ¡ÒÄ(,Ûó/Lá[„¦ÖÙ Uœ¹ärJ{bb=t«*ø0oãAâÌÎ$H"!AbÌcÌü&7ËïB¡z$$Fs€¯¥Uµ,ë[ÌDÀíe~»ºj»ÍÁ#ò¸ytªX}FCÇ̳m‰Q·ž•%´B£ã’ØTG˜ ²*Ïaz¯Äl<6>b} ¯¦Y™QW]K§»t!<Åcka¡vUGK6Þª”Ô8–ÿU ))‰ïq6&Âh@jZ<ê}aƧ?‹9å—¹‚g`¡$©Op¤…Edä"$M/‚)8¬W4Ô"!ÈFÊv…LÞ É”ã(Éþ,”Š|0vÓÿd5¡í *v|­-í JÈ3B¶Šsm“Cþ™"g ŠhôÛ ªõ˜@܈}ÛAJ~èü÷áß?s¥º*  ¨ª€*  ¨ª€*ðïW ½PuØaÃqê©“°qÃ2ãJyX'Påck”D¨[ÏÑòÌkþFX"b¤ØnºuDØÁ‡k޹B"WêÛ»/V¯ú™NŽ“ìcêÚ•@UÇYUŨ¬ªf™_223“Ík99]±yóö%E›>,—;ÌHõŽœ¾È¤ÿíªä?‚S‘ÿîøHo=â[ aý.ß[°«Eq+e¯­Eª”ñ†³"ÊQY­KD’È)“Ò±§f×îµ£¶çkyH;ÖlF€ê·¡ŠQ;ßNâæY7DÄ­kÿ²«ë«ß2Û¿?]SPTU@PTU@ø£(Ð^¨š8q(“Ê÷§ëTaÂ(Ä}’ Šf‚„Iÿ“9UJÇv_ Ñ´CI¦ƒ¼`ܹ¡‘«¯Ù/œ”àEcu)ãG@cÎ KIQHKMEn^>ýä3tîÜÙÔ Æ'Dú«$â|Ó¦-ˆãÎky:Ä&tfô8ƒv UòX›]AÕÎÒv”²ðKN{÷…pÛ÷Ù“5k{ùœc¸ÛÌDØeWyÍI[P7,I^kû¡E`æ7€•åJYsƒÛPO[§*¼;òÚI4SºÈ}ìJ¥¶(jm&ç.4.7ˆ.ª€*  ¨ª€*  ¨ª@GP ½P5~ü`ì»oۚЩS&[žü¬Ê+73|eÆm G;:`E\m}µá§ËÁ‘OÑhl dÉ´¦[o;”N•öpBîëf\x°ÉÃì0:efr'ÑfçÖoäà|‚U&b㥪)É)(+“ÒM±aœÛ‰PõÈPå&œ%$$¢‚'±Ð~ªþ±(M¨V2ŸPÕ66B.Þå’þ+)y³pc» Ôzã˜rÁ<µ´Nµþl'x¹î¸D0Å|h-Ùþ7úAí˜Olû,/̸j¿U­Î””ùµìÆÚ~w—!ë–þ²H¹åosÃ:œ^ƒ*  ¨ª€*  ¨ª@ÇS ½PugT7¡`¹)ýs1xBÄcc Èã3ñ?Ö(*+Á„W´ŠŒŒ²AÞ—¡ÆÁ€`' e-àÔf¸ÏÖýÉþy‚2Ó×ZD¼,BeiYjjk[Ðpûû»*ügnO™ƒå ØDÛÑ k¶_ƒ*©í”þ)q©¬^59}§ô§I–¾(ÈòLé£2yúüÝO´x¾òª«pÁ˜Ð«æSÓmTU@PTU@Pþ›h/TM˜°'ÆŒéG*B2M#Ÿ¯‘\ã1ólëÙ%Á|â^¹8÷ÖfgXž= w”ÅLöÆ™À<Ûƒ÷–a¿n—Ÿã~$%F¡±.z„PQYŽììt‚Eɉ X„)_±¡´Šòj6iÐŒçCºëÔ KN*ÖoÞ€sÎú¼ª²³³qè¡^1>8ßÿ¶r°¯,¿UX (ÈÏr\qª„\´ÜLðÉ.Bˆƒ}`ƒP^‹ïMprV– ÎÊꌗ¼‡3Ï8K–,iMüÛùƒokÔÈì/2Hë2xðøè£%˜þøcÓ3fÁœõ½½7’•("ߥvS–H— W_}5zöìŽN8ÞCJ-¥–SÊó,àùÇrÃÈ‘­4BéÅŠô`¹ UL11¤ÅãðµØØÔr@²9?…¼î¹øâó¯ZÊ;#3¸tQTU@PTU@PþÈ ´ª?„Ãñé¹ÒT#]*(1‚ä9=!!Á„Sˆá"Ü$b³ wH¢CS€­7O?1“PÕLúª"49ÏîpsꘘžžŽuëVqLýs§càÀaLú+E}ÓË.;ª«Žaìzbã\LÃp£ŒƒÏ>ó4ð2×éo{”'’„ô´4lÛVŠÏ>û'Ÿrš9Áö@•VQ´á’“Ll»„fi!5ú8xXªîÈ ]nNö3˽¼¼Òˆa#°ˆ˜ÙÙ¯¾ú ®ºêJ|ýÍRÎÇr³l3×ã”â6Ë?º4LRMKKG.{ÈÞ|ëMœ~úxýõE†œˆ-[ò#ñ눚Y*)ð„,µ˜Bµm—dÎì’íÌùq›rº^âɇ›@ÚMÂ¥—^B÷¨Î>ëlÎ ³QïT×Tš·Ûj»‚*9w¡k‰Æbb£@eee ¯?‡Ÿ—õµŒ®¯%œ‰Iœ–˜Ás¬a~ ¾ýö{taâ£$žXóä?"=wU@PTU@PTÿmÚ UaÚ´á ž ¶¦¥¥e|öOm©Ìk6Ï×t¯fÏÙvšRµ'FËÙ6e{cálV•YÒVÈ{‚d­‡XKH‹'†3›|êëpÄÂHIñ¢¢z) ôxݨ,sð¡¿šNPºyøû–Ž=úMÖ"zðÌsóù0ÜqÇ]8þøãqå•W´öÂ[o½ÃcÐege7)yL¹YrwÔ‘30îq4h aȆ… _Ã]wßm€¤+éœsÎÆ¨Q£ÌÐâ°Í_x ·Ü~'ê?h¾xä‘G J™[Eym†94sþT´¡®.Y9„%m­‰ ”uë7s`¯Ÿ%j9Ü„¸ø³­€PN^wì»ÏC„aà: Kq;çšk®áƒû-åi-±æ¦øì——1cÆàÅ—_Äk¯¾†î€õ‹Q˜>}æÍ{ß}¿£Ø—er'`þüçè˜U£_ÿa¸ñ¦¿bÎõ·âá¿=¸!" ݺæàÁ¹÷aÎƲVjlª•fþVÛÅšå$¯E\+q’±ç¡Æ=zì±GpÑE—`éÒ¥Œ–-[fê.D÷êu\~ùåX¿~ˆíÍë= ãÇ'Ül6ûzþùçMóÛM7ÝdàeèСøðý÷é˜m2¥zƒö¤îqÌÉ?ýúõ×_Ã}ד’ë±rÕªH\cKXÅ®nÙ÷sÏÎgÔ}î¸óNç|úé¼ðÂs¸ï¾{pËÍ·à‘GÂÓÏÜKG2W_uâcpã_ç˜ã xŠS¥PÕþ7¢×  ¨ª€*  ¨ªÀÿ¶í…ªÃŒ1c{#Ú)÷kf™&!9¤±ÚNªÏ„¤š‹žš‚>òQ•©4 7óõ Ó ^z~ZØë1€ÐÀF¬X®é'‰U–W!‘‰}NGjkX†Æ~%_Ëø~ á y=²8»j+çTÅcÝÚ èÚ-Í<ÃÆ?ƒêF`̨‘¸ó®ûðÉÇŸè¸ð¢ °qãfTUUµŒü q¦T›ô¼V±€xŠN”s챨(«"|4bM´{˜½^ƒNºj\ìñF³ô͉»î¼Ÿ€U‹i3¦óº|Drè-~}!N;ý|ñÅWF(©”0 Y,ÑÛö,I/S¤.²Ò{ ÁW_}AP:ÔôeEb0l&bý©§ž¢SæÅÕþ³ÙWªë®¿ÛJJè¤g JÖà™ûÐ\¬ùùgSÆXÂ÷­&–nr;àš«ÿl\¹i3Ž2¥|¦¯Šû ™ÁÆ‘>.«wª-z½^Âå üàËØ×v^yåežçxèá‡ñî»oáÃ>¢E/â·ÞÃñljW|`®auúûßÿnn¹‘vU»›éõ¿ý§ªW¯ ¨ª€*  ¨ª€*ðߪ@{¡êÐñ}1vlOrŽ´ä¸Ì³¹ÌäMMMG\lÍ£ š&¤ÏÅ*²††:>³7’\ðû'ùK><=\]]oJé222™ü·‰%mýÛ˜»ÞÀ@†SÈ÷˜(:X(®lLL@Yy±éß)).Åžt^ò kpÚ /²§ŠÉ€,<`¿ý™x Æ:†Ã„+±beK¥#ÆÞ Ñf‡YQ­n&ÚxñÅçñÃËpÃ5‰v¨îä?!C?Á*›.Ô=wÜŽáûì —”ÿñb£=±x÷í÷p䬣YfÈ=–7v#Ì|HÀ˜=ûd¼ýî¦&ÒJÜ]ˆx$ä6‰¬1dðžøâËÏqØÄÃLP…€œ¼@Õ«¯¼ŠQcG@ëÌõŒÅÅÅáwbêÔYfƒaIàõÈéšNQP°'p"–¯XÁ ÌäÝvË­èÙ£¦MŸa@мî6‰<Iï‹$õEncÎA¨|ù¥—PT\ŒÓèv-^ô>úø<ðàxûí7°äƒð5˜ÿü+=êüøãj“±ß½bš]ºd-2¼xÇXÄH‡Xü·þOCÏKPTU@PTU`GÚU>K;¤;ŽœyÚ« ¬ ü°ÿ)ÀÂá{ÃQ3§á¹÷Ò-Z‚E¯-ÀåW]‰ö6D°ãL(ƒ1-°ðìsOc+áã²+®â±˜Rhw›ÀÆÅï½iãÚ§àþûïgЄ /®½æzºj~L˜<1’˜‡ ºª>ùäcœ~¡ê÷wP¼=¨ ˆ±ÇƒñåW_bÊaSð%H`“k1–üÍ7ß2å‡wÞu+…%ðÑíãð/E”cåŠõçK«ÌÝ5Éß46ª*«LKÄ«‡ÐDsƒáxóçOaø\¤Áª¶¦ž¶—›+¹àÆ%Ø%}k×®Eb|Ó÷’°yÓF˜‡–žHp¨g˜Â6p¥˜8îm¥¥`¥ {œÞã~—¤™+½º÷2¡W2yïå—Ðͩı'ž`ô§Êš+Õö± êª?_‚Ù§Î>¦‹ÙWµÐÌÚwø0äo-Ħ‚B<ÿì<Â_#Nå:²äuË£ó¢)­3N…hæydçtÁÇ„ª3Uï¾K¨jqÂÚþµbp¨zçÝwZ‡{˜ø§K.eãX†BL§S´Í Ð^Ã÷`y_©m&š}ôèÑì1û555fPò=w߇^½zãCÆ›šMË ºõÖÛùz/Å€ 9éU à¬ÅÍ$ÄO<=^·Ür;Ëý* ÕŒféÏŸ?ŸŸI9 ÏÂ[‹ßb_Û[xø‘¹¦¿í«Ï?Å÷ÜeÎá–ÛnÁm·Þevyï½wáÌ3ÏERR²I%Ü>àxÇáÈ2EZU@PTU@PTUà @{¡jÔ˜,LšÜiI1¬Ðk4)ÞÂ"UÂCbbHž›ÏÚ>'Ì›ˆObîAÜ2¨š÷ädnâbÙËÉX{æ§ÃË;º,…[·¢GÏž|ÍÇHMNåÐÛF VÓÕÈæAëù¸ Ã"“„‘xrgËÿs¨¬>ú8œd>ð7#—ƒ„ClŠKcì˜ñ($x4r¸­8b¿VѽGžé÷IJLÂÏ«™’G'§ÓóN›=Ë~\†ÓÇM½+øžŸ ˜Ûµ7m9JèÖ6iJ«S•ÄóÇC† Äg_|I°«Åí·Ý†5ë6´tLýú­‘—€çž{éé„ËÍ&ÔaÖ1³Å)Ê2oêU¦ì?Ÿ}þ¹qžrózÐJœ„­…Eøƒ Ú˜‹¡Ãö† k «,düú…^ˆ×^[h\§Èªf3×ëÑGÅ·ß}cܯ¥Lç»ïþ{[S%²ýÅ—æc@ÿ=0jôA&^€L JœªÂæùç]ˆ·Þx‹®Ü[ è¸ß8U_ñº/þÓŸX~y.¹ô"–Uþ@¸s›Ô¿¼Ü^<ŸL3p8Ô,g•ÿÉwË%Û]¡ä¯k§k¨ª€*  ¨ª€*  ¨ÿIÚ U‡NÌÅäÃ{Ó’‘GLSàó¸Œ;+Û8:£ÇÌGýXF²¿†O>}ŸðMøc Ábÿ# J5xÿ½éJåšÙRÏ=ûBËœ(IðÝ´N˜ûîGp[j mâÄ#èzÝlÞ“ùTY`¥PõŸü€KPTU@PTUà_W ½P5ñ°î8bZ?45rž+M i3Šc¤zCcŸãe•ýUi4Iü(mÉ“¨’œ‰¦]-f:Ø}drXl­ªJºN,”F+äë`ãU§Œ4†üÀT¸ öëÄ÷%–.V ©ë× «KûƒœýÄyJŒ`މå +?¡j+­%XHrÞžƒãh::—üé2S¦f‚Zz…Lm"ײÛw~ŒÐâöWí6žG¸Áœgs8`æ\™àsþGz¸Ä1“ÚG„iűcKz†l¶f¾g7e† Ûˆt7EŽ)@Õ^ÿEàI`¬µ<Ž''©|N¬|h&þ9 WB°momCÆÑÛxažW$í/ ©Í$pÉútåefU´D§Ë5YWÙ›uD’ÿ²Ñ÷nЧEéÛ­„ªeñŠEÉÏ@ÀLêLȇ¤%ò|ý’`"Ús[ ½‡Ùt'Ú˜´A£h¨²®¦½øù¯ÿèTU@PTU@PTEöBÕ„Iy˜J¨ª«ª0Ç.º6)©É(,ÜÂgh[ ÒQVZCwŠ}TŽ“kÉKqLþv°­§ŠlÄù»óž˜NHô`íšÍ|¸·!).“%g•t¥Ê°ÿ~{aKÁ:Œ›0OÛ«†îwÆ« ²T0++‹á,JJÊùÝ »§N<ö6qE1 CBÂ\/ƒýD£Ü6±Îúyw‚‰ƺ È„ÃXqÒ%‹ü¾ëŽ('Kñ$м‰ç( "ç Á ÒÔà~+&ˆ#e‡çeÅŒ[?›øuœ+æôZÀE(¡ÇagŠ"!J Ò@#GÌyít"<°ÀŽ@¡ÔjJZ!ÁŒ5›Ö9„Ù×VK ®,eÿ‘Èõˆ#ȶ­í‹E²Ò¬cɺBk-¿=¸â·ªõ¯ü趪€*  ¨ª€*  ¨ªÀ?¯@{¡jâ¤\:¡gIp ÆO8øŒî40/žUyt­Bì£ò¢²¦‚í3’ÂÝ„úú>ÛKÏŸÑŸ{úŒptŒ%y›ÍLªÔä.ظù Fzg!>!ÅÅœ,boT¢é ŠåŒ(†[E’“Xs§32u˜LLJ~;‚¶dœyÆs¨§IåpyÑL÷Æfg="ÝSÞf"Ù›ÍTâíËä¤_IÜkf”ü.ÏþA˜]ØLnöTI¿Óö¶TNi [°@á·b‚qŠZN×Ì’-dc<Æãic¹™SlyA È¬OÊ2´p%ºX-Óº}ë¦@b>¼,>üÀÍ +þ*Pf9væ÷³É<Ñ"rž;{Ö§·Êz7²íoUëŸÿ#Ð-UU@PTU@PTEöBÕÌ£rm4Tq®T½±¤ßKµäžÃ9nW,¹(Ÿ¹õ¬âKEyÅ6S.&M¬’³ÝvÓÔpç,&û±‡ª²²š4M KâÕ“öP„üü†? 41Ýâ°T¥§¥Ó…ª%µ¹QT¸ÁÀUuuk S¥nÃu׼˞)Ž•h¥`S­ÑÀkC⸴ükæƒz¤ä¬õñ½å‡ˆdAU˜"‡tzÜR IDATnœyÄ'híÚé²Àa;¬YP%`q}d[–ܵó“Úþ¡†Ú@•¼nΫ͎¶ƒLdçÜ"3æ´"C‡#v–°–œ@U‹â*µì?Rê·ÝÙÛîJE^”RB³w£_KY¡lÏvž)µ½¤ï/8rm»Û¶C5ý¯7‹®¦ ¨ª€*  ¨ª€*ð»+Ð^¨:tB6ƜΩ9fЯË%Ï÷~¶6Ñ…jd _ØÅмtòP!bXAG&*//å3¸‡†Ûk8gûÛ'†ã؇hdiû•‚vtÊtáë¯>å<ªDdvê‚MËéR5r8l– ¥Ø’¿‰u…nÔV•ÑsrÎU>Ã$úÓ©ŠÁÅ,Fu={vŒuÆ9N¡ÚHÙ†Ä/@’þ"YB¦¼-2dw{™YÄQ± Ê”±YŽùJ¿”IæØmCÔŽ `Jà„cä?fÛˆÃÔ^¨ÚÕ]aXDø¦ÍN¶;LØYfZ‹}d™9 i¢jÙ¸MMb[—ªíq-Xkõš¤çÊ”÷‰Ò%ÖvÙñÊvå4‰¾­=h-z[ÚÈ÷oDS:ùkõš¿ûŸž€*  ¨ª€*  ¨ª€*ðϲ»ÒDæTMÖË|9ÚL•d?™!Dæq£t[#Ÿ—£97Õyµô‡˜È °knv‘…êMŠ·íÅgÏ ç¬bLxWŽG=M‰µ%½q ºduû¯8ƒj‰Ígâåý¢¢­È`…Ô­uÉîÌž¬ HOÏÄæâBüåÒŸà·x@q¤è „y2 Hhfí¡]&›+{†¥‹-XcANäb·—©I©Ÿ+éxÉÏÁ Ïôe‰ƒ%nSß~½™þ7†ñ홿Û ªŒ§Å ·ãðÉSXÒËTÀ猲XèÑÖ‘ùWnÂ]q„—¬š¼k=ú?bkÖyh“Í,3«…ª¶ó¼ %‘mKûvE›„Z;“²H+˜â×*QK¡ê_¹;t[U@PTU@PTÿ”¿æTÉs­›æÄªñ‡v=ebÓ].;<^Ž‹òÕ¢ºRfR±A™™PçÛÆ6#¦˜3°™#©jø¾ÇÛÛ¯žÎߺš1êñ\/2Î;ÊËYJ›Í0XÙ())ëׯgTy6©•|åF ûªêêêé`yѧo'Ƭ¡¾¡Áp.<÷kÂÃø/:: ¼€ûï|ð¾ú ?|ª I°‹¤Øµu©v”Yú“8tø“O>Á)§œ‚/¿ür‡û±cÇšÙQûï¿?Ö¬Y³Ógé?b±Åq㉿?ŽhBáŒ#$œE"Ý ¯´”JÏ–€[¤wëÿki UÖü;ŽÕŠZª~ãnÛsãýÆ]êꪀ*  ¨ª€*  ¨ªÀ­U“ï‰Ñ£:!ÚoÊÿ<KúÂhd„ºÔµÅÄij äIC£Ö}åTÇùUÑ„­Z®KžxoÑ©áÆ†*ÂMi¬Áôã4²éJ‚d“4kI}›ÀVbR¢q›<^¯ééYúýŒ]ïN@©f¬ [¶lFf—>œôüŒ;·ÓaêeæS½Á¹HGbø°!Øó¤Ž;î$5m{„vç€ÄÄÄp(m'¼÷Þ{¸ôÒKñî»ïÒ-󢬬ŒàMh UEEE¦œMl»¶KRB¢ ªèѽ‡¸ï–~×Ú¨dk Á HK£óÆÅO£¢¢˜hXò_{艩ª€*  ¨ª€*  ¨ªÀ¿¦@Ëø$ùO¾!a©–LTGcÈÉŒˆÒÒYÍ—˜B#)_#[—IbÂ4ÔK‰`3êë¹.ªÅ/ŽŠ²{KB%âããP¼­ êKÊ씉իWÑêËЊ|tëÖYíÉœ \G2+c@E€ŠFï>=è&-ƒ‘ƒ×\µñi‰8õÔÓ€‘ÄUÓqïÝ÷àñ‡ÀǤÀ>úOÎ{ ù<Ư-+V¬0 ”m@J.Tê:ê(|ÿý÷Œjm†ì^rÉ%˜5kuÅáÊ+¯Ä’%KZw=cÚ4ÜtÓÍÜÎI×l.¿üò–t@Ì%<ÉþN>ùdœvÚi¦°-Z„«®ºÊ <ÖEPTU@PTU@P:žª&æá ƒ2àM%ÛTÓ¸i4qê)䙄¤hšN¬F1„bÌp`_£•à ÿuÐDŠ‹†íé¿M§g0éÑé2Ÿ*.>©éœÌaµ55µp»]¨!Èäåæ À‘^¦¸¸xlÚ´…ñœ¨«oBnnÄM…«qÎé+‘Ó½+n¹õVôéÓÕ5¥\…ýöÛëX¢'0uñÅÓÙÊçpÚHÞî–cŽ9†¥‰)¸ì²ËðÔSOá›o¾¡ƒ7ß|“%‡¥ª”¤P@j̘12dˆq°ªªØ Æ¥[NWLœ8'|JK¶aúŒé–¬"¹¡C‡â7ÞÀ#<‚¯¾úŠõ’™×Î?ÿ|–)Êc]TU@PTU@PTU £) Puð¸n92™Lÿ+§‰#@Ç*¼f{+õbLFÿPSÍ꽨VøÈ HMsš¶(á#ÛÂù3ÃÁ`#“þâ¸b%¨ýöbÒEWö3Ø!ÕLø‹æÐߨ('‡\U›)ÂÒo%exåå•´ÃÜ|­yyÙ¨ aÖÔåhà>¥ñå—ðë9|öégXºô ¾¯é×Úmh_›OÊ !ÃL'œp>ÿüs³†° õüóÏãÐCÅ×_ž={âÃ?ÄìÙ³ (Y‹Àß¼'Ÿ¤ÏRÄtæ|­ç &n× 7Ü`ªššãŠížÑÑn!½U@PTU@PTUà[ª‘vÆ´é{ ÊKÓhÇDE#!ÑË™»’ìg'DÑ™‚›QênºXNB”˜N5ðÆÖ¾¼d¥z¦ÿ=½w8.>^›NU7äàôN(È/bª_6Á"@—)„(OˆîU=ü>îØ—o²PkrÙÃTÎßC¤µxÔøJqò1Ë‘Û36‡ 7̹wÞu3ÚS˜‚sÏ;+V¬æIERï~)ÒÜ N° êÔSOÝ¡¬OÀjÔ¨QxöÙgé‚íÇŒ}ïb J\¦×_ÝÃÁ4·Ç{Œ e1ªšXêh–ú‰ó%åƒ&L`¢a†éÉ'ìÚk¯5ýUº¨ª€*  ¨ª€*  ¨ª@ÇS@‚*9´;?¢?\,﫨,£û¦Í~*+ë¶2˜¢‰A~qäVè5Ô›J>I÷xÈ,ÿ“ 8ÛSå…»wÏ#5°>Ñç„*·'–T²:gFÂXµ¼ 9Ý’QV^Œ`€Ôæ.BJª¤`4aãÏYHN‰fÓV=rºf`ÓÖõ8wöj¼ÿÑèÞ»;Ä~-Úcqq1&-£¾®={ô"ÀÕ˜OÅšy»«è× J¶¨’Ò? ª¤÷jg¨’¼=/¯úÑGm…*‰h· ÎJÿ“²Æ½÷Þ3gÎ4®˜ì{Ù²e#Þñþ~ôŠTU@PTU@PT¸U“ëKs¥7<Î4e¼”›FS3*k6›Ô¿`“_v¸]1¨aÀ_t´—`å4œoZ£l‹^NHLDçSIZ_€5„I)IìWÚF™Ãð2ÍÂãêjbk8°Šp†ÒŠ¥¦oiãÆÍ(ÜœÁ  /ûžbÙåA­°“ý=ôˆbàÀþxèápãœë°ðµøæÛ°ü§Y—‰-o1¬vù‘Z%~»sªÚBÕ¾ûîkœªœœUçw/^ܺ_/(ÛÉ©²JÄ<„®uëÖ€êß¿¿éÝš2eŠIÔÙLú§ ¨ª€*  ¨ª€*  t<œ¬|›2yöÛ·âÃ짪e‹gP±ZÏíd¤zØÊŠ:¶E™*=Ðɘ@ˆ å™t² Œ(¶%ïŒ ×Tû8×KKËÎ:Á0²»7cýº-LùKcù[Ñž¾h$MIzžÝÆÖâ/\ÁŠj?J‹¤9‹d¹k‰$/¯nÀ¹g¿ú&¦ü-ùïð.n¹å¼öÚ«˜>õT×úLlûöž*þLÙ¸XN•UÌ›7ÏD«/\¸ÐÄ>üðÃ@\d‚)æÏŸÁƒó¢¶´B•”ÿ TI9àI'œÈòÅ( 6Ù .~ñÅÙÀßL‘¿µ€i#M_–¼.Ž8UÊ!P%ó¹tQTU@PTU@PTާ€@Õ¸±X¡Ö‡! 4˜êhÅ&p²Ü-!1޳«*ÉHAæK°Þcšìd'ÓÃű 2ÁBfVÙÞ]4†<ã¦S“„úZ?ø‘œQÍ>ªÂK¬¢œ=¹ã¤g¤˜$?OLÂ!ŠŠ+1tð8¦d”³Oê'Ö¦Á—†YG?k†î^yõŸ™Ì÷J¶à¸cÁ½÷<ÀØöf2Y"peAÕ/9BâŒIdº@’,7Þx#¹ƒ btû©¦ÿIz¡RSSÍ{]K—.exF.¹èb6œÅ›¦2’³¶šTш9sæ`ã¦M&R]Ê>úh“ü÷é§ŸPÛ¼™Ö0­Žw驪€*  ¨ª€*  ¨ÿÓ 8höŒÜ¿† ëÂá¾~¶5y9¢×NÇŠy¾ºS1ÔÇ.a²lŠb…›“}U’½à¤éä`Ø…í¡»{sNU¬  Ø{ïX·v šÖ!.6‘;Žã<*?j*ãL\ºÇ#ÍYeÈÊvÒ¥j@ˆõ… ¹æ ËW,GçÎ \i8nÖ|4Ò ¡‰%ì-¡ÿÄG&&ΔPà΀#¯‡B‘2ÂÈuÚZו߽¤Gq¤Ú®Óö¢Ün Æ!Å<†ÔBê¢ ¨ª€*  ¨ª€*  ¨ÿ; T:¨/Hcu^€}qð792ÊOX’/˜Øôho4N“äE0E\Ü**1ˆâcã`»åº!„*·™œš’ιL2%ØÆ˜À2Ƨט‰íÌð 7bIjþ@wRCPib#WÝ-i¦Žp•Žâ¢ ‚T<®¸d ‚,l&mÙlRÚG`Ù)CÝЗÿ7”± iO~ûÿν£Wª ¨ª€*  ¨ª€*  ¨T@êçÆŒ~}Ód_UŠ™SÛL~ £–e~&…{Yc)yOøÈ˜;Í!W64¶Gœöùüt¢\HOOFaáté4˜VVЄVlݚĄN(¯,E×nÙ<@ßk䚌´m[É)L Kãï@Y¥ÿ>áŠ0Eòc¾ŸùÀv¬¢“×mÆY ‡·;MÿÖOV¡êß*§îLPTU@PTU@èh T0²?Fîׇ±é>r³"È'Á`€‘À“ÍpRss“™ßÛDn’*·úz¦÷qñÆ21\d{àîá€ßeÒø“az­â’ØU ÛSóº…CþlÊÊä”`/Ëÿ±­¸„¹™^ÈÈò>†Î~^³¹¹=ÌNívJ¹q#á«s—Xäççó5/K;¡žD7ûÔçª:Ø §—£ ¨ª€*  ¨ª€*  t4œ¼ ìŽÉ“‡ÒdªGS@Êý˜øç¦KÅʽ’âÃH€.·N“ɹQl,Û¦M„.ަse{mÁÐpMe"R’sXÆÇtGõ-¹ëde¥p'M¨â _ ¦K•Dxª®®&••p§M¦+11}UÉ(ÞæÃ©'«SÕÑn8½U@PTU@PTU £) PuÀ¹˜8ib¢¨­ 3á<ÀQQi((Z §#uut¦šäž°q®¢cdƯƒ3¬¼,ý«7Á¶—_Ú+ìqfÓyj ‘Å0ñ‚%€œ?UVZ†Ä¤D–z¸ã*~¹8\·žY\ǃ¢"ÆFóu[ÄxHq¥èÑ£7jêm8c¶:Uí†ÓëQTU@PTU@P:šU#Fæ`øðLôÈCa•{!†ò%¡¼z+úõÙ «V­£ƒÅQPͶVÙÉLvöWyYè@ùÈãeµß«/ 7³WÊífß–B¸\QLÿK'9 I=ÛÄ¿kðùç«éT¹Mlºš õ7iUlÎj`}a§ÌN°»ñ§‹5¨¢£Ýpz=ª€*  ¨ª€*  ¨ª@GS@ j¿ý;ó+ {‰ÁÇl䘩$§ÄÃåehESêjšPS`UK9Ó×icJz–÷y]èÞ«36m¨æfH”"55‘Ì$©„ª””•×3j3«ja›÷÷±a Ÿ¨­nDL,kkëI§•åCQQ1öÑC­ª°je>23»aý†•4p8Š kIl~¦Vs²p€v ®¾¹={âŒSÿ æTu°\/GPTU@PTU@Pþ½ TÕ S¦ bI_!Û RQRZ€ô´x¤¤wÆÏ«Jà´G3%þ‘=HJ ÊÎï¬Þ Õ %) [·ÁöÈÃcÂÑÞhRVˆV5©ÍèÞuC'Ò›âЫr¤g¸°yK12Ò3™‡Í 9(¸3Ëÿ2P°õ[Ò\4çTy8$Ë‚â"ÜxÝÊßß©ú÷ê­{STU@PTU@PT¦€ÛfgHE?ŒÝ áÐFtëÖ[¶üŒt©jjœX³º‰ 鄨²NêëËiDyYÑ’²eÈÊì ë ©þäFú¹UA&[4r°o4¶šCN”•UÂÅLöL¦_lÞ²–tÖ„¡Ãº›>ªÕ« íM6• Àj í5pÐ|ÿÓr\xÎç Uì†ÓËQTU@PTU@P:šQv&LÊÃá‡÷FÁ¦µluêÂJ¼z& '`Ý:«ø\&fÝíŠ5‘êáP=yÉÁÑRÕˆI¬Cs0–íS6)ÿ;4ÜÜld¬‡Âü ¡SF$F½¤¸˜ÍW6ôeêņ?›×öÖ‡;ÙŠâ’b:[°…ŒSOe¨EwÖÖc-]¬ ÎþL¡ª£Ýqz=ª€*  ¨ª€*  ¨ª@S@ jÜø.˜5kj¶!>ɇ޽û™¿šj–32Ýæ@ Ч;Àé½.ù98fª I)~F°ÇÓÝʃíá¹²ü/µì¥jfÖDs3çP%$0Å"DêòsvUÝóckA ÓÿBì›*EBR#R3XYYÆXA6mÜŠ!C3~=…%¥8ö¨·ª:Ø §—£ ¨ª€*  ¨ª€*  t4ª&Oé…aC£i• '×Ι½PQ…‚ü0ƒùRXú炯ÑOþñǪ¶®„Õz>¤u "³©é$¢ïŽòxØC•B·*Hx*…‡SƒSÓb©YÉ~•ÈËÝ•å·j ;U»³ˆ–X*ý5¬5¬FeE£;1Ï=Uuµ8ÿ¬ïª:Ú§×£ ¨ª€*  ¨ª€*  t0¤§jÖ¬=™Q!ƒቦŸ‡Õ+ªhJ4C££c`ãz½zg¡¸¨ÕÕì¹J‚Ã]N¨rÒ˜ ÁöІ%Œ"³Sü­a8E¬ ä¼`RZ:¢¼@uM ­¯j:U6df䢪º” Z ¡oßlº[,[¶’ƒãÑØdǹg¾§PÕÁn8½U@PTU@PTU £) P5fl›Ž}˜ò×কÉ^ª8šGe(ܶ£ÇŽD§N‰xÿÝ/çíBðò¡SV+Wl!€õFCcl›;&Ü£g*‰«žŽS=I+6¶1¬¢d[!úšÊ+¶òwgR9RÇð û§*±yó:¤e¸ UN¤$wâààhT×7áܳZ Š¥,(Ü…öò:ºl„·0kuQTU@PTU@PTUà?¬€‡P5ztŽ8¢+²2À„ótÄÅæ`ó¦ÿ„ŒÌ4šHñÝw?¢ÉïDç.‰ØVºýd0¸oÜŽ$„š°]÷—áìœ8æ­Gsb°ƒó§üˆrD1é/Ä„ ;A)ÀŒ2P3(߯c™_gfµsà/0\QA®ãá\«F¬'€y8çŒ%,˜x +Tý‡o=œ*  ¨ª€*  ¨ª€*  üºU3gõÀ¤IéðÕ ¯ë`ú­fx_<`¯f?•¡°­Pµ2Ô/#ÃfªõlöFrQ}i,tÃvù%½Ãq !–ÿe3éÍV[-=U‰ê[‡.ÙÉX³îGN ŽFlL ¶•øˆJ1œMå€ß_gÈÌív³§*•ýW«Äd7ëHùßn*¹@{ËUîÊÉúut U@PTU@PTU@Pþª¦MÏÂÈ]èÞ%I‰(bßTzzúCµåäh©­|Ýeø¨¾á'–¦cÓ¦MHJ컣¹y9°]}Åà°¿©={ôcOU-7Hæ$á:e¦r£ZΨŠGaáf45—#6. Á@ ·VˆjMùžËEÌŠñp§6øvqŸ*l—? š"ŽU˜ß8ÖŠßåwyŸðe~þç–H á?¿ý?wÔÝoõßv>ÿîëÓý©ª€*  ¨ª€*  ¨Eé©:ú˜®8ôÐx$ÑXjƳ¼¯ ™™]QRRÒÒz”W–cß}÷bKÔ:X?£[N,ÿi3ûª†ÁËþªM›8ü÷ÒK†c¢éBÅ1d¢/n.@×.YÜ€9ì„¥(·Ì¯ !Ž1ëùùv…´Ô®,ÿk0C€›šXè ›aX œS¶Eã²?}Œ®»^|1|:ãdI`33Û£Ü^<ÿüË8þøc Tíºl9¦€‡\ûUW]…=÷ÜcÇŽ5ÛÊë>Ÿ¯uS¯¸¸8”••íØä\RSS±mÛ68umA)šð>ú'œp¾úê+ó¾GÎcgøÙÝ9 <¾÷Þ{8öØcñå—_šs•ëµÀS~–/ œd?máN~—÷å\+**Zµ’s‘/ dÿö·¿”pöÙg›uäuÙºqäÿu蹫ª€*  ¨ª€*  X TM›Ú±ê½…TWºQRZÄ9TnrI"ÙÆ›£‰ã§¢›°]:Å0Ü•zŽ®ØT@aA=¢<|~¾ð‚}ÂÂ-qq ªàÃùÞû öâBdÓ­Z¹j%¥Œ;‰ãl*;ã#‚°‡×Í÷4ü°“N:…ä䶇* rý$\zé¥1b„ª+®¸?þøcë'- "õ‡v˜yßzÈ/--5ë\rÉ%?~ûì3:o½õn½õVs‰C9úÓŸÌ{Ï<óÌû>à€pýõ׳Y-ڜÃ>ˆÇ{¬u!ÇaÆaÕªUþR.¦£·lÙ²_½» „o¼É²¼×^{á›o¾1ÇWM móæÍf÷»÷ IDAT1ì}»òÊ+ ¶ûaÆ ¸æškPP@[²¥TRÎá‚ . Íy¨?Uù=??߀ØwÜH•ßW¯^màõõ×_Ç_ÿú×_=O]APTU@PTU@ø#( P5er6F”€Œd)Ü|>÷36ÝEŽÉ`Õ]Í¥:†SÑ¥k2˜3QºÍ‡Ê2Öom`¹`,ÍBÕ)' '&ưªÉ„Näåe³ ° Ýó†`ÅOk¹³rºÅ£¶¾Ö<`WUÕ2f0 \%‚¨¯ %ÍÍDÀDB]g"æ\óÑ?@•¯Ñ‡“O>Õ@US“8"!U2.|Í8;[¶la\a_YPòš,ùË_X*x<>øà999¸üòËñóÏ?›÷:ê(ì½÷Þ3f á/o¼Á ‚ÊÚµkñÀìà(í ®äšŽ;î8:ûï¿¿qo/^l\q”^zé%sÝR'åvMž<W_}µ&YÄéûé§ŸXYbœ«3Î8£Õºçž{Ì9N™2ï¼óNëz·Ür‹š_[òòòpî¹ç2u$rüwß}×À’€æwÞiö׫W/<ùä“üœª 2ÄÀÔ¬Y³ØWh~0;òÈ#ñÊ+¯˜óÜc= ç ×&ïŸtÒIf;˱{óÍ7Íõ lʾuQTU@PTU@P:‚UMÇøCR™‡Â|ºR‰l­aÒùÖ;’Ò’˜#ÑHS…fPêD»8¿ªªþF– Öø8Ó* ¾rØæ>xL¸¾¡7­DçΩ´¯ìðºbPQ¢µÕHòJ‚Y$$Æ¡o¿¾tXVºÂœH”ú¶’ tÎJ4Dçó7$:áî;>Ý-T9œ6ÎÂ’p‰f>Üß„Aƒ`Ú´i­àñ /`É’%Æå‘Ez‡Š‹‹qá…îPŠ·ó‡(nLÿþý ,´]¬å—+§Ûn»Í”ÿ  µ-—ëׯ>üðCã‰{%‹¸c÷ß¿=BºqãÆJ–]õTeggãã?6PÔÖ‘kO–Õ?Ö³gOs>gß}÷]ëõ‰SwóÍ7ðØ”ó’ý~òÉ'ÆU{øá‡ãôé§ŸB@IJ&­Þ(9Wy¯m/™|âjrÊ);”üIä¯õœu„?2½U@PTU@PTŽ­€@ÕGä`Òa™ÔUaó†ZtïÙ¦R|úÑ:DÇzÀ×H°ªGn÷ZT•ѧ÷PÄÇæâ³o~d+M **óa›3çà°¸2 ˆOð0:Ý‹²b6[16Ýé`.{ˆáZºX͘0qÞ~gBA7ËCœke7¥mÑ10½S‹9îq¸ÿîÏw U Ø@¸Yú¤šé½É³^¦/HþåA]#)‹;è ƒ ØÈH)[]]Ö¬Yƒ•+Wbîܹ¦¯I«¤Mœqr¦NÚÚdAH[˜Ú¬¤|Î l²7 ª˜4i’ kÝÙ&.Ž•ôXIÉœ@ˆT§NŒS$ß}ôQãúÈynܸˆâ&µ=fzzº¹^)ß³–ö@•µn= x @Š;fõ ½üòË ‹o¿ýÖW®MJ+Å•’2GÑK I~½å\å<æÍ›gàµíòÜsϵ–¶·ß«cÿÙéÕ©ª€*  ¨ª€*  t$¢'3fôÆôé=°eÝr8ìI åKGiyY' }h*­^½ 1q¤wbå^8‰ÆGL…cÑl&TU£¨xl·ß>.ÜØBL¬›@Å ˆªMÈßàc`¢½1›&öèxQ°uòº÷€Ó]G+¬õ.”—6"Ø\‹œ®L½hD€NUuÏÌ[¾Ûžª¶ÂK/½b JJûbä_Ààûï¿Çã?nz—d‘’]WPTU@PTU@øO( ‘êS§öÃÈ©h¨ÞŒûe&Ag÷–`ðÐðDÅ`ÅÊï™»àF0\Äò¿lέ* 'Iôz Ÿ©eÌT¶;înnvf =°£¶®¾z§q?ö²'¾þæk …x•‚F Á'ÅAÁ6ÄF§²w§‚…a†`%''rPð6ÜsÇ’_„*§Óeà䡇FJJŽ9æ˜Ör2 Š(gJþ¨$”Â%éc:øàƒ“%¯íÊ©j¥nÕÎPµ3 ÈõZ ;;UR(à'eŠ+ĘHzž,ò³W€K–””Sš'Εõš¼þKPÕö\w¾‰Ú^Ó® ÊJû“/I(<úè£[5•ßå}ÑT®[UzØd‘ýŠK%ý_Ó§OoTiúßâO\¡ ¨ª€*  ¨ª€*ðÿ­€@Õ!ã31tO'zævå³zWü°tŸ™Ýq`&¾ûv1)bbb³çCqa#{¨XÑÆÉMéÙÂDvvWØ{|j¸ªºÜÌ pp¯Û‹†ºJd1ý/9)+Wp§¶ê˜ú—›×%bù<¤?V,ÿ]²»á£%ŸÓ§þ ¯ØFËÄ­7½½T=ÿüs¨­­¥Ë4› d7ɲtë–ÇòµOM šôïH‰™8?'žx"Vð¸²H l;þ|“H÷ç?ÿÙ¸1. `¹@qûí·›ô@ hØiÛo´»Þª¶¯K Ÿì_Â1Ö­[gÊá–.]jàN`EÜ')ñ“’¾“O>Ùœ¯|`‘€9G9é9’ýˆk%nÀŒµˆ‹$ýLëׯ7 ‚²o‰V'¨í¹´½¬xv ¬²²²LO•hôÔSO™m¥¤PŽ#¥Šo¿ý¶) ”` ¸óÏ?ßœ§”ù K£jH¿š$Jº¢¸p¢aÛE’gÏžmÒ ¥¤QÊ/ªþ¿ÿ¼uÿª€*  ¨ª€*  ¨ÿ ª8 ™y Y°‡Â4‘øœ¾ýú÷@F§$äoÃ$ %Å•H ùÓØ`e_•i9Z»v•™Ó[_O§jîC“ÂÑÑN3 Øé`PAƒÕU[1tèžæÁ¿k·®¨bävbsÚƒö2•`è°ÜÉ:ãÆÔÖxZÖÉÉ\$7Ë“qÝ_^ߪî¹ç.óÀÕUWï a£FdbÑ¥Hú•-Z„ûµ¿gäÈ‘¸è¢‹ aRf'É|=ôPk|¸5[Iú©.»ì2Èúòš” œí<ŸIN ­KeŒ¼Ö¥Kô %€Rzøâ‹/šXvY$B€IÀJú°$]ð¦›n2à&ç% %@'nÀÄ›K™¢¤üíìŠIÚà9çœcâÛª$™¯í à]Ý@mg^Éñʼn:óÌ3+%À#%|òyÉþ¤üPÒ ÅÑ*//7¥‹RÚgÍï—O4—÷åsY¸p¡Ñtçž*ÑCS¢ÙÔfµäï?ñ'®ÇPTU@PTU@øÿV@ jüø<ôȣ㔙Π¼ †SØØK•ÃV'ª8“Š1|¦®FlŒ‡‰éQ,ýó2‹Bf×ÒÀHcE_Qª‚!¦öybM¢ ܰÔÌ’„>)k“xsyxü\̇óRºU°|Å2ôè>[jc$4ž”?à­·D"Õ]Î(‚˜?2Ü–%‚?-­],b© (Èú€•8PfVË"ÛË—¼/Àƒõpo98òšôdÉ"ëX}ZÖ@[k»ƒ*y_ÎÃêå’}Èñ¬0  kH±€”5l×Ú¯šÀ•±óû²½\«œ—ìÇ ˜ßw¿v‰ëdA¡œ§U)ß-Í,¬}Yr|ÑKÖ•×Úö}É>å=«_ÍêËÚy_¿v~ú¾*  ¨ª€*  ¨ª€*ð߬€@ÕÈ9Øgx'$Æ„qИnØV¶i™Qøöófò‹‹#¤êÚׄôŒ$šIUHMIFyE ºævBáÖ­=” Û÷öù‡ÞÔ .©ìr¹l\!Þ¸ii™-¥}æëk2¯ 0„ÎV”I‡·#&&ž¿»ØÀÀ_®ý˜0E@k³H²9™¨]ËoIÂÓR´vIú‹+íNoÕö_×V÷  ¨ª€*  ¨ª€*ðß«€DªrHWì5,‰¬ÞKË` [JsÊ}X³¢ å•M Ç‹:“Ì1ƒd†kjº‹ù(*,†íÆ9ÃÃýúöŸ—(o„‚>ö1U`øð¡XÃ2¿NéÝÙK³ÂĘÇÇ'š†¬ÄÄ$îÐÍAÀ±ìµÙÌEÑ…ñò{2þtÅsläÚ¾ü úï•\ÏLPTU@PTU@P:’UcÆä`ü¸\ädƳ”opŽ®„õ5SXî—DF’Ê;Á ºUqäŸäåöº ߘ@‹ØØhØæ?;3ìtÉœªÿcï½Ã$+È´ýçTι»ºº:‡ =‘f`†8äœWD%¸ ˜vuEL*ˆ¨è€,¢»®~†Ÿ®¿o×uWÝÅDPQ09tÎÝ•ºr®:ßsΈŸ~»+ì?Nã¼çºúš¦ºêÔ©»Î<×û¾÷[ä\Õ…^¦­ *ÕmœcZl¦@…zžA*Ï%¿mºf}åÊÕ|ÃY¶èžê|ÕÝ\[¤j½lÅ·?ªXÒÕQ/…B@! „€B@eC@ U—]º’Õª¶ÿÕh4÷PÐWÆâbÑè d©ŽÐd~š9Ûéiè{}5Íz6SbÐÉÀn ÀhR |ê¾mj ¨êË}Ëœ‹òz»ùošU(…ÉÌ€%ùœ‰³BMx|6”Kdµée°Z­»Cev²ë?ûö£Þpà‡®‡)mŽªÉÁ.9„€B@! „€Ë€ª®¸b%¶móà„µ-z‹ßøøÇ›´0Så>ÞuƒZÐÍà½}AŠú|t9¥°ˆ”gNR(±ˆCÙùÙjK›ªKŠLeõšv«¢÷ æ9”ÅmT¨WÈæ§áöð±B·‡2NQ…ãRY¸]˜›Ÿa `6nd¡ÉƒCùánV÷îÝÍ0¤r?”ÕªjåLfŸdAgW§¾@Öåh㞪6îXÊb16Ma…‡ƒ\í<9EÓ%}“°¶—ª§¿—\öÀo#{yUsk¶ V¯ÕN®[! „€B@ü¹ÐTé7½© ;Nå¾Ü¥ †V¯c[Ÿccèî3òã¶ TP˜‹˜œeÅÊÇÊÔl?u#Ǧ€ß<;ŒU|ò•¯œ£zÜ^¾ÀÄÖ¿,²Ù4õUš-\\Pk¡B°£K|ù¢7 {Üî´ÊÃp3"UjQQ)Ù™Þ|}‰í¼áßÒ¢ÔQõßo Un· |! „€B@!ðÚ&p4T…qþ9 ¸”6îçõ±Ío •FŠm% ÝX½ê$ŒIà©_ý‚ë¥ìˆvDXŒrPÒÇ}¾¡®£±ç+ÿpž:19†h{µé &®÷MÕi¾°ð$ ÷NQ8Ñ4è3T“³T¯W˜•TôtÃjS¸ôŠÊÁ… UìNV¹jH¥Óxï{vÿV©.¡êµ}›ÉÕ ! „€B@?_Z¨zÓu^œwvöFFk«¥eJP€AédÑ©N£9«Wã9kÕξ}˜šÌ!Ç¢æ±~ãj(ßþÖêÒR’ ©¬Ô˜º,´ù-ÒìçÖC•Áؤˆ¢¯;Âå¿9þÝC½º¿/p0« ¾@•û© |ÎQ}z¥^Ç­·üZUêÿÍTlÿÓùóýFä“ ! „€B@!ðš"`âLÕ%W^ô…7²y¯ÌBQœ]{M,%M”ð¹‘I—°|ìÈ«±k/ÂÜd¥°Âˆ¥T mQ+W­Pîºcê÷»õ=SÅ"ÿq³ý/Ë+“¾«ªZËÂîÌÀiëÇÜlÝݽúŽªD<Áçbø²ÓôW¦~0³Å‚&—SÝrãcLw Y¿ªþ;ºGÖB@! „€B@?-ÍþwÉeÀ›n°£šVá Ø9Sed®ibnÊF‘ßJ‡¨Lç¨T£9 £ºÚÎ'ÇçiKw¡oÐ ƒ™{{¿ðùsÔÎ'­£QP•nc`ZHÃç 2<± Юr?ýV<˜™^b[ ¹\’ŠýíiÎZU±nýÌÌLê¿­6¼ý–Ÿÿ7¡ê?§, UÚGÞM! „€B@£´Puõ5.\yµÍ¢ ŸEšÑ-¥|]=˜ ´Ân·²ÈäG³fæJ)«G„Û H¤Çºh@ÿ_ÿpj²Æ D07gPbp²˜øßm0¹Ø*¹ÀÉ«_èÅî=Ü$ÜÝÃ7q =ÁììŒ^Í\ÑÃ×%)ªÈÀêðãm·üXB•Ü©B@! „€B@,kZ¨ºìr?Î9ÏcÕÍ"‘ÂÖ?ŽA™³¡ÕzB·¤Û6䏨e¯aõê¶.RÞ—@š’¿Î.ÎT=üùíªÃb`;öïã2_w ÚÛ<¡Ö'˜ES­³RÅ’CUl1ÇJ••}†uÝ Xä²à¶p.ùüÑ¿«¼óÖŸH¨ZÖ·\œB@! „€6†ª‹/ à”S PÊmrµ±õÏ€X"Î"S7|~ L*g«ò°r'•¢Îsµ”ù\‚sÂëk£j=åÞ»‡Ôp´ŠîÎM8°w’•§Ê)ÆáóFpèÐ( ª0¨^8XòªÑhe `ž-€­€V­¥¯¢ .jêMÞóîÇ%TÉ=*„€B@! „À²&`Õgª"xÝ_°ón¾‰d¼Æ"“ ‹‹ X½v5 IFÚÍSôJt!Ãμjõ z{;hHç\Õä<ÌÆ0;ûÒPîûÄFÕd2¡½=¢/² …ëì Òäg£w=ŠÝ/Nê%0UqËÎ%¿y” ^.úÕöX¹L-ð-4†119 «ÓŠ·½e—„ªe}ûÈÅ ! „€B@!`¡dïš¿èŶíì¼KxX@ª°ûΩû%XUÒ³”* O Ž@qGU»—]{)ÔªIvê•ilg WN}ðöµ£³qÚüÌfÚ;¬œ©âÒߢAß]õ›çöÁáä@ƒ“ÁЀڬ"Ü:„ØBÉdN§N—‘H'Ì!ÇeÁ7]ÿS Ur ! „€B@!°¬ h¡ê’K{±a½‘àZŒŒ=‡ÕCki6Ï ^5sE”‘§,†Ö `zzŒE'«Uyf 3ÍéEþ ðz½P>}ïÕj¯R2QÔËZS6V®2Ù œ/‡°rˆÅ§DõRØÄÄ06p&mÓH/qñÅ—°PÅ‘#û™âj™˜ÄnIBÕ²¾}äâ„€B@! „€ÐBÕe—àÄÍv,N—í6cÆ ìàÃøX]”ô-Æf¹`1+4¢çYh;ù\H%“¬l{ªÚ¡üÇެƓ‡Jp¹|ï{ßûðÀ Â®—ÃI `áÅøö·¿‹.ºˆÕ²&þýßÿ" «þ¼?v¬\¹ïÿûià0rh¬¤¿Öáp`çÎØ»wï}­&èЮûÕÚó_>þ'¯{µç—ç ! „€B@!ð§# ‡ªó¸ôŠ^VªT¸ô·’E…sS¡F%Âö¿yäòKðzüèé ¡Xh`øð<]î£U¬\ Ê7¿v•ª6š-jÕ*,Ütµ”NrÙ•‹ÃYЗ‡Zܘ€ÉReˆ*2¹±J;BÁL¶&ÃQÅ„x‚û®ümøëwþˆE3V§X¥êêîÄ3»žÁ 7܈ááÜwß}¸ñÆÿ ,½‹¡kÛ¶múã<ò¾ô¥/a÷îÝzèÒBÒ;6oÞ¬ŸS;á„°gÏ} ñG?úQ<ÿüózÐú¯ªKÚc 9^)´ýþ{k•6-¼iZùÿi ûÓÝòNB@! „€B@¼ZÉä´3¸úš•H-.± ¯ñä,tFT«³(d¼h* ó™fDÂ~V©ÒÜë;Èð5ÁbŽKôL(ÿö/7©õZ= LYÔ¥óg݆^Ò,k-¤ô¹©Ö6?žyöI†¤2²ì'´Zœ·òÃïoÅÌÂ(ÃÍå:,ú”ëV|ðöŸA¯ÿ0T­]»?øÁ÷±}û)xÏ{þF¯"½ç=ïÑ+DZ@±Ûíx衇ðÌ3Ïàßø+Zã–[naUë°¨^)T™Í¬ÇñÐÕ¿ýۿ᪫®ÒÔ|´C{ígË–-¸à‚ ôÇb±þéŸþIÿWÛÃõÇ-Hi× …°;vè?ZÓ®÷g?û™´¾š»Už#„€B@! –!:'°e« ×ßp&Ç&ÑÛ³ py (–8„Z½ˆtø11¾¯«•«§Œ,HUÑÍq­T òå/^¤ær9Œ*å Ò‰ºzü(e4Øæ×ѾMµŽñÉ#ºÝÏÀjU³YA6[‚Ç@®˜C´ÓÍ×j1Ê„JÍ÷Ýö/XÇóøãé¡ÅÊ¡.í=ì_haJ B[·nÅÝwßóÎ;² '/¬ ‡-Àh¯ùú׿®‡¯W U/W¡´P¥µ ^~ùåxî¹ç~÷•iç;߉ûï¿ccczØÒ>ëÛßþvüð‡?|ůV»VíÿøÇñ†7¼ òÑÚ{{{ñÙÏ~V¯ˆÉ!„€B@! „Àk€ªvœÕ‚SNñRÖgD[xææÇ8[åA)_`ÀZ@KØ £¹Æ(ÌJ•ªêÔ—°öE~,ÌqOÕÎÏœ¦jÕ-8¸Ýný~œ̪O+ûOb+†YV¤‚­nüìÉ£»}U,­ÊÓD6_âQ/ÈVÁ4µì^ßî¸ã{è²`㦠z0:xà žf à?ÿÓ?ãÝï~7öïß—^z ƒƒƒ8ãŒ3ôçÜzë­¸âŠ+Ðßß}ìcú˜˜ øB3\¼Šã¿ U.— ?ùÉO077§¿·æ“×f¸’T jæŽW:4.Z°ºùæ›ñÓŸþ”¹̆뮻·Ýv5ôݯt ù»B@! „€Ë€ªN9ÕSOó² ¢¨¯Ù%NŽ!5Ê”Sp)0÷T™­ Ù >Îe¨ ÒYá|U#E~(_zø|µLs…‹;¨2\rÕjуL18ø„ÍC;¨tãùý¿@¸Ó‡t–å-öæ˜Ü´…Á&Ø·ŠÔRK©<|U¸÷ÞŸ ÂŠ”•§ý×ïá#ùˆ^­úîwÿCCCz˜yy>é†nÀe—]¦W¾øÅ/âÙgŸÕçªþ'"-ølܸñ*U/W°´¹)­½P«`íÛ·O¯NióZZûžö¹_MhÓæ§´¶EmîkõêÕzÕûð‡?¬´WsŽexÉ% ! „€B@㚀‘ÿ_Ö9œ~ºµ²f“‹9¥ÂUãï5‘T[ÌJ Ê¢Ä.=§ÓÊ=U(ÆEvïÕ 6ùûC;w¨Z`ÐÂÍ IDAT6ÇÔÞÖF5:Õ/Š ¯ éÉ:¶®< Í`Ĩœf PÑ׿ÿÌv¡o0Œ÷²úGkd%+Má#w~~¶ÙuÖÙøù/žÄŠÁ•hoê3SZ…G (W_}µ^­Ò檎9‚K/½¿þõ¯ñ£ý>øà«þ‚ÿ«Põû/ö³ú¦Yµ–½ /¼¸óÎ;õ®W DZ(óx<º CkUÔ*^ÚL•ö¸ä4AÆËó[¯ú‚å‰B@! „€B@sÚ~Þ+®Šâò+#>áHS­a G9fž"»ñTîé循`€Ý|K8|0Æ,Ѧ·Öj%dYˆj  |êÞ“T·Ëññq½ £µ¶iÁÁçä¢_ƒ ññ úû†àmSPWrü[‚©-Ä0Õ'žüV¬XƒdfN— ‡탕ˀÜùxèA¬Y¿ŽoÒ¢ù4QÅÜÜ<æççqýõ×ãì³ÏÆM7Ý„uëÖé­€ÐôèO=õ”>¥…žÿɱiÓ&½ õû3UZØÒ-´imÚ¡ÍCivÁOK¥U©Î?ÿ|¼ímoÓ+?¯Vu®I.´9,­­ðá‡Æ;Þñ}fK“Rèmœ©ÒšV ÓZ µð¦Íle³Y\rÉ%4T½Ê´ÀöôÓOë³_>ú(Ö¬Yƒ×½îu¸æškô Ÿöy^I¨ñ§½=äÝ„€B@! „€x%Z¨ºêêA\tqì@G´•)#sÍ$Z˜kF†gá羪by6{±÷Å"»Øü<­ŠTj‘¿k">úüîüÐZUkÓBL†õ«î®ntu´c~K®øßÍj¶ñ55}Jë3Ìf¨:çªV†­&ÃT¨ÍÉP¥"–Hbj&Ï?ø¨¼ÀýèÑßíœúÊW¿Š /¸ù|þwŸmýúõ”ZÜ7½éMú<•VÑÒæŸþ'Çé§Ÿ®6-\i‡V1ÒÛµ×^« mvë-oy‹>ÿ¤@Í,8<<Œ·¾õ­úû½šC«tibŠOúÓúÓµ¥]§àV¬X¡‡@ U¯†¤˜ÉäõA±Jåâ1Š'z4Ã…ªVcõ*ÒÖ‚©¹Ãúc¹\å"w6 8 ¨njÐiL•ðÈP£n½y|G//>>ª|D! „€B@!ðgE@³ÿzz—_± Å\ù†ë—ü­ìD3¢RT™Êìæ[Do )ØX€ÒÆ¡¢í,(YY:Ì×$ |úÞ“¹J¥¤b…ÞþW,øb’ÚPV£Îùªúz;ùâ$Œ•}†t·âH/¥Xù‰bqž³QQöíak`6g >|Ç¿2Éig•C! „€B@!°< h{ª6èÇég¶c°?Ên· U-ºõ¯^å%#Xpjpm”ƒ]p,±jäæ>«fÝÊN¾"œVÔêÎT}ðDÕ`l°BUÀê5œâ ÕÌÌ‚ +WLNc·ZŒú\•ÏcÆä̰¾ŸŠkqò¯ç|Uak=B>Ä’%\zåíhð%T-Ï›G®J! „€B@Ž$Ây¬Ä5¯ÛBoÄ Ç|RÌö3.É’P%7±B@! „€Ë—€ª6lhÃÀ  ƒ½09½‡Zu7-.”ŠKœŸ²ro7×NuâÐá4FF§ØØdÐr¢@ÿ¸jJùÛ·«N§—Šðq´µ…YÖ2ÑìgÁÁƒ“,kåi!¾8m'mÅýô…áòøÜNø¼A*Å_B‡ºRÉyÏ:µµµ•Ë~'a`9Ëãñ±üµJ7ûiËzççÇàg»ß¦MÝ\r[Al²ÙÜ‚­t·Wl äb\c¦À 6drFÜsïa¶ÿ5ô@9„€B@! „€Ë‘€ªÎ=¯7Üt Gž¦±wÏNظ™]| ð¹[ùç§J9J+Šøµ`fa‰ÿ†07¿-C†ÀZ­åÁ[Õb¡›ÍF•ú,{­ÜܳٯNjùÅqV¡pÓð‰xá…çaʼn(–àš(­¨°]ÐÂ7š‡Ý€ÉàD2ÓÀûnVBÕr¼käš„€B@! „€ø-Tm?ÅËñ¥íȦSȤ™‹ìftuÓ9‘s`ll˜­~ZÏM4¥qd4K[àZŒON1\ù襨2¡ìüì&͈––fçæ‘Y*qp­-Q„9cࢫ={~ŠÓÏØÀäöZüÛñÒK/Âí5ñoNƒATk1¾®B½` ³÷|â„*¹Y…€B@! „€XÖ´Puι-¸ù–ÍX˜I!™L3Ûd±v]?æ§TŠûòÈåSX¹ªŸY)ŽTÚ‹ÍÁ%Àu8Vd³yÔP¾ø…­ª6+ÕÑÙDœË~ÇÊ0hg‡‘Ot" ówV®lÁ¾}Ï"•JÁnwcvvŠ7ÆZÑÕÕƒO62rë7oÅ;Þö¸„ªe}ûÈÅ ! „€B@!Àúο €k¯ëGzÁ†ÅøzX™2 >_¡ˆ/EEgì“O°”"_,q鯅£Q9„AJ,8ou×G¢ªÙåU«W ²p“ãjyYÊ*!Üf2ëE…‰MÛ$<<|ëÖ¯£›e.¦¯é©ijØéi÷hÀ(òyv|ê>iÿ“[T! „€B@åK@ôUo|ã œu¶ ÓàùÜ×Á¨ DBkðãŸ<ŠsÎ9‘¶sžx<†ÉYÀæt²;Ï€Z=Ã.>‹MÓP¾ôÈv5Ÿµs6ª¢/þŸcl$FëÓZ:ÃÙ*3¬VK™EœrêVÞÏäf¥]Eo?CÕ âI–¼T3:;z°ËàþûŸ?jÿQÅò½‹äÊ„€B@! „ÀqLàåPõ†7 bÓæò 7zú(W²èíkC6eî§waÇY'Âã¾ÿýI$³ŽV`挕ËmD­”§=åë_=OãÀ•Ý[¹0:2I…zÅN.¸:‚®ÎN*Ó§àöX±vý*Äãs(‹ˆ-,è6Œ5Ô2ÐϾ0¿ÄÄ,¶l=o{ûÿæž* UÇñ=*]! „€B@,k/‡ªë¯_M—ÄüŽUµT˜TŠûøÕÏǨK·¢-âaË…ü`F»WT8MOÍSFÑÍ!¬Ò™YXíuôtwÂÇeÁ¥R ÉD†–Œ*OÀÄd_úân UÇ÷=*Ÿ^! „€B@,[Z R9ÎdævÞ×½~.¸0Œji v›¥bƒx®š2p~*‡ÁÕxöÙ}h `j6EiÂå5³ªeC.[†Íì…rçG7«v[€I+ÁàÔÆ™¨*ÐìÂRº‹ÉGyÅÚÂ-\lUFkØÇ •£“½È7µ³—ÐCk`&£>&µ…X ûù=ª–í-$&„€B@! Žo¿ª.¿rgžÕ³¡LO„ q:"œNÎI³tN˜‘ªvF>Ãq¨ ›Y„GK›‹Þ‰:»ül,@-@ùüß^L¥ºÉÔ C‘‘œG{d/¾xo/Ûþj¬JÙ˜æªTV™ÊÂH§–œêpÑ~¡µjóUn—‹C\>¼û]ß—Pu|ß§òé…€B@! „À²% …*í0©À…õá´3ðšÊP>j¥é¼ÁjTªGWÇŒæYlêÄää;úX5Ô “¥ÆÇôQ´B¹ûg©¹Œ‰›ƒ›Ô¡/aõšÙB2^„×A d¢ñ¯A]à8b¬D ôŸ€J©»Õ\a†Ë‚ z•ÊH†ÓíÆ‡ßÿ UËö’ B@! „€B@ VZ¨:¸Åž H½õ¯BW„ɲˆ«°ZZpè@¹´ ±Ä Âa/ü38=ÅN>3s÷TÝùÑÓ9SÕÐwM¥Ói ­A.Ç©Š•Ë|'ªlX9ä`‹_ ós)¶þ•á°{t¥zwwUŠ6À<\â‰4ÚÚVàSŸü‘„*¹O…€B@! „€XÖ,Š—]¾çž×‰Ö€*¼xþ7/±åossÓØ¾½‹ò>öï£N=Þ¤ù|‡)°0ÃlѺø,È,e |çÿ³Z®”pàÀ” UZ£ºábf*É´Ä$æC°µÎö¾O¼È%¿íÜiåâHWƒ©Íˆr5Ãa®<––r f OÆW¿ü‚„ªe}ûÈÅ ! „€B@! …ª+¯Äå—÷Áë°q7¯•ŠJw„O/uvx03;¶Ö(WNy±oß^x<üXtJÄ©^ïfFš‡òñoV3Ù$jU#"m½´\´"ÚáÆððk “òŠéÙ½l4¢§«“•*«Vd³ ŒOÆÀ`‡´ÌÈçŠ(j€âÇ—¿ô¼„*¹G…€B@! „€XÖ¬#U'Î;¿N‹“3%æšCUÝ~ÚÐà„÷òªl LP`aaщ~‰t ‹‹i-ry¿ðÅ3ÕÑ‘E–®ê¼”P8‘˘õ€4>:Ž=˜˜z «W­ä¶à0±*+ZM=dML£%ì ·=…öV¶fX±2ãk_ýµ„ªe}ûÈÅ ! „€B@! …ª+®lÃÍ· ¡’Qpx¬À U…¾ˆZ>„|Næ¢4*Õé6 ìN++YešÐK™j VirY°…Ë?·UOPîÆªUƒØµëqýÚ˜ÂüÈdrðúl,oØêçb"Ë3¹øb.ú­Q·n52ˆ1„åSˆ¶wSb±ÄÅÁU|í+R©’[T! „€B@åMÀȵP7߯_ÞØŠô´ K%7³Mn¯k†6`b|ÉYvòù˜—´ ä†ÃæfË_š—{|Íp»9õ÷_¾Pœœå¬T‹>µ°@+½ëÉXŽ/j°:åƒÕV¡ÙÏNzžÏqÓßnç^«ZŽAlˆ‡'ºl¬v5aà^«ûîûßò¾}äê„€B@! „€ÐBÕ[ÞÂÕ—àÂF§«p¸,¶x[L± C¨ÕÁìG´ÓÅ6Àn uvå™ 4ƒhRÁž/ <ôÐ9jµVbUªÊö½$6®;…Êö õ9©M›7`dôÎ[õ`v&zͯ§ÚÖìì6oÚŽþÁž}æiî«j @ÑÅÇî”P%·¨B@! „€Ë›€ªn|£×^¥ Ý½6ï –Ò1ÝG¿(ª°;¯Æ"Ò(ÂínÔ«tKTQ¥ÂhhcÕ*À ¤ByÿíÛTm7U*YåvàEÎG9˜Æ XišÛ¸Q8 “¹Â!­ *2LcvêÔmÔ¨gqÂÆu>2MYE'ü~ %¶6ñ‘Èòßå}ûÈÕ ! „€B@!`0ðÆk½¸ù†0üÖŠU?*Õ&bÌ=‹™¦¿ »ñªÌ>˜m ÷Rya6»ÙÅçÂäDÝ}QnšjB¹í}½ê޳OÂî§©IwrÑU•Ûƒs·vé!‹sX°ÛMlõ«ê3Vµª&ƒ›B Ú/hÃÐäÝ=Ük•£94.açÎÇDT!÷¨B@! „€Ëš€ªÞzsï}÷ r³ OǸ· 6kšóU£ÌH½kïðȘ¬Úº^Û)³ðú°rM/^xá%¶ÿ=|²ÚÝÓgŸÕS×ÂÂ<üA íÂÜì"Ü*ìõúu1ÅК5xâ§/P1Ø m·U³Q£ÄÂÃ×)èêlg›by,„ü_$T-ëÛG.N! „€B@#÷TÝp}+n}+ç{ È£ˆDÂØ¼y#ycc»˜sú`€#cc„ÌXd'_Éårsöʉ"M€Ê×ÿ¿3Um±ï}‹ú’«tv A'Eźõë9G£õ‚i­'ˆ¶H Ɔ3ðº9_57£‡*ÍŒ‘ˆÍ1„µSVaÅÌ\<ò„„*¹G…€B@! „€XÖ´å¿×ߨ7ß܇©fXìØÚ8¨N{N{­­íìÊ3`jj }.šÿQ.5Y±R‘bv R½Î=U§ª…Bs3K|±.O h†P¢Ù¯¯·GOaGóýa}éÕý1ýƒœ¯ZdUÊŒF³Àv@°Zeƪ•«19µ„OÜ+•ªe}÷ÈÅ ! „€B@!-T½îÚAœ~ ŒtG$Ò6ÎR9`Pl¨5ã¨DZbEÒ™Ôѽ!–Š\¶T¢F@ퟸ{«j¶(œ™*бîd8·g9”ågJ3q_•##‡õÄæõz1;]áž*'::ÛØc¸£‰‹°¬,{Q\`+`ï nxÃ¥R%7©B@! „€Ëš€ª®Ûˆ3w´ÁnRiþ«2ߘh÷«QÚÄÂÜ+Um(kà^)Ø9Seµ10ÅóÏs–Šë¦ñy(;ï?WmªUÔê%ÎI¡6kLgxÜAZÿ¬Lbu]+h2YêšG´mÒKо§*tó¤5†©0ºXÝÊcÅšþêm?—Pµ¬o¹8! „€B@! ,#.¸pgŸÓ ƒJg¥™L†YÈÃüÃv@ XPÒ•¶ô$z»;(ì‹¡³+ˆžÞvØ;Fú(?ýÑÕgžý•¾È7òÏ ¶þ Q8ƒþþ \9öNsc°Â€@wç¼øÒsضíD:ÛG`1k½…3ˆ´·¢K±n½åQ Ur ! „€B@!°¬ ˜ÙŽ÷úkOÂЃÝwuhcQ.vêiŠù…Yf¤<;÷ìÜÅëG.E8Ô‚ÃGöÂÇuRáˆ]ÑnìÞ½ʃŸ»DÕôè*¸ÝŠÙÌl1ÀáÎÂåSRÁ>²Â^A;ç§Ê¬J)˜ž, Z²£ ÅRžvÀ*Ü®.:xDo´»¸÷ž_H¨ZÖ·\œB@! „€Z¨ÚqÆjœ~ÚJ枸ÞÚnmE¾ãÜT Š)Á)ƒ•ÉX ‘p;öîy «V TCk‹“Ê}÷ž¢ööubfv‚éÌLÑÄ(¢f-Ì´z©UÏ¡½≠5ŽmÛWsŸÕ žnçœ{&^xþ7 U~øÜ½ˆÅÒè2Ãá3à³÷ÿZB•Ü£B@! „€B@,kZ¨:ó´!và­`QIe†)Ájµ ŸÏ¡^³ RŸ@Ww+Íèe”‹Fh ö…¹zz1;¿;v¬ÁR*å3Ÿ>Mèf˜šb°š¦á¯ÁþÀr\håõ‘L”PÈ—Ñ®áÔÓÖ`ßÞ´ÜYø8”µ¤ÛÕGQE…³UAؽ >ù‰_¢ÖhÐ ¸¬ÊÅ ! „€B@!p0sùïYg®Çg¬A¥çX““ÅÈ*U–3Qv¨†8¼>+圫jX 2ãä %®  Pf‘DGÔª' |àö!u`°ÅB“sS3z+sº›Ýj1£TRQ)˜‘+Œ±å/IJØÃS;_ìÅèðr3“œÂ7­¡=Ú†ly÷zêÍŽãoH>ºB@! „€Ëš€fÿ;ýô!VœÖ"±8‚P(H_©T~˜{zØ¿ÿEŽB©ÌHEØív®œªÁð¢Ù,ÁÏÖÀEÊ'ï>Oíí`l|ŒCYv¸}ÀèÈ$ÚÚ¨Õ*ºœÂï‰"“žÃ ÃWµœÆèè—í‘^ÎUÅh ´ca!N‡ûJ¼¸ïE¶ÿ¡Zo°eð?Š¢è‹²äB@! „€B@KZ¨ºúê3ÐÕeG!3KI_?2Ù4|tEô³›ovn{÷>Þ¾ˆ.óSࢰN¹ƒU°ÁPe¢Ì‚¡êCïß®n8¡›Aj6›±Ä(uhÕ+ZY©ÊÃföÂïõ0<±¥&ÀzÝ„D¬€H[Ûüâl #‘ˆ¡©6Q¨4pÇG)u“ ÏcB¥R…Óád™ŒúöËgr! „€B@! Ž!-Tsî* ®°ÁÐÌ £#Êðº"8˜¼îVŒŒâœ3Ç9SÕÆJ–W7:\Ml?yéTI U'ª+¸ÐªTåU’m* hmóò±,ËZÎLYðùQ«V¼T†++Õ™ ˜œ<ŒHÔŠ––ÀÔ:fpç‡æPçńí4e89Ÿ•cèJê*­R¥Y³ìSl6¥?ðÞCòÖB@! „€Bà¸& …ª+®Z·øQ/'è‘“‡ 3«Kîð­T̬\%¹Ø §7ƒƒ{èèŒrGïìÎ:ÚÃ.0'”OÜs’êb ·tã…w3ðXU2DÕ‹ˆÅ§qò–S‘Œ3p9<Á4²úhû˰¢•§%PÁ™çôcß¾ç°ý”1½0Žë_ÿ<®¼ê |vçN¾ÆÅ™«,vî|<òEœ|òÉøË¿üKÜrË-Çõ(^! „€B@cK@ U—_5ˆË®è§G"Ée¿ÎN•0;˼m¨Wìä›ÇÀ@jêâ T®3xårlôÛY¢ÈÂÛ¦Ùÿ¶©v‡¢ëÓ¡°Bay«Ê%¾V½JU+wÀí±³ÔeÄÈ‘´´ú¹k-ŽáP–J[`[·nÄ‘á=<1{ë)Üúæ—ðþ÷ß•þ°Zª IDATŠ…ùEœtÒV†©mèîîA’›ˆ¿ñoàž{î9¶åÝ…€B@! „€8® X9¯tñå!\ryõ’&“‰ €3ìÀ+s°餕£O*»òÚà ä0>–áÞ*²™œvôÁÁŸï·¶ŒºÞúW.—)Áh×_¢|x! „€B@! Ž-T]zeç]臱aG<‘`˜²èË~ Æ:+U~vZ±¸˜aÉH7D™ùFá‘ãRFŒŠ,@ù üý?lT› DÕ²‰«ÁA+E7úuuûy¢&]ì-X½j µAî¤:È9«2ÓZJ…ÝaÃèØnD;Üúc&öŽO.âáÏÏ3±)z%‹#Tœ½2êóS‹USÚ\•vˆðØÝ@òÎB@! „€Bàx' …ªë®_‰“·³J;JÔ¦×jEJ(4‹FJ©Ê f,¶ÒKYd2%¬ŠÐèâ¸TʃŸ_£Z,&n v"ŸUõ½SK¹ýlñëç‰J+òú"à7ŸÄÖÀ ÒéÒ™Œnñ‹¶·#ž<¨¦Ë'4ïÄÈH–{ªÆ`üm¨:Þ¿(ùüB@! „€B@,OZ¨:ë¼®}ý*,ÎÌëÝuÚè”E{‹h6l Z ,-•¸RÊΖ?m)°‰™g§œ²©¥:Ü׫ì|`µpfÊd£Õψ¹™<û ì%Ì¢Tæ‹­6Ô+,.¶Úi\‚ÙlF’¦Àžž>¯ƒå<>3åq–Ë:𾿞ú]¥jyâ“«B@! „€Bàx'`fGÝ9çqêéY©òù,ÕØ…WážÞµ*<7ç“”U¬B¥6ÎL䤨¯ÀÝVm¬Ri#PF(÷Ý»ƒ3U˜, –´4ñ„ÊeÑè e±ÐE¥ ‹bÁ,ƒ“ƒzõ ÆG’Lo Î^YÙhB:;î/Ê•ì.#þêÖY UÇû*Ÿ_! „€B@,s&÷TÅu±c/É c`w^A_þ›Ë(4—X¹rÒ ‘äÈSê$üþ.ù^àÎ^#MçyÚÿî»HUª*µ4úz£xñ¥g–|ðû‚Bxñ…SDøM©n‚Z`j2‡<+YV«‰™Q(i¾vìitõ¶â-7í‘PµÌo ¹*®9yœnÎRÕ9KUB{4ÌN=}5 ,LìØ[B!_ÙbaÊ¡;'”{ï>K †Ü´øÍéO4šL`*lVýìóøÛYþœ.&µ²¶—ÊŸ§ÓÓsºˆÂd4 —gK ¥ÊJU­‘6|î3êŽ÷;T>¿B@! „€Xæ´Pµõä ¶ŸFeº×ÀŽ= V­±ecö©¢Y«ÓúGwD£ÆEÀ|Üî¦}’È‚öÈ LN/ðyœ©úè›T—Ë¥·ó-,ÌcݺuT.q30SX2ÍÓ Hz't8ìllò C˜ŸKÐÈ]V ÿÖ¤Ì"Ïçš¾ÚñÅ¿TBÕ2¿äò„€B@! „ÀñN@ UNàLÕi]pØ«0šÙÁW.RȦá/¯ËËÊUÅ'…">+ƒUƒ§mæ&=X8tN‰”Ûþf@ÕÌ}V›•¢‰,f¢Ñ([M±ƒW NZÿ|ž¾‘ƒm€]TQ)7¸ˈ\6ÏY«†Œ1t¹º<øÖ·ŸÒUê,dÉ!„€B@! „€X–´Pµys+•êí RN*Ò+,,%X…jãnª9˜qÊ Sí‘(;ùf(®ð`dl½=í(Rì×à\•ò¶[#jOO7«O~ÌÎ-°Be×´xF¤X©b&£B0‹Ö–(ÿ®B"9¥ÏSY¨œŸ]` ÐÙӇÇFhÊ00Ñ©øÑ£øz UËòΑ‹B@! „€B@' …ª5kÝxÓ›NåØ Fù$çªØ‘Ç Q¥Zäž*BÁvæ©z€*hR ®Ž*r,*ñÀãuÀfsAyÇ;£jkk!IÆ*R«ncÚjÂíöpËÍ…¿ô+;«T==½lóKéÎö@ÀϹª}7UoÿJ=x•«é™4þãX±âÊ—%„€B@! „€X¦ŽVª8kGz{ýX¿~={ìIŠùf:€F 3M]ŒŽM X¬2'99gåB*;§[3¨› |àÃ+U³Ea‹_“ 'õé-º°Z­ Ë}µßS4\X-VXLš[±ŸäÉ\Èæsü׋zSfø­PvÜþ¾¿[¦Øä²„€B@! „€G 袊“ü8ù¤N=ež|ò)}ÌÉlr"ÒîfeÊÄ\T¢N=ŒÑÑ%¸< 3›ù§‚á‘ýèîf@厭PË%M2á¢éÂÀ+?f¦cô¯(¨ÈèÁ*“Îðä ¬ íb k6nÄüâ<ËQ lN7 k[‡—°”¬âÛßzVëdÕ œ¿2±ÒÅá.þn±XtE¡v(,©©ªÔ²ä†B@! „€BàØÐBÕ)§zñº×­†Aµc×®=z¾Ñ G«†B˜[Ò€ë6¬ÆÞ½³l÷¼~;¦fF¨R§!ÝÖI ºåãŸ\­*ŠÊ¬‡³ú19žâ¢«*N'KYFV° ÐD¹t=ݽô²§OL!Ôâs\ŽU‚/ÐÂVdÿaŠÏ7à_¿»WKMì/´R^á`xj²Ú•þ)í±J¥Â6ÃÆ±¡'ï*„€B@! „ÀqO@ UW^Õ‡ëoZ§žØË"“^ îçuÚ±ýÔ>üâÉ=ðú\8ùäÍ8xpíÚ*ªr…8÷úv"6oB±”…rçÝj{´ s„˜œÈpV*¦ÏLÙívÎJ%ù{>—o°„ ›N`pŠ!µ´„4´Ê“K‚5I…V©ªÕøÖןÆÙ眃÷ÞvÛÛ0¿0‹/ÿý?àûßÿ†††pÕUWá®»î:î¿D „€B@! „À±# …ª7¼i§œîÀožž¥”¢Ÿ†¿)—Tœqv/~õä~tuu¢£3Œ½û&áôhwYî±E½xá7}æJùàªÏçGÐ׋gžÙËMÂ!îŸ2ÿv°Â–=*¥"ÜÜ?UÌ×árר_Whý3!) ZÏ0|qXËç£v0Æ ”ßü_ÏáC¾C¯fÅ lÙr"Ö­]Ïp¤è"‡ï~÷»x×»ÞuìèÉ; ! „€B@!pÜÐBÕeW®ÂÊ! Ê%…m}9ج Î8«ƒ•ªÃèîédž ñXŽju+[Uî¬J ¥%‚—^2p4ŠJõ»?y‚:;;5CÛ0|x†%/;ç ˜·š5þkä Œ¬@-0Hù0¸bˆe¯ß ØêáV‘YÊQ7ØÔ_õõõqyð/„¯}å%1üXXŒësSvF¹®®.´µEtñÅ‘#Gôp%‡B@! „€BàXÐBÕÆÍNœ±#»¥Á)ÏQIfœvœÛçvM£¯·‡Žˆ2óK™;{íÜÅ ¤Æ¹Û·?{"‰PˆË?ðá5ª |žv*Õ¹EØàªåI´GÛ¨ Ôd­lù«é¬†Ê“Õò¬\¹¹³*—ÝÖÁI} –B;…?ЉÏ}æ ºÛ )~ÿЖ×j5ý¡ß—V+ˆò¾B@! „€B@¿´PµvƒMU¡À ªÔcÌ=f¢N?+‚]¿œ@k¸…Å¡ H ôô¯Ç›Y½²¸ð½ŒÁôÑþwçÕÂò½T Ú™Ì,wY™Æª¬RY(¦`•Ša(—Éa o€³Ti´E|$_C¹œÅôìCWV¾&›á˜üøÜýÿu¨:~¿.ùäB@! „€B@,7úòßõV¬ßhÃÆõgá…çÐö×Dk‹½+j8¸'Í®;+Âá׌V73Q!‘~ ÿbá¶0CÕ]¸§J³ð™iÿëf…ª†…ù8}%ÌM[°¸£'¢Žž^ Mœqú&Ž>•‹e¹6Ê+sò¶w­U¦Ûý*ºLbjrngO–ãO.—›Ñp+|n?¦)¡°"5bßá§íŠbš:öz½É`Å V1cçΟ,‚r…B@! „€B@×´PuáÅ=xýuk1|0F6¸œ&vìÕQ;󽘟Ÿçã*ÖopãÈž$¶m߀Þ>ì?ÄVk1>>å¶ÛÏWÓ™9"u€¶öåÑnç2à%Vª*¬SY`¥ÝÏÎMÁ-4úÍOæ©Jw¡Ü sÐXB¨µ© ß’;ª  ÏÄH¨:®ïNùðB@! „€Bà5@@Wª_Ñ /jErAA0ÐÏ=½ 4è—0ÚãÔ¬‡ôLd0*Øx‚ ɼ3úüÌ?qŒŒ”Ë*Pnÿàiª6ÿ”Ë-a͚ܛ¥Zð{}z¥J«ltÒ<÷ü‹Èå“ú›ÍÝÝþÈ^x ”KB@! „€Bàx& …ªó.ð±ZåG9ç†ÃÖÎ]¼)}® ¦*CÍ~mW¯ =)Ä&Œ,F©ìðsÁáp`øHAbÊÍ·®QÕF±D›6¯g2›§¬ÂÈÄUbHbxª©lí«2Xàr¸Q­åX©¢F}r×\s žþõ.Tjs\þëÆ†pèÐ">z×Ïß|v! „€B@! ^´PuæY\ý½yâ(Ôô,ì”RPlN+z¹%/šj…Zu7z“lÿ+sýT‚û{ð²Øä÷¯ÁèHL UkÕd÷@(—(„€B@! „ÀñL@ Uç^Àõ7­ÆÂ¸ ÝF‹’Tª;™X©r„1=s€•ª*å| $ãëXlj2H5Y±r`t|V›Êû?|šº”Jѻދx<Žb¡@iE‰+éͦÊB{3èïw=ŽÖP'¼îa¹«Ì¿{øº*l6ŠE+>þ‘ÇôïFÛT¥…5mp£Áa/ºEá9µßåB@! „€B@;Z¨ºö=ØvªÉY7" S· j£#Eff—\~V·ý-Ñ9±0»Ž¹¨Ê 5ƒU«‘ÉçXÅZ‚ò¡;OV.RÉ$+QV¥²,gY1=¹ˆ`°•v ;ƒS€ÉŒX|¯…NöQ´µ 𠃬h- ‘œ¡ ЛÕM‰Åîxߣ(Ò(h2™ôP¥‰J¹¦ÓÒBÕÑ ¥P€Qç{É’àcwÉ; ! „€B@ã—€ª®¸&ŒM[Ëhqo¢>=‡BŽûzÓilÙ:ÈB’™YgÁ‚F“^‰éµ˜Ÿ‹qá¯{mÁÔÌ”¦92Œ+õ S(ä19^@£n¢ñ"þ¾~Šq‚>Ú0&ÐÝÆØè ßЈž®A„#&ºvíz áÖ(Õ ÷ò7\ˆÕ÷üÍ{F155…o}ëÛ|Î3èììÄe—]†|!ÎÌÞÄ£aK! „€B@! „ÀŸ’€ª®º¶—\D.a¥=4Í¢¨5bð{L,%ÐÑiG1oC*ÝC½ºŸÚõ,JÕ8TƒŠÅØ"”¯}åBußÞCúµwvu±ÌeÁ¯~=ŒuëNbµjŽ^öšJœ.ö.hm‚€/¼¸›Õ«0g¯XuR X¹º £cGàtU1;£âï.ãÞOÞÇÇW"K`Û¶“ÑÚÚ†J¥¢©G}7Þx㟒—¼—B@! „€BàX F¼þýØz0=¶€H[;ó‚Ì-MŽ7Q†©ØÂAšþ؈â =tH$XlŠaåú 0ÙÁn>*Õì¾K˜Çüc„ÃmðQ¡><²ˆÕ«7ãðáQêÔm¨5c°˜l¨”ªœŸjåîª:ÛûòT©ç¬ ðú,°Ùkðl[d9S¥¥¹NŒ³½<‡— jëXýÊà©§žÒç·d¶Jîj! „€B@! Ž-T]qu'NÞFºÅ·«£k¢X\R,¬J)øØúWGK؈…¹³Ø½û0zV­AÓXãóLP>ðÞnuõP?’ÉŸcµj ±%ªÓÛ8e¢¬ÂÌÿ>³ÑAm ™Ë°l\€ÕÔ+V»w¿€.¾*“Lt`¥ÊÀ¡­2>sOQŸ©²Ù,´cTÁ§ÿîÐæ¬´Yª—ÿ=Vå}…€B@! „€8¾ h¡ê/^ßO}ú,B>ìÖvîåÍ#W¨ÑxÞGÙ^…sUMx]\ÜÖÄ Ï5°iÓ)øñOžÂl2wÀ‰P¨ ʧïÙ¨Ì Î>u`||}}[ÎÏaq!ÏŠÕÃSµ‚E4© ‡Ãèíé¦ý¸|˜(7ªµÃW&8ŸßÊ¥Àe|â®ØïBUµVÕ¿©f㨠ðåãeA…ö¯È*Žï›Y>½B@! „€8´Puñ¥œ{~³Ìt›“Ó ôEtÂ;Ú#½uÀtû÷8¶†˜…œØ}d‰Ì"ZBa­RÕ¦BM¬ÂÄ„& °#ÒÁäDÃÃS°Züœ«ª¡¿·G—X¸\6Ê(òì%œãœT+ -Èæp2Á…BV¼Lxÿ{ŸùO¡ ª¦Sgûí!¡êXÜ6òžB@! „€B@¼L@ U×\Ûë¨UO.diþ3ÑrÞ¡›Íž‚ÇÕ §£ÉY+và©»`jlC©là ª:&æ5   f‹Öþw[«Ú`ábß¶H„sRuع£j×®ÃÈçT¡[¹³ÊIJ—ƒ‹¡ÊmEWw O´¯Ç¡µC˜_8DÆLw…BÞûW?B¡r´ý¯Vÿ­Ý¡JÛS%¡Jnb! „€B@! –-T½ñ†A\yUÓã3hoëF¹ZÇÀàJŒdpêé×Âj,b÷‹!]x ùLóv‡{öÍ`݆tW„òÐÎj&B¹’§Ñ/‚î¾(ö훥pb‰;«°,í8ÿ¼3Qȧà÷»ð½ï}^|Ûü&`±*\ö[@o¿ƒ†Œ"Òé é›ñ±;~м„ªåpŸÈ5! „€B@!ðß8jÿÀIÛXé“`k%|^Ý!Ñ܈–À|ï_¾AGD+×å9"eåÈS Ç Z0=]¥S"€É©i(Ÿ½ošLPª,ñA6l¢&p¾@“_/+M~jËèîêÀ¯~ñ+DXÉ2U–ÃÒH-ÅX™²Q‘nEK[ƒU®¾ÿýŸ!—íÄw¿³ ¡Jn]! „€B@! –5-T{^ç……¿7 &ÍåƒôMÆül--,ÆGÑ=eõJ…Ç‚Ùädþr°ÛÏÌbÓ ”½w½Z,–áòÔ±bUw‰³UMÄbEX­n¶þyð›‘]j@mXØ3hd*g…ªÈ-::¸¿*»gœ¹™û«žáßBøÇoÌH¨ZÖ·\œB@! „€Z¨ºäÒõ¸ô’õ04óرc×?ý ¥R fkAW¬d媄HìäsÀd´ÓfnáÞªºz¨VPþæëÕf³ÎýTÔž8ˆla u ^|i˜{¤ LfA&¶ r™wU9ѠƯXÌè&À+V`„&Àñ©_±Ÿ°›¥2;öî1âáö¢@•ºÕjþogª^þ Åþ'7³B@! „€Ç‚€ª.¿|#.¾h Ù9\xá%øÅ/~™™Yx½¤’î­;ö‚˜˜zÁP'»õ<jÐC¯8«Vn(ï}×V5G{ß 'v0iùqxøiøÜgâÉŸ?ÇŠ),Ô©g¹ð×Ä™©‚ ³, U­X¿~=µƒcØwðÇØ²u§™U.7î¾ë—(Vkl d Ms©óЖkÀÿ÷ú±¸}ä=…€B@! „€°ÐN~åUqÖŽ^,Å'°rå<ûìsúˆS__7g©8îÄN=—Ó†—ö<…¾~º$ê V>}GïÌÜ.>ÇåÍoÚ¬¶wZíÔöLÙ0>šD{Çûés(”Sx© ,ë¡Èag¹ËdfEж@ªµ7èí q9Ö0×¶À fg¬øÔ}»Y"S9¥°Ú¥¥©— Urë ! „€B@!°<˜i'?ýôÿÃÞy€·YžÝÿH²d-Ù’÷v¼íìMBY$¬²7-£…Ò-»”ö+ý€-|´¥¥Ê*´@ ef ½ÛI¼÷¶,Y¶¬÷ž'8_hKþ”¯Ns¿×•:±¥÷}õ“¸.Ÿžû>ǃI“,HŒM‚'.…’ }}~˜­avöÆÂá°³V*«V½ŠŠŠI\²0GÂîÞ®JY9½WÓ׿¶ÔÈʉBrúvïÞ·#Ÿ'²£­¥--]\À²ó¤}Tcn&†^a§íåBBb<:;Û0erF6Š'0N°‡£nÜzÓ:ž¾¿œÚ‹íï#ÕÇN¹ ! „€B@! 7Ìûà Ççcö,7\Ñ6¤§aë–\cr#1Ù¦Tˆ¨¨¨[¶lfˆ…mmmܹ Põðûˆ…éÊËO4úé4-]^ŽÕ«_GFê,Né p+šÉñœtã•—ßb:à0Ë}}t¤T|:E–+š»V!¤§ÇRHµ³I8=ìê6ð?wlЂjQ¥ÇÿäB@! „€B@ŒJT~Z!–.MÆÐ@11iç",ÿ5Ñ©êëëARR×¢ÂèêêâžU"Ö­[Ç©a—dsÐŽæf¦ÿuÚ4c8ÜŒNšƒ7Þ|…ù‹ÑдeeÅÚ•òy3PUÙ‚U¯¿Á„¿…ŒSÑ¥¢°@L¬¡`7ÒÒSQµ³==ƒ°Dypû-'¹ ! „€B@! þ1%ª/ŠÇw®\€];j‘šRˆæ¦dff£¾©ŽM£®N”––¢»»›¦S"s&z(¬>„ é((ÌAK{ L'WläOHFL\›6oFVÚ0ÌMÈÎM`(E,U°uS7’R“ÐÈS·Q­Åaûö­ŒSO¥ÀŠðq©Ø±½òËÙ_eàæ›žQ%Ÿ\! „€B@! Æ5%ªÎ=¯'ž”‘€í½Lö Q<%Ð2ÁïïçTžŸc1Ì”pЕjb`2ó#F˜=á¤àêãc‚0pl±tÙBlßV…ººäççah¸©}mXxôl¶w¡¹ÁŒÌì&Vr*ÂC;êë똈‘Ç‹nÚdÑ\Òr£«³É©pû×p °GÆÿÆõGHnN! „€B@ÞTPÅÉÿKM§UPLÓ(«NÜ£ Z±cÇn®;Ñ\Jö"@Ç*6&»vUaöœ¨©ÙFó)D DñEÊtîY“3¦bÛ¶J¶×"Ÿ1Ngæ¨>,?öh¼ùæÛèí´Ãb5Ó…êbJ`úú{h{õR¥¥ò$Ý<ñdF©×é2`ÿ I‹ª‘0Ë‚?~dŸêðþ°Ê«B@! „€ˆ›Õ† IDATã‘@EÕQ‹R¸æäƒ•É冀=:#ÃV„QÓ@ŠuÁƪ¨ÁÁšIN®C Ñ€ ÀaF´-YYù0]sårÃårRmհ䪑NU>ÅPù‰˜9k26s$pÚ”åøð£Ù]õ6J+Jyé0*&VpªŠq‚m(-ÏFCC#±~C5û]Ã'ÆÿDTÇÜ“B@! „€8¼ XL&Ì›—„… Ó©q‚œÊëÅì€Ëé)œˆê]1uêd´soj4lEJª—FÔN]Eåõ1ÄÏ™B½´¦®_jtww1d¢GMáŒ`AA6j몑’âeñ• ÌÊ{ÐÒÖä”t½h¶l݈¡¡º;Œ]÷³»*Ž3ˆL ã™'›÷‰*T‡÷U^½B@! „€¯”¨š5Ë‹+ éRµÑ‘bÙ¯ÕÊ€Š r³ŠÐÔ¼›‰ñ¨ÝSO—*ñqŒToo„'ÁAçŠ!}æ8Ô×1ýïºëŽ6LÛSßôû›Ë6ážN °È—ÃÈÎNå 0f8E&Mœ§;Œþ.´·5¡½u½V232xÑf ­hüþÁ"ªÆë'GîK! „€B@M@‰ªÙsâ°dI:ƒ(úáŒÎáô^bãb`5[¹Þ4Êñ¿8fGô2jÝÁ±@•›Ú¨“Âk€",†¦TLmŠÍ¢«¨¨(AF¢GQPõò¤Ñ:ébÚôb`Ury+Qt­23ÙGEW«»§Õ(:Ûú)ºllv££³“¶Y~û«u²O%T! „€B@! Æ5%ª–¯ÈÆÜ¹ôvw0èܳÅ`XEq±nvò¦¢²²Šé‰\{Ò•S¡ašJ w«FÐÛÓÇ‹8˜¾yù\C•Y©f`ƒ³z /ÅSúzYeÁ䩙غµ’"É‚òŠrääÅãûžÁäÉ¥{Ë®ÚXlG`0€HÄ`ip"îùù{ûàɸߏþÉÍ ! „€B@Ö€ÿ›ƒ ’`õpi€,FèT9ØC•¥§ù:™pÞ×7[tŠŠsPS½“Úh%Å%üY‹jœ‚é:UЧ֖V:SAª/ÒR3(¢ØGåEz–ë×mÔi~)i)˜>£]¹™>X,ôõ è†a5{˜˜èEíî.<òÀ&U‡íGS^¸B@! „€84(QU11‹ça4ä€Û‹7rr¸?å±1ñ¼ŸURÍt©‚Ô?Hôz°cçvŠ-?ÊÊ3‘žÑÀ0Lß¹ö(£££ƒWÉÉ)\¾Jä¼  ›6U2¨"‰c&þl”½SÝÈc2 L )ÀéŠB˱úôè_zZ:ÚyžuÕ`Õ m"ªϑܥB@! „€8l (QµhÑ$¬X>ƒÉæ¹#NãYPZš«‹€•  ‡ Ƭ»X%e ¨Â‹êª:45ô"-=™éƒìªr2Rý†cŒ@ ðñžTÇ÷TÞz,SÙëvddÛ(²,HJIAc—¶b­hiöó$^Ú]­0Ø:m*£wÃépbó¦=xî™FU‡íGS^¸B@! „€84ìTŸ€I“’™>HÕÏ.Þd¤{P¹£Uk"·+Ž/ÆŒáá¦ÏI¢Dåö6šQÉÈÊ´ÃÏ< ÓÅ—ÎfúŸ‰a1F{¨Â܈ŒFÁçKå‡ÙWåa“°AAÕÈHõVæ±Ç ³}K[¼p$ÄÄŒ0<ž8DŒ0óÛÛ¸¬5‚?=¾çŠ*U‰pèð _ ús°?ˆSþÓ‡¨k©Cí•©CÝ«úûØ¿?ïùåùB@! „€B@Œ/JTM›æÃÒcJ0ÐÛÃ]©~öR•¢©© µGûÌ,÷ÍáäÞ0Z[;1mŽ“ÙaÇ#tcΜBЬfU\qŒÑÚÚª“,|,°ÊËËatz%êêêP\\ÄѾ½9íU5Õ ©ÈA_Å–‰ n&c¨ÂQTWoçŒa*ëÄH0¿¼kõ>Z*Uð»ßýV­z 7n 7Ü€Ÿüä'Z°©C‰™º`Ç<xàœ|òÉÚ5[µj•ÞÓù§äÕù•ð:ýôÓù‚³ðÓŸþT?þ³Š1uû (umµ+¦¾§~¦DVff&nºé&Ü~ûí ïØ:¾>r7B@! „€B@|&z§ª<³f1ͯ É­(.šŠÛš`ÃÔ7Ô0g"…éèƒÔ£(›œ¤³' 8ÑP׎Դ$tuuÃtΗ0TÇ”•••L¹Èg‘¯ µµ{X\€]»j8WèBKk | ®hhn`lº ee¥Ü³êD<£÷ì©ab ‰Ö³ÛÙSõÈë?Là’Wvî¬ÄqÇO×…«¯¾çŸþ'ÄÒ-·Ü‚|îksÎ9øÃþ …ÑÚµkµ˜9X§èG?úgKqÚi§íù·îÓg"ü±àÛÿúEEEÜ5Û„cŽ9o¾ùæg=<^! „€B@qD@‰ª²²$”—'q7ªÅåf$'å¡nO/¢­iغy¼^/+zhøDáÌó ¡¡•;Û°»z+RIº×ÊtÚ™3¨ ºNfvSÙØ£»¨¢£­ØQ¹C;JÚÉrØŸŽ®î\Ù”œD!6Ìå,¸vk ›9¿¹ç-^¡„WVV&~rÛOð˾‰3Î8S;b÷Üs>úè#¤¥¥110W\q-¶&<ñÄxòÉ'qå•Wjñ¢Ü²9N§œr JJJ°téRªÈ$-Ê”«´{÷n<öØc]ÕoÛܹs¹„¶ˆ‰…¼ÿþûx饗>!¾&NœˆeË–éûV<Ôý=ÿüóúú………Z¬)§ê¢‹.ÒçU÷¦Žþð‡ãèc!·"„€B@! „ÀÁP¢ª¼Ü«÷ªÌ¦6LœæFÀoFS£™EÌ›0³:jPNii©˜uÄlÙRM!5ˆŽ6 W£|¨ÙU ÓɧM5âããi[uiõ•ɨÀä”D $7Ö­[§ï§ªºŠîU¢í6$úö*µH$¬…—…¦³³¡!õo'c½Xùô&Lš8¯¾ºJ‹-'…×ÀÀ¿25ƒŽ˜:óçÏÇ5×\ƒc=–åYý}•@¨D7TÂå[ßúÖ>Qôi`”SbI9b.— ›7oÖEÆ|ðÕS׺øâ‹qóÍ7£¦¦FgÍO˜07Þx£¾†:”¸{çw´€T{eN'_ïcÁ‚úñ“'OÖ#ÑÑј>}º¾F__Ÿ~¼[”C! „€B@C€UGÌË¡d¦1ÓÅË ™1 gt¶l]Ose‚6YvUW£´¬&ó›»ÑP?À ½D¤Ótih¢Átæ¹³ —£ú™Za·[éTÅ";;©TduüS¿¿#£aŽÿ%Áðó߃|¬® £#F±ûè4µRÀ˜àr$aåŸßa¡ S¦LÁõ×_5kÖðÏZ<ýô3øú׿ά*îmm×1ì‹—,ÂUW]… /¼PïSepñ¶ÛnÓ"¯ªªê ß52¨FÏ<óL-ÎÔ¡Ä•Ú×R®”r°î¸ãýýï|ç;¸ôÒKµ°kiiÁŠ+ðûßÿ\p^~ùeíT©Q¿mÛ¶i±§%¨”c¥^‹r´”Û¥Ûœ´ƒ~ò@! „€B@! þO (Q5s†$LãŃâÒdŽüÕ2Õ¯µõéßÿk˜-1yò$öúúa³»ÑÖÚC÷ ÌÒtbÖ„'¦+¿7Ãò²cª CCƒz¯¨¨Û¶Ö 7·ý}¼÷þ(«(ƒ•ÁítnÂáˆvrš›[=8ŒLDÔÕÕÓ³"3­÷ýê™}0ÿøæºkQVRоÿÜ &fô~üó.¼à"¹`>.»ì2¼ð ¸ÿþûµÀQ‚hLÐ Ù[o½U;PûïT©1@5Ö÷úë¯kçJ 5圩¤Ãyóæiq´qãF=Ö÷ÐCq\±Lÿ[ ¾{ï½WÛ©R_ÕΖrïÔ¨áêÕÿÆq0÷'B@! „€B`|ˆbVÄÒc’1mFR}i¨Þ¹›¦‰‰ i BuR-æÍŸ‚Õoâã^?§øœˆ  ë×oCIqLßûÑT£¹QçŒDDFu‚^^^^[õ>_±é9'¸æ( í.«Qº;ítný³`xdˆJ.SÃùýL›|$þçgàÎ;ï‚Ï›‚IÛAq’‘™Åñ<7¶lÞBWê",^¼~å|Î!z¹Ë¥wªfÏž­Ý!%X¾÷½ïéݨƒu‚þVT…\L:U;U~ø!ïϯßEuNå2©}(µ¥Ä[nn.***´Ðš5kcâ=8úè£÷íN©ç‰¨_ÿÈÝ! „€B@ÏC ŠO^ºÜ‡y œ°îR±sÊdCN6£Ò»™îG4ÌɼlZç§.ê‚%ÊD!•‡`¨IOûÅÇÆÁtùuÙFS=cÍ“S hB^n>ÅÓÖ}¸“ }fÄÅ&!Ñë`GU†8V7 SìPÓ™,# 0EPTXÀ`‰ô )1>ð4Î:ó\HLänÕ$ŠšWpÉ¥_ÓQí/½ø2~ä”p¼nÖÌé8ï¼sðÜóÕ¢FßÝu×]Ú R¡Ÿ¥§êÓD•Ú‡R#}jÔOíK©CsP°©ó«Èöý…›rÈTð…ó;餓ðÊ+¯ì{ŸÆD•^o¿ýöçyÿä¹B@! „€B@ü› hQuL…U"l&'jªz81gÇpÈ`T,WŸvaÖìéœz[ƒÞ®GÕ×"/?G5Û+ßC|L,Ú¨“L?¾ã£¥¹‹.N„{R—$cgeç-°F¹à°Çƒùèìn§*33ý¯Ÿ•‹‚ÄÌpŠgc¹·”L§©‘î•]÷T=õÇ׸keÁÿ÷SÁ…pCzøQ\öËÐÅ‚`sT4å‡?ýùIÆ­‹³Ï>‹MÆ“ðíoû cÔÕ{0æH©Aµ'¥"ÛUÐ…õÛ°aƒxøá‡u<¼Ú©jllÔ×Qcßüæ7uØ„ ¤¸ä’KôØ¡z® Ÿøò—¿¬£ÓU´ûØ¡v¶”‹¦Î§Ä–r»D\ý›ÿKË ! „€B@ÿOJT}é¤r1? ½µÌ•HÖ¹éééè÷×`ÔèGJR>X³f¤ÃêhGJªS~£hooEiI1z»ú`:ï¢BcBA¯Za²Œ b’¹Y ±mKGñª¹3eæ®Q!êšvÒÉÁ€ˆß³3)ÐAQ5¬‹°ÊÊ÷öY9ìNŠ)žzüm 2V®\©…LCc‹þzö9gc ¯Q6ÏÁe°¼|ÜøƒS~Þ¥R»OÐÅRÇÁöS©ˆtå4©ô?V¡Ò•Øyã7´Ó¤~¦¢ÖÕ8¡êÁR¥¾*P%¾úê«Z”©=2õóóÎ;O‹0Ǯҟzê©O¤ªk©Hu56¨‡††ôsåB@! „€BàÐ# DÕì9é4[N€1Ò…w?Í¢=ðòïqQÈÍ™Œµï×c°?‘Z(Oò{«ñö;PÄaʤ)¨g°ŸéÜ + —ËL* ‡L$¥šÐÒîA5#L·)4AIiª&îS ñAD(ªlF*ñ//oÜ1vTWUÒÑâ,"{ªž~òl.-çÈ]-oÕ¬w”ëØ‡eCd4¯~ׄôŒTö\1aƒ»\j¯ê³„Sìÿ¶)Á£"ÕUB‡R@€)ƒÕúïJ ©Ÿ« õ˜ÚÚZ­@÷njìOý\}mnnFww÷'vº”øR#ƒJ°©v•*¨\­;vzŸ¹c! „€B@!  DÕ²c ™¥Ëj¨!T”%síUde'SGŒ`(èa`bœèZáNèÐ9;vÖðgf¬X>Íí0á\£¥u'&M)¤¨`Š_}6oª/1ŸXÊžXdd¹ÑÒÚ„¶Ž6îMÅ1.Ý­U¯n´-Z»RZt—Žß?ø&o‚~†‰_ ÞªöûxT5éÛçaDø?c>þÐØHßç}ŸÿUçù¼÷!ÏB@! „€B`üPªdÁQI˜<ņÒâxL,©àºÏ+œÔËcÅž_YÍЊ"N¨r*i¹mèë÷sÕH…õMCn^õ{z`ºþÆ)ªê›ŸB1æ8_Ë}Ô::JЍDŽüõ «‡XŒ›ÊÌ‰ŽŽ^vA©²_+ÿD`ÑÏž¨4Õ÷âáßq‰‚j¯¨ƒgÒ{X{½"‹Ø'ª”ÒßùXTlêßø{k䎄€B@! „€8èñ¿¹ºUÙÌ\`æSÉ7¬ßÄP½DE»±n­I¾ ®ÕÂ0³J*' Ì’‡¨Ì˜P€'&¦ß=úMcgÕöN9Ðß߃¦æFd³ìª½½áÑaô¦ â^ƒ(dÑÕÕÍý¥BН;¬¸ƒÄtŒp;Óû²õhÝæõ»ñÜʆ½&55Ò~Bj̱²ì7z¡ØÚëNíìßu(¼!rB@! „€B@ZöîTùpô¢,–{`îÁ®šX¸p lÑÙh¬w`3+¡’’|pºã¨‰:´SÕÖί¬„:ŠŠò)0}÷ÚyÆ(ç= ^èíí¡Í5€^ñ ví\%''¢½³[;Nv»»W6*µ&Ú]¹ÜMªc亣è€Ïç£kG_7ðòs­{M(}Œ9Pÿû÷ÿuªÔ÷>V_c¦¸Úlï`+­·OîV! „€B@7 o`æL/Ž˜—ÊЉ tîÆ„²³hku3ûa˜{Ø2uvîl…?aöÄNšO@vŽ_-ÒRèT}û»3Œ¶6[¥²58â~”Û·Õ"Úa ƒ¥½IhhjC‚'‘##K?ÕÞˆÿ뽪ÿëëèõËÏ…€B@! „€øÇÔrÒ”)LŸžˆ’Â"Šª*,YV†ê]/c8XÎtt0í徿0Ö¼ßŽÆæVNè©ð;æÎ†Ìì>´0åÏtßgMèëó£›ëñq©´¼ö O6³Ø79%w¬LV¡>Daeö¢™B+Àxõ‚ ©ì¢jáø  ½=½TtÃXõb—6ªôþÏÅ_¬Ët+++ñ­o]Ž{ýP!ýÊ,3wµb°dÉ<ýôÓXºt©Nê{á…ö•ò*¡²|ùr´§Rý.»ì2üüç?×]TÿìØ_ ©¿«~*~ôÑGóõöáä“Ofiñ¦}§˜>}:þò—¿èXö±ï«Ä¿Ûn»MG©«ûRé„+V¬øDÕߊBõﱤ@ù ! „€B@!0> (QµìØ4œûå 4ìD § žŠ›^ƒ'.)9,þ]Çñ¿TlÜP£Çw9=èéÑÁ2ßEGφéºïM3ìÓ,¢QUµ©É…èìjgÂ…Ÿ.UýaÀÄè¨Áöà hjiCS#K°’ÓÙ8\C‡ËŽ ‹ƒ§Lžªß_»Ï?»C'*Q•žž-[¶è(%ªn»íœuÖ™ûD•Âûƒü“'OÆé§ŸŽ‡zHwZmݺUï]©À •8øòË/㪫®ÒÿV"gáÂ…Ÿèú´·I ¡1qõ«_ýŠíÇGáòË/×%Àª¯jppp߸aqq1n¸áÜxã:v]*b]•©,õõw¿ûUªSëÑÑ,6¦€ ±ôXFÇç±B@! „€B@ŒCJT-Zy ˆÆ$´5÷Âë³Á‚ÃnÁ``ˆ}AìÞUÏ| 7Cú¢(ª’(¨‚tª,°;«pÌ1ÓaúñÍó`¨±1‰Ø¶­šB)Iºb Æ –pÌm=w­â_ÑH« >o²4=¿ËÈ4£¬ðHüõ¯¯èsx}ÙøÕ=±0x þÌq>nH1ì"Y‹ U¬Ä†ßïçÅ—ã‡?ø/Ì™; Zä(ñ¢«/þüç?ë¿×^{MǶ'&20ƒßÕUN’r¶Tað?;œN§>Ï”)St¹¯:×Î;õá%—\¢Å›:î»ï>Ìž=[û*Ѥē:FT%>ðÀ˜7ož.ÎÎÎÆªU«ö ¹qø’[B@! „€‡5%ªŽ?чEKÝ0c˶Гa_¿Ï[ †Yò; ·=z²//?¡!ƒZ‚Xw"Ò3™œÎÀ Óµ×Ï4Ì–AîD%pŽ£v ño|Kÿ}ôÑGZ¸Ü|ó-5UxõÕW0yÒdŠªÙ¸ôÒKµ uÜqÇ!##wß}·>{öìÁ 'œ€ï|ç;úùÊYúéO 5Ƨ~¦D‹GÿìPãj„OÊ¡Rû[j—JíFç…KL›6M;ej¼oݺuú9jœO‰8uü­S¥¾§DšŠj¼ñ[ßúN9å-ô °råJíº½õÖ[ÿ²BãÃúS//^! „€B@ü (QuÌŠœs~.z: 쬶Ã?ÀÊ'Ä ÊºFØÄ©=fKPX s2¯¸(‘Z)þ>ƒ~ÉHJ©E¬Ç ÓÕ×Ì4üÁN”£¦º™i~8]aLš\‚ÖF§gÒúJE]]#‹®š¸'ÐAÁ`?fÍœŒáP3Ý£(ü×ϰ §+wÝùü½BäÙgŸ¥°ú†¿Sbhö¬9,öën*%jÎ=÷\œxâ‰:â÷¿ÿ½v¦”¨R‡;ÊÅR¡W_}5K‡;ð‹_ügœq†ÏÛ_ðü#¶êùcáJü(Q¥œ+%ªÆÊ…Ç•ú:cÆ Š½WuhƘS¥ÆÇF ÿ‘¨R×U kJ@¥¦¦Ò±û«Žž2%Q ªª*œ}öÙZØ)!'‡B@! „€ダU &2È.‰c}±I“™ò·‘ÆÒs&êžš„>ÖNù’1<4Ä\ u„y¹eŒVß ‡³yñ0]Ã#¿ ]N]hk²cB¡‡»RÑh§ˆÉHË£åe ¾¡…EWt°ì¼ˆÚÁbjŸËA Ì…¾ž::]nÔìÚÍñ¿ ¸ó®µ¸þº«ù÷d,Z´X;LJ¨‘¸_x ßgÄܹsñ¥/}‰{L…š¨ržÔ¾ÔÛo¿­C žzê)\{íµÚYRi|J´¨ý*õç7ÞÀüc=‚x cL4}š¨Úÿù3gÎü;QµDú§‰*%Ü”Ã5‘5Ì*C 6u½ááa-Õ>˜ÃáÐBK! „€B@! Æ%ªfÏNÀœ9‰Hò¹áK)BUe§áLp:ìü½>ÄIºT®2Õcp 6ë('ö¼ô‡hu0S+S“3`ºø«“Ä”&î21JNUw*ò&xè±)¸µ ™ùtŸt±vó¤a—Å vÝ™H ]*À³‹EÀ.m‹µ·wRͦ«ôýn@aQ1EP>Þÿ}íÚ¨=(ÕH|ã÷€é3¦sÌn9/^„wß}W .µÓ¤˜Š3ä‘Gt€EII‰v~Tœºܽ{7ª««µ¨Kè;Ð[¢\"å&=øàƒZ¤)ñ6ªâ ÿæøG¢jÿ‡|š¨RÂKÝ·yW\q…¾–ZêJ\õööTRá^‡ü\! „€B@ÝS553fr?*-†¢)MNÅ9˜)1Låf`Åפú‘’ÈŸõsâ.Œ´t;<ÞLJaúÊ—gQŽ:NIsFcC€ŽKw¤j4“]ÄT‹ víªe)Ö.mÕ£zí°ö âc’P21È8ôXîuÒ«¥ªËÄC¿k@`h„âêJ:X±øÑ~„Ç\ï©t½±#!Á‹'žøƒv¬Ô.Òœ9sðõ¯ý””Ë£„‰|òÉ'uúÆ?I5§Žûï¿ñÜ;õÔS?‘¨ÒUˆFyy9TØ„J Tñï*pB¥Ž¥¢~² IDAT¥¥ÚIS‘ïj̯¿¿_;SêP÷®DÛoû[ív©ÑF•v¨B2TÀ†Ä«¦·L,„€B@! ¾p{EU/)„1ÚCcÄʪ'̘$1jç ™Y.ê3FB0ƒ°°Ë7';æ¨>$§™†`:÷ÜLLûs‡˜^¡Røüt¤LWƒ,¼JEnN RÒ¢±}ÛN¤¦¥¢¶n=3ÛíLÂpç)ŒG¥` °ÞÄ Ž±ec÷þº‰Š- ï½÷.ƒ)nÕS>ú(æÏŸ¯»¬öL›:×rLPÅ­ß{ï½Ø°aƒŽ_ßÿPBH •Â÷ë_ÿZ—«QÁÏr(Q¥Ü$u.%ž”€S£yêlÊSS¢K‰)5§v¼®¼òÊ}—RUÉ€ª¼Xó©¢`%Õ¡Ü0µvë­·êç*¥*TÂàJŠ?Ëk‘Ç ! „€B@!ð¯! DÕô)X° Ÿ;U§·PL™iqmÇO·Š¹ñ±\ꢋL,OäŽÕëÛ‘™Å¿÷ï†Ùb‡éüóffsžD ¾h‡Zèàè_ˆ‰>¤¥æð{íÜ}ªà®Óèꮣ:‹eº]GûXÎÇB`3~yw=Û¬IŒÜ¥ÓóT'•JÅSêo¯èˆP(¹ù}¯›S©õõõúgûï1eeeé8vU¬âÔÕøßg=Ô(ž:¯ŠTWçVác‡ÕSßWçW‡Ú SjtO‰!•B¸ÿ¡ž_XX¨ïE9UJà)5f¡:ªÔ=«ý)•z¨ÄÖXzàg½oy¼B@! „€_½S5'‹©åEÔ>£ 9Þ7DãÅ©E•Ù2ÄßíÓ9µWðÈ( ò\ óë@G+;{]QÈÉM &ÂtÁÓŒPh±ñ$$²ÊG1Ñ9—“]Tl .,¨@wO'Ã*†õΔÙ9©óàT¦Ló¢µ­š‚©ÑtrÜ1Ù¸úòõ¼¡O{ñ¦¿ùñÅQ’3 ! „€B@! >…€U“&§bé²É€ÑÞ>µúcf…”&‘™+M4I"¬‘¢s5êA6C+F8ñf¡£µ‹æQzZEV Lßþö<£µ‚È>Œ©ÓJùäVº.:1ɨ¯kA‚']LÃHe@Ä.ØØÛäõÒͦèªÕUi™6.jy(¬v³«*‚;om?ìE•rÀÆbÛåS,„€B@! „Àø# DUI©—ݹìÛµbGåG4š¼œL+B]C-ÒÓÓèD p²®1Îx8è`©i5«UuÕ†áæJPgW§ê©:Ò°ÚÂhh¬â,áUt¥†¨À¬hkëÑåVí¼œIðuuwR€'2s\¯Ù™åpŹC5ÂÀ8¸])¸ø+ÏüQ¥`îïVýg:UjLPP1þþã‘;B@! „€Š€UeåI˜6=›uPVöòVéäsµÎ³eÛfV‘–šM=Ôg´^íÕ×Õk=cgÕРªLé;ßiÄÆ™1†(¨YŽë¦¨ ˆ89Kèb¡ïèDe0V½ŠAñLýüËîªF$ûJùïVÄÄÙèn9¸¸eàîŸm8ì*ù˜ ! „€B@!0¾ (Q•—ƒ©S3µS1ü ŸèavD6Lƒáv.†RtR\EÃÊlˆ“¿t2ÓËŸ¤ðr"Dow/ 'fB\sEU¼Á4;;ES5Ÿhf^“.¸tÕ€ÙdÕ‰m­íÚúr»Ü¿bšÓûü°ÛRØÃÔª[†kÚàv§â±G¶a_Èßßq”ªñýÑ’»B@! „€‡%ª 'Ä¡‚nU.C'2˜†® %Ÿ/Š2YéôÚZœÞKâ# NèÅ2øÎLçªþÁn”–ѱbOÕ•Wix¼Ê¶ ³`wìÖtüQ(*ÉBGg «a—TÐæj`r_”.²µÙƒz¾°³³ñ±ÙºÐ·´´))iLÃëÆÏnFDÕáñ9”W)„€B@! YQ¼ó… ‹ÙS»¾„(fH¼ŒìD—fâOO­C]]?S½ ¤ÈǶ LEå˜`2ZZÛPR–ÉZ*?Þ_ó:LW]3Õ°F÷ÁÊä¾ø8&ù5¡¡.€%KÐ=Tk?|3¦ÏÆk×QDõp¯Ê‡áp;ŠŠ ±‘½R¹Ù“YÛΟu²÷ ±aÜuÇ ô±>í§êýÔÉ ! „€B@ÿ JTt–ùÒ‡m`o1’S²ñÚëï3 ÐÔôîQ¥¡²²Õ»šíŒe?U*fO¸F0jaì:ªŸÜ1ßè÷7Q@Eq0‰ñÙxóõ-´¾¨ÐJòá÷w1éÏLÕ¶‡Ùìø¼>ì©Û€E‹bÃÆ Ü©*¤‚kÔ.VAA!#×GpçíUÌŸøÇ"ªþƒ>‡òR„€B@! „À!K@‰ª©“]X0/ §œX†8¯Ïüå 4ÒhJL* 3t¶÷"&Ƈhj¥ØxÅ–=,60ÀnÚfNë%Àtí÷s “ɓĄ X ZY¸[_²ùùé,±­b…ŸíÑ:¨b4ÒEÑ¥Ûó$Ü»òx(Àü촊ే6ò1"ªÙO—ܸB@! „€8Lœ°Âƒ\œ¦Q<üô´v0Y`ŽŠE04Ìu(K~ó©‡0:<Š¡¡FGÁÑ?öS6˜¾yE²ár&ÁåHBMM=•VC*V‘Ío§xJBÁ„R¼øü+èhïaGU&²smÈÉÉÀÖm[08ÅÐ +Ÿ—J‹wßÝŠgWÖé )·êïqª“ϧ¼L! „€B@!0® ¨ ŠK/MÇ×¾šŠMoÇáµ÷üð$»Ð5ÐLAå†#ÆJc)Šá}fŽFØákgx_€bÊ£ûªR’c©{8þ÷õo³J)ŒP(L§iy¹¥0Ì5Èæ`aUU(;JKJÐX×í/\ˆŠncž{^ã5XŒl¤¤Ær¯ªƒâ«õÝøå/Þÿ'¢j\s•›B@! „€Bà0!`áë<çœr¦ÿ¢½!Œ¡j!3Ú{º©y܈vE£§§—}‰{'󺑕 —+ÕUÕ°FR÷°§J•ÿ†G{ÙQÅ_f¯;ì‰ÈÈm¥"s ¶¶‘‘éIˆ„G`„í:£=1!‰Éý8òÈÙøÓŸŸ‚ÍTˆ#š„õë¶À›˜Š¦f¦ÿÝùŽˆªÃäƒ(/S! „€B@ª”¨š=;+–ODO¥TíªG ÷¨L63¬¬²Ð©r9c8öÔº¨¨°ƒ ÅV‚ƒ}ˆ‹é«—äS¦1g}uµõVnäé\RXÙ¯nCý®.xâÒèZ9˜ÑÃÎàí˜9«œîÝØ±­ Óf”ãí77¢½-ˆ¤äÜ}÷U‡ê'Kî[! „€B@&Ôøß‘GcÊ”4îNõ ¸¸œa|;h8„ýˆ÷&ÂÍòßQ¦üYX?eã:”W *Žûq°Ng4L7Ý|”10ЊäädìÞ]ÇyAƒu˜4¹ª ù3cÇævĸa1Ûø=&¤!Á˹BS?®êëkQ·'€ºÝ~Ì_8W|kµˆªÃäƒ(/S! „€B@ª”¨RNÕä)^ §næDøÐÝ7€jfM¸ãr18b…žx¢¬QhoiEIi ƒúLhå߃ÁâããÔøßFJZ,"‘0j÷4ÀéH¤Xòæ^俥òÁM ±Y˜ûVQÜ1NºV,jAL,“(¢ê1àïæx`<ºØQU1¥ _û²ˆªCõƒ%÷-„€B@! JTMŸáEy¹ ™™1èênalz¿öc` ÃÆE“&³]p;¢uTSS3†˜·>ŽPG1-ð¼óŠ  %%ÅaÕhØÆ?+Ú:·Á“`çýpÙ²ÐÇ<öáá>&„DOFF{8?hAz– M»“Îâàæ·›qÛMâT.ŸDyB@! „€Bà% DÕ”©18팩ÜjÝa‚“‰æiÙxïNô ö#68½7ÌP¿&äf Â@ß W¥¬TàJT¦³Ï.5:;;é:ÅbÞó¨º1jtpI+½½]hoï„1üp®¸âн˜˜¼òÊ+X·n¾ùÍoþÛîC.,„€B@! „Àø" #Õ§ù0q²‡{ýHNòÂfê_Æ(WŸìq:œ/¤à²™œ€aîX55´#9#ˆ™sK°e 3)þç§§­ûóæÍã‰"hcPEWW }ûigyx‚!,Zt4S·s_* mmÍœ7 ¢¸4ùiøóŸÞgž{4›€Žff•â·¿~ ƒÁ¼½úM|°ö#|ï{×SñÅë|w•.¨®£Žh.x)ÑàìâØ¡®á¤æ÷û÷=Îf³étB ¡Ä^Éc‡úžJéø´cQuå•Wêó¨ë«ó=O݇ºîà áLJú·z¬zÜØaµª±G§þçßÞÇøúxÈÝ! „€B@!p JTM›ž’2/úúkQZ–Ëâßn&ûq2¤/V‹*æWèQ¿vRù˜:+v·|¸¦ë¯™g(QSVZŠÞ¾~îBt¥:d©U*ݧ… ÂëõÂÁªPÈÇÿøK´6ósa˜Î•ÇãEwoËûø=7E“çVnÞ'ªlV;::Û0kæ,¦Æà†nÀÝwÿB»`_úÒ—(¸¾‡%K–ì[êßǪÁÁ6nÜ‚ .8555HKó!Á‡Ukaµ¤sºÏNת“·á( _;Q»ímƒøÃÞ§(ÛëTMž4 ÌÇæÍ[pƧãþûïGQQÅKE[*ÞyçÜyç ·¸G;Wk×®ÅÿøGüä'?aÚ  O<ñçÝ8餓´³ôÐCqß+‘/þèƒz[Æœ*uͳÏ>o¾ù¦QJ -]º”ã‰jÁôÖ[oáꫯÆ<€ÌÌL¬_¿·Ür‹ÞÃRçP£ŒjßìÜsÏE\\üq-$O>ù䃺yB@! „€ãÀ…ž„Œ,7«¥öЕŠè ¾ŽÎZĸ}°ZY!e1PV^Œm[WchÀA­NçŠIè¶.lßÖó&L§ŸTh˜£B8å”™î×N7g--­hlhd©oŸU;w¢¸¤ˆ;WAÔ5î@oC,#˜Œ¦¦ŒíÙ(Š¢­ øÝï^ß'ª:;º?!<^ã5îdÕSØ\¨EÒþð-ž”ësä‘Gâ׿þµv®ª««)äÒðÑGiwK+Ç(??wÜq‡vÐ6lØpÀweÌ©jiiÑ‚H¹rJ©=«gŸ}?þñ™æaÆ“O>©wÆÔcN;í4üò—¿Ä²eËô.ÖÌ™3µ¨ºæšk´ÀT÷¡¡~êåB@! „€BàÐ$PQ‘Y³ 9ö×ÄpŠ^ä ¥µùyUvìÚ]‰””xø’ÍØüQ#R’“ùïdêŠ^æQ„ípÃôµ//6zú+qιgp$/ŒáóÖ+·¡¹¹ 'V0½o¯ˆèã.TEWii‰èîaU/>—NÓjttuhaätºèVyñðÃoíUo½¹7Þxã>Â>ú7Gÿö:*G!##"«]ÿôR80S"uՃ쩊âäœU• ´0J“ÅtÑù3 »#ˆ‚Â<ª°Ý˜;{—´Z#¸G5 2‡Ýfs ž»Fʉ G ÍŒLg/…TQQ!zz±iÓ&LŸ>ý}£xô¡ÿU;+«qÉ¥_ei•IS~úi&j1µWTeddèÑ;%|N9å¨0 5‚§%ªÞ{ï=Ž ^@‘׬w¢TP„ÚÇR÷·°Ä§½…êñêÜ;é¶]tÑEZ$)‘¶råJ†olŵ×^«ªXÎE*÷JE¯wÜqzçêé§ŸÖ‚R‰ªW_}UïrmÙ²EÃVÂL¹sÛ·o?4?=r×B@! „€B@`ÚŒ HÏpÃå6±{ª‰ ^¤¥¦ëI¹á`˜qê,60uF¦ž¦ËÍÉe®DvlßEaÛ[CuÉÅ…FÄðó._ !Æ•ÒÒbíÌ(GI‰‡+®¸=þ…U#zfa‚CŒ_·Ú,¨¯¯Ã‘GÍÕ‚§¼¼kÞÛŠ•Ooโ<ÇÛHNNeÖ,Žvc„<îL}À}¨£(N¶R°ZÐ(gH‰)•´WRR¢_ÀØñúë¯ë=+µß¤’=ÑûÅ/~¡ƒ*þYòŸ:‡r’ž{î9ÚxzdPX°`žþy=æ÷â‹/êK)¡¤ÆùT`…ñSJ´)¥ªÆß}÷]<õÔSÚuSb*%%…©ˆ‹ðØcÉGQ! „€B@C”ÀÔiùt¡R9™×5åæˆv²³*¬ ¦^:SfŽøÙÃÈεÓÜ1¸S•ÁÔóvŠ0¼¾8ê(ŽÿÝ}ç±F`¨}=A¶Ç#Êœ€ãO8O<ù£Õ»)p0uÊT‡BØÎ¤»˜˜x:RG0„âmÄÆÅp¡«V‡G1—£©ŒPߎûî}–(+VÓõñù’9ÂW©œÚCz2âŽ÷…4v%ª¦NÊâàUZ´¨.)•È7v(×è¶ÛnÓ¢G¹UIIIZ©”@åXèP`”0‹×î–IjÄPíH)¥ÔèØ¡Æýžyæ¦Þë®»îãöd³oóçÏ×cŠJPu3fQ½–ßüæ7øÙÏ~v [Ÿ ! „€B@!0N Lšœ… Ž††{`¡6 ‚0›,Lø‹gÒ_GüXúkô×Bí‚8:Wk¨šáItse(ƒFª_;Ñðúœ 2w}8 F$†ŽRgG±~Ã&xWVVBwÊBQRKgÈ„iSçàõ7_§b‹¦ b$BQq!c#,ÌÊÁÍÿý„Fv Ç1z¼W­ŽHèÀ /¼@a²·§jìPÉ|jÔN‰u½ý%h”Ût 'Pô9t*Ÿ5T‡r©Æ:¯>í=R¢M·³³»9Þ¨v§ÚÛÛõ}(¡¶¿0SâN 0å@©ë(¦Â+ÔuÔÈ r¬/^¬Ó׬Y£ÇåB@! „€BàÐ%°páD®B%¦Cøšè@ÅP\Yh #É—È„òQ8ÝNêÕ##mÓÎ[©+ª‘™Œ„DU3ÕÓÍÿ­vªLäBiÉt~3 —3 #a¦[laÒœrŽ* ìƒ>„›;JY™Ù R¶mßFaÂ<“•BÃÅç‘W‹»o_GEG1rlU<úw¿û]ü§…Güþ/%º”0Sã|ÊýR‚J *6ö³þ_Þ¯\K! „€B@ ÕSµhñ$®'MDCÓvüzh sµiˆ¦SQÖûz‡ èíaHËKÝÔDã%ÂÇÆÑÝ¢±4ÓX`ùP³sˆ‚)ÂȳÕÄ ;ņ…‘æbbyýlÙ´ÔP˜ÐOk 6¼ôüVZŒ½PPñIy5æM˜0ACø|>“®Â!”3õic}³CõYñ*J :5ö§B-T”ºê«Rc~ÊÉR‚KDÕg¥*B@! „€㟀U_:é$§(ýÓƒœÜøiò¨Õ¥ÌŒ¼ðÒáI`ˆ…Ó†Ö?EUM ¿Ö7n:Z===Ô UW\‘fLŸ™‹·^«¢˜q£°h"(t²©ÖÚ˜j±;Uùì7AB¼ÅÅ,¿Ú± [¶mañž•Áý«Ní2¹ÜÉøóSiŸ™´@R“‡ª xì(ê«r©T¢ž£S }*ìâ‹<öÞÏÞ±ÁýììlœwÞy:ŒBýQ êPNÕX»ú÷§=ÿ‹¼g9·B@! „€_ ¥TfÎÊÁü#‹Y5ŠŠŠ êš.\×À¾¯UQ(Ó§®–;W&7JJKY#åÀšµk`v Ì(Ó—gÇ?‹NU/6n¬ÆÄÉ3°qóf §©LökáÉèLå ·3ŒPÐÄxÁTxTèCí.Ú`ÑL¼p£«©€=Lä`KKOý‘»F!ª¾4ÿº³~ÎØ¿îîäLB@! „€B@|JT••ûØ¿›‹)Sóu¾ƒªPòr’nÆÌ"vUÕ1}<õuùvÃËŠ6äåçqª- [6oçΕ¦ŸÝ|’‘™FížFØg6!róïNÄ0ñbó¦íð&Ú0:ì@sc7&Oä"³ÙíŒ[÷¥%ñ±[ñä~›+aŽþu¶™ñà}ÕÚÕÙ+J”;ôÉ` õÂe¤îó¼ýò\! „€B@! >/%ª&MJÁI_šË.ÞVÔÕ×ë §|Ö1™mUº£j(8ÊnÚz޽PØõöö2ÔŽ‰ZgO]3L¡@§±sÓ;x饘pчè˜NX]õœ ŒGsS/[„­°qƯ¼b&•[3ÊJJѺ½ ©Îú§ ucçp+º~8âcuBßnX»oTîÓÆÿ>/y¾B@! „€BàóP¢ê”SŽ`5”}½ut¢uò¸ÊZð¦´Ã›äÅ–­Û¸æDjJ*ET/“ݰY“ÐT?Ê+ÌY¦MïüÅÈœ;]ªîþìØñÖ¬ÿ w¥¼ª®ƒ© IDATœ´22°1éqÖ@’¯²·jOsç cðË;?Úoÿè;UŸçÅËs…€B@! „€Ÿ—€U%Åñ(.ЇûS6[4M";«£èB¡¡pˆÊÊ>ª< øýLoâÈ]´®¡jn b82„Üü˜~rýFl\4EPK¯J[…Ê][ðêª÷˜¬{šrrèR ²UØg´æ^?ü´¼Ü¼à´‰“ð⦰‹Å¼¼:Z›ðÒ³U"ª>ï;,ÏB@! „€Bà %ųÆ!7Ç#f— =#ƒ:Æ` ßffKìA”*2Ê0>Kýˆ÷¢»›é'Çÿ\üù(¼Éq0}ï[ÓŒ˜ØXTïlDRJ\qÌ_4Ï?ÿ&ç “<ÑÂïÛátdÓ½ŠGåÎ`Þ †]ÈÎÊÄôISñ¿·ný¦æ™mÅÿ}QDÕúöËÉ…€B@! „€ø¼”¨š­ÒÿæbÚ”<æ>Dh&5 Â]©]Õ0ÌÌ–>jˆ=¼ ìËB_c‚RæD;‡ÙUågúߥeFbB*º˜îŸ€Õ«qü©˜ægdžõUÚÞZ²d1ZÛz©ÎFÐɈÁì¬,*6•8H¬M½}ÜÇŠFkë2S ðèï^Qõyßay¾B@! „€_(5þwþyËpúi ÙÙ¦ê@cs ªkv£µaI© (À—ìb`#Ç‹Ùã»™Ñê14”F0cÆ$¼ýΛ0}å¼ ÃçÍdÉ•…;Sa–öö"9«—»Qغy7¼Þ ,X°;«vó„´´µ²M¸‡}Uqt«bÐßÛJÝfB_ÿ“1Ø6ló0R};ßOKÿûBéÈÉ…€B@! „€  DÕ²¥S0“½½ý¬‰êêîÒÅ¿]@8–Éès&úáIŒFJЇB*Œ¶¶.îY¹—Dgk)©^˜.»¼ÐpÚS°sC&q´¯†‘’>€ìÌR¬ý`#R’r042 Uk³ÙP×X‡phVæ²§$'£±¾v¦ct÷ôòÄ45õà•—vê ªCD•|–…€B@! „€”¨ÊÉu³—7qñV:U~˜MfŒŽ„i%rÄ/»#‚‚¢tNæù±mÛ&¦bÖ¬™{Ë€-!Ä{\0}íŠ †Ý–ÆžªÜÅ@³¹™‚)‡ÅW•ˆIæƒM˜>s:jvÕ fO RYl¡;•ÀàfTÄ:œŒಖ•­Â ¸ÿ¾7˜éÎý-õ?P·ú÷=UãªÜ“B@! „€‡üüxLžƒ(km#0fƦ[át5sÌ/ÌŸ•a÷žmÈÊÌÒViivVÖÀíJ£dc1ðv˜Î¾0ß°˜ Ìcç8_')Âõ³ñd ‚”ELœRŠu>„Íf·[™ÇM¦Š¯F‡BºÌWeºGYâpï¯_Qux}åÕ ! „€B@C’@yE*¦N˦~dT7¢«nçÚ“ÕVÇŽÞ"úe5#99£ÃLJßYƒD¦¤×Ö6¢­µGÌ/‡éؓӌ„„lîCEt.{0Ø·±ü=Ht™X3e1¹šáCGÿb¹K‡Y¥ÿ¨FáðÐ0¿Z˜˜ˆ#ç/Ãòc¾®?>Ä©:$?]rÓB@! „€Bà0 0±" …E ã³p§ª•Ó{vŒp•)+ÛŠ„¸4q2o„|t´÷¢µeiééhá÷ƒ}4¢¢PVÆöëÿë8£±¡ƒƒCÚmŠDBfà„²¿2³R)¶œœn;ZZ›t»°RQ ìaêïÆè(G-t­ú¸à5‚çþº…çQu|å% ! „€B@C–€.ÿ-õQ÷81uJ67—º˜ˆüˆ.$&&b 7D‘åÄöí;T‘†–&'3(‚ìò`§• ñî(Ƭ[aºé¶3wÞ}¹¹¹èíëça?(m/«çs¹\,NB]]-cÕ[iƒÙõL4È"´£ìTrV ªÁÁA„èX†yæг1U%NÕ!û);„o<šnkaa!®¼òJ&´´iwõÖ[o…ŸMØr! „€B@E@)•¢âx:U1˜;7®_ §qOª v«“Ièy¬Šbœzgµ»;èd ÁÆ5©P(€Ü¬!ºV£ü“é×ü`‰aµp—ªŸNS?m¬QĹÒu$zéʉ*˜PÄ$ öT Q[W¯S£¹[•äõi1egU(ÄçD(¶XùôÚýÞ%Uò‘ý÷P‚ÿÔSOÅĉñðã²²r_Ôÿ¿çŽäªB@! „€㉀R*3føøûb,S±òÙwpúGâõ×ßGFZ!bb3ÐÝ5ˆÆÆNŒ†Má8Þ(ÿÏúH$‚ŠR ;zÃHO¥áôõïÌaêù Ë| «.eÅÃjŠçBV€¶[‚)žlV;v±Ëëó1åoP -µÀ¥þß5öÇ=«aF±wuu3-ß_/¢j<}Z³{1™LL°4ëqÖyóæaÑ¢E¸ë®»t¨ŠB@! „€B`Œ€U+VäãØãŠä3á‰'žÇò‹°î£íŒV÷°Ã7šûS]\‰åï—Qp°§Wý÷vj¤œ´Ad¦…áõx`ºüšEÆP¨[«-%ª¢,læN•r®ÔᣠCèd|`\¼ƒþ€Rv‡] ¯””ÝaU__¯Ý¬á¯¼¸QD•|VÿmDTýÛÐË……€B@!pHP¢jÞ‘1øÚ%3é@õ`õ[›pÔQ˱ví6¤ø*Ð;ÐO#É Ù4ªW jêöP`Y1•‚‰tª|ñ~ 8­wÖ—0|I. %36nØÌî©TÖJÐÛÓGáäd\`:»z˜æa"F/f0Ÿ½±q±t¦º™‘NË˱*ƒ±‚uˆ2Çà¹g?QuH}œþ³nV‰*õG9Uóçϧê?ëí•W#„€B@%ª-qàòïÎDí®FîMõ"3³o¼±“&΄+Æ©'òŠŠ Yü»ÛwìÖÕSÅŒZ÷ÄtÁÙƒÄø4˜.™hD;ú¹ÀßE”3J¹Qðy“PP0A‡QlÞºÐ0m.Oƒ––V¦bÄòkJŠ‹x¡ ÒÓ³±uË:]ñxá¹ÿÝ©2±‘ØGѥʃTuyyy_µÚS‡úåWYh^¯—³Šˆå}8t¸€áR»]ÿìÈÉÉÁ9眃{ï½W‹¼¿=T`ºF\\ìM+lhhУ{¨ç\tÑE¨©©Ák¯½¦ïkìþöÿ¿;ï¼ót©²Ú :‹ÿßkü'>oLTyä‘X¼x1n¹å¦X2ÆR! „€B@!ð1%ªN==gžU€îÎf´wR$yrÐØ`Š…¤ä8X¢LZû¼ð—m|´»Wåp8-èëݼœtNê…aZqâ4#5ÝŽþ>=âgŠgéoG-8 =ݽHINÁÓ+ŸÅ¨Á=« ‰¾dËǨÁáḒU »¬¬ÈÏ/bv»?ÿŸÇ÷½QN§úÓ“¸ûî»ñÞ{ïáÀù矿ï\õËïܹsqÖYgáŠ+®ÀøCªÀmøãÿ¨ÅÖ~^²d }ôQL›6 MMM÷ÉÊÊÂÏþs¾øÉ:`C¥¿½úê«xðÁ±aÆ %¨”¨yë­·´ ºþúë÷í먋)7ä‹<þò—¿èkœvÚiz‡MŽƒ'ÎoûÛšŸÄJ˜~1}ðW’G ! „€B@Š”¨:fyN<) q±ŒF·t"h9úè£ñÐCaöìÙh¦ØûÛ#33K—.ÅæÍ›µ»¤„šr-<\([¾|ù?b{%¬V¯^Í×õ=Ž… ¨Ç)QøEºVO=õ”fpæ™g~¡×9ÿ#8Ð=+QuÜqÇiwR¬>óÌ3"ªM~.„€B@È€ª86'’Íz¨^d°ªJy&ÝÝ=ÜŠA+å…œ³G;™ž‰M¶ 9ÙÃb`/u‘›6U2nc€·ß}¾ñÁãàÐѺ©)üÚÒX·#¡h쬮M-„ÂZ].ž £vÜ«Šu³£ÊĨA+C*ºxÚÛ†ðÜÊ­HNJÅW¿úUv\yqúé§â7¿ù æÌ™£ÝåXýö·¿Å‚ Ø@\†cŽ9ÕÕÕØµk—vdž}öY-bž{*uާŸ~Z‹5¨Æ/¿ürí*)Á£Ž1a¦þ­®¯î㥗^²eË(øÖðc£~)WbO9[===8÷Üs±jÕ*íZuwwës*a¥\³«®º ¥¥¥Z±ªûxùå—÷]_ Kõõs%o¾ëÖ­ÓQŽ˜Šÿþ÷¿¯G •§ÎÙÒÒ‚3Î8C?F‡|ãßÀI'¤…¡zíêñciêŨð•+Wâ¾ûîÓ"V¹t›6mÂñÇÀ×*B@! „€‡ %ª/Ig¬º ñqQ˜2݇ݻ÷puÉÏÊ(Bƒ)è¤h  += “*æ°x#íZTmßV­õŽéª«N4*+k`˜ºQ^žÅ²ß~ ºVõI®8üNôôñ¤Tÿ½ó€Ž«¾¶þž>’F]õÞÜä^1lz%´„ @>R©É#H(!!)˜z `:ÆÆ½Ê’lõÞ¥ÑÍÌ·Ïß?AxØ~žÁçfͲ5ºõw/^wgŸ³O 4†Ôô x½ÙÝüýÈHK§@ƒ?8b’0rs&àÖ›ŸÅ„ ÍK}{³FF†Ì<«… áÔÙÙ é:÷ÜsqÈ!‡˜—~q’¼^¯ù¬X±Âˆ‚;ï¼s·îŒ8UR"÷ /à·¿ý-#ÁÉ'ŸŒyóæ™cÆÜ.=±ž$‰Ø~ä‘GŒ¨Z¾|üL­|DT‰ÊÉÉ1dÛÛÛ·ø Î?ÿ|Üÿýf£üü|¬Zµ ×\s Þ}÷]sòûiÓ¦ñFt›u¤ôQúÉDÉyÝ~ûí&IQ¸È¹Š¸|àÌ÷O>ù¤qÒ.»ì2sm"ªdóÒ¥K‘KQ{ùå—Ñ(Qá+W®Ä9眳«ŠxLT‰Ø—HÎý/ù‹ÙDJEÝsÏ=ˆÇw¿û]Sn&ç}à 7`Æ ¸öÚkÍ6))ìW㺲žœ—ˆIq£/^l„Ñm·Ý†™3g1%)#²¾0áuÚi§™òIqï.¸às­²ˆX’ãˆ8·O]"ªD ‰ÈÛÃÝ>mº‚PJ@ (% ”€øQµhQ.Û…Ê04ÐŒ¼#Ò¨ÝÖG!•ˆ´Ô\´¶mä)¹H–*>¿-()ÍGSK-{­²‚× ËonŸíïÉbºEŠKÒ[F1:‡æÆ^ÖÎy1<âGKÍú9ÇjŒÍRRš&¥jýý°! ßh7ªªÊ°~Ã:Úf…¸ç+¨Þ ) ì¸å–ßÐU¹ÔF±~ýzãºH©ž”»‰³"å€ÒóòË_þÒ”îIè„ô]íNˆ˜ŠõTIª_II #_Á…^h8±€‡Ù³gñ#"†¤œowû—g'Vþ'û»úê«ÍãtÓM7çꬳÎ2‚IÎyêÔ©»R e¹>Kâ@É:ï-"'+‹¼¹H¡ô|I?˜¬'BMúÂľrýâX‰À’²H ôŸ¥1Ö“&ç&,…ï&Š^Y¤—HDÕ%—\‚gŸ}ösøøë%)% ”€PJ@ (ÿœ€»˜9+…íAYpX}˜=¯=]a §(ÛkøK« í5X¸hòr °~õj ¾§™'‘d²%ü°Üõ»Œèª>:MqüdÀ Ëá̪&ÔlëGjR>âÜ,ý 2RЋ!ß ûzR‹"epáÐ(ÛX‡8Ë–½Š¬Ìr<þh=žáEŠ´R 00`Òù¤?jO Äá’<&ãEÕE]d«˜h’>&I”@Œï}ï{{ÔK»M1Q%Η¸j²üêW¿2âe¼¨¡#Γ¨@ ÀúÊ4lݺÕ.¹~qê áRî'É„Â@\&qÁ$‘P®_Ü®X•”AʺÂK\))uAK:~²8Zò§Šªÿü?.݃PJ@ (% ”ÀþAÀˆªÙI,Ìå¼)Ëù°Á}>Ÿ‰ñèb~B ÐEOg@_/Š8£wûŽzæKtRëx©—6ò¾–‡öF>/m­fF¨ÈØÀ`{œù’>Æìõ©èè„ÃiGwÒ7Ø —;Lw$µuÛ™–»3BåV„÷V®gb`}¸…Å¥¦g©zÊ4üö¶[‘Pqd6oÞlD™86R'i|"ŠÄµ’@ õõõ{t'%¨BÊÿ¤‡j¼¨ºøâ‹ñÌ3ϱvàâ'?ù‰qrD‰Ó³7‹8[o¿ý¶9 –Eú“dF–ˆQâ`Iyž”æ‰Ø”EJev–¸OâHI¯•œgcc£q¢¤¯LJþDTÉïD` é “R¾ŠŠ SÂ(ÌDdÆ~–° é “¹_±²²Ò„]ÄzÆbN•œ‹$ꢔ€PJ@ (% ”À¿QµpQ2ÓÿªÐÖTcz¨RX¡7Š´”|l­ÝhŒ’””dØ­a~çA]ýV¾Ë[PZžBÝRKT ËcÿȈn¯CœÛÃ/{ÑßgÅÈPÕ™‹½?) «ð¢¸ ƒCƒðfg¡ƒC±<–ºM¢ ³‰Yí~“Õž›S†úºVtuŒáÅç[\1fÂ&Þ}w%$n5"@ˆqaDìHÜú}÷ýÉôY‰Pñ"Šà—J>»›Í$Aâ‰(“ )%”ò¿o|ãÆ©ñôÓO›}‹&BDDœ‡Ì-’õÈyŠSõÚk¯™ícN•ô1Iï’$Ê"%xR(ÊñÄAM’¸S±¤>qd—8g"*%ÈB„¡ˆ*ù»ôA‰(‘$ƒ’¥¼Q„‘ˆ-Ù§”žwÞyÆõá)I†â†‰ˆwLa ç+BLÄ .J@ (% ”€PJ@ |¸¨:dIN8±qÎ(º;­°P+tPW$'gЩjBEùŽžâÜ*W”Ú( Û‘”ìÆ¬9t²zv°5*–§ŸÊÖlaJ^8.g*'{("ÜÜ(GCSc'¬ÎaFQàí]­hl^Ç…IèênG ØÃïƒ \OFBœTñíKî`aW\y…Õt¼úM?•¤ù‰¨‘Eú­23½LÉ;ÊôDI°„Ä~KÜÞ,âI©ŸÄŠK¼¹¤Š+%I€âˆ•——÷K„‘ô0I‰žˆ6)E×IÒ?j&bæg?û™)”¾/YDØH¹¢ÌØŠÍ­’r>¹q°D8ŠØ“hx)y”}ˆ£$1ëIII&|bÙ²e&øB„¤/Ù¦ªªÊ”ÿ‰ q&‘óò½8}ò§D±‹(!1±'}V».CÔ´8€wÜq‡pº(% ”€PJ@ (%ðá¢jÁ.3ü·0'›fF õD:Í%ücƒpÇ…8£*½=L=Ïtc¸?Œmµ[Q^Qˆª‰IèíßÎQRÙ°¼üŠ%º}ã,8ì™Xñî*”UeÐuªÄÛï¬a}`)#û‘’楀BgW ËP\š‚M›×²´­ ‰Œ*çdá8wV½·™ê-Ë^i¤kEä#ïœÌ’ÏG¯õŸÞü¦ÿý§ûÓí•€PJ@ (% ”€ø|ò¿ƒJÇgNÅ@o3^_¶“§ÁIW*0Ö˪·\Ž)òÑ0²"=‹Y] ¬6óÂ@n¾ÆðGUóÊËIÑ-k¦ÁI¡uåA[W —0‡{)  èê$Òó³ÔÌGá•Æ Š~L›ž‡áÑ~Š©g3­FSîâãSÑÖÚ`Pá5Ñiapà8Ö"ŸÆÿ¼SP}ò¢êóq»õ*”€PJ@ (% ”€ø¸ ˆ¨:õÔR¦u{ÑP׌åï´±½fBáAΩ¢Så`ºy:Ãî8Ãjv*V¾ÝÎþª$ÄÅÛîÒÁjDf:{ª^|>%Z·a63Ö+¨j§¨ÚV»Ž‰ROe/R"jkº9×)ž„l8¨Úü¡VTT–0b0Œæ¦Ì?`!þI!eÕ’€¿>°þ}þ“ä¿‹~мOU©¨ú¸ ÝŸPJ@ (% ”€P{N@D³æð‹¦¢¡vˆ½S~æä ¯¿vW< &™›ÇV›.dåúиð³IzªJ+RÑÕÙˆ¼ìɰ,{)/úÏ'Ó˜f‘_Ùì,ókDÔêG/wÀò¿¤b“ÑÞÛ;_`˜vXE¦È“…^ÄIÛ°zÍ –æà7¿f€OP>"¨¤/Â~ uªöüëšJ@ (% ”€PJ@ |²DTÍ_\vùll]ß…P ‡ÆR㤨…8ò)ÉEÅ § ¦Ø1Ü—Æ »fC¤Ñ­ 3!pÚ³`yîù¤è/fsÐï0²³ó‘[À^©!lÜÔÈ„¨ÙÚÀò> 9#Œ o<­¯ Z[ú˜àÁŽíM&õ/!Ɇ9s'rªð6îÁ5?hÂXx§„²RPÉò~Qµó;-ÿûdÝ»PJ@ (% ”€Pÿ3U'~!g}9]ÍêœdöOù8'¶6ä!#›¢ª¯›Ièý8à ,ôu'qŒS Ó¶óÐÝÓÀdÀRtwû`yâYWtù²|N¶Â›!®Úa³˜èçDaQ9ººº™Af^/\qƒú=L½ór&Õ6Nö¡¤x"Âèb½aÉ)NŠ­x\ù¦†LÇ”$ãíUŒHßeUÅD•üæýþ•Þt% ”€PJ@ (% ”À§A@DÕÙ_ÉÇ '1[bGkW ²ýɆ1fG¤¥fÑpÊAMM"+ª&»ÑÚdcËSié©àL+›Zay~YRô¥§’ÐÝa¡U„†¦&Ƨ'pÀo*–/_‰…  @êec–•9ì $#'»„Ãi9]xÔÉx`±÷0íjÎ1ÆHõ©¸ø«ï  ø—S%Å*ª>GC¡”€PJ@ (% ”ÀžQuÞW‹±èà0¾TÔlö¡ áhˆÑê©4•Ü ŹRQ'ò‹æ†(¿ÏÆÔiS±ní:Žgªgß•“‘êo§G_xÂŽ–ÆW(Æv_¥Sy¥¤æÒêFn^6SþºQZiÁÐH†úH¢Se·{0êc"`:ƒ-V#;'*LÅÆõ×Ö˜kgë±Úyq± ùû'©¾'0u% ”€PJ@ (% ö/¢UlÌ}8çÜ27Âõ²zÏÂ>)‡ÿ¶s.•©Ièì@rR#ÔJb%ž•}6ôõöÀj sn–—Þ¨Œ>ùPƒadfd£¦¾¬Ôüù¡¥¥©쩲"3+Žôqð•4 OâC+:áv¦± pN‡‡±êÉ(È›ˆï~ëáãâþTTí_©^­PJ@ (% ”€Ø— ÄD՗ΚˆY¬Êóa,èb8Eó%j‘‘¦Á”ÄŠ<'¦VÄ<‰V44vÃç PûÄcˆŽ¬!j ,=9!ºôÏ=üE222²ÐÑÓÂÚ@°N°b©©i©T`ý,í #!ÞÍZÐÓ=Â9U-8êèjΰÓ1¶ni4Vow>²Õ¸Q»UâX…÷eÖznJ@ (% ”€PJ@ | ÄDÕ)§”3‚¡%¬Ô£TXXÆp¾UJ Š3ÐÑ>Ìò¿dø}NØ)¨ÝVG!åaR %w˜É€vX~}kVô…§(ˆÒØK•‹ä´8&^ ³¼/ „Z.øƒ­ì‡ 1c‘°ƒñ‚#È+ˆbáA“°üí$y ±eS3ÞQœ¥âÒïß…ð{¨ÆUö}¸S¥¢êsø|ê%)% ”€PJ@ (:„~ IDATÏé©Z¼$“§) ¢hîe¤º“a|U°¹:©y¤*o%€~ø†]L8wadxù…yl‹êa¬º•å€6X.»¬8ÚÓ=€Ò²±bdúh#)ho§­åJb©_›làf‰àý'7zú·ãð#fa`°• -žß¥bã†&Ø,‰(ȯ­·<ÁôÀX¢¸kÑò¿ÏÄs¥'©”€PJ@ (% >÷bÙ"ª,ÈDzf¦L(àŒÞ>¶19PRRÎÙ½›qØQ ðÊËïšqR9ÞX¿~=ã÷˜1£ ­mµ ô†©—~ù‹Q‹£…” ƒƒc,ßK¤âÎÒ©ºFFN>Ý©¾Ò‚Pp í]M˜=»kÖl染™˜LQU‹,oz{"øË«Ì°ðÑ)+UŸûgS/P (% ”€PJ@ |&ÄD• :éäÉœ7A|œ›7Õ!''.W:;qÌæâÕeo"=-ë&6"%%žs«:——Å ?Vôq”ÔM7Í®2åÝ]~¬]bâ……+•aëVöI%Çá°ÃÀòwVr(pUš—•¿÷bãÆM¨¬(¡’+¢ªëAkkÒSKqËMoï øS§ê3ñPéI*% ”€PJ@ (ý‰ÀxQuøù˜9ÃEíASc3%2˜pî¨Óf–`GC#û¦â°£Î‡Œt²²Rácu_&£ÕëêÚ8üw–W—]}å»8Ð7N{*Ö¬ ʾ©Òò lܼqngU-ÀÊ•«9›ªååŘ}ÜsWÓ¿TQµï>TzfJ@ (% ”€PJ`"0^TÍ[„y ‘ÈñP)É^´µ·³Ê»#›5ÅTë—•0°b\ö, /††û(º<¬ÎKFýöí°\|QV´´´”ªùùú›Ž5k×¢ŠÌnw20›ÍŽºúFvèxgù:~çcÙß4¦bLĆ-ϳ$ ZCaÖ!¦¢«sO>ÞÁ Š÷›Uã*y¿tøïþôÐêµ*% ”€PJ@ (}‰Àûª LæAJR¢ÉƒCìò#™ºhxx€ è>I»S"Ô8ˆî%â*1%‘-Tý°üäGs¢ë֯㊈‹ã«‘a&`,âbY¡«ß«e`…ˤÿ…ÃÖö#=# ;ú)²¹n"U['´‚FÙ57õãÙ§Ú ³˜ŠµWý·°RQµ/=Tz.J@ (% ”€PJ`"0^T²˜íMSÐÓÕÅÀ¾1ÎîÍ@jJ*FFGæf(Å ÒÒÒéZÙ^ÑÍ ŠÞg·ÛÑÖѯ7WDÕ¼hcãõõQE1{îLXìœE¥¥•€Í»9Ü*™jl MM;0sv ‡^XCÈ” ƒб$ЬNÖéXYðúkœ.ü¯EäSL`©¨ÚŸU½V% ”€PJ@ (%°o/ªŽF†YÖçEg;˜€ÁÀ£2üw©é<«—J¥§Û0qÂ,¼·b#|¾ˆŒõÆë܈KLP©¨Ú7&=+% ”€PJ@ (%°?/ªæ/(c’›na¹ßƒ(<¬Ðc¬ºÓˆ§ŠªJæN°zwr°•¤¡÷õöRûXïI¥ˆå´ÓR¢6{€e|©z•ÈЉdt´×Q™°wÊAEÖƒ”tÆ‚¢Ê2±ôÁåch0D·ÊƤÀ4:T$z²P»­™ñƒ)xìÑ5ÿvo4¨b|\õš•€PJ@ (% ”À¾G`¼¨šR…%‡V³¥i3²s²a·X …XçÄŒiÓÐÛ×ÇA¿­¬ä@nAF†ƒú-ü9§ËÃ6)¦ÿùåÌè‡þ:aÆ£§STYàrØ%8ˆöŽAädçSE¸c çQ1§½q#×°aÊÆÒ¿0ݨtSXR<¯¼ò&¼é•¸ïÞ7÷@TÅü+:ZÓå2'fÝâøeîܹJ<ˆ-[¶ìSw#..ŽQó ñÏþsÏK”®\_B‡*³FS–H„,uQJ@ (% ”€PJàS' 뫽8dñ$y¨‰¨Iü|O_·f-.:âÉϾ.lÚ¼‘kZ) l,¤]e±ÓXb€Ea)ºº»)ª¾’mmòsÖT6öIùPXP€´ÔTJ±võêÿ®4cPŸÍá¤^ŠÒ`3ù£~Ìò¿/Ÿ7=:eò4·ì pò±‡ªY™¹&ˆ¢±©éÙú›†ŽÎÌœ5‰N×vŒ[à÷Ù‘]ŠH(«WÕ0^°‡MZ¹Lÿž~r ]%…îÌ׿ö5œp‰tÁâ±ìÕeøÙÏ®£P³Óñ âÊ+®ÂGfrߥüOœŸ@ `þ¼âŠ+Ìý|衇ŒxYÍ úÖ·¾ÅÃWpà 7°ô0¸ë~'%%áûßÿ>¤TP@\vÙehhhxŸØ9ýôÓ1yòd<ù䓸ꪫÌñ®¿þz¼öÚk»}nDY)Š.ºè"wÜqFäÝÿýxùå—!3¾†††L ãâÅ‹ñÃþÐW¬œçºu¬Ì²ÆôôtãPIãÛüùó±jÕ*ŠÔa£z¿úÕ¯šòF]²ÿóÎ;Ï”JÙ£ìcåÊ•)Üv{º‚PJ@ (% ”€PÿF@DÕôi9¨¬J¡a4ï܇t0ßå{ÑÜÜLÍ3J£ÉÃj³Ltõ0œ"¤f +;›&T7R)¾Ä€²œzæT&©±¤OjÙ;Ų>ëC¡¨±³BA Ü.έ °$0‚ “DµPD$±ÜÏÍ´¿|Ãƪ{Ø{åama?þúç5Ƶúη¿¯RT=÷̳LtãŒ3ÏÀ7ÞÄÏFt}öW¨ §aÑ¢EFl<òÈ#Tˆ#0D€Èòøã£¸¸ï¼óŽéC:ñÄñõ¯/½´³Ä°¼¼>ø ±êDŒUTTp~V>¾ò•¯ ¦¦fWÉà×x?þñMrdž Œ(«¯¯ÇwܱÛÇK2è¥DQÄ΋/¾h¶=üðÃYòXe¡ˆq äüEh‰0¬¬¬4=WÇ{¬P"ü~ðƒ0ÇÞ‹ã?Ï>û,…h§qåÄ©“›¶dÉÜ|óÍX¶lŒ\°`qð^}õÕÝž§® ”€PJ@ (% ”ÀžQ5iR:ÓÌS©#2Q=u*Ö¬ZúÚ:&KÐÔXðX”&Êò ‹0À÷þµ€;>ý^~ö\ÙítªN<…¢ÊÂ_¸`h”Q‚éȤ«²eË6ö ¥šd?H6[‰ÉNLœâƈ¿†ú+‘§àÁöº.&`8©àRdQŽ­5kðøÒF¦ V` Ý¥›o¾?¼ú¿Ì•]uÕ8ÿü èÔ€®®N~³³GJ#C"Æä`ã*"¦D‰XùÓŸþdú˜N:é$#˜D(tÐA8òÈ#ÍVñ¼À'žxk׮ŷ)êbËùçŸo”ˆ!(â,‰€iw½ZÕÕÕÆÑúÕ¯~…ë®»Îìò'?ù‰IÞ!‚®€6¡@íïï7¿±ô裵{òÉ'gLÎ_úФüï”SN1"pü"%…rýâ`É".–ˆ¸ÆÆFS.øA6{þ¸èšJ@ (% ”€PJ@ |€ˆªòòDj„j–Vòý|3FYùà{}.Û“êëjpࣶ¦Õz£t°úáÍÊ‚Õnã»ý(ßד(¼:a9ò˜‰U~Š€Q6^å3ům ­HIMAdÌÁ~©07 q–Õ׉C†×M±`ÁŒé³°ec v4´0^=`d%&äâw¿}‡¥‚ ðæ[oa Kâ^¡ó"9 ©ÜßæÍ[1mÚ ´··ñxhþâ¶ÛnÛÕSõAáððÃññ£ýÈ0%ÎÖa‡f~GK~–’¼XÄŒ3°cÇqÄ»ÊET}ùË_Æ!‡²×O“l#Î’”÷-_¾ÜGz©Äš8q"zzzÌ>¥ÄðÜsÏ5‚jdd³gÏÆš5kŒ¨r2’Q._/¼ð‡Š*që¤üOÄ¥”Ê2sæL¼ûî»fß*ªöúÖéJ@ (% ”€PJà$ ¢êðçâœsŽÀãÝKÁÄU4n"ca1Ü—L “ïn`eZ.v4· ´¬”|ê™Fääz ÂrØ‘Sh™ò¾H$€Ùs¦#¢Phc©Z† ¬hi@bR û¢â¡XNWÍ[^Ú`£Ù¨ 7)ÓË€‹Ž îûãÛÿ&ªäJ’’"ª:¨œ‚ÉÎv¤0?ìp<üÐØ;gßç[Q˜3&L‡Ë™d´Ëko¾ÉÊ´>4·63“¢–ú)ÈJ¿X?ª2j±D9Ü tŸ‚t›,(+Ée|z„âÉÆ8)°ü,I 0Ý"@oi?œƒá NŒ1CrþBc!žH"]®a¼¾¬ÎÓõÝï|¿¿ûÆ©:þøã(tnǬYsŒ»Òc„ôIoÒ‡•¸‰S%a×\s!'ARÂsœ¤?+-- "H¤üNÄŒô-‰«ÓËIDZED•¸=²m,‘oOo…8NRþ'½\ýë_Íöâ&Ý{ヲKŽóÆo`ûöí¸à‚ LX†ôa=ðÀ¦ìOJåg9'U"¼D$I‰âøE’…‹Ä­‹Ó%³°¤T°µµÕï£R÷ôZt=% ”€PJ@ (% vQuè¡i”ð]½ÇèŠ+ð¬ÀkjÜ ;gò´h þþ÷GYY—JgªE ¥@_F|l…„å §NŠŠðzÓaµYÌË|rb<[¢ÊÊþ¥Dl«ib½`¶Vc‘S?èv'¡Ÿ1ê‰t¯¤®£½ÃÜÖíÊÀsO­¥ÃM7ÞÊt¿#pá×/@ë ÿðÇ?0}ïi oP 0†ÓŠEX‰ !!©}"*¤/IÜHÒS%îô]U‡z¨ù½¸TÏ?ÿ¼^ø Ž™ô>IàsÏ=÷>QuÖYgír¸öôA1$.“?q¥$•P’ù~ó›ß˜™SÒß%½R¿ûÝïÌïúÓŸÑuá…²Ìqš[gžyæ.ÇL¶•4?V’l(¢rÅŠ&ìBœ5)Y”RGùùâ‹/6®ÜÓO?/}éKZþ·§7M×SJ@ (% ”€P{@`§¨šDÇ©™QêI ­˜D‘4Ì\‰´45 ¤`*Ó¾+ø¾¾ ¾Ñ6š8 ð$zP·½ÓgL`p]9ÚÚš`ùáOJ‚““‚Ýî8¦[„1ØK!á3ág„b*ˆþI¶p3ûÒÖÒ‰Ô” Ĺ(Ä|ý,´2$ÂÃøõ<üþ÷Ï#Ì9UqL¿1²`Þ|6vù™j÷ …Ïhjj2—h±Ø¸O«)דЇ£Ž:ʈ""’¶'ËwÞizªDpˆX‘À q©¤?J~–s‘þ*ù^B#Ä !&ÛÅB#d?²¾¸TÒó´»`Šã/®Ñ­·ÞŠ8À¤ J•ìKÎY‚%$ÝOfi‰Ø“8uIð“s“^ªñ²oq$^z¨DT}öÙÆµ‘&‚P¢á¥/ë©§ž2Á"4/½ôRuªöà? ]E (% ”€PJ@ ì)™55ib<ÓÿÜÌ–Èà8©|¦úõqönˆIÃÈÍ®4F˜P™Þ?MLÿ žÆRb’ƒ ›6n‚eéã—F% O„€8[(bd诇=꫃ÎË×BºWvãfEÂQ¤sÝ#_Älv–¿½¾†"*‘¨÷Ýÿ"ü¬ å[$çogÖ_lÙù[)=ü߈œ=õyYooK?/׭ס”€PJ@ (% >)"ªŽ<´eå”—¤cåòíL%ŸB§ŠI#](ȯ0ÆÆºuë™-ááÚÒÒceÖDºÉ;HJÞ©,|s~T FgÅív1R„¥ÉнR[·ì`)Ú°™,‚«««›.{¡¢î0Ät¿y¨¬HÆ¿ÂèA3*ñ‡»Ÿ 3%[ÿO¢J~û---]”€PJ@ (% ”€PÿŽ=2—³e  ù±jå}ô‰ì—Åv–ÿùýQΖí23dLüKNM`ëOq éhok§‹4Š)“'0RýØ’¨Äw‹ÒŠ‹cI_2z»Û14<ÀrºBLZ·ß^Éá´V’za§K†‹Í[™é˜4¥Ó«3ð·¿<ËDÀbºQɸó÷sæÕû‰¼ß©RQõð¼è!•€PJ@ (% ”€øÃŽÇ1Çy‘—†®ŽŠKªà‰à…—ß@º7å€ý,ÿ¡+•Œˆe˜Yéì«Ê4 àÁ±~LšPËñ_¨æ°(pC†U0—=';‹„k¨¾ì,Ë !-Ý U“±~Ý6ÔÖ6°D0à Ě3k¶iÞ*¯LÁ¬9øËŸŸdOUݬbÜ|Ë3¦§j|¹ŸŠ*}~•€PJ@ (% ”€Ø×œ||)æ¨(Éf ] ó ú™'1о¼陹&p/Bq#¦SN^&Ú:Úi …¨•"`$‡§ÃrÂÉ•Q EZA§Ó©Óª±nÍZ3â\fIõõ Òö 0âP´¶´q¨n-æ/˜‹©“«ÑÜØ„ÿ,˜[€G~í­#†5·ÜºÌ*UûÚ#£ç£”€PJ@ (% ”@Œ€ÔÏuX*+]¦§jÍê÷PPX„ìœ|¼ðÒ2x3òM0ERb Fa ,+Ïý ÷sý S%Ay–cN˜Ä9U&ýѰ²Fè6¹à÷rˆU:zzûhw 26<Í úZ=;éáA2ÐÕÖ…éÓ¦ÃhEQO=ù‚'‡þ.fRÝ*ªôYUJ@ (% ”€PJ`Ÿ& ¢ê¤&QH`áȧíÛ0oþ,&û%à•WßDYéjœ0ª«grNUÇF0c¢¯¿¾œÁ~6 ­aŽœòÁrÔ1³9íi”©}C(,ʦH‡CªÜ®¬[¿î•aöG¥¥e±AkÕÓÊ™~±œNÕ ¼ôÂkøÒÙÇ ¼ÔÎ:C;ú´À¼’{‡Šª}úñÑ“SJ@ (% ”€PJ@DÕé§OÂÜyqظ¶FÒ03%*Ù7åBK[+’“²YÞWŠÞžQ¶Cme0Æt«Í‰… ²DðeXlÃUÇNg¢9-­d'Ê+Š(˜V#•uƒ^oêjë)ªÑÕ)qëif†Tz† &¢q{ ¶×5™aµ¹Ù~ö[5£fëÌ®¼âŠ4-ÿÓÇT (% ”€PJ@ (}—€so<:§Uކº&¡»ÑÛÛÁV¨L´·ûØgU…Õ«63K¢&R´ˆº§ŽZ)ÙÌ¢X±f%çW¥Ár쉓ÙS5†üü<6fÅaõê•(-.¦:K¦ÀÚÈ ò8ü t°üì­ò!í¥%6u5 f×ÂÇS=m…Ýá¢øêebàt\~Žðs›ñ‹U컓ž™PJ@ (% ”€Ø ˆ¨š6ÓŠ/žQ€ÏÉ(õ~ÎÞµbÒä,§…¹¨¯oEkóû®&˜¹¾c ©Hfbz7GM–£µ½–“Níîî KUf,êê¶aBeëS°c{#œŽDÖ :¹€ÃaEWo-ª&PPùÑÜÐŽ%‡œÂYVOcæÌxï½Õ,,ÅÝw¿ 3ÝUTí¦^³PJ@ (% ”€ølQ5¹Ú† .ž†e/nAAA*lÖ0ªª °~ý&T”NÁæ;°m[7Ëý汿*H”„ø87ZY芇Óm‡åô/Í‹öõu¡¸¸Ü'ššš“•ÊhôL¼»ü=Vä3Ñ"‚îžN–Æcú¬RöK 91Öˆƒ.UÞzãÏX²d1wÜB»l Kÿ¾ #£*ª>’ž¥PJ@ (% ”€Ø? ˆ¨Z¸(‡,ÉÀöÚnÌœ1o¼ñŽê¥“N--ÏàÆ3xd$Ê>ª(²¼eXöÊ;𺑓›Ë!Wõ8ò˜%hjÝ»3Œ-¶áüó.D\¼Ï>õ ™˜áfoU?¼t¹ÌLá]‹öTíóÏ–ž PJ@ (% ”€Ø/ŒUÓgÄ¡¨$€¼ì”V8¨o"öËùSÁ|¬{¯ yŽ ¶®Ž¢ËTŽž’ÅáŠPl•¡®~;,Ç8+j³`öœi¦ü/Í­hïhAsóXPöÖFj;ró2QTœƒ ÖRTMACÓ6d寱÷Ê‚žN'«úšXþwÕqÑÔ4 ú:Xúçã€_²2“‘WheSV}ñؼ)Šw9Ø×•àA>çYådç0Œ&A+v›¹BÌkB£Ø¯»ö¶ÿ3QôÙ»µzÆJ@ (% ”€PJ@ |Æ‹ª ]8á °E¢HOÏ@KK3"‘1‡ý²Å©¼€Mö_ IDAT=œÙ[P‡M›Ù6L˜TŒ¡Aú{°fí Xn¹eJÔjIA{[?C'|,Œ`æô)hlªã¼ª,ô³®pÓÆ13-Ø•ÀÚAÖ¦2ñ"’8kýú žâvNØŽ²¢)øñÝô¹U}ü"™öº(% ”€PJ@ (%ðÙ&@}„yó]¨¨ !?;i¬¾s¹Ä0 Ðl¦¸ QëDÑÑ1À Š,â)7‚¡0+ö"Œ]w1o¢–œìͤҊpFUƒC=¨b\à–ÍuOiÜÈ†ÖÆxxR]°»­°:íHI£+ջ݊Më)ÄlñXIü$ÓKÃõ×ݵKT‰üIòy™S¥NØgû?={% ”€PJ@ (%# ¢jú 0¤ÏŠ’ô÷÷ÓXʦ°r1įyQVçùávyøIÀ/Äu9Ç*ŠŒ ¯ÙM”bDzôñyÑátŒ EÙ#5FU懃V—˕ȩÁ0{öl ö{ÐÙÛ†‘À°q«JË“©ØèîîDsÅØPùùùˆÒ2 ¹pÿ½O~¨¨²X-T{1™s"Ë]u:ìéʤ5׿ßÿ±ì3¶“ôôt$$$Ð ³®Ò†öövF-÷øã…Ø]/s#þå|ý§‚MÎMö‰|4S™ú¼·ëÄÎíƒÆ ü§çã¢.à?Vº¢PJ@ (% ”ÀH@DÕ ãpÎyÓdøD/[ L8wÓ@êErR6gøŽ ´t¶nÙÆ‰,lظÙdIŒŒ°4°´ ·°\ó‹ÒhfF‡þºPW×Hk«ƒÓ„s0}Úl4443VЇÖöNøCcS† ¸$-ÍMhnjf¸…¶h­²4ª¸ª9žzâ¿çT‰„:÷œs±ò½•<øÁ~€oü5û·ÿBðÑà‚ .ÀäÉ“w ÙHÄÃo~ólÛ¶mÆéÓ§ãÞ{ïÅ׿þu¬\¹ò?Äûß›‹ùõ¯“N:ÉVçœsÞ|óÍ=>†œ¯|®¼òJÜpà fòÉÊÊÂÕW_믿ž¬h1:P^^ޝ~õ«HNNÆ?þñ<ûì³»@±ñx<øÒ—¾Dqÿûßwß—¿üeŠ\}ôQsŒÿ÷ÿþŒRsNâ0ÆYo¼H***·¿ým<ùä“xå•WÌ=²aâv»)ÂǰjÕ*üíoÃààà®íg̘ /¼£Ý\«°“ÏÛo¿¿üå/¨¨¨€ÜÛx–•Ê"çüÀ°NuÓûîõCÖ•€PJ@ (% ”À^Q5s¶_ÿÆ\ôuoƒÍÑÇ÷×8ŠŠá}Y4›<œ_¡+•¶Öv¤¤d`Íš-œé›Nñeã;{ ZÛÚ`ùÆ·Ò¢••ÓÐÛ3Âüõ0êë1sælx™r± Ã>?ë 'bó–­ÈÊÉAVvvÔ£±a‡<)|1÷Æjµ¡¯g O?±y—S•›—‡uk×âÜsÏöº ˆ_á‹_<}EÕ#ýôÓF¬`éÒ¥øæ7¿Éd6­íÁ"E„XSS“f±å¯ý+gŽuâ;ßùŽ9ï½÷ž3FÀÄÜ¡Ã?ü}Gùþ÷¿oßo¼aD¥ˆ&9odkyOE ÅÅÅ¡¤¤ëÖ­ÃùçŸÖÖV³³Î: üãͱb®žÜ·§žz 7ÝtŽ;î8#¢Ö¯_oH3®ÒƒN8«W¯Þƒ«ÕU”€PJ@ (% ”ÀF@DÕ!KRqàÁIˆsD12ÚİŠD¤¥ºÑÙæ¨))?߃‡ùþÌuøž.³¬rr¼æ½8##ƒóz9§ê»—O¢¨šÈ@Š!:UÛ9À·ñ®xºJ›››Ï‹^ZZ½HK÷R\"&%;MF{k *++ sð»Ü<üðÃæ%[^Äc¢Jþ”?yI·å+_ùŠq}äåý¾ûî3n[l‘—~yQA ÂâÈ#Ä;#—_~ÙìO>â dggãÌ3Ï4û”TÃ'žxÂìg|™ÜÌ™3À}¼þú뻽ƒ±ý‹hN/¼ð¦M›fD•,"ªä»cŽ9o½õ–<"¦dçG„–¡Ý•êÅND„’œwÕò¹çž»KpŠ3ÔÜÜŒ+®¸Âð1ôÐCáöÛo_顸Y±%55ÿüç?QÈÙc"vŽ:ê(³0ž4i{ì1tÐAÆö”‡é÷¿ÿ½ás»äžüüç?Çhîµ,r}âPÊGîÉ=÷܃E‹1†r‡9ŽœçæÍ›qÞyçL% ”€PJ@ (%ðIQuÐ!©˜5Ç ë˜›î¼Û&ЉŠÃ¦ ­Æ<êïï5Içb°t³Ô/== &JCj”ÕfCtº aùúÅåQ‰QONNe?Ô¦!TOªæ‹pݧ¾4qà/§ûCŒt0£½)iq†õëX XŠîÎn㮄¸}NV%î½û%:;ÓðËÆ$n=1Yœ¬6X飹]qt&FpüñÇaãÆ »e$¢J\)7ß$/梥×éø&Nœh> .ÄŠ+víW8D"”ä#âK¾ѱ|ùrSÊ6uêT³Ù¿ÌÞòz9o‹îÐ)§œbÊÑb‹¸L"ªŽ8âˆÿ•¨!'®—JÕÕÕÆ™:öØc¨’EÎAÎïùçŸ7×ñÝï~w¯D•8{âJÙ^LŒÝ|óͦ¼îÒK/5¢JD¥¸`wÜq‡‘1·o¼¨:à€LÉŸl#îœ8V·Þz«UÂYD•ˆB«ÂPÄ”¬7oÞ<#F¥ ñÎ;ï4Î\LäʱäÚEdŠ0•rMU555Æ9“}ŠkuÆgQ%ëý§½Z»}Àt% ”€PJ@ (ý–€Iÿ[€9óâàë·SÓDàáüÞht~Ÿ´ºøºgo4€3ªŒ)äŽ£î Ž²üÏ–Öfš3^X.ºdZÔfu0õoÀ|18Ø Í#of6_ Cœ$Ü_ Ä<:)Ù9)œ$܆õëÖs pט7¡àJЧâö[1öؼ¹spùe—³„pžáy,}ø!ö]E7b ûžÞã ´ôàìÞ©’ò¿¾¾>#ä%[ˆô‰ïä…\\$`‡rÅÚÆ÷‰*q\$ÁC^ôÅ%’õd'Ÿ|²qMÄ=ÊÍÍ5Έ)Õ{ðÁë#å„—¨q(er1ñ$ç-e…âø|¢J\9)ÿ›2…‘ø,í>"L¤DS\!)ç·HD•¸Kâ,Éõ˽7þZ¿÷½ï—LDªˆ% ùâ¿høL˜0ÁGb¬JÖÎ5¹fÍœ}öÙ¦üOÊûvÎ3ƒ9æi§fîˆ*q¦~ö³ŸÑ%âKœBé³zæ™gÌú*ªöÛßô•€PJ@ (%ð©0A&£¬bDöTE8—JÂ(jP—û¯¡¿ù›„ •%haEXÿ@/N0дt3eùÙ/‹¶6÷¢·¯—/±–Ÿe0á¢nM…U.Ý’÷ÐÅ—Þ¹óÀÂPŠæ¦~_ŒÚú:¾´3ùÏ×›ÓbæT…Ç"¨ªœ‰ù8*›Iú{üÑÇðãŸüqñnüé¾?±?j® ·í/ý;{¢b/Ѥ'HÜ%±t»-[¶‘0~yÁ×çàƒ6%d±Eƒ8D¦pñÅ›õ[n¹‡v˜r⊔••A&âA„ƾð#d›KT‰3&e}"ì$­PJì¤÷hÉ’%æúbNÜÿÖ©Q%n”2JY¡,"‚N<ñD<÷Üs¸ì²Ëv‰*)Ÿ×MD–8MRbù§?ýÉl#œ¥œOÖq%Û‹c%‚IxI)£„WÄD•¸”"|¥?L.Ù—\—8QRú)î_¬wKö+ýqÒS%½^Òƒ%¼%˜CÄ›”oŽw$Ç;Uš ø©üÛ¢QJ@ (% ”À~C€¡çXHQ5uš½ín¸†ù®[Ϊ´µ˜1»Äd,_¾¹9y|‡3´1Ð?ÄÖ•R¶F5²-ÊfÚ§,×]rÔ?j3Â¥¿¿‡T=_´Y¦—Âa,z™|1†L&\Hy ¸Z==ChgÉŸ+ÎÃ\vz†èRÙ/•ŠIU³ñ«ëÁïî¼Â,’Ê'Á.· ™fXðÙgŸcÊÑvŠ¢Ý‹*qx®½öÚ]/Û±—kyù—7ùyoEÕâÅ‹1þ|óB/AwÝu— âðˆƒ$=>R"÷­o}ëcU"t¤äOÊìÄ9“2:qwäøÒ¯[ÿWå"ªD´µ´´'/&:E܈ˆ½ä’KŒˆŠõTIaLDÅ8Êϲq³ä;=ÂYz£$ÈB¶'O”‰ÛgùŸ³"Üä>ˆ•ÒIiâjÅÂ+ÆGÇË5‹€q+ç+ÁrΈ!8¶¨¨ÚoþMÓ UJ@ (% ”À§N`§¨ŠcO• AŸ‡‘évó bŒFB þú(¢Š ˨•†àg+ËàÐ0«óJÑÑYƒÊª,j $XÎùêÔhKÓ7J¦Ss¹ƒµ„}™¤‹…FiI“.ÈËÍÆ¶šZt÷ôÃMáQVQÅhñ-Q³èjX˜þ—“YŠ;~ón½åFºV“˜Œ‘kz§æÓåhïhcÿL-cÏ/4Âbç²SÅ–ºâ¼ÈK¶„ˆ ˆ½ü‹c2þ…[ÊÇÄ©:ôÐCßWþ'NÕK/½dÜ“ñN•ˆq^äxþóŸ #Å^ìL1´âþûï73¯¤7)¶H‰¡8JÒSõÚk¯íö¦0¨BÜrâTÉuˆq"!"bD¼Èu‰¨’k‘ä<)ÙÛ› ŠÇܤÿ}ík_Ûu~Rf'ÂåòË/ßÕS%\¯»î:Ãôƒ¡R>(}h⬉ЧIú§Ä!O’åZ¤lRz¤d‘hx)§”ë“m¤Tð¶Ûn3e™’ñA‘tüñǧJ~/Ε”_Š(î—ÿÁÄÃØs¡nÕn;]A (% ”€PJ` ˆ¨š·ÀN­bG²'ƒÚ%k×mB|‚ÇTéµuö¡¯7H÷ª CLèëGˆUnبŸ,la)¡!á†åŒ/UG{»æå:''‹s¨Ò‘;ÂR>6mÜjæ'ÍšYÉØìç¡£åE-£Ñ«§OF#çT­XD„C}eç2ü· · 7ßð#L~qÝ/y@àÒ+.Ç_ÿò~üÓ£¾nEÂΫÜYæõßsª>L\ɼ¥­[·â',!Œ‰Ù6¶®¸JR~&E"º¥oJÄ€ì[Ä‹ˆ*."PÄu’ín¼ñF㼈°’EJÒäå^Ü Ò÷#¥rú îNl‘@ ÙÌÚúÝï~gŽ1~6Óï݇‰*9Žʘ¨§JÜ ¥fÒw$N‘\‹”1þ×ý—ZÒ¶;q%,Ä]“ô?™×[¤TRœA ½V2ÇKúÅ$ÀBÊc‹/9)¹”s‘ –v"l…,"T…¡œ«KH/”8_"¤¤œP–ü,âIÒÿb³ªDp 3I<MúÉäþÊñDôJB¡ìw¼¿UTíῺšPJ@ (% ”Àn ˆ¨bw?2 qL÷%ñ]ÕGAåâŒ^ºz$ÂÆwæxVí…ù{ ®­ØO•ngežÝ¤ZN;cª)ÿ}“ž–Š„DÝ©-´¸JÑÛÛÇ $ÝÉ´¿A´°ŸªjB7Nã0àž}~%E#Â9ôW„’“[ÑH"þvÿÛ|‰^ã,'I€×céCKqô1G³ ÍGa#‚JNF–þ{y‘ å⪌¹ŽA$®ˆ8RR'Šø!%}=ò8 âT‰ëUÒû#=Tò’/s—DÄH©š,2ƒJö#A±2ºØñdŸ’„'âD„ÉxGèƒwm|oô!‰‹$ŽO,ýO„ Ìm÷GÊêdN•œ«„WĆåÆf;íI̸á%nÛxQ%ev"˜D Šˆ’ 9¹v9NÌ©‡Jö!nœ¸Ec²"tÄ=ñ$å}2üX’úÄáÁ&½S"ºbû¡%k¼”¿Ky¢¸oâ(Ê6r¤,ýý?ïU;—wªö’‡®®”€PJ@ (% ”€Ø+"ªæ/tãÈc²˜)Ñxw]) .”$²²ËÃv¨v†ø%1°nœMbÅ^$@NžË Õc¹à£)ª¦Ï%))ѸY^ª³ðk C(,ŽCWw="챊ŒÑ‰0ecÙÙùtl&ÒeÙŠÚšVÎFš‰äÄtî8ß½ä.U{u;ue% ”€PJ@ (% >m"ª9, ÇŸºmõÈÌ(BEùüã‰QZž„†ÆQô÷PT%3¼Â‰§‡`£ÔCƒð$9(8‘"l–#œ•²´-,ÃJe ÝnAb‚ƒs§Àð‰QÌ™;‘á“Øõ2Ëÿ|hjÞÁr»d)˜’œ' 5[ša#¢ÂJÅŸÿô¦ŠªOû‰Ðã)% ”€PJ@ (%°WL¤úA©˜:Ó·Ý‹Œ´b“~ÞÞщàX3Û—FèPÅ3ü.•½SCHKJeÁŸ¥# ìEO·ˆ.šQWýàËÑ8†.Ä3%ÏãI`?áÐ0ZZ(¨fáÍ·–±Ëœ¬LöSØë²CÃCð0fÐçe‰ ‘ ¾¼½›Ñ‚yøÇãëUTíÕíÔ••€PJ@ (% ”€ø´ ˆ¨Z|hʪ˜ì7DMç圪Ä%¸ÙÄšuMÔ=ièíÆXd.›“]ÈÌŠg+T2¶ln5ñê–›oùv4//—á=ظi=ÚÛZ ˜ð—A‘ÔÆÐƒ!ÁJ!» ‡q(³Ú{ÑÖ–fZ_aÖJma”AíÈËŸ€¿< NÕ§ý@èñ”€PJ@ (% ”€Ø;;{ª2P\B¼3Ÿn”.·­PA¤eXØO5À€Šx´¶ö€^ìÅ–ÕÕåhni vbˆøŽ;¡2šŸŸÍþ©1 #?ÊÊJP^2‰—‡ pÛ°ò½ŒS÷ÑÁ’¸ò1~ϸÁTî¨cÁÞ[±–¥=ìÅr3Ž;éI¸ë®gÔ©Ú»û©k+% ”€PJ@ (%ð)Q5}V"&MvÑ…ÊÃÀÐ( óÑÝÓxF¦ Xi&õнc›”Õ$–—³’Ïʪ=öZ¹Ùc%éG5!âöOùLó;©0uÊdþER¢D§{‹máì¦×0ÈhïFK‰ÛniTQµw÷S×VJ@ (% ”€PJàS&`‚*å!+'€á~8ß—óXË©ƒêጧÐó0˜Â޶öN£… ™褓Õ“ý˜Iáñ$ÂrÆiÇGeÈ¯ÕÆ:ÂÚZ›¸‚ 9ÖÛÕÕ?ÉÆ{âLò_*…•73õƒÍ,  “Å$Œx'ÒS’µŒ²t0.çŸó’ŠªOùÐÃ)% ”€PJ@ (%°wL¤ú’RSt"0â‚?Åä)PW_ÃÈt §kŒ)¥Ø¾£ ÉÉI—ª¹¹ÉäQDù¿,VïY­ ª8õ”#¢%øÂŽFk{#]¨ ¨¯­3+õöõ³GŠ‘22l†[ÙØ •––Jû+Ÿ¹ì½Tp}\g“…Cœ‹ÿºâßpDù|tQJ@ (% ”€PJ@ ì+æÎ­`¾D'“­°9¬K… âc ú«øº9Ë×KG*ANl¯ÝŽŒŒ 8ív£‰†‡‡æ‚åˆ#fEe6UVZFÌÇÂ+̤•ÕÉò¿p8ÌØt?ÓÿìHf™_#×eÃ’â¦`°$05i©éXµj%sÝã̬7—5¨¨ÚWž=% ”€PJ@ (% þGÅ%)Èð:8o׎ gQ¢»«“½S.ä墱©­QèîÀŒ©ÓQ_WǹU=‘ô?u’ƒ‘êÇ-ˆZ­a*/f†F18Ô‡ìœlöW%r°U2³Ù(¢˜ÈÏd¦.çmã9•””rÒp;‡i‘UÑÁòSpµ#t`Ù‹5*ªôÁUJ@ (% ”€PJ`Ÿ'P\ ¼¢$öUE ûŒSÕÝÝhXÆI¥ ££SªgÂ?A”)èííí ŒÒÉò³= l‹bOÕª¢2àÊ›•¾!ãT%SL¥¦¥sðUúúú(˜Fhs¥#==ÍD§{™ü·víZºTü¹­•Â#ü]:¬æµGðâ3UTíóž PJ@ (% ”€P%eéÈ/H‚ÛýA]#1é§•#¥‚HLLeUM& ¿«‡ã¥˜ HQ‰„åp* 5夓E‡èN¥¤&ÀOA%n•ÔÚX31uêTºUõp¹âho‰Èê¦3UÄùÈøÀ¿ëEgg˜ˆß‚gŸZ«wG (% ”€PJ@ (%°Ï¨¨ÊFiY&õO çñfg£©±‘¢Ê‰Ôät:QN ްÊßA~„©°"‘•4NQX->¤:šÈyS鉈c<úÝ*qzûÐÔÜŒ””är¨okkC)úè`¥À“HgË›ŽúúZƯ{`ÁHv*´•Z8äÀ“½³ÏÃÓTJ@ (% ”€PJ@ äå'aâä|Ž‘²À "ÎíÆªä¤—.¢Êárc°oA_É€!ºVAãVY­ó±Ì™] G‚¦ü¯°(+V¾‹ÌÃÌYsŒ ëèìDë}œW544§ÓN«“&W`ëÖN4ßKA¡4i¥$çà±¥¯êÝQJ@ (% ”€PJ@ ìóJJ3PVîEï`[¡‚(,(@(‚ÃjgO•v‡“•yƒøp0r=ÃüÆL Ýne #Õ¡Såõ¦ÂbcøDU˜#¾öJu¡$Ñ镌[o¡3µÝl˜šæ0=VƒƒÃèë ™@±¿âžš‹»îxðßáižú>ÿ@é *% ”€PJ@ (ý@Qq:J)ªî(]);«÷âM%ž‹£¥¤¼/~-Ím4œBpr€¯Íf7ÉèýýÆpÊÊÊ‚åðÃgDãYöçe¤zFF*¶7Ö±´Ï‹îž^ºT]´³dCúT(% ”€PJ@ |”•g¢¬"QÛ(ƒúâÈXĤ›»nó3(”•âÕW^7Ó‹F&¤‹FªªœÇÃῇ͈:—dCÊ-6© ´1.‚ÊæàC˜Mg IDAT'èA&w0ÿ€ùظqÖ¬YG×ÊÁHu*:›“³¬X—@ÑbP…Ä®`ùŒi ýŸˆòù<ü¢× ”€PJ@ (%P^áÅÔéÅ ©ð!ÄTs#Å^‘V(o† ž$fNdÂïÅú5ëé$þ‹›}V Må÷–cŽ™MKOfº_˜ lŒ;bV ›­¨¥X'ÈD  Z-a¸)˜ÆÂŒäÜî†V ˜Ua:9Ld­¡FY èÇk/¯ûÄD•\„‹ƒ¸ä"ÅEú$œ$'“>ä8RÒ(bñãZä¼ÅÍc¾ýG-"XääØŸÄõ?¶(l9–,£ì›ûà"¤¨ôñ‹ôÎÙ9EZÎS}ð÷/ÝPJ@ (% ”€ø¤ L˜˜ƒêiL7é„• è"–DTùý£˜:e ºº{ÑÖNà 짢èò0=5%ëÖ­ã:S2hùÒ—F=‰"R@ᣠRÒ2XÖ7ˆ¡a$ÇÅÁ”’Æ:Cƪ±ì¯¿×ÏXÁ~Äów£ÁQÓg% 2«¸xîþÝû¨’x¥¥¥¸ñÆqíµ×býúõ»({sD`œp 8ãŒ38ƒ+O?ý4n½õÖ=Þ…±Å‹ÁñüóÏÑ1^ýüç?ÇÊ•+ñØc}ä>+++ñ‹_ü—_~9ê8±ù“ZòóóñýïÕÕÕFqÄï;TEE~ô£á’K.aÍh¿ùÌ0»òÊ+1{ölsf­­­Ÿ¸ü¤8è~•€PJ@ (% ö_S¦æã€'¡©uÅ”ËôKÙ¨oF‡pèáÓ©9ø>I5õ΃)†Í»sÀ€Ïç7=V–ó/œ‹sÑñsŠp<‚2ÕŠ±Ã!Ô×nÇñ‡ŠÁá,{m2³Š)°âéâxLÊ…×›žþNqêº0äd—âÁ^ÜyW(ÐÛ¾sWþÇ’D“š±·‹¼Ä‹àxùå—qÚi§á­·ÞúXEÕ¬Y³°bÅ ¬ZµÊ 7~ûí·q÷ÝwïÕiÞwß}ÆIaöÁå‰'žÀk¯½fDá‡-16sæLȺÇwË,×ìÕñ÷få¥K—bÉ’%FŠKuþùç¿osN÷Þ{¯Y§««kç-¥p¼ð 1þ|#ª-Z„Ã; 555{sh]W (% ”€PJ@ ì<¸Óge ®¾›¢ÊM½#™Øh,^2«WmFgÇú‡(¸œfây*+ô(´ÆÌX)Ë·/•¾¨HÄA+ݽC°;°ecëý8öÐEðf;°¹¦ Û:Ìà«ÞÞ‡bå ó¬œL Œ‹w™!Yåeå(,œ€CŸŒà(k)ž<ìÅšR=Åä½ïؾÛùq0%#cö{’IÜ´iÊËËM“—ˆš»-â•••!''íŒv—yUâ(‰ø¿îGÝÙ~„ Æ=ÐÌ\1“‘‘aÜšªª*Üpà ¸âŠ+Œ ÖÛÛkÎmO’,3½®ºê*ãØÈ~äÜÛÚÚvíC„ÒsÏ=3²þŽ;˜¨X¿k÷Rj7gÎú!³ÂÄ)3qõÿZä|eŸ¹¹¹Æ±“Ÿ%™DÜ/QË{²ˆ(ÑG—QܰF6»çž{L©¡>Yd¿“'O6®Tff&žzê)S¶(Ûø|t3ÿµäååá6ë‰ø“ëø°²F¹¯Œ¥E¿zõêUïÉõê:J@ (% ”€PJà£LœœôL¶=1/"11™ÂIz¦¨eÂ6äd23"ÀÄóQô ´aþ¼Åxçå_Vúùn0¹–?/=.ºzÕ~»ÍÃ8u®´`ë–z„X#XYì…ÍÙÇXsPSÛˆµë·²y+‡¼„éqHLNaÉ_ _ö­hniÁÖÍøí­÷ApÑ{àpìQÇšëðcÁüø"ÿÿÙ»À*‹¬{_zïBKèMŠ {ï½lˆëêÚ»ˆeí»ö‚‚‚`GEEÁ†Š MÀBï„ôÞ“÷Ÿs¿7/_^I(»îþ3»1佯̜¹3sÏÜ2k5%affWùæ›ïäå—_–k®¹F‰Ä˜1c„nrTâY¨”¿ÿþû _½”@P}ôÑêªÖVR•••¥D†Då÷ßWRF—5’>ëÝwßU2D+“‰£š8qb3ëMK˜$¶“ä‚ñF$8¦þü±œ}öÙzÿMbHBDKÏŸþ)‡~8òÞê÷LÇ8{öl%¨¬ÇÞ{ï­„É’SO=Užzê)%;¬/ßG—ÃY³fµ:Zxm4ÒÞ“ÜÑ Åz³®† E!}$ IÝŒ3”|åàœ2^K‚ÉëÝqWݺu“/¿üRŽ?þx%U¦Í|¯e_žþùJ6ùžn¸A-»*®Uì‹€EÀ"`°X,ºt‹“¬îIšRùú`0B2¾új‰Iÿ(“è¨x\%I)8—7$Y¾žù=Œ5D*7§@“÷yž{cˆ·¾6ZVü‘/UeAR_ SEœ”•WIVHC¤uD,MR‚¤wì UÕ^IÂYTu`nuuA’ŸW#¿ý±‰+òÕ°¡6Tfÿø^£DƒqVÿxôà ’nü;¿"áºw6Ü k¤¿¾2gÎ/j)!9è ƒäºë®Sk ˤI“T)¿ÿþûõoÆïXì·ß~m"U´¶¼óÎ;ªä3ˆ‚ñXüûôÓOW¢‹Y×®]•¸½úê«JðxF"±é·UHÊH&HÜH G­Ïf}IòóóýqQ´ø$%%ÉØ±cÕ‚5aÂyæ™gä…^ÐWôôë×OøÝîhÝ1…„hÚ´iZg¶ "ˆ­EË—/ou€K](i¤¥Šd襗^Ò{i%dáuýû÷W áO<¡¤ŠæM¶‹ BL1¤êØcU«››T]pÁJªˆùü¡ñ¾ûî~Nbì¾¶ÕŠÛ ,‹€EÀ"`°X,»ŒÎÑÒ½G2 I8 ¤*"2¼¦R¢ÂRA˜"þï±2IÏðJXH* 0õHæ Ž’$7m‘Šò ñ<ø\šWjâ%!²7Sxdá|¸£…T"Pk-,*ÁÒ·gÄNí÷­).­ÃO)øPèë$9)Yjê=²yS¡T”ÕÂÂÒE*Ë«å‡YódØ^ûÊ?ü(g{–Z†P3I…›ÉÓ¡Cà WÁ-ðã?•ȪA77ZgèšG‹Ï’%KT©gÜ-S†\DLŸ>]Ž9æ%Un—³–2å :T­.]t‘º±±ðy¬-T$O,´N 6Lã‹h=š7o^«Ý˜ˆ‚“ ²Çov?ßO;º²ð7ëwä‘G6¹vÈ!òÕW_i¬’;¦Š–¤?üP˜`‚¿'Ož¬¤„‡„¬½™ ù âÍ„-ºDÒ‚GBTRR¢®—ÄÛà¼-RõÑGi‹þóŸz +ûõâ‹/¶¤ªUé²X,‹€EÀ"`°ü;ØmHŠÄ%–KLl†Z¨"##àeU‹˜ªp‰ŽL×Ü奒ա=«”Œj…â@à-°Fà,«HXªn›‰S|C¤$¯Rz÷è+¹[àŠš¸täd‘ÂÜjì…k_9xQ˜ Û¯?\ö¾ÿ Wb¢’a"‹1Y¤ùÜãâš=4Nf|5OöÙÛ!Uk7¬U‹J=㤬‚„c$¼i  j™T|òÉJ&èúG«-X$TæéÂ玩jT 4¤ê¸ãŽÓþ!Q2Ö;ZªØ‡tƒäõ¦î¬—9c¬-u´×X,‹€EÀ"`°Xv%=z¥J‡ŽQ°De€×”Š'¨¤b©«ŠDHS<ö %*:D:w”+6‚T!L*AsRÄĆ"lixNÑÓÕ ñQÕ2h@7Ù¸ñOX?’eÙŸ8—ȃ3©<—Ä Išˆ")½B†î‘%¥…õ2ïçß ,ÇË¡õƒg]Á%°¢"Qyô]Iï.3¿ž Bó¥<òÈ£âÊ‘ÞÈô7®“&MV2´5RŘ)&0&B )cF>ŸÇ\Szï¿ÿþ~Re@n‰\1¡Ic~Î;ïÄ·5÷?“ä‚1LŒ¢û³òsZ¤Ř$bCwH¾ç¹çžSÒBb³£¤Ê¸÷þ›)æIÚHH™RÄÈý¾ÿ‡~Ð88f*d!ÙâÏÍ7߬TѲHXwö-†íµªíÊdŸm°X,‹€EÀ"ðÿ~:J÷žI¦œ'Í‚Xy´gBA·­”„Ähð!äçÖ!YŽ—*mW‰„j ®®Ï ÷ ð–IuY ¬N¸8<‡ú& Æ©AbãReñ¢M#5HçW\Z,^O-Y-΋ê"¹›dÝ” ožlÚXd)Ò3ë4¹`ÄUj‰Úgÿ}dÚ'Ÿ"U{î-’øØycü8¹ô’ËUñ6¤Š.}L¹mbªÜ¤êCÑDFѧRÏØ""Æ=mÍÌ(íüM‹ I‰“W”1ÓžqM#ÉÛîŒc ßÅŒ&¾ª5RÅ3®N;í4­‰ÛÊzñ#FŒÐÏxp0c°H²ø7¿'¡¢û_{KK–*¾›xÓ¥éÓi #n,ÌÂÈx(w!©#ÉeLÓÅó|.Ö‹÷‘@1u<ÛaRßÓ•ÐXÛ[_{½EÀ"`°X,‹€E`g#°×>)Ò)³FŠŠÂp–n¸„…F‚—ÄHdX(Τʃ·V½dtN–$'¯« ‡¯;®˜¸p¤\äHÏ#ÿ<Ö»zÕj©*ÏGÐU¸$&…I¨'IÖ­ÏÁaWG˲?²eŪõH:P_ÂJ<0C’Ç=%9®@‚Š*üÞ2ÖQ:w 9éòÈc¯à ,'KÜ€ÝÈQG¥–ŠßqæÓÌ™_+bIOO“sϮֺöѽìoû›&``v<“à€)»™4g*1ŠÄãí·ßÖkZ‹Í1äŠD„I!øº¦¹]Òøž»Dk É Ÿ½=…í$ᣵ‰ïæyXLŠÁ“\16ŒÖÖ‰éÓ™….Ìüg,F¬1áYY´ñÌ„Èç3;cÕH\ø=‰L[Ïì2í"bÌ­,$T|'SÀ3í»ÁŽÄíbR .ó9ëÆŒ‡Ì¢Èß´¾õÖ[~ØH~IÎèÆI‚ƺ®X±b{`µ÷X,‹€EÀ"`°X$F SüF|ÀO©Û3‰Tf¦rhsÙ{ß Éì¢|‡.}O(tðpp£)(Ü€œé’’'kWoŠÒ`))ª„ÇXñ"CzB¤ ðÙ×.ð.š¿¾€BÁ’¨˜réÑu_Y¹z­Ä'€uy#ñ°¤ÅÞ '¥v”Êê`p R[W&}{uÇ!À^}ÁšÕ…R[™)S?žƒƒSo·¹E-\耳ýÅ€½µç˜ Àë¬{Úöcnï´X,‹€EÀ"`°ì ‚¸[gç†?’>€³8¤ª­Äjðî`J·)•œì*Ü骸ØÉ+X ãNX¥Âå×ù¿ÉÆu¥Ò)£ÎÞ=X*kÖI~ñ\©F~ Ïó¯^ìÍÞœ' ñÙ:T£LztƒÅ© XÊË*`¹Jƒaº¬]Ÿ óW%ÜøjðY0,>Òµ[ªÔ×ÃL– —Á8°ºpü*¯¿ö£ÔÂýog”]Mª™®yŸ%U;£÷ì3,‹€EÀ"`°X,;CªnåUeJppb¡¼R_çX«\_m³»ïÕY2»GIrJ0¼ô ü¤Š¡I•Õë•TåÈ–M¥ÒµS_Y¹|5,Zarèá»INÁbX¬‡õì‹×z7lZ·¬?;S¬\{Cµ"±8·¢¢DêaÒ¼ûžòÓì¹ UHjƒ6THÇŽ‰ #YT$’Iü‰ ­H‰À!YO]"¸e§”%U;Z K®vA{¿EÀ"`°X,‹€E`ç `H•c•qb “¯(©ò€TÕRÕ6·Þ}¤ï€$é„C€—.ÆÙS´TÁXš 9y¿ÉqÇ!³~˜ ûS´ôBˆLnN¶Ö =ûf«/GXøC‹)V2ö9¤Êib[-UÝ{ÅH¿IÒ«wŠÌŸ³Ycªèç‘péØ9 9zÊŒ™_Hei¸O°ôï×G ò‘;¨VªéÑ3Kþøcí“Ë/¿\­X¬GNNŽ^wã7ê!»'Ÿ|²^«‡Ÿzê©ú÷âÅ‹µN$W¶X,‹€EÀ"`°X,» e*§bŠ O•Þ2rä„+õ€‡ÆúÔµí¤ªÄÄ0Yâ$&¾@²7y$&™Îñ†¦~$ë6}*}ûw´¡R´iwÉÛRŠð§RçÝ"½ût@8T˜,úõwñÜr×>ÞêªZ=—ªªªÙû¢$ä%'7Oÿݸ¡TR’zKXdµ”UHRp°W¢¢B$,<¤'QòAp*+jƒU(‰±ä­7~‚ÉÌ!Uýúõ“Ñ£GËê•«dÈÁrõÕ×ÈᇠÐJX¸âåçŸçÈwÞ)ï¾û®’_~ùE>úè#yà”D‘ÈôéÓGž}öYµ\]qÅ terá…*ùiÍZE÷?Zªz÷î-=ö˜,X°@ß?bÄ9þøãõ}ƒ–éÓ§Ëßÿþw™2eаS~úé'yüñÇå_ÿúØh¤¼òÊ+¨o‚Öƒ„ñúë¯WbEh‹EÀ"`°X,‹€EÀ"°ëð…MùÜÿœ÷‘:í6p€üü<ýŒ$†.€´>Ñ:Å¿÷ÜsOY½z5Ò¶wV÷ÁSN9E6mÚ¤Ö,’£wÞyG ]úZ³T‘T‘0­ZµJFWÆrµ:}òÉ'2gιùæ›%""B>øà¤¦¦ð1 –M›×âÚJøƒ,ã^TÞ둤V{",U>RõóœŸå¦¿ß$¸•yýõq 51rúég*êgŸ}¶ZŽ<òHµBuêÔI?#ù!aaìã«h"©¢ÕˆDé„NP˜X"°+ ©úAdtó£u‹…–±•+WÊ­·ÞªŸ~úérÇwÈQG%=ô’7’(ZÅHöf̘¡Ä®ººZ‰‰ 222þMÒc_c°X,‹€EÀ"`°˜DD‚®~È™'}ûôE˜Ð/_JƒaÈ*† µ/r‹ž={ÉÂ… À&Ê^{ ’ô±òåÌ)ðàC¶¿üRMÒçñ„ µz”D£äåŠçœó‡x능¢¢Bªªk`¡ŠCPV1b¨@bçT R• ‚ÓY~øáGŠ0¸ö!aER,7 YšÄñP!Á!R…¤S&Íö“ª…‹ʵW]«e£'N| )ÌOªè÷H‹Ó /¼ W]u•Ü}÷ÝòôÓO+Y"©âwgœq†&¨ ‰!©¡Å+;;[c®Z#UQ`’_}õd,•Ë.»LQå=´v‘$ÑåÏ‹‹‹ÓL…´P]}õÕê’ÈìüޤêÓO?•ÓN;MW°|ëÂx2[,‹€EÀ"`°X,]9…£Ïó?ïëÝ»'Âzæ#Œ'¦_6Õº ``­{ö©Z´@n¿}´Ì™û½Œ?Nºgu@–ô"Dtuð½˜aH8 ØS£õð{Â`P¸2% ••5`v¡ø´þ‚©' ’Jø zàŠ— ×·e FÑ L± !‘H¯ž,Ù°þ { |KKËÔR5~Ü7~R•””$Gy”doÞ SZ,>_É]w݅ʽ â¦þ‹LDÁ'ºÕí¾ûî²lÙ2m­RL‡>yòd$·x_“HÐ%°oß¾òÙgŸ5iÿÖÜIªSE‹“NÐ}oذa·Åät7daf?Z̆®î‰tù£; qaÆÀo¾ùF^zé%ýaÒ‹ž={J×®]Õ‚e‹EÀ"`°X,‹€EÀ"°ëØ©êR5OI,UJªLi=±I~G>A>B=áÂ…2fÌXùüóO¤k·.òðC#ÏCoð…—dÁüzè0­`4,Ñ(ä¶š7"—¼8|HRO;’c IDAT‡¼ìQ8›ª‰!pUp©tî%2z kØYˆUˆ‡4àl«tŽr%‰´ôtéÒUVþ™-“'ý(¡Aa2cæ é×·Ÿ¦7Ü’½EzÂ*ÅL|gœq\‘#4¿ëe=öИ%%fþ3.z„‚.xo¾ù¦Z¥HªHŽxI˜»l‹TñÙš 'Q"1ûñÇ•@ÑÍ…÷Ÿxâ‰òá‡Ê“O>)7ÝtS“¼÷Gq„’»ÚÚZuG$±{íµ×äºë®ÛõÒcß`°X,‹€EÀ"`°¨÷K ¥Š¤jÁü…ð¦K©ª¢v¯ÞeLŒgŽLÚ| cÒP&_ñ“ª±7‚T}¡|%""R&¾ùøN/ä‚8]¦˜,ƒsæÍÏw„û_5^’“Àð’¤Y¡Êê\¤ ,ýûËüy¿JIQ(¶œ¤?±qш±ªÖTë¡!ÁR^â‘O¦.7Dn¹åfY ÷¸M8ÿéÌ3ÎT·¿{î¹o†[™ WÝëH”fÍš¥n~ÌÆH–Ljóøøxuœ0a‚’,Z—Hr¶UxÞ“QɱÇ«÷>õÔSêBh IÕ~ûíà>W7?º ²°3Hòø=Ý™ŠÏd}ß{ï=}¦-‹€EÀ"`°X,‹À®G`«î}`]Zð«$&&#D§d*I 3ýúõiµR&³ø¢E‹ôZCªîàV„MSkIZŸ>ýdìƒ#¿C”‚fÎüZ¯ÿéçYâ9ýìÁÞœœlX¢ºJö–\°¿péÐñTà*ux€'–¨ <<¤§lذY r£dóÆ ¯Ça¿iHäÐóWXaNŽ–nÊ=w½&Þz‡45!HøƒÉ*Lq,UuKuþùç+áaÂŒ¶”Ö²þµöŒÀTìt5äÙT$M¬]YøžÖÒ¶·ö.û½EÀ"`°X,‹€EÀ"°ãlTõéÛK-\¬dŠÞvAA!2p` ×iM—gh³ý1) Öño’¬0ÑÒe¸ÏÐ¥!†D,55U ×ìÙ?‰çÆÛŽ÷æl)€5FdýúM’šžóVÿ-qœ •³b¥!™Eµ”ÆÉâ…$Y½$&6ZÓªWÁšÕ¥k‚dtŠƒ«\ÆÞ½Ö+·¥1YEéòÆÅO»Á?ñÿx\èZÇX%fßÓS‘Õž·õ²£„Ê<Ù¼gèСrÛm·É “&M‚…í–VÍ„;.ö ‹€EÀ"`°X,‹@{Ø©êÛ¯·Zª’S¤¢Ò ïqJÛUC ÇÄÄÈóÏ?¯YÉ[JŠ×¡C%kÉÉÉzŽîܹ?Šç¡Ç.ò®^µ)·à¬ªb£îš"0$ª,GÒ;6àÔàͰB%ÂÊ«T]œæÇHDxŒ”–THII ÈU(ˆU¤¤â$âÈTyãµïAªš·ÁŸƒƒgl\ÑÍðÊ+¯VÈ„&¾É@Àìî}dŠüaÆAºÞFÙžÎPx}ÄÀ 4Hϧb˜ÂíSÙÞçÚë-‹€EÀ"`°X,]ƒÀÖHUÿþýF4OR’aRRå0DµÉhcjkÈÃŒÔR¡gÛ3Ï<ã'Uóæ}/žË¯:È[QQ+¿þºTâãqîT¢”HŸþ]¤gï.R\²B~ÿc1Üû’@z*$==KŠ ÂÍ/–+&›ðHXx¨ääáÞ0‰K‘ϧ-VÒH ÕRå#‹ü>1X!! d`xÆúd²ný1¥9ä.ŒÁ2)Í}ôQ¤hŸ¸Ý=fˆŸiÌz­YɶûeöF‹€EÀ"`°X,‹€E`‡0çN)¥ðñ æjèׯÜð~–ô´Ž.Rådè3ÙýÚúâÖøÀÅ_,Ï=÷â·•ÇÌ›7Wøà.zË_ï±£GÖJ=øhó_©.íí)SwfÙÜ…ÉcvÕ³··¾Å:mo[ì}[G€ý̲+äoW=û¯(›Å:öúCÿ“cuWÉë¿£MÛÓ·ÿÍíýw`ú¿üö=Iç}Cªhán7nŽr:t0ÌD ÞézÖnK%77W?f\Tk…rÓ§O÷'Ôcvr&£ÑÉ!U¨×‰§öñFFÄãL¨p¸ùKdL°,_¹ ‡úFhÚóYÓ7·öûý.FÀ’ªÿ<‘´¤jÛŠí®PjwdXmÏ"½#ï³÷þgØ•ŠÕ®zö_Q6ÿŠu²¤ª}cjWÉkûj±}Woüý7·wûP²wRå©ÚÝ÷‘ãâgH;:ú裕è0.ŠÄ‡ñOîDæYÌÎÛ*À|þd4øá‡ÕêuÒI'âø¥Ê”"—]v™<Š3¬\&n*HÝÿ~øá$׃û_ÎÚÅu #"¯Ø…yL®‡‘#GèñLGq¤¦\Ÿûññ{bwoC}¸žÕ1#]‚Ãêd\ËQ&ðà¸ß¤ ÓdµÈûÞ±c˜Õª%'/­X)éjh`¦Œ8)/ª—o§/Aît‘ÌÌnòÝA$q8\˜Œp⤷ä¶[o÷ýÄ&šô͵¤jgt½}†EÀ"`°X,‹€Eà –²€3?B ©r·zGH ¼ßªÃ; F òÉëÏ‹çøS²¼5Õa’³%_âŽC²‰„¥"©G–½èˆh¡U¨ „(¿yn‡i’_X¨14<× QEòÂ'KDpœÌøìg©­ù|úçÈØ ?òœwÞyòðØ‡…™9>ÿ|ºætwŒS–Týo‰ºmEÀ"`°X,‹€E`× °=¤jg×ĪŒŒ ¹{ôír͵×!QÅ9}½y¹5 MÕx_DÅ…Km=þíiPŸÁp&špjoÏž=¤°©Ó‘ }Ýú R KVPP¨T×zõÐÞw“¼ìR™1m¾„‡ÈŒ¯g¨î…^o¸A®»öú&m²¤jgw±}žEÀ"`°X,‹€Eà¶’*†]rÉ%HTÑe‡Á áéÇ”>ø@Ÿ5räHuÿc’‹jxïÍÿe¶xFŽÚÓÛ)£·üºh¹¦Rö‚$U‰7¨‡GHˆ“7È+!¡A2§Ó—°¢¢LIIU]½W*+ðSY‡Ãs‘ËÖÊS¿=óÈQÇ-O<ñ„|üñÇ2°ÿ9oøpÜ[©‡7ÆUYKÕ÷´}€EÀ"`°X,‹€Eàÿm%UL@ñÈ#HŸ>}ÔCnG ß¹víZ5j”>+TÍýy–xŽ9±ƒ·[×>H(-ëÖeã0ßb©©­†mª^Ó«× {FM}„G…#YE$Nø©Ö °2$©(/‡U«!X†ÙS~_ºR~þ~1Î’ ’ºÚeˆ§žzª Û{oY‹¬3g~-÷Ý¿”—5žtŽ©Ú‘n·÷Z,‹€EÀ"`°Xþ7h+©Ú•­wHÕÓ°T¥¨¥JIÕˆËz—/[-±18}çUÅÅ¥Àb•§ndx[¶lO0(–Ç‹D¥Î[«²‚…G„KaA>PÔKjJSdÉÆuy2ãóÙR_‹¦ð`”^}{©ûߨ1É´iŸÊ”)“åþûÇøÛjIÕ®ìvûl‹€EÀ"`°X,‹Àÿí%UæúÍÞ˜¬Â##/¾DžzîH©^]S%¿Twqïúõ«%9±“”•Õ‚,…H—®$<,iÖ“Õ08,XÊàò*Á¡ÁÒ£Rªƒl1EAAÒ©{$6.V:vH—Âü*™þÉҀϺtí"yyyÒ‡ ¸èB¹÷žûäµ×^E\V!,X— Ðé\Kªþ7„ܶÂ"`°X,‹€EÀ"°+h/©ÚYu¡'ãFØ©ú^ÿÿŽÝ޶¿y̶óÄöâøœöÞ¿£íøß¿Ÿg“²lذ῱háßÿþwÙ}wçðÚùóçË“O>¹KÚýæ›oÊn»í¶]Ï~ýõ×wY½¶«B®›Z&UÁÒ·o?5uì€sª*M˜‘Wˆƒ›ðß»îºËÿÄÀïùÅ\°j¶Dª~ÏI§w÷&¥„Ȇu%’—[!ýûí ¢T#›6n”0X¦hN*@œU-˜Ym}$Ãw°k×®’›“'yùùpŒ’¨ˆXøVÈ[¯|*?üøœ{Î…âE‹aû“Ë.½L²zdI—NeÎÜ9òÒ‹/É÷ßÍò7®¥¬êNLUù ‡ÿ’œµT¢"qøï·Ë‹xÞСCq¨ð½ŒÌ}¯0`€Ì˜1C6oÞ¼£ýÙêýÑ ‹å¶Ûn“÷ß_-U=zeÊH¸ûÝqû8·Êøü9S•™òÜaÚÂÎèȯ¾œ.L‡ØR1¤êù_ÖC&~,@Éaòú™šót ã›oNÃ;õº]ÆŸ BÒV!mŸ4xå,àôøãOÊ{k,Šáòꫯùh 2ÐúäÜÚûwta4ÏGûð"h­½µõï[&P÷…Þ?Ýït–½WQ°M½µÎ¸Ï‡ëym™šˆV68Ûú¼˜ŠqþßæÒVßß–ðufä{krÞx-AôU/ šTŸ¨¶¥ímn¬^Ø’ÒÒ¾'4¿º}ŠS«cÈ'Wæ:$0ý·—Vëøo¯ÑΡ{,·$g[;†£¥š´w~Ü@P±4ã¡5C“k¾Ð~‚|8õo$T¦ŽMë8éµÓ¶ Ÿ{ÃÄe³vš¡ßÎ9Ÿ}µÍM,*][yfKuØùó ì›_Œ¦AìX©¶LÈŽš¨Š­>ÇQ"õÊ<ÇϹ®ñç3~Šët£·à¶"Ú·Ttšíz¹oä»›¿wk㪭U仑š¬Õ ¶˜˜ÄèÓ‹©y!={ö”W^yE•Þ– ÉÕgœ!K—.m\ŸÚ»kŠæÿëÙÉi§žÖ–ÕìšãO8~‡6ÄÙñª$¤ÃrrÇwȸ×^“» ”{ï½y £A5Ͻ÷Þ+K–,‘‹/)Ž}P7ÿƒñ?²o/· ¹×ÿšä«½Ê¢Ï¸p>HÕà Uwøô^Gvi϶VÞ Ð«Å¡¶]:ujySÈëÓ¯µ} ›Ty!) ]äq{:aæÌ™rÁùç7Ù>pdÐ×Pÿ«ƒ0ï6 ܨ¿üòË<%¬U["Æ'Ÿx’¼òêKòÛo¿ÉĉýÕ Qù#é伦 G¦òþýûkŸ¸å“ïå1Q_ÂàÃÂD|ÿú×s<ˆ©šƒ˜ª£OêèõIRB¦¬^µE ÜG–-_.U UÁPcc£%6>ë¥ n|^ì¼'$Äê•§„©ÀFG„"õz­Œiš„…ÀjÅ,"+W®”K/ W}ÔQòü³/7bê› /Í ¢ ¬a_}ùÅÖIˆÝí·ß.Ï¿øª’*žhL?ǧž|BÂgFõ8“G¿!U·B¸&€TmÕ¤µ=½î¿Ç!T#G^,_ýµœ¾c:üL}¯½ö”[o½M&LÀ»›ˆGÛH>çC‘K/½TŸÉ]csWù­·Þ’wÞyG>üðC9á„P‡óUXˆÕúõë›´Ž“ÍŸ·Þz«ŸŒÝ߇Á^YY©æiw9餓äÚk¯Õ˜¸|X)¯¼òJmozzºÊ HF‰Ðö#Þ@Ô(NL†òí7ßË¢E 51ʼyó”õki'©â»¢£bt×ëþîs&|ߢÞe4pm2u¼âŠ+eúôé²jÕJG”Ð]Äž_Ö­[·Miá3Iª0Ðëa¹m©ø‰¬K ‚C°5/(ϸµYa¿i;￯ N~…Ôw‡÷Cšô‹g÷½­†¶.¾mJRE9mEïÁ^#”«¶Û¶ÃÐÊ•m‘ãö²÷ƒ| c³À>ÙÚfÇΨï6žÝš.×L·5z7· Í:dêØDÐL)PmlÎlsÙ–Lèäí•é@R宓i¢™ƒšÔ7ð½íÛÿhsÓ ©pàút%SÄÏGªÜ7Ãjë¤Êë»ßÜîünT2+é(Ô»¢pÛc&%ß šzD´Tµ§~†T)YlA ¤!ͽŒ|š>{úÂW\¡Çöl«PŸ8òè£dƒ[wiÏDˆ>5úa{Úh®½õ¶[}:ÛöÜ{ð~JE‹$…zj[ ‰u· Ý”5!©rÝè–»–HÅ‚c¤ê¡‡Aª ÿ½ñÆx}BSë’oÖ1z€ŽM6‚½þúvÆÁ¶-[ªM"-YHÍ-r,»ij‰Óut`ÐÊFBØ©Òv žz¿¯Î̳ 0õîÓî” ÔÂTUA÷?g¼oBÈ‘Áµµ>02ÃC}[*W…ò‘—^,ÿüç3²Ï{K0›üöDþ{a‚7(¨Bž„˜'¯ôéµ”õ…Pª«t—!!!AÊ+«¥¦²ààPTÏ#qñqÊì8€Iîjk*@¬¢dò3pp°ŸT±2T ßž< ŠîýRZ\ÖX¿m*^Ôµ[7ƒ÷$&:ºI›Ø¹´dÌšýµ×ßl6hæÂÍðŠ+¯’Í›²ýäiת¦"A‚qÉ%#µ¾_|1áŸÒ¡Cùì³ièÌ’™™%Ý»ww¤@K;G\=yòd¹ð »#>ú¨²dw9à€äÿø‡š{IªH®Î=÷\5+Ÿyæ™j=t~ÆØ¹o¿ûV?æà|äáGáRù¢âÀrÙe—Éü!ßÿ½~E&Ê)§`ÐŽÅŽÒ™ + 3pÍóÜJ'`U>|ͧoÂø7嬳ÏÔë_~éU¹ûî»!ü·‹T™ ÞMTZS”Ýuõ¹€EŸ×ddt’›oºYMÈm&U˜sB0ø¸ÃTWçôõرc‘¬å5Y±b…baêê¶d…" eœc‡–bÓö'û¬I]ÔfRÕÂsïÞ½äÁ1cå’KG:c{+ŠN[ñl&<Ûüà?OªÝv­ÿ ¤Š5ÔY§Mß»Jk_ï¶éj³£ŽM;-fÅmI“hÓ·ã¢$UÍÆ†Ÿ'…ÞW§;° ÌBëçÓ~ÚÚ¼ÖH•ë9íÛœoÜ:½û~ÿ†ÎVdµ5’ÚÖæµ~].i‰Tùv¥]Ú)ئÖý–I•Ó$’2‡0b7©jX×[òÆi½Þm¹5þ*h..1t÷û^{í%tSy'!tHÏ:=î¸ã„äË”§ž~ZNƦmVVV³†¿óî;rÝu×5~¾¤Šú •kZÊÊÂMÏyóçI)bþYØcؤŠPíŽð“?ý´YÛèöÇbÜÝœpü ²º8õÄ@M±)™wîr¡ì—çacŸ¤Š›êo¼ñF‹BeH¿4Ó/£ $±ð;Ñ’È Kiã& TK®¨üܪ¡ÐCç‹mÍ9ÆÊ¶uRåk³O_sH•¤ªŸ’ªÔ”Tð*U ½ªWê³@Ö¶åEÅöÑøAÜ)ÀJgæŽ;ÊÍø7^—cA~‹ó󤯡 çŠ3½11¡Èúç‘¢ÂIKé‡]ùµN·¾p¤Y¯”µ6¡ÓÓx°TáÀßX®JJK€)[[S)±Ñ12qÜtX"pOsß͂µ²Ìþi¶_y¡`N8dZÉz÷îÙ¢žC+JøÍ›7IAQi‹;ô½êªkÔ7—eë¤Ê½ê˜Ú´aó‰ñ¸q¯ËÑØI ,ì¼iÓ>ÁyX† ÛGÉÕ'Ÿ|ìw?t®o©r+Õï½÷žœ~úép+|UHvØÁãÆÓ€GZ¤>ûì3y饗䢋.Râ½4¥»ËÓ˜Èh]2¢q׈.›$T¼‡‡åÎ;ïÔëF­n–(ÞK3¨îàZt8YÑ*gŠ’ß ÔåÇt®oPÆ(¼gu–¦éçNÊß®ý›¾çýCãú&Mš¤Äðý÷?ßÿ]3O2ƒ$IɃÒDž­>¿7Ý|ƒ”—â³± =ÿ1c¿cÝ™¥’~°œÐ£AÔIþ_ÇÂ!úÔ~ wÓ/¿üJŽ=îÙwß}•4&'%ˇ}¨±î†g?&‡~¸^ÍÖ‰DöÈ#ÔçKw~÷ÝwÂÅ`ø¹ç( ~ä‘h[hþ_½z5dó*\[‡Š 9 Ï;oxã½³~ø^>xÿCùuñ"éŒXDš«IÈI¢ùobFrvÕUWÊßþö7­û˜1 ¹L¨Óölà‚¸D¶¿?ü°$%%©_õ-·Þ¤g*pb¡rÄ——_z‰d:Êi§†³ç*šÉt{Ö²gï­~ø#U-)¢þ‰û?g©j¦ºD¶pK›ØXû_µ£w4#UxàÖ¶2U¡s3 À¼Ìû·B¬¶‹mÛ RÕ¢uÊý2ÇMlg…ª•.7mkíºÀº)©r£»Õ¾wÞ¸µgnžÛâÂu˜±à¶T¡–¾©G7k}À:s=Õ¹Ñÿ?ßýZq®f¼×qÉ¢ÁÐ/~G×8ç^§ì‚nõ=· ®¥mtÿk/´-i.LlFOqoèXptƲÐ;Æm¥¢.óÓO?©òß中1ýýúõs=×¥K´Vq¼×m©"A0±3Ô ©³˜x¦ .¼@ÝïˆÍåGôÉê]ôfãF±ÿu[Y#šUÇw]ÖÕ¯¿ù¦‰+ÙãÐqžôyU]¯ ›o 76;ä0u¤¬ñsJbà˜ ¬Ž‘öó± ÿÖ~Zª^ß ©r×Ù¬Ä|ž›Tµ¼B7êˆT¹‰•Ceü¤Š–ª&ã8Ûf„˜V8¿ÛOª@tHªúö•ùóæƒ³¤TAÑxö%Sìß–<¼û9(ŽgYÓbtÚQ#.‘àY5~îBY”]"u˜jÂ0½xNžâŽö€ E#¦*î{©ÈÊå,B•Ïj¸ümÜœ'IÉiŠ4ëTÆ"ñ9‘“Oxh¤{ê¥Ö¬÷'2 …¬²EQç4}ú—°"] ÒjU¸C“ky •öº–üž|WjWú”yê…Û2ïΘAfzaSRE Ø»Û¸Rh?†á‡Oç.Ñie5Â×ã^ׯ£›µ—¤ŠÖª‡~$`œºÙ‘);±UYqV Æw¨¥ëR ¯¥ï'-Uœ|Xh‰¢•È”£à^IÂA¥™îyüþÝwß•#FèŽÌ˜1cÔÐúÒªeJ7Xi¥:ùä“Õ|?lØ0%.¦ôêÕKûu!;ñÄAbáSúOµhÒŠBÒâgÂç‰Ø£>â5J …ùŽ“ê·ß~­ƒŽÏ~衇€Ùp­{Ê!UÀkÒDªáçÊ' òu¦ZO‰%­E$_ <ùä“t‡`êGS•T=R5úŽÑJBN„/mbb¢NJ5Øxûí·åÖ[n…Ïí/~RÕ£gŸseÚgŸÊ£ï–'žxLöÂaÕ´.žxÂIJªˆ©ZªþKÕcCžÞRKJ²J¬Ï>ûl•]CIÕå—_ÞÄRÅ]bùöä)röYMïýï$¹¡5òsÈÙ”)Ž¥Ší‹v>óÌÓ Ô—Ãðô}O%YÏ=÷,ˆ[Ó¶Óÿ—$Œ²À]Ä•+—Kö–ì&¤Š=Fbõúëã%þÁgŸ}V3bõ¿CªÜ¹oþÙšŽîÙ»ZÜ^å¶Ùäãÿ@—_ß?t–¹6š¹¶þØ]ñÁßu¸®Uma9Çz#^Z Lq¯!Ûkp”ä­ÍùMcÛ·¦®gº°»žoÚå*Ó7Û×Í‡Æ IDAT½óßÊÒdž¢Û`K¦]Ž/N†k Šp@}\ï5^#æ ÷{ÿ¸¶µû¹+ÄJÄ*Ó¬íºæëx¾°¡ÔE|îÆsÀHŸ‰—"F$’Ž· ݺ®U7-Ä24àFuÕ:©Ú^Yåûè3ä2<™Ñ¤˜i—¹ôÖ@qhϼfºŸSC¬oÓ”¯ÈÌÌôë'Ú—MH$‘I›“7†RÅÛ˜¤Œ±Vƒ5f¬Û{Ÿ½‘L YM£Ûº ’TQßà#7Zi!2¤Š‹úõ vë­yóæ¢Ùl^ÚÊÜÁ}C‰Äêç9st%iꇣ† öTwÿøóO%]ܘÝgïý”PÑrd\ëÔBD9 v’Wøòo†«SQÊ2ÝQHLΩzHóŒÇ¦µ‰©2ub¼SKe¬J-YªZºÖMöKëxÛxëS5_okJªÿDºÈyܧo\úÖ(Öýa%K¬KMÈZ‚wÙv6ð †wï¾ÝU7KIî€0&ÇÀÃp ê›þÍ·Ð4*p¾ ´jQF؇KÖæÊ x]ûîW²:´žR.Ñ00y>²‹·®®J ê2U+Gj©“KÒ¦ï·ï2Á<üsHŸÎ /º–––!3Ò©‡EÂÊÐ 5•µ2yüLü;„É(ÖMkËMú±šrkkk|ïêf¸|ôWl,MW#F¶EªÞÿà}¹æškâuèarëí>Yóóh$á¾Wúv³¸xo«TmÃRõÅô/tǃDå¾ûîÓ]‘Waeq§qÔÇûê ùrvÉ{ÙùÛm©rÿ›<-/TÚ©ØREk ?£å‰n‡<ýÙZ8¸S³lÙ2ÿg¼æê«¯ÖxnZ£x*ã’Æ»u1>«­eeƒ@ðN€5.Užáy_ò—ãÆ6tè•ÇAr.“åˆ×sH•B2^ÎYº– ü¼|%)Jª`ýÙ´¹‘TM™ B¡¤*qsOi ç¨Ë¯€Õå9âˆÃ•”‘t*©!5ê ì2Ý‚>w¾KLL@{¿… ªUmÙ²å UN°!Ë /¾×ÆßŠó'µD‘`^}ÍÕð¼C>œê#U7Þ,¿ñzyìÑÇåišµkÖiŸŒ5J-AÄš…ÂoºQÝü†?S‰ç#<®ß“ {ý5Y¾ î˜8¹?üÐ#ønú¾Á¹øóÞ³é ±œb9Är20 ±$©b;IœizåÕWô½oÃvç]£ñ,àrË-Ú¶ýE´íÌ3Ï’ƒ:Xeá_Ï=£qvÆEÇ?ëãÌöóî;ïêóN>ådUjÛd¨|ˆðìÏV”O3·jìŸ.hicÙ0œ*RFóÜñ–²ÚZ\[ûI×Ö,UmÔÈw¼imB3 ”Ú„ÁØ0-CÙ.÷Ül¾7*°ûµmLÇk«Åg¡ðoÂmíBßëšðÕlÝlÄw³OпØTåU.o ’<ÞæãÆMúß]Õ&ÿnÚçœgÝíj¦‹¢T„Œ *äî[Üuä“\k“‰©ò+ÖCBçÓ'fÁ5XÓÝ x+RÓøÌm_¨õ2*÷àVˆù1ÊX {4V|½&@œèÒÊ£bgÎSaH¸BàÅCÙh¨¯Ug-7ݺn?©2^.Ûrcr„ª9F¬éÿüg†[;¦;Çe’óaGz•˜Ò„¬QŒ}ïà=Û&UuM´[£{àÞØäÜÞTëM-U4†{[¤ŠÃ~÷¿[GCGyÃ…˜¿u’Ñtœ¹Q÷í•«ˆ!U%%%HýÝ· MøÓEª†í=ÌGªqÖ`i¨÷D9 ˜œÑæû/®a\ÿCŒ©ºž@ŽÕ…wr^à37ø\!¥„qTã}1`èþ§ïuXŒc‹m.(ìßFy"i‰ñÖÈ‚EóôñÍ,Uf®ñ½ÜÌA¦.­YªŒ[/äÉlà7€¨õîKÕÂé Uˆ©*–„Äùðƒw°¹?@M‡¤hk…¤Ö¨¿Òk ï›6mšnx_rår _ÿ¶T ½þà…÷‘ç£z{kjŠ@–jäÀƒ†Áû#Ø^ H (@OMM–ôŽÉH"°TâÒ0JŠÕ5ȃìr¤S±ŠÄn.ýåÏC¦ß˜EÃT†nR#o´vÍZW[ȨÎ1k–NFkP“Æ»f(³æ¶DªHú®»þ(ÒÓµo¾ùºfÿ»õ6 Äñ8oó˜ºòÁO«ã[`·Fªüò¤›£>Rñp—™3¿†Õe,÷Áâsîˆ|öÙ‡pÿ»h’ïz73ç€áÕdSÉ÷HÖ§^]¿HrèNF%Þ$­ %è~Ä—ñ@f’Z9i9V!Që³(2ë¡óô`×= 3;ý”• ÓEòz>“«{ï½Ç¿kÉÝ#¦ˆç. -3÷ß?nrûÈ Ï“>äi \ìžñ ,ìØyéÞ¢d¡»f†g;=AuzÀôxôß—$Ï?ÿ,&åó`±z¸Ž—µk×"{âXØNyy×£;HO=õ8HÕà U—8Ý!‡q¨ô‚5n2,>ŽKÒ :—ã»ÛåÐCVbQ^V©;Pï¼óˆØ2wî|ª¯Q'§ÆŒ¹GûèŠ+®™| f ,S·Âv;\§jbŽÛ´zÝuW÷ÂgþùOdÉ©š2Ië5¿‡¿@c ßzë 9çœóPŸI Fç >°«ÜŽx¶Q£®Ä{î×6seá®ÝäÉoËyçп'N|Äél&ZÀN‡ n˜¼ëæ9çž-S&¿‹wR• R5FžD²ŒK1¦n¹å6X#{ÈUWÃRõìórù(.÷ž8ŽÖáž°bÒµñCÀŸÙH98Ð8ÉyäòòË/Êõ×݈vBák'QàdjvØ[Û½w«î‘b¬)îÏÌ;Úª›ëëæ,DN1ÿPæ| ¥^p{Ó]_îNsÕpvªYœBw½Ûøo*²øQC¹¯ê-|û•7  Qßè»Ì½´´¨¸h+ö-iam½×Õ/f® ¼Uñ5šß´>€âŸÇÚRŸ–Úi”"·ìùÞ¡DÚüÛ'Mì°&ï“ot¬^ 91V"f¤2Z¶óƒ#g\äÌsg«âÔŒT¹,Xª+1ØñFG‰r~\¤Le¿Ö¹…?ÍdÇg¡ 2G6åÙQÞšâã´ÇWGnv6¢ãv%æ¶ÆqÄ ÙWÜ6òÆÍT*ufísK°‰¢Â§v×—Îf†ƒ“[&Ü÷»?7í|>×À‚á¼BÉ¡>†Ê7q&g=²‹yk¡$9 ¨¿q …ã'e•ÚöO¨¶©¾¡†ö«&•ò©ÅM>ÛÚlhzµõdp4^iæçFë7·›ÎØ|¯³ó¿¨s³^12>¬šÕÞ 7…þpºÿ9¨Ç™1TVV¢îLNeJ£ûßÐK®iò6†šô…¢Ü„¤ùE4pn0·6ŽcúÁo22ºÁýúš@7üL½aüî>÷0uÿóyð¨¥ÊïfÞeƘÑi Fî5Æy¿ê:pÿÝc¡òæÄ k1þ¶1tÃl°;ñø1òÔ±Î=çuU3d”7º­B--=>ä}¢ Rå³öÜ~ûÝ UoùzƬêõ²~ÓÚ&X›?:et†¥ÊɘÝ9£+êàX”8.GÄÆùÒ´ÞÓÎø J$WÖÂR…\¿ûPÇýÏ踴dáä[Ÿ¼˜)ʇ>=ï‚ó`© }åkXª.ô“E3[-ǬÓŽ%´^úôé R…TéI¤¢¢Þ@aåÈ‚·ÔÃ2–Bê?$V[+$TŒ§bl½§ÜrʶQ¯¦qÀ‚uï½wËÍÐ ž=W¼ð¾ÛÞ]žƒèë­©-„IgT ìv¼^ÊŠct';B_[Wóé’“[ ¥´¬ ®…¥#„eÅRP˜+µ•uòÞ¤Y¨,îq‘ª¢¢"É¡B-’èg±ñ-40úçlàiŽó1¬×¿nq+rP躆?I]»®¸âX:²5ƒ!‹ ãÇÁjqˆÜÆN¥V‹fNp gÀƒ°¾À6î*qüèÃõsgW¸qØ5WYÝ¤Ê R>¶%å¶qìú oü8KAcq+¶÷4ý[wå\;2M3¹=Ñ} žÙý÷5ªQ¡jTê—¬¥‹„5K“ï[]Z^ü›Ö;.Ìi¶†9ˬ%ݧtnk7înE6@­3j׸UB’ûúÆ7ÙIç<à«“Óo‘ð`öRåÓúü¯3zŠ_ e°¹¼­ÞÝwÝZj§ÑªY7ù6×â]ü¿|Ì;s2êLß/ŸÕ݉'B«È0Щ&à[¸w…`ÄŽê|¨®/\ V`Ü3k•³ÎøÆ‚ŸÜ*^¡ ¦Ó Í4ݬˆž§®VÈØ‹ëx‹®¥M†¥#¡¨¿`Œ§ÛÈé*ÇmËx’ø³Þ¹à óÀ;fs|ï`¸k̾ ‡K=HŠžÙçA}t=3Ê™{]§n`Ö ·Ê ‹#—4™4–duÁÓX oMrv5|k?³½©‚ÁÉ_OÈ©oÈø"áæ îÆûé;á½Ø» *Q“ÆAéÎÉbFUàu'ðõ°¶:uTî#xj9§ºKyò¯¹¤ÊL¼Š½ÝÀTÝØD ‡®AÙÄ&¨£‹8.Œ¤Úf^2bcF°?á€Qã|wUì4C:¹æ†#1©Áy>ÇÇõ¶ÑEý„á a,ï¼;›™×ûI•_¶T®yÓmÁqôÆ™ÆMªÖ­[ïó–u/äF*-t£õˆeÿý÷÷Ç{1¹—3Ú7±S€Î\fÞç ­ƒv*¯vHÕGЬm|'û&6 õ<ÇáI¯X¸pyc»i¥Œ²y5:·øéšÿƒ‰‰ïýú2è¾ãn™ð6óusÈÁM6nZ׬Nü€n’ð:#ćóå¦qQí×,)Z'[±CnX¡(ýlÁ"lࢢ{ìîdÿó©ªxžÑÝ~jÞ ÃÏÇûÃúI•qiÔUÝ?WR(ˬ Ú9¬oßž²`áÏ’œ˜…£¡p”‚T±S³ÿÐ.¹x„fÿ‹‹M@&ô™ O'ÏQÇíé­­/ÆË·@Àb$­c„,û­d v›P'ÝeLl 2D]£‚ƒb$‡ý6`F`™ÿ㤠§D&¼ò¥.Tü8P?üð(h×:“Fr³©€³¨9ßa]Qeݰó-á8€¸•Ñ. ¾M ø26îõ rð!‡êBwçè;dò;¥©©g!KÇ8äܧûSdÒbct N.Æ|ŒI ×ú²®±Þœ`ÑW‹Ôñi~¸@ù7†)ÕÀ¦~ ž¯Ý9ó«™jêåî w=B°q‚shv8g`8 ™¡ÒO¡§eÅI«Ý8³¡ -Ä—”Š«*'cÃ?¼éïPOâf&7/­ÁUƒŽ}‹¢Ÿärð¡ÝQ‘áH§_íS°AÿðÞ°pÄáEx1§rûrdjŒ@j>ƤŠÓ*ȈC”ê8ù9#ŸiÆ=ÁìSŸnÀ>B=þ2A¿è(žt1¥òY^î(¦ðߦí”ãÂÂv„¨Ï1újLÀ˜ŸÅÆFá'^ró6ËXd¹ÿæ$Äþ Zƒ÷0žˆfá%'E…H¼âAk­SIÐðC<Øî:œ·‚úVWSqñ­Í.…Û-ê9;ðl< ÿ¦ëÉ„ Nüš¯uSÊ7!q h¿ ’ï×â›,8­ mKe%å§©å×Õp 7kØŒƒì–ÙDî3uÿübúØ ÃœPe‰~0žUbŠßŽk¯‘+¾mPýÒÕ?Ä\EÛÕNÞB£6áš L?ª«!Ç@­ûA>ÎÄ[\÷K[¾ùÎÐP,aq'3i=Æ3Î׃¸ 3\+”9Ô‡z°Cþù£RB¿uâXƒ˜ÒÔGÛ竃³áãŒA^ËU¶U©rêÎç„"iHEE•~FYÖ…濫…±R®Ê*ë«U‘}ªŽãdKe uˆÜ°0 µ±&8Šƒs*«ø£îHÅO}‹.ïa¿ps‹0cŠÏ¨¥K(E\ËX?¿‹¨ wþ“õ5… M{È`£›-óóV(Î:äsëT±o¬—ÖU|¾Â›w7 2ešû|ë‡Óެ¹×÷Ðù2`¬J:—@@âc$?ÇCh_ã3ß<ËëqBˆ®Ð U†tL®Ã³È’ìÈ>÷ì|p±TŠ(³:öù=ÆBjÇíû*Œ1íj6ÑÈ›ï}ŽðáÿÚw¾?TÞŒ7 Ç?u­`I‹ “ʪËJ©8;Xp®¥•‚nþLnÃ^{§þ¹Ö×~?úü;`ž‹Œ ’â"_Ýt­'̉WUõðLÀ8AGÖP¹ t¾ºŽHe±¡2 ®ÉÎ8â¸42c6 !3u4ó‚ÑØ>ýŒõôaoú‡Ö'Þƒëã«j±–y'îÌKp¾qú_ÑZ¥ýKâbøÕJFœ·X8ws7¼¬”V8;µ.ø@Óñ„y©²’óõ´‘ëtÊ,1bÿT"l‚ü–øùïU œ5‚VNtã×ÐÎºÇØ ³a¨ Ï<ª@ŸsÁ Å}ÜFBYÄ©³?àv¯af òÀ žH‘%Ù`ÆçQU7á;}dÁècFv¹vè\íð0§Ý¾ñ<Ø—RIËBîő;aøw-úžý¤)Õá>ÅdUÛ*¥°R{ܰlPùâ¸`{Ã#B0^€¦îh¾¹ÕìK™õ—u{ë-G÷Ûžrû·âþ þµ×àê9n„VU5õÌò×2F꥗_msJõo¿ /”óWã:ªó~Ìú錱`èªõÀ²港CHFt0nZŸpâéðjzD‰*ÆKbB tãZÝÐe_Ï™³È¯o™å{H€h|8¸u휡ã™ý¡ºìß#g-h\ãµ>¾¹r¢›Q(?Ívˆáž{vçSלéþŒíut8·“¹ð믙éå)›¼Ï„j¬ú ÿðÑ}¯é׿‹|ÿÝIŠë1…LÆš#¡v*\zñe8*êY‰ª¬®”ŸæÌÏþ‡ôò&'GA)Þˆ¡^öÜ+K~úñ7‰Kؘ€$ u‘tÍL’nYdÁüå¸ñ•`Žïaqq‘¤'w’ ¯~¥œkÅí£á~¶f@ädR æÛ |1Æøw]-“YÔBa¯Ý÷Tá¤BËçÁÒR†tf‰áÀìÝ»Ÿ,Zð+xHˆ¤¦u5«×`P…è톮–3O»MÒһț“îƒËØ2(É.P‘Hã^,µúQ—ß'C“7&<-ïøF¤ˆ‡…Cƒ`ê j=÷鯲fU 2Éy±ÐUJfÙƒF hz‡t½võªõÒ«û@$M˜w»A:Áäæh›“’R€A²¸­õ)j$°õp%ëŽDq ,EÌŽï/w~òóa-¬«Þ\0 ˜Tv0:’’4Ùß·fÝÉèØÙãd9²çq§«ÓÕ­w¯ÞȨ·J'@þÐ]É'¸ô2Ž®¦¦J6n\‡ä%ð…Å£ÖÊ(,¦éã`ùê´ì3!Taèó5ºãyæ™BÂËUá+„Õ'£cùíÏU Q’ˆ÷WV,‘>½HnN¹äç6Ȧ-Uú>º0v„u/ Ó‡šÄ’»WŒâáÓt¥ËÍÉE¿”é"Â`žeÁ6ôéÓG}¥YXºšæ!ÕeGºÇÔš'0Jã›""¼òÜ¿n€Ë`_Xçb}óƒ¬4X³ ½Í€D¼Ÿ˜ BÆf¤õ“Ö&*µLòA3ò€ô`´´4µÖQvIˆiÅ¥¬°o(+tǤ‹­C,¡‡Ô4&„©B¿­“CReØ>eåªe8¼ÖÕàX9÷Ôä«ïÇÈoKWé}ɺ,È/Á8댊U¡O@låãÄÙñ÷hŒÍÕùùú7]t‚✗¢!A¶N^×¹Kgý÷ªÕ«°³ÓY~øñ+´!Rúc /g ˆç*9ôðÃTÑY¼x±ô@p-û‚÷jö&ŒI/”Ü ¸Á”ƒP›XHº’²¸˜r¼³z"™Ïö¢+eXJ¤±iœÈy$Ï*cšÎÆ÷™õ5?ÊË9™S‰À*˺¸}ž÷Øc•_>¯¼¼AÏ]5ã(1j‘À¼ë_7­'ä=z¡Q:f˜-ˆ2µ){£¤§aü•á§DeVɵkÖÃ"ÚWÛÅ⢢rÈ^™Ê;µ/Ê:ƒ¬)sTR¤Ä©¥Ózì`ïE_b.…›ntL¤b:®¶lËš5«0†=Jf)sl?ÛÆß”k~%¢¼¬TSíóvþð<Ι$Ñf—ÖÙu®S…9ʬc]©Òq®çÒµ´u#¡+ga¬‘òì_Êe¯²ÊI~ˆ•û”äDàX«÷Á²q£ñ—„¤TdP-б™‚º@.©ã®_»ZÇs5櫽÷Ýøë†)û†ë…nò8 ÌnëŠQàéMAü²07óûÅ‹—`¬7`.ļ‰ï8'£Nõ ùºòBÈ]4ÖÞø.rWŽwP#c[·÷H`œ¬¥ÜŒèܹ3Ö¿2]³vöëGyà‚5¹@å…ó,ç]¦Ëæo¶mùŸË°žFKç®=4þ›2—–ÖëN%ðÀæEæbož Šð†Q× ÏY³&ÏEÒ€ß+¥²ë^E86—ãÐ/]Ð }ãˆä³ã®^’Sb±ôFhDú%îZK¡Ÿl”¬¬.ºX O¶§K—n:/eèW,Šø Áº1]ùé†umåÊU2ëÍ–ì-RƬeÁhK8ä‰xp¼Ìšõ3Æ!ô$¨_Ô1ˆOa~^ÏyBÎ~å¼’’œÌcåPl8›?ÿ\ ÙŠÁ¼-i1×”ÉÞ{ï!ß +ñÝ’“­ã>#££bŸ€8d®­”½¨HlJ"ž™óñümÉ*M•„츕Х8+¹QÈJôKœ¤$¦K&6ëÖ­_«™¢;¡ ÙÙ9J°ƒà.™·²»FE_F:š¹O¦9ÅSî8÷¾ðü=x~æë Y¿a³”WTã½ñXëa@¬QQ¡^kÖ?Î=”bEæø¿îÚûä€ýtæFnêjHBàfcYâ§«ærn–ÝûÀp‰†Qaã$qƒžÀyœmÜŒ$6œ{tCN“ë8dÕ!uÔÁ +Ô â1EÊyç\-ŸM{ئ! a¬Ö¥: =‚î½÷fŽ:êD„¼†5çWÞiRl˜‘ßnÙ’ yÄ\ƒÐ Òš:ìòSæÙ±lAæÈTŒÁôôTŒ¹Ù}÷ä’‹¯G2¬çKö¥ÆÜÏüúKÈáF‰I€1¤` ÖŽbôs)ôÂŽX c »,‡\%É=wÿK†ÙG®¸êdûÞŒwÄi=rròtî‰LÕõ’kÉm8ôªîÝ;¨w=lVüY$sæ.B8ÁtÕgÍæ›³–sípøœÍgSÍè*ì;Ó¿.þaw*Ùâ†eÄ0:*^Çû;”G9á½=z§ã\ÝFÔãÄ IDATh¹èü{àþ·—n’»i£sN³ºK£»lã§-¥Tor“ï#.”'_ŸŽyº ëäÜŸçŠgؽðÎjtzÜMEC ÐÁu²yc.‚`y`Âcv¿šBÉÌÊÀä‹Á¹ =F™+…–Jc9ÀGïÌ–O 0SÞ{C @EE.`Àv#0irò¡`–"M&Hx¨W'Ýа<§Xý !ØuèDøŸB@IÈV,_-+–­×É­ ¯\8péÙ;“Åïp!’W°\—’bXT°èäæÁ¥uÃbZå £SøåÑD+ñ¬%] ²R—‰Wê-¿.ÌÅû¡Ügc„‚„ã9õè¨HLÊáRS\+¥ Ø%iJ˜Ì«ë«}înÎNw·h©v;t—ÓN!ãÞxJV®^¬XÅDÇjÅääTUJ©XÇÆ`°mÖ4ñùHu ìH”Rð¾Jø„®^µ ÷¥¢¾áPÁ}%gs)ÜA×*aØsN¨ûø¼?¬3?cW2Uönƒ;¡Nä¹ç§á]áxW?Ú"tj„äbÓ£:9„]­åXLcñIê²° U<äÅ YK’Eó×*ìÚd¨ g¦•CqŽÆ"€I¡b,—½TËË-–N]“ó‰µËWàwœnWW€ Õbþ¢K = ´Ÿ7a¸1Á’gCy_Nž³áÀÍ‚P¬ ÙìS*/4‰ÈëþûïƒÍ”|¸š)© åœõ¡UóëOå“G ÄÇ%s2*àaØpKS¥¡òŸŒàeöO> ®'1 a­Å\IKMÈ@"Ö¨´tnæeA6*åë/è"N«gÇŒ4œû7@>ÿb6`zbþ¬Çz-sIª•¹:'qèÙ3 ʯ˜ÃAB¡„a¾ªñYƒ9.Œ‰É xn.2­ö¥¿gËÒßÖ`C›fXk¨¬D‚ì'%¥A˜¥$šÊ°Ó^ÌÑè§N ð\ÿB!©§BÌ3gv06€X BЭW–Ìÿe½ºìgfv¡¹®ù…õ*Ê 7A²z À¹ƒ‹ÔµŸý™”‡~£…2_T­zp9u^*œèǤÄX|O2!½úôR}a>6=¹öõí×W3û®ZmŒsôÛ_§Î¥ŸYòÖÜ®÷´®(9gò…cs kçŠk0WôœºZúãy••¥²xÉ´DŒÊ"°‚Â|ôg²*”Ôβ±ž€Í²>òù§ÓU&ú 70¸&®Z¹ï-Ö¹!8„ò™-Çßx–ÃC'¤Š|qºü0‹dúG|:Ö$Œ!¬% ³åe܈9‘­«­€¼Tb|5HFçTÌ]‰ J<¿³ºA†DT–{” ‘ø¥×c?T¦~0Uõ¢d̯)©‘ª r> «fKe¿ý‡a#m•$ë\\ßP&q õ8/'FÊK@úVçàÝX7òZ+Òi7\_]%+Vn@æÚ5 å$ äžž@{=‘ˆlƒ&ˆŠM(‘âÒlè»Ëü¹k€OÖ2%® ‰ñ:'%b\püWVcó) ã¦NõžüüR¬ w` TÌ$‰Ê+e’á$ÑÓ”ꉿÿöÆRoµæ-±Ó_\Ëù—Šñå—_%úï¡óÇ2çE‹¾ÃÚüºtÀPl6n$è}ï¼ûÈD6c{‚àþ©J:çnÎ[J&1§ÕÀs*6;è-Üø¢Ûi_{ò‰—ëúC%Œz"]Ï4Âu4ƒ.|I¯Ê×/ó¦Ë3^Å='™}eÞ/¿â=¸¹! º1ˆ æÝd{ãÚ Lé6™ý`ó†rõØâæÎ† kÑO\Ï“ÐÇ ÕVëX>ñ„3aºRI «ÀP‡‹F+q1œqdÂjb±›2™­í)Ì/VÙRB]1sél|ªžŒ_VV¨çi†BÖËJaL¨-“³Û}ÒIWlþcóƒWœóMæ@CHù›–.+¸9L–Hx¤qlÞxÓ ¸¾R7x6ñ®‰ ÐØ<ëÔ!2»B7;vL×ñж¦k_cŸóq.\7Ÿ„üwÔ1nȯãÂ^£«,Ãl $uS-Î)¼ž›‚?Ï™y~Uçäuë6«RqaÞŽw…BÆè5«!¦šÌî]ц8~æc2õõ¿ùÎl<Œ1² /â†ÔÔ©S È9³a–‰ßÔïÕìÝäÇRÜ ó*ÞáæBUU…n¶ƒTõÖ\L«•ØDÅÔ@áC ×/«0Iã<¨ð$m0&hûì; ^ž³[ŽÔë4#ê¿è‹‹aie„;žT@—,§E2¹‰Ê,×`¦õŽXðŠŠs1€#¡´gé9ã«ÏTù é‰Á.FZJ¼ì·ß¸>JÙLŽ[ð}¾duOW²–³ îÄÄB˜³±˜‰ôA¬Hr |åø ¶2M~_ºŸÁ×ωhÀÂA«Ù,»GÉßçCéÞäk MªN† ^L”ÕePn#¥"§JJ « d8Û JL5:žL]Íú°z‘'!ÛHRBŠ|ûÍOÀ‹6žÁ:¤$§«‰Ÿƒfx.ÁÁØY‰ Æ·gW(½á „y Xxò×ÊQGƒIi%¬U›¨ Wvd Ф-•7*ï§Â9'w Ôº£Ké: ޾Xø °›Ú³gáõ¦ÿWÖ™7~Þçý%@Ü‚$Hð¾¯ÝåÞ§N[²Î±£(™¸n'I&´Í4ÿ¤´µÓ™t&ÓI;u§‡kçª-Ø–¬ØŠŽÕ®´ÚK»«½y_ Á A€ A$úy^ÈŽ;UGãT{øýÞ÷{<Ïó}¾5^ãåÒ1Ênð\J|¯Yä½ê»ÉÔÕ›4QH™ Nš³h“€íw™C}‡)n#“Ùpa§¿Á{ì´ò«ªÃ3ÎójâÌ€˜úÌ¡Ãa3=å²LœóáóÆA¿`à ºínÏ I,MOwìÈ´©‹qþÒUæÁý¸9súisïÎ2… 請‹‹š0NPHLÉ \t†Ã#­¶ÙþôÆ}Îr€U uõë«LùðY ÙMù]óõo4s1UæEL]ðœ¹üÑEÓÚQeúêÍ7ÿÛeæ [i¬ÍøÄ,Ï¡@©··‹Ÿ?66Á{Ó¾«:’ÅsçÖŸiÓŽQÎ!Ÿ)·aÜR¦ SÐPD’¸B¡f’l”tÙ&™?ºÎ{ ‚ho›¡ÃÄåö­-Ì_š(L·(H§¼G‰‰5âÌ‚R¦Ö"üzµM `=M]Ä6ISÓ|ú=ÜA?3Y(taÑý“J¬ÉÌÒÈ1‰ó œá½ 9ÙIræ¼ö~·Ä@Î’5æ4¯¼òŒ¹}õº¹†D ±©`@èÁ“¤¨fA÷×A¢¨EÄ…¬½³*ðô[|>Š@ê[Ňj'»åV’4Ébg¯™QŠÓb¯ÊkîÝ{`0ÅÛ‹$ÈbõœhÙmíA€@mý’&q§½åç~Ý~§ym¯FQ…|Ô?ÀgYCš|çöŠß6@bfÔœ¨€8Å|a&)B±|$õX„Išv`ŠÎ©˜%m±¦ÐE%P!¸VŠA½º ›¥¢šç&SŸÞ^š4Š¥%‚?÷¨Hrèêé¶¹PF‡‚˜çQK±)fV3U€K#ƒÍ ˆw,ÓQ ¹"¨¸©BZ÷C¨ž!Û£4nbÓ²$…Fš„,€‚Øë‡÷Y‡)íu U¦€WÁý ©t_O/wjÜFßñÃ(ŽÒ¶¡Ø•DQú½Ä¦æX«m¤‚5! ¦ ,hξ¬öÓüÝM¸|hÎTh«1i"MŒó®kbŠœ5jHùýbÉÅêè°´²sÍM2^[YA=a~È®=±bmw¾égfiÚ£566ÎÑ8î_šcQÓÑÞjeliöî䊶YŽ65ÛBÀó&Éq4«BËïv©ðÏPPcuËw›e÷¢mB¥f­Ê«€R!×ÝÝMS󈸰®´>Éš³Ó°à WšÞyHa7Ç™ðÑH Ø3råÊGføP?91E œ „^rf•ƒáºjsòÔ“N%°JÞ1ÝÃfo‡¢mhèùÑ[?FýцL+ŽKg7±©ª;Ãg$'T3lM|Ãmn³ñ_j #æŽF•BdpöF\ ZÏ+Í{ñ‹ƒö~Bcuôìasùâ q£Á6²m€“¹­Ï#c–1n@]òÉÕ1Š$éÙÄÚqÿÉG Ñ U–œ£â¦†æI•Žõå½w´“ç9Gmí½–msòë·nFjâ]Å`{ælsªyNÝË©éqöí5šCGžm˜ä* BV@•Ýy’ø i{qõêmæŸ_"Î> @¬"Ï·PÐ'ÉSk\J,){ì , gÊ8vÌðÀ)“ˆ¯b¿Ms©µ WOO—yø`œ¸Øo.]¼V™Û®Ú5¯~ÙÍ;ðp^“4Qsé£%À·(±;Éó®¶Š1j5°ºÍM4+¦Ä8ñ(Í»ÙÝÍÐÕXÖ,Úä1™‹‹Ô2äx|†<]‘¾E¢¼»jš/?uÏç¯Ö憎Ž:~À…Æ+ |²ÎßÓK¬š3)r©dKUT %˜syp—FwS ¢sW Z$Jå]Ž=Îòï¿¿ÆÄüügïp~9ßbòæðÈ)˜®+VÓÒ Sx¶ ÌÍë+œ~+”ØLLw½rvˆYj&wóÄÒ°òÙJƒA^ßq$ÅçÚfRMyq¯Ò˜lQƒ ÷Z‰wz+ÉsÝ7Ý)÷î$øÙuü·´`ÖÖh<úÎ^êÑVîn¸×HÜŽq&OY­èÔÔcf®¾Ã}€à¨­±J€Ï-¿ëäû¨«ãümئւ0¯jÄpåv¨Å÷óÔ>'-“ŸÙÜæ3îQ{ÀØùÖÍ7~çÏ9¿ýV/òjÛPÙZñ8PMªX) Nw*KÃÿÎ;? /ý 5‰>yX ©¼%Bµ ÚË{`\“J¯›Þþ& Œ,Æ|^œ0© T÷Ëðç¯l$;DͶ–æДQ³DR›¨¨Û;k*΋ed©Ü%Õš«Ô }%-mâJÊæóÒ}D;&°0Ã'>6k8¥}ù“?þFu:’w`7çç<ë_ùGµ€ƒ¤"%†Ñ+ÿü£nSŽÐ ,®4UŸk‰ùwTÔ`é^|ñEËÚ¦êÌ“ƒeÑý.¶V9ÌñÀÚ¨ÌnUÖ…-ÚB¾L·.ÄÃçw˜Þž~+MÛDvÀP¡î¶¡º8» òÞnÐ 9ÜË;9\P‹ÈÞìœÈŽäRzèyèñæhÌ.VS×? ¤ ý(ÉcôPry ÌÑ#ïU_ŒfbˆâÆlbŽdDÑÎÃ,·èë,B$ÄKdb‰Y1Š€§Ÿ>Âwj$h$Ìf榧`ã¬}=è_ˆK7ê7m]ÈNÌñy…Œ§‚ö“êAÆh4$ÉXñ.§a´ÎZÖãÒÍyµ£VÒ'Y‚Ð&>‚æ«/¿‚‰Äwùìe$eþáÝz1;w¥³%fki‘dªçžoòþôVi°»nHl¸sá:n·Xâ*Ü¿hö‘,Í-ÐÀ!}[OÓðÛ(ø)Örµœ7ÑÔÔEjÐ2a. ±"O”‚c¿LÂIƒä®)‘*(j„d¢G¶ {Ÿw%‰“¤e%$)ŠBWa0uu&³ oz–eŠŒƒ} ©2s~4ÑÆ>¯‡¢é¡E¥Â° -­QÎþ´ – È4žï¼ó!A¹ß"ìJ&Jz´NlFМ?wÞ¢ÞbVï~vÛ>C ”†‡F‰Þ°…f¾äv ¤¦Œlš&²€§£ƒÏL"ó¢õ—ôIžPM1Ó>T )&z}°êüŒ>—M{{÷aÒ¼öÊóáLjµ°=H®×) ľŠUË!?”,g ®¬Þ¾ÛH’£û&¥‰k’æGl‚ !+ ¤ÙPƒg¢ ÙŒÕÔŸ:y'Èw-ê©g®ïVlÁŸ Pd9Ũ¨©ã“ø@DW‘Û*–k¾qÆ:ȹ”|CÏGMR”aïtŠæUB9¤ä/Ò»ë}ë ïìnÂÚ„¯xµ‚Ìí‰'Ÿ°ªäj°f‘] (ÓlI]iÈ½Š¯Õ5tnblˆÏY°³}[™Š‰X¯7ù·1 ë?³C¡=Éûòs/in )ž¿æ{vÈ÷õ Ñ‹ää:ÛPI~T@êÕ×å;ŠaA1ѤçƘç6¿¯ÑÞU =çÎ#?/ó\%MÏ׬ŒÖïm¡Q£8ÎV™…yÎ9õÎV# Noê€%T1Ã4þï¡d!÷r%zû#Äwd£ I¿÷¸{ViRÈ#ÙàÆwª i‹³»pqPc–rUÚ²,Ÿ² sôX?÷±èS¾{½yê 5si»xŒбÆ\¿Ž”|¯–»†Œ“æ:Í™êæzÍ£ûÈи‚Nê!2¯E˜ýVÎ^S3 W>Aœïá×¹Ù¢V(ÀÆÕÓ8œÛ€Èš‰‘õáq©• nßëqARù£'¼f†ÚdrbÛ¤²H±˜ESÓÞ;Pk6VsfzB²9±ÎÈÏFÃ4aý}õ¬½é3ï~@½åê'gݳ³ÌÃ#]ðN£éémåYiè6ìÙ¬¢ø:\4±Ö;ò¹b3wuñ# ²<¨ÕÞ•ÄrÂ9Üf%fKqîD¨÷U2-ØÎ«.òœÕÖFmØçïpÅRg™1[|ïlë´`€îäÏ@ûOu¿¥¬@æ¹;Cƒ{ {©ö”‰PŸhã÷‘eù,aÎUCë°¹ŽõÂ2#옃q )~â0µ€¡CÔŽu€}¸Wî h}üh–z¤‰|‹¤€ßës“kÄ­ï%UNr=có’$5‰m4ãÄô»ù I‡uu”âÖ§Ô+bèËÄ2rîéÓgÙqù&ùÙa†FúÌÇ—g¬ÜM€à2ÅbqÝŽ¥”É…ÇY]4<Å· a&ÓØ–ê¨$ö*’=Α“ü¶›‡‘’D™x*Ř“ç¥3•‚Í_\L~Ækï’â“£]š».žIÆŽuì וÉ×3Js83³@üJü¨E.NíUÚظ¨º}Æm;CÜHë)Ñ„YMÕÂú X)Ø^i†Â´+ê) ò³— ¤‰#ÌÂéã4f¼óØ¡sñj’Ÿ] Z½°Ue×¢¹ðäy6`óÙÔäA¹³V~³¶¾l2Ç ÉŒÀ³ÏZý¼73/½üˆØ# ¼ˆ“ÏܽÇN šª¡ÁQ‹}’ò²Y@Šyáܦi‡üé[wÌ‘‘QsòLÔ<»ÍwoÀݲdf§ØÕâ}ëöc³2Ф!’î˜-lNB?~4ÁóÈÛ"GÁ\¼ ÿô$Éœâ×ñIé c6[M²ŠøÔL ï~vf“ç ›–Ø$á„ÌÈ(ÈíN}s‰@ZM1¡Y¿Ÿa_Ð5Ãíœ÷Mžgˆ›Ê?[0Ó4›u¬:¼Æü…ªÈm(“UB!‰–Œ[v(FêÐMçilsü¬BçæE¿ï¢ˆ«#È BßNb ø¶Ð_ŸåþÍãHÃV›Ý"Ϙfd‹æä½©øËR0 æ¹&iØ6l‚òÌÊšù‰T3° С9í1 ½°à°0BkÛ;ƒ|ϲÄþjÖRç­žB}“ÀŸ¸Q\2¨A‚Eµ^Þ—^žÄHÑH€† A‡QõÓ8º)T»º;h:Vjxîü!Øwòà5~†—»´jí;“ãš @=g9D® Õ9qBh³5ÜÁý;¯ IDATEb‡Al_~éU ·Mšÿ·-ˆ$FÚ‰F^º±6šóÐ<ŠæPæ ÞB³5ã£ï/™›Ø{7…£š¾9Ø[XB¶::º(p§Ì _D:}³Ô¿4núJ\»ŠŸBÒœ¨°Ös:¨{,F?/¦‰ÕÞÚn zµÉM¡µa L’k|¿gq„X\´òIí˜Sáµ/$NÏ]lR’"]’¾ µdVj̯U–‚âòÿ4Iæå·Jš*Ðãµå ‹«™”Ô%³#yÐà`…½“ éÿÛÌSòÖŽöËV]¸ðMʧVÖ¬ïª¼Ú ÛE#§¹kò±Ï9á™k®v ö¬‰‹›†;DÁ¯¦m}C£Š[\-9«*˜d¤Pa°µ¾¶ißµ ‘מ?c&çpw–9mHÓ¹gYÎõ$]ȽüœíTj‘sž3§žàoRx­ˆœ2ÿî#³€sCc„»5>žÉäÂLÕ?K,$ À€Ê³ZHèÆŒ#>~è¤Ù˜7øÏ×üøïß#ž3÷Xs€ì¥BêÀ¼ÿî¬y+àN3ï>GlX¤‰§Y&fë^ ­V^»¨_²WÍâÌ2c,x# ‰sGì—0ï|pÅ||#næuÆ ã¸_Ú1‡(–"Áes 'O„ÅÍ«_ù'¼ƒ÷ìà· ·mØ=7³nU9‰.žIÅ ¦Jrpôp;ÌH;qx P³‰¤×oÜ ’ã‡ò|À¢ÍY$iýýHc˜;Ú¿0ÿe›ÍAÔ‚d¬ªa{¶uOBÈ 5»Ésô¸k‘«ß%Ÿ!Ǫ&Õ’#O„h8(ä‰ãê ¯OL0'M•ÆrêPWìïÖò® ¶)íê©5 KsVq3þx“3¼Íýî¥X4çž 9ؼFÎÿ2‡7½À@{aÔʯ÷‘$‡Å.ä t‡ŰQ“¤WgV ™W|q§‘÷U‹„>n†ú™Ñ½zDzŸµÔ"z[°=HÓ[ >Ç uS?¬X¯yôè–ƒj#U“ÇŸ½€!›ß6“ó¨7¼46?ñ¬ÁܺÆ!*#k% xjmAá^< ˜²lþîÍesøÐó4F)3C øê×^C2ËâšÙ GøùÚ/Êr[îrï` £‡b&3H3ä{f6Å ’hšZëmóöàÁ]ÌŽ77øsrgä=Á@»a¥ÅެÀ˜ë®‡h|4+§¹Î=òŠ˜óŠzAÀ²èãöþ 1ó²0¿fUIQfJûÔ1˜ö)³Ã÷Gn^}á@IÚtöµ›%·õÆÛ?ÿÐä¸×>Îf¸N5'õ ÷ZñF “ÚÀ@‹5pf:øÌ.ÌÂîÐP¨cŒsÍÊù¬t›¦Jù\T9‡4sËWâ\CeHRUr[bãÌóøcIÐPK4'&&tqqг²f:zêÍÕËʹ.˜®ÃäPìþÿåïÛ±òÄc£­|®}ËfÎM%aÖ¬*CLjV`9à  T&«°‡y‹`6Íý–ªÉiKV:ª‚^¹NRFÔ¸‘J«ÁÝÁ8l‹û#ÐÉ ÀúÄÓQ»O­æûæÍ›¶¾h–!þ·5÷qqxÇÛ(¶ò|'¯­}¯}œà< b¡7è5󳨑øó2KÑìY/Dí½ËýÌã· Yö]b`ܺNË;2„·ÁäÄ¢•‘ï>ǘ%>ûäóæçï]6 ë0‚Ü#ÝaÏ@cR.g’Ïôg¹à}EȽ¨H ªX}ÔÝ+€’;Ïýôp…üÚ‹ø9<ÔÆwè2?øñ]î!ÏŽx´oVx^í6®þéŸ~Ó<~f=ο±õl¥ÒS•ÊFRÐl±>/yµçúåÿ­ßgת¨uøåŸ©p}¶Å"H¿õÖOÕTÒTT’°½rÚ#©hpyŠÐØóJD×N!%nˆWv;i—É*ÁÆÛ]hàÿï5çŸj1¨á•…Ɇllªíž\n$R$ÙÅ.Ô›˜ŠS,†ý ËU¥{V‘ÓÔâ¶2 $î4}¢ë.Ì#ôwƒ¢Ñ< eK¿?Ë¥ßL/‘\×I|›°e)’ì2»Z[ùoI›Q <È ‡1x B¼…äbÙŒ‚>3h¾ÿ½< 5xB,ëHgÍWŒ ¹$ÊöömŠê(† ?éÞø£ptShlƒúRp°Új³ÈsBŽ>óÕúŒù÷ÿî‡v^Ëk3µÃ Z~‚¶ÓI1C²‰µV››4B‹ö˜S¡Îl©8™ò;)ì{¸üi‹,gù’U0R š5 ;{Eè˘›uÅMWËÖΟ?IbëZ~Ó£oÃF quIºÈÏþʯ½h—ÞÎ-0Â,dYŠñmN¢t»­±šÍ<ï®Ä¥k0[«ám‚“ 40L!› dAÎ<>ÒdM”\Nœì$`L03"¤dM0"¨—a"·3›4Uýf6df¡ Z5‰œ©ÈhÈ"ù¢Ö%ó°±z–Y„V ²¹¹i–ô‘Ë%T\ÅíÆ÷ƒUÜnž³ éVVd=nu®…= º‘Aô‚ê‡Iú”@ñ> Gˆ:9MÌ©SmHÁ(ÂŒ¹ÃÙxëGk £Ã³$ô=´§S|ÂÝð7ÌüÁ×ÙõÍ¿¤8ÌvòžÎÌðÜv¶…tÕXTLÃÔ[H‰bm`¡°;›)!æØðî3H^ÛDsµbQ·o‰‚·öõ!…L=Ûöž, ¼Ë ISs=ïbI‡Ð£0´“»@eNK.7j®H r¦Œ1lÜËîªÉɇÃΉ†„;;û~Ù4ˆ9Þ¥øÓ#F%—å’`¼Ìr DLnó!{ÑFÌÏ~šÆd6ÙÕaò tN΄frvö@˜!PC °JŽ'×D%ƒÔThƧýîešIFŽ;+5kå[j ††Û-º¹Ma³ÁÙÕw–KZê1HfŠC[[èÅ‘Ïìå)´ù{%ñ­EÙ'Ñ,3Õ‘²z@ëì”®?o£ÄòÚõ³Ä7I™)N>•¦_Ž••ÁY[¤ðó„fkFcqa£×‚Gjô¤ïWq¡ù¶ù/þÈÜtÏ ÜË ¸ifö”˜ÄI&PÞ¯&ñRPëÔT‰9 ÀÙÄNÍøäy§r”ü@Ï¡ $µ§Óa®^Ýs`ÿÓÜ 1)zw*PÕÐäAíj‘¨i®Iÿ]óQMÈgTTësúµ7¶¥bl¡¹²Š\AÒ1}O1e_zዜéûÄBÌ)ÎJöR ¬LÉRVººÏuhÄõ¿bª%Ñ‘Q˜¥I%9SaQGaÔ¨ù2¸”4ÿH-43¡ïéöª! Ï(À‘Ìjøy²BQÏ÷…^´‰^ [–˜¶ –#>i^«Xbw"LS€3-éîyA ÿ&8Dz€©F©PLpª¤ùÐ*ìÑœù£Ì´lS f@*aÿù{õŒr0mg†Ìó_>g¾õí7ˆõÈW@võû$õpVÅ\î$¡ÉÀ¦ö˜.¡§N’ïd2ÒØl¾õëœMË4Ö›ŸãòUXFÀE;gLâup¶[Û¢xÌ 2Ï{ù”£~óÔÑab—¡PÜ1&‘ñªª'(Z†&6(´Ä7QœlXô¶U³:Vж5Š:oKÖH` öPïJ`ç¦ z2ž8ZÅ\râÔÿø¬E®ÓšÞô LMUh™ü׃4ý ß5 .[&ÇÁÌç”g×,*ŠÈnN³äMuvÎngØœAq…¢=ŠLظ¥Œ5µíÖ¦ÑX«9¿Å¸É HåÑ _Êî È\‰à F¨XJ(`è Ô†üvvPJýw©A|r…$ç¨Aíé—%H¡ÿ˜³ÔÀb¾y‡šÖI`Í<¬›ˆUüh¨м`h8póÆæ‘ÄŽhgͲã°9vÒcV‘ƒ·t ›cÆ:ÃùÊg{˜§šçóaÂ.–º£½É΋%M­ËЪX´2Gµ@Så§i/B¹)„;`d¦§–¬;›ä„›)šxb¥j§biËÊÃcÔ#—._¦§ÅxH£r±3fÝ? 3´¸Ûæ0fp#›£Î›²Í¢ž q!€ k[sc`îÊÜÄš_#f½•ß¹{×|‰ÅïÈÛ¶ùܺWMMešÇÊоfX\Þ t¿˜ÆÖ–HÌ«bW#•+¢@ÎïÐ|'TŠÁ  ¾‘‹šm±AÕÎΘX(/j—}$²º«R-¨?pyãæu›‡uºzß](¾Ts†·ø^ûÜÝß6€ cGÌgïÂï2+ãåý8­šÆë ŸÞMæ+‡5%À)C/…“qf¸s0`ìuL>ÚÃÔ¢Ì²ÒøiÞ0 ÍnÑ|Z,X¦yY;c‰šGre94jV©l69¿VöÖÈ\ËC¬Ê”hnxV:¹rò$>Òiç‰jÂ{æÒ»jªÜæ_ÿÉŸ°#ò"ßy•q3[°AÝ]²¦Sc4wÝCæõ_ñ€;æ*ªœ*Î~ ÷ Ãg÷Îä±—N4ÚD®I‘f;¥묘· Ѐ H#OÜFU³ Ùw‘¬’ïߟDÁt‚ç‡d\ 0j5—¥š“1rÆ%Hð¶³"B€j“¸iŸ &ŒøÁÄÃÎ+‡­­Àì–<–Qü¯ 0»ÆtñÌ"õ>[ïä‘÷Ö.Ú+ê¢m>ïû‹&„qÉ—_9…ÂÈa~ø“÷Íž°­3†…Yëž÷*éqŒ¸KŽgþT3XŒ,PS727¨ZÕãGŠJã,IßÒÚš ÒOꊀ§™ù´‰/3ËK£ßD3]<P 2cVij¿Ésÿ¢eçÔ©1+YqýÿÿùÕÿü ãŒJ#V1ùªüóùL˜:û*l•æÉ*L´¤ %3É'd€à¥ˆ—þTtmù@Î|žJ‘Gaî¬.X‰ŽÔñã £ O®.ӀРÇFfR,Êà¡–ƒ^éø+î©•bH詚Ù÷3o°ºž°ŽRÒø;]s%d=È™§Ú9^Bæ·ÏKj@ŸçªÁQ£³¹ ¥îÎÁj·.>.Û‡Þ²Î_$ô#ÑzúÂs|äP?ãsï˜èô8.Éðâûo\7§Ïµ4ZyfxܰøL¼¦ ƒ=d[»9¡Ú$×Úà*‡”!Á•M$o e°E’ó9”d#Ä€a[kÌÎÕ„:=Ö‚”­Ý|ó¿~È÷jä%0E€‹ðœìÉfA¡«vÌS_è2ÓãÈÓ¸höB.EÉuÍ4UhX7h^=ß_éÁPûÀJß%´Äõ4€­m-°6ÚRêwxd€ËzîÙØs‹ErqtÎÖ“‚ª·¯™çUà×ûÍÇWÆÍC‚sä’¨{-Sñ¤ÕSÕ ¶Jn1dÕe‰Ãá$àº:šš$Á±zÀšÜ8nÎ\¶ïø ,ƒ‹3¿x‹@³ƒ«Û àhºÈ$«å_X…2^s ÁÀMäæiûééÇìjø Ÿâ•Z} [GSS“hÂ;ms¨¹ äBPä$ §(¦—M1G3MS%ó¡Qjb%=’¦øK_:A0¿áC9Þ¼Y™*»ÇE–t”Ù²ü)’|¼Û Á4 ºg>$ã*Ü‹ûkæù/=eÞzó"!Íp›³çµNz’Á¬¯2PÍ\Pw¡¨nß¶9ÿ$vÌ&݆ÃÃÕí uë 30WÈŒÒÙšªÍd«ùÞßྉ ²%&dlÍJ%*»8dÍ CáFVÕÜ‹$…jàê-‘ä_?ç¤b‡[M“=h¥w9ŠwÍ4IN&Í»>“$*HK’pq-XBᬦ±¡Õ v“àgï°£lˆÀÚˆù ƒâ¡QS¤Ââì‰ýPcÀn]ÏÔ ré!KG®ÙGY‘ ÑƠ̈憬-6³"­Hóv)¼¥§×`o/s?Y{qQs[$ðe¬ÕD8@ßTtÌ/h†DŠ´@òXUmÌØTSäf@¦47¥¹A%W¹©ˆC¸¼KqFŒG®èAfÛÞ2C䯈ùé›7y-&Ë×˼‚æSg™;pÛyhÔ°¯ZˆÔ_çAgaœßÞì2O^8Ìú’=$¶È·aÞ¶ó^ÓÀ»ÖÎI=sœÏ¹ð€bCUUaÆüÞ<Ã.ÂqX(¹>ª E~K\–ÁŠXSÉEý`$< ¤Ö9ä’jªeà‡©V¼- s Ù¸bbÑíÛ˜¢ÖÅ6«¬­2ÇNÄÌøä<³@ ê€v+µ«rÌR3GÛ°Ê$ñVKÍF òr¢óc|Ãy™@úºu ~è³ sAjâ#3…5AjFrÔÊ25?Žªå}F;57·ºV@¢°ubJÇ;/WÑRÌ‹ ž¢Ž Îào$™KãïTNJaÔ$S%™~èž*¯ÖÕ×Ú93Ü^±w]€pMÐcUóÌ`©‰²j/¬8€·f²5ó¬Q©‚ªN=Ñm‰,u`r±ÈIAAAÛÊ„lB#[+o]´ûÐV¡~OŸkBv3^œäÒ0Ü7Ú-.‡Ê³“Ö\BÅ‘N‰_—TzØè²ºÝ î/¾'ŒEP ‡䜃¤®ÏîJá¿÷ö!Ó²(’IRìàÏcª€>YÎÁYš¡‚ûìÛêé‹rh öÂk0>úîFfuþÜ“6Y¯$¦¸¤b¸hѶ&1o%¦ÛΉx =;E•(Fß+hIVˆîŽ;’í,Âl€Ðy×@üh @䨶…AÄ2ˆ†¬…­c?_²À³ç1>h0o|ïÁ½ F@:T'ÎyGiädË¥fÑÍ (Ó` f¤f§Ñ¼Feþ¦ Ín P‘ê"çÚm¤C1 <°Õ\>¡9O<Ùmf&±AÁÖ^‡zŠå-˜¾™éÓÊEnF“:Sp÷Þ-ÜuzÌ­»’Pœƒbø9RÅP´†ãå²ÒŒ”OÎBu««@O Hš-s†gJòŠBslëÖu Sž½P–¸µé_Yä;D9xÚí W³ärÑ¡éËnù@@ÿÐÚÒt‹Ñ€LàúuÖúÿ¯.mQ§à’\ÇL ¹Äoìd)ÎLØÖ NZ§yÏëXá¶ñ¾æmHÁ˜îðs…¾ ɯv%Ís/€lS¤¥pßûäý$³Tè¡ ~]¬¨®n„Ž/šëXÝ¿øÊWh"Æ( nò>Z¹t$Þä+X[¥Ia®F+4h+öv‹ u÷ÎÂHuÉÓï#`qDü»1¿¸ÜjRšù‚q²¬âàÐQ,Þ™}’ KÀ‹äxb§TäÕÀÖ©X—ÃPÏ*$Š.ŠB5-Š3I‚Z[{-Íß÷9ž Ò>‹œ*lMв«¹ŠBÊĽ*ƒÈ±ñ×ÏòÕˆeÒ%+‡°H&z‚9LO^xñY~ž†äyÆ¡ˆEÁe{\>`A³Î¯ÍÌ"ƒ½Ô³–k™ ±ã’jŽC`³‘C£æ¥—^áó€c±œÛÂé°R˪2fH„Î ÉRLÕ=of.J²·6‚²f%Õô+F4Öó|Æ„b?~¸Î¼ùÖ É Œ)HRVŒpxwë°m²ÂÖÙk–$’FG ‡œ #q_B¨âe1¾l‘b±W ¶¥hår¯¦¼y` $Oæ1Ò¨KJ¡D YIítd¨¡¥é2ÐxãnfwéïÛ¦8.É£æÑ4×%'Ãyîæ¾/絑™9I_C ½ƒ$‹;©3 â©C#)Öê!æH’Hoç¬æ] «ª5û&Ù˜d¦r/•4TNab¢dM¼”˜äÎâ¨å‘;›f­vlÓÜØÐa Ц¦æ-Š®QÞ×$nÁð©9÷y‘Š1’8ÉL×CÎŒd™JŠËÌùb!VNóÚ»»)sæD# vÌœ>uÈ,Î/›w1•Â^C±)öÔSÂ肟W­â4™ÆC •$tÚ{ØÕÇ]ƒÑž| ãÃ/s$ÉÖd-NšQÜƒÕ C¡9fdûš¸'ÔE•AêÐuVÆ4#­Û‘e8Ï-Çè… á~ù©w*g´ÊR¼T>“³ª Yµ7ÕNª¯N;‘â ,“ÁÌ¡Ñ(uÒ&uî‘ZãÒ`™¿Eä»2k)ò{0㢇l`S[CòÌä,"—ÕÝh@(3c”EA¡õ r-îoP£ª^Ãt‹¹¦ß]5_þòkÖ˜hañcŒ® „܆eÆ@ õÁÒØ¿üöÀ Œ>p~ýõç9NóÆ_Ϻ”®ñ 4b+/LÃ'&zuŠæ·we{­cNIDAT‘² ày á“!Ú‘á#ùwy¿IÆzÍrrÉÌŒ€ñ.k-’î‹8%EÀ<óŒÕŒÒþå  ]µCc”º“¦fæ’˜ ×sÑ9èë=eWň‘ëèÄNŸQ‘,9b„F}æ)¿©I–YË 8QÔ‘÷r1Nv™¿ú.+™`÷r»»BØíO[ d7ÏŠ W#2=­êÑXj˜ÙMjvÅÅÆfL׆ꨗÔâÕ €Q«ˆí"hÜJÔ.OΪö˜•Ìl*ní!_3(ÕóÌ_Ý¢`«±Ã×b ä¤!ÞHƒ.7óc$%¦•v;H†sPŽÀæZ0-$ü"Úòj.lË~ëþÜA$<˜Px@5ƒ Ž¹mIL¥–K£äÌ5RIJ"[p9Ói¿Ú. d¤dE{rJÖÒTàyÐd%rY*‹)vÉæû`ÓÎ(ÊD$ÂÀúþþ7(’e³mÖ—¯˜_û혹}/ mOÑ_}¤²XR-þmî]$8š'“‹›ä_²µÖg0!éœfktf5ó€%bø>ˆ»ÑÝ»·D&ÍiÎÙÌtÜ¢Äo4“§å´VÙ_Cbž$‡ªBg`u;:qHÌopŽi‘&¹HðbÂä¾¥ -Á,£Ã^À…Kk$Ÿ™^¦à׬„dr{Ôü”f|Z+¨Qüì³[v~PwR¨¦æK5Ë¢Á[9sn"ÇÕ¢änÌzÄ~íÑP»™):J’çÏîŒsn…Æ)€µŸLMpg7’u–lJF@ìíe°˜w·—çžÒt‚©ZX@QËhä®äA ~ŠA”{–⎟ ¯mš“”-ÂwÑP+f$*ü6ˆgµÈú$ÃIóÜôó›Ù‰'ùŸØ‚!tõBoÕ ©i”´Vã:æä$ï“M¼ž¿Ö´X†ô˜BNÒP1vÏÿ+t¯2ï…NG9•½@4p°ªj¦5r7Å XNmr¬ìiCÎÌ_B*±ƒÙr%Õ<»ùßþ?Ä|¬‹ÁgÍ[ÈæØ6F¶©R’åsÊPa—"‡ùß;+f'˜Ñt©U°Š‘œDÅ‚ÉPA?_–éZP.† xðÐé ÍÀmï6Ë@HBªd(%…ò—äƒydµƒC°Ð1í)†Å¸}g“œ#Qs; € ëEŸÑG¼ t°MhýÔø]’<ÒAÌ™ßÛ†šç;¨è«³ý¤Ú^¼ÍûDâ )ûg?ï°“ÝJ°wÚ8¤çä‚W±ªÂF6Í~Š]Ê_j3†OÒ˜½c¡sí“ÏÌù㯘ˆË(N\ªFMÚ;OD¡VM>yÈežfØüÙ7ošéÅ`Åé 0i“âVn>´KC#@+÷õÑÃi #±Ã\¨ÑMoaãÝ ›M‘¶ÉÚŒä爋'§¶/<{Yà#Àµ9óEöêÉ1›[¡Ö$7hH_ ™ƒ¼¹ÇóQø‚=¦°lÁ@fžüŸ'÷Üu÷°6‰}>'ƒäyHpW™-÷û#Ö´%€+% Ò½‹%3”ÖÕýCÔ\«•rÞÎC–­UÓ%ŒQÄØ;˜ì¤ÖÈÀ¾Õ`h îä*së÷‹è29Có—rF´÷ÓšÚ` 5ÆŠ ºåÖÌoÿRô–Ì=r^‰{áö AåäÉã>dZšimŽ dÖŸOΘ<÷^2[t (‘ÔÈ¡š†• ^˜‚£•Õ¬*(ÕOÓ|÷§•gRJv¦ùÝ[IœëÂP x˜mï#WOLaéOsªFÆC ’ËÑðÒɉTùZß±JÀƒLjp?¶ì(w'Ä eóá(zÀ¶&ãœsbÃþŒêDIyËüÃháÆªÆV¹L,½æ’u䆫©«Åô-±GÖmçw6xbkeÞЊº iÝÔä’­ïºe¯f™‘Œ(Œ ?Pe2dSc!Ó §SÊ ò¸vn©ÑÃIWæÊGm­nF8²æ•—0·¹ø c#ß1aãʱ£Ç̃O5{ù™uÍ!í{ÿçs|&d”žÚ`µùOÿñÏÌÅ÷~Nn¼ƒ[½£ÙËP;X€ÈZÑK~OýñäisóÓ›€©ë8þÑäF‘44\G£| &µÇ¼óî]rxÀ~ºÜ‹KÖÙ[å½Ö"ÉýYîÈ$×ú”;Ô2Ë1ÏÖÑ)7˸mäË[ÔGOЬlƒflmæÏQ³m e–2@ü9ÖâhÎ]s¬ûÔ3EŒVj€ŸêîÁE$î,Êe³–&öHj)s,©oÐðRVm&u‡ Uö^"4‘Ý=Ô¿NGˆ¸Àúš&LhL¢n 8[_CÎÛrÕw€ˆÂA`³v6ct6?Ÿ3kÌ_£~ÑÞÂ"MŸ<]œõ k(˜5=1À’®>óÞ¥«8,Ëhù%ÍëQ$ÛëÉYËjÕ‰æå$¿Ø#öKêÅ-;J@Ž©úÂKGyO•¡-}]ä’†2Љêp*i¥ÓšG¨ «öP¸xHzÎ Ö†JæÙçÚ8j4Žp0Ó 3H’®ÐÍ H®Z%.´†ÕÕ ½üŠ´ô7ЙÞç¨(R±‰]9·I6´Èš‰ÚõT2oim°CžõP%Š! £ /!PÔGÖqLØÁ^Y|îr°ûû»pSš ª,µLq§<À>í2Ðw-J&‘íåa»ËdÝú£{,"% k±ªBǧXg(>›Å¡¤.D× i«åP8‡UïÔV§Ð³èɘÛÈf8L0FJHÚï”…I÷؇q¯ýÇ$Œ2†Õ#–•'²“ãÇŽY‡. zZfǀ轋ÄVä“ o&LcµhÄ%QÀÐÿíÀbZó’1íýü|ÍAP˜ ßêç;Ÿ`/M™ÒË/|Õâim&“¼o¾÷ƒ¿6ŸÝ½oØ((K F„`¬f2½I‡?ÈØE.Ai›Y“vô«]-æÁøC³qDôµ ÷«“°Jèø»wæìäR»»uüY9Ò„± 8\Ÿ!Ôqļu`w¾@B¥ã<ÌNAÍ7²K„¿Cƒ¶^æ§¶´ „÷$W™h³Çš;h׌š‡\–ä€Þ'[U¬P–‰qd N’¹zlÁ³&zc5p‰Yж×sð} Š§N3Tü ÒÏ“g13Y$xL”Âð!Ü™@ת™++˜I q7šóL&9Ä68’§”d}ÏÞ"š¨'ž¶Å¡I/êëNTA6;7´o‚÷ESu-×5Ä»\³sK ýq+ dil|h”µ8V’›dÖZf TDR-¯ÔŽp;dý}²žDz&„¤»«çsshsLJdžA7.m¹ ^@Xn€º jF¶9/5¸HY ÒkÏ¿n6qFÛáÏ<÷üÌÛoþ¹9Œ©7Åü¯o}fvAG‹–’ƒÖ£ÇI0¬•S‚Í:͈ ½)ž=£v…±Œ¢œß±szšGX IS zçјŒ$yTCZgån*vE×ë(Xúꬲ!üy.BG)$HÞb˜$ÑÕ>#Þ 3ÍbÕ#!®‡Ö—Ýû û”dJ!»o´b|õlİœ:3 ¢µFa&VŒ½CZ f]f ’ÞRcS`ªÈ'ü³?HU”æ= £ª!ëê:Ì'ß¶¬‘þ‘à>»ögiVDˆ¡†³Å4©¹‹ðë”Nü»Ç‹QNœ¦Š¢B˜ßU’ Hae圖@Ó0V´áÚ(Ï÷ãý ‘ ³7!\ÆÄ4-Ã4É¥OMUì˜XéâÕLWfÖ* ڮĠ!V«"½²´—ÂLwMͲb¹ÕŠCeê¡Ù+‹Pò3’)¾/"‹ðƒJËÌH* ³ç{­K˜#?°¯$ÌúúgƒBH6é>V={’œ±jÕÚ}´E¾ Ñ®ótÓ<•¤*:çBÈ}$M­âˆ"©Ù‚ÙPbS3.¶Vrì-Š !š‘ºfÛ¼(¾ ýÛ)†ºÚƒý/è2ýŸ¹w[¬† Ä-í_Ñ(»Ü)à>YUUœÆf Aß±Å.9ŸžR¼ýèÀNž\#5ý¶ŒG4“+Ëq1˜Z\¨Í"ïb•öå ix÷?k Ã3ˆÆtŽ·íŸ‰Dš‰Eik5^Ÿ›Î±|û‚ÝÓç󲔆J.®ædÍ®ùäùyÞ£YlKžÞÖ°¨ük2 ðæ¬øa|g§1ºé¡ðZüa–•€S’Rj€0Ži¤´2pi–‰?çFÊ$—0½²VŽF÷ÙawÖ\|ÿ€¡kµÜÞÒg:˜‹7n!k¼ƒIŠ Ó ó›_c‡ß 6ÔÌO?Š)òÇÿü??£™ÀbVUïWR°R pí@ çÙ»F—VZ÷¬œÐÏökY9+·ÊÊýtnã¾ëó²Ä–ÂYEô@ÈJíZ4¯µ5Á&kƒ]`ex˜z#­6$SÍX—ã½¢œG‘fn7Xfìø‰£ìP»éŠMBàÙ$w_Öár6”«³]Än-þÖ.É®.Ø!Àf/g'M|Ð °†ó5d¯»¦Ë«Ù˜ÉyVSÀ°kI³ÏË^›Â<ÊŠcvÆü4ß›½Žš i$ÕÜNŠæ´²ë +éù„éiÏ™W^²© Žt’zy1À8#Κ‰æ yìæWx'Òa¯à«u4šÕ¤fêªpLqŸ‘Û…™³›á™hɰöºÁ—µ´¢¥å€rn ×Gîõ,ñ8ŠÑ‹î¤ÖÆp”ø5-^ÏQ—ÙZ¤ 5‡ž‡À ýS$.iæT’NÙÆ×5?Ì ÀJVM4àÐmbfÜ„(¾e¦&SXbQ0T»ë$û–üX1L;‡ô¬’(ÒèæóÌUµ5[¦ÛGnPÌl&†i ²XævÀíÖÓìilžÎšT2^B ÍÀ´,f *‚±‡ b\ ç`ÞÎ;¶´Èù°²ÌZn¤èBˆ%-4U)š´Œi(¶É]7®ß5O=seÇŠÍ%AžÙÄ}Õ‹æü…ó¬q¹Æ‚önJ~Æ,Új®¯í7Ì{¬ÚÅðãöú~ä~zæZº¬2T‡¡²[­x`PÌ‘AH5Iv˜¦«#ا¾ÖUL{h\<œå&¤ÊªÓêX ÒÞÖC þˆ÷&õ À5e#9ÓGM u‚vOi/­Ÿg™¡ÞŸG)4MM*gE”]iÜ6£Ä8jÇvyê `_ .. autosummary:: :nosignatures: :recursive: md_toc.api.get_atx_heading md_toc.api.get_md_header md_toc.api.build_toc_line md_toc.api.increase_index_ordered_list md_toc.api.anchor_link_punctuation_filter md_toc.api.build_anchor_link md_toc.api.build_toc md_toc.api.build_multiple_tocs md_toc.api.write_string_on_file_between_markers md_toc.api.write_strings_on_files_between_markers md_toc.api.init_indentation_log md_toc.api.compute_toc_line_indentation_spaces md_toc.api.build_toc_line_without_indentation md_toc.api.is_valid_code_fence_indent md_toc.api.is_opening_code_fence md_toc.api.is_closing_code_fence md_toc.api.tocs_equal md_toc.api.remove_html_tags md_toc.api.remove_emphasis md_toc.api.replace_and_split_newlines md_toc.api.filter_indices_from_line .. automodule:: md_toc.api :members: Exceptions ---------- .. automodule:: md_toc.exceptions :members: Types ----- .. automodule:: md_toc.types :members: md-toc-9.0.0/docs/assets/000077500000000000000000000000001460560256400151455ustar00rootroot00000000000000md-toc-9.0.0/docs/assets/black.png000066400000000000000000000002501460560256400167240ustar00rootroot00000000000000‰PNG  IHDR@@% æ‰ pHYs.#.#x¥?vtIMEå "Öf¼tEXtCommentCreated with GIMPW"IDAThÞíÁ  ÷Omðn0@’øIEND®B`‚md-toc-9.0.0/docs/assets/blue.png000066400000000000000000000003231460560256400166000ustar00rootroot00000000000000‰PNG  IHDR@@% æ‰ pHYs.#.#x¥?vtIMEå 0!ŽØÅNtEXtCommentCreated with GIMPWMIDAThÞíÏA  Óþ5…7h@%“Ï:Ï Ü[P‡FϨIEND®B`‚md-toc-9.0.0/docs/assets/green.png000066400000000000000000000003231460560256400167510ustar00rootroot00000000000000‰PNG  IHDR@@% æ‰ pHYs.#.#x¥?vtIMEå 2’ ætEXtCommentCreated with GIMPWMIDAThÞíÏA  Óþµ7h@eòZ'·Q†}°ÑIEND®B`‚md-toc-9.0.0/docs/assets/grey.png000066400000000000000000000003231460560256400166170ustar00rootroot00000000000000‰PNG  IHDR@@% æ‰ pHYs.#.#x¥?vtIMEå 3Žƒ$ÌtEXtCommentCreated with GIMPWMIDAThÞíÏA  µôÕ7…7h@'©Ï¦ž¸·‹WåÝ6/ÏIEND®B`‚md-toc-9.0.0/docs/assets/orange.png000066400000000000000000000003301460560256400171220ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞbKGDùC» pHYs.#.#x¥?vtIMEæ¶n¢áeIDATxÚíÐ0 ùþ9¬©=.D ¦39ìå8 @€ @€ @€ @€ @€ @€ @€ðƒýÐ$œ"IEND®B`‚md-toc-9.0.0/docs/assets/red.png000066400000000000000000000003221460560256400164220ustar00rootroot00000000000000‰PNG  IHDR@@% æ‰ pHYs.#.#x¥?vtIMEå +øuÛtEXtCommentCreated with GIMPWLIDAThÞíÏA  Óþ5‚o7h@M~ëR…Ç4ôIEND®B`‚md-toc-9.0.0/docs/assets/yellow.png000066400000000000000000000003231460560256400171640ustar00rootroot00000000000000‰PNG  IHDR@@% æ‰ pHYs.#.#x¥?vtIMEå 7õÑ”tEXtCommentCreated with GIMPWMIDAThÞíÏA  Óþµ7h@ÍäµNn c~h­ˆ’IEND®B`‚md-toc-9.0.0/docs/conf.py000066400000000000000000000131361460560256400151460ustar00rootroot00000000000000# # md-toc documentation build configuration file, created by # sphinx-quickstart on Wed Dec 27 17:32:50 2017. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # r"""conf.py.""" import os import sys sys.path.insert(0, os.path.abspath('..')) # -- General configuration ------------------------------------------------ # General information about the project. project = 'md-toc' copyright = '2017-2024, Franco Masotti' author = 'Franco Masotti' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '9.0.0' # The full version, including alpha/beta/rc tags. release = '9.0.0' # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.coverage', 'sphinx.ext.graphviz', 'sphinx_copybutton' ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'default' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_book_theme' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # These paths are either relative to html_static_path # or fully qualified paths (eg. https://...) html_css_files = ['css/custom.css'] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'md-toc-doc' # -- Options for LaTeX output --------------------------------------------- latex_engine = 'xelatex' latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'a4paper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ( master_doc, 'md-toc.tex', 'md-toc Documentation', 'Franco Masotti', 'manual', ), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ( master_doc, 'md-toc', 'md-toc Documentation', [author], 1, ), ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( master_doc, 'md-toc', 'md-toc Documentation', author, 'md-toc', 'Generate table of contents for markdown files.', 'Miscellaneous', ), ] html_theme_options = { 'repository_provider': 'github', 'repository_url': 'https://software.franco.net.eu.org/frnmst/md-toc', 'use_repository_button': True, 'use_download_button': True, 'use_issues_button': True, 'announcement': 'âš ï¸ starting from version 9 all the functions are only accessible via the full module path. For example: md_toc.build_toc(...) is now md_toc.api.build_toc(...) âš ï¸' } html_baseurl = 'https://docs.franco.net.eu.org/md-toc/' pygments_style = 'default' html_last_updated_fmt = '%Y-%m-%d %H:%M:%S %z' copybutton_line_continuation_character = '\\' # Epub. epub_theme = 'epub' epub_author = 'Franco Masotti' epub_theme_options = { 'relbar1': False, 'footer': False, } epub_css_style = [ 'css/epub.css', ] md-toc-9.0.0/docs/features.rst000066400000000000000000000267321460560256400162250ustar00rootroot00000000000000Features ======== Feature tables and comparisons with other similar projects. Listed features correspond to the latest versions of these programs. === =============== Key Meaning === =============== ✓ implemented / partial support ✘ not implemented ? unknown P feature planned === =============== .. note:: These feature tables might not be up to date or accurate! Do your own research. If you find a mistake you are welcome to open an issue or pull request. Inputs and outputs ------------------ .. list-table:: :header-rows: 1 :stub-columns: 1 * - Feature - md-toc - `github-markdown-toc `_ - `markdown-toc `_ - `remark-toc `_ - `markdown-it-table-of-contents `_ - `gfm-toc `_ - `md-toc-creator `_ - `mdformat-toc `_ - `git-toc `_ - `markdown-github-bear-toc `_ - `mdtoc `_ - `markdown-toc-cli `_ - `toc2md `_ - `github-markdown-toc.go `_ - `markdown-toc-generator `_ - `make-toc.sh `_ - `markdown-toc-bash `_ - `md_toc `_ * - Indented & non-indented list - `✓ `__ - ✘ - ✘ - ? - ✘ - ✘ - ✓ - ? - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Ordered & unordered list - ✓ - ✘ - ✘ - ✓ - ✓ - ✘ - ✘ - ? - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Anchor links & plain text list - ✓ - ✘ - ✘ - ? - ? - ✘ - ✓ - ✓ - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Reads from stdin - `✓ `__ - ✓ - ✓ - ? - ? - ✘ - ? - ✘ - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Inplace & stdout - ✓ - ✓ - ✓ - ? - ? - ✓ - ✓ - ✘ - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Non-markdown output - `P `__ - ✘ - ✓ - ? - ✓ - ✘ - ✘ - ? - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - List marker selection - ✓ - ✘ - ✓ - ? - ? - ✘ - ? - ? - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Newline marker selection - ✓ - ✘ - ? - ? - ? - ? - ? - ? - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Universal anchor links - ✘ - ? - ✘ - ✘ - ✘ - ✘ - ✘ - ✓ - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Last TOC update string - ✘ - ✓ - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Remove TOC marker after inserting TOC inplace - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Detect differences between existing TOC in file and newly generated one - `✓ `__ - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? Filtering --------- .. list-table:: :header-rows: 1 :stub-columns: 1 * - Feature - md-toc - `github-markdown-toc `_ - `markdown-toc `_ - `remark-toc `_ - `markdown-it-table-of-contents `_ - `gfm-toc `_ - `md-toc-creator `_ - `mdformat-toc `_ - `git-toc `_ - `markdown-github-bear-toc `_ - `mdtoc `_ - `markdown-toc-cli `_ - `toc2md `_ - `github-markdown-toc.go `_ - `markdown-toc-generator `_ - `make-toc.sh `_ - `markdown-toc-bash `_ - `md_toc `_ * - Max header level in TOC - ✓ - ✘ - ? - ✓ - ✓ - ✘ - ✘ - ✓ - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Min header level in TOC - ✘ - ✘ - ✘ - ✘ - ✓ - ✘ - ✘ - ✓ - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Skip first n lines - ✓ - ✘ - ? - ? - ✘ - ✘ - ✘ - ✘ - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Include headings regex pattern - ✘ - ✘ - ? - ✓ - ✘ - ✘ - ✘ - ✘ - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Skip headings regex pattern - ✘ - ✘ - ? - ✓ - ✘ - ✘ - ✘ - ✘ - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Skip headings based on a marker - `P `__ - ✘ - ✘ - ✘ - ✘ - ✘ - ✘ - ✘ - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Skip all headings before the TOC marker - ✘ - ✓ - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? Remote usage ------------ .. list-table:: :header-rows: 1 :stub-columns: 1 * - Feature - md-toc - `github-markdown-toc `_ - `markdown-toc `_ - `remark-toc `_ - `markdown-it-table-of-contents `_ - `gfm-toc `_ - `md-toc-creator `_ - `mdformat-toc `_ - `git-toc `_ - `markdown-github-bear-toc `_ - `mdtoc `_ - `markdown-toc-cli `_ - `toc2md `_ - `github-markdown-toc.go `_ - `markdown-toc-generator `_ - `make-toc.sh `_ - `markdown-toc-bash `_ - `md_toc `_ * - Works offline - ✓ - ✘ - ✓ - ✓ - ✓ - ✓ - ✓ - ✓ - ✓ - ✓ - ✓ - ✓ - ? - ✘ - ? - ? - ? - ? * - Remote markdown files - ✘ - ✓ - ✘ - ✘ - ✘ - ✘ - ✘ - ✘ - ✘ - ✘ - ✘ - ✘ - ? - ✓ - ? - ? - ? - ? Other ----- .. list-table:: :header-rows: 1 :stub-columns: 1 * - Feature - md-toc - `github-markdown-toc `_ - `markdown-toc `_ - `remark-toc `_ - `markdown-it-table-of-contents `_ - `gfm-toc `_ - `md-toc-creator `_ - `mdformat-toc `_ - `git-toc `_ - `markdown-github-bear-toc `_ - `mdtoc `_ - `markdown-toc-cli `_ - `toc2md `_ - `github-markdown-toc.go `_ - `markdown-toc-generator `_ - `make-toc.sh `_ - `markdown-toc-bash `_ - `md_toc `_ * - Provides CLI - ✓ - ✓ - ✓ - ✘ - ✘ - ✓ - ✓ - ✘ - ✓ - ? - ? - ? - ? - ✓ - ? - ? - ? - ? * - Provides API - ✓ - ✘ - ✓ - ? - ✓ - ? - ? - ✓ - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Tries to follow markdown specs literally - ✓ - ? - ? - ? - ? - ✘ - ✘ - / - ✘ - ? - ? - ? - ? - ? - ? - ? - ? - ? * - pre-commit hook - `✓ `__ - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? - ? * - Active project - ✓ - ✓ - ✓ - ✓ - ✓ - ✘ - ✘ - ✓ - ✓ - ? - ? - ? - ? - ✓ - ? - ? - ? - ? md-toc-9.0.0/docs/index.rst000066400000000000000000000004071460560256400155050ustar00rootroot00000000000000md-toc documentation ==================== .. toctree:: :maxdepth: 2 install api features markdown_specification rules/index pre_commit_hook meta Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` md-toc-9.0.0/docs/install.rst000066400000000000000000000006451460560256400160500ustar00rootroot00000000000000Installation ============ pip --- #. install md_toc via pip .. code-block:: shell-session pip3 install md_toc --user All the necessary dependencies are installed automatically along with the program. Distribution packages --------------------- Packages exist for Arch Linux, Debian, Ubuntu and Nix. See - https://repology.org/project/md-toc/versions - https://repology.org/project/python:md-toc/versions md-toc-9.0.0/docs/markdown_specification.rst000066400000000000000000000367001460560256400211250ustar00rootroot00000000000000Markdown spec ============= Introduction ------------ md-toc aimes to be as conformant as possible to each supported markdown parser. What follows is a list of parameters and rules used by md-toc to decide how to parse markdown files and to generate the table of contents. Compatibility table ``````````````````` .. |unknown| image:: assets/grey.png :width: 16 :height: 16 .. |none| image:: assets/black.png :width: 16 :height: 16 .. |low| image:: assets/red.png :width: 16 :height: 16 .. |partial| image:: assets/orange.png :width: 16 :height: 16 .. |good| image:: assets/yellow.png :width: 16 :height: 16 .. |most| image:: assets/blue.png :width: 16 :height: 16 .. |full| image:: assets/green.png :width: 16 :height: 16 Key ^^^ ============ =========== Color Meaning ============ =========== |unknown| unknown |none| none |low| low |partial| partial |good| good |most| most |full| full ============ =========== Status ^^^^^^ ======================= ===================== ============ ======================================================================================================== ============================================= Parser Status Alias of Supported parser version Source ======================= ===================== ============ ======================================================================================================== ============================================= ``cmark`` |most| \- Version 0.30 (2021-06-19) (GIT tag 0.30.0) https://github.com/commonmark/cmark ``commonmarker`` |good| ``github`` \- https://github.com/gjtorikian/commonmarker ``github`` |good| \- Version 0.29-gfm (2019-04-06) (GIT tag 0.29.gfm.0) https://github.com/github/cmark-gfm ``goldmark`` |most| ``cmark`` \- https://github.com/yuin/goldmark ``gitlab`` |partial| \- Latest unknown version https://docs.gitlab.com/ee/user/markdown.html ``redcarpet`` |low| \- `Redcarpet v3.5.0 `_ https://github.com/vmg/redcarpet Gogs |unknown| \- \- https://gogs.io/ NotABug Gogs fork |unknown| \- \- https://notabug.org/hp/gogs/ Marked |unknown| \- \- https://github.com/markedjs/marked kramdown |unknown| \- \- https://kramdown.gettalong.org/ GitLab Kramdown |unknown| \- \- https://gitlab.com/gitlab-org/gitlab_kramdown Notabug |unknown| \- \- https://notabug.org/hp/gogs/ ======================= ===================== ============ ======================================================================================================== ============================================= md-toc version tables ````````````````````` Key ^^^ ============ ============================================================== Word Meaning ============ ============================================================== ✘ not implemented C Commonmark G GitLab modified Redcarpet ============ ============================================================== If a parser is not present in a version table it means that at that moment it was not implemented. Status history ^^^^^^^^^^^^^^ Version 0 ......... .. list-table:: :header-rows: 1 :stub-columns: 1 * - md-toc - ``github`` * - 0.0.1 - unknown version Version 1 ......... .. list-table:: :header-rows: 1 :stub-columns: 1 * - md-toc - ``standard`` - ``github`` - ``gitlab`` - ``redcarpet`` * - 1.0.0 - unknown version - latest version - G - |r1| Version 2 ......... .. list-table:: :header-rows: 1 :stub-columns: 1 * - md-toc - ``cmark`` - ``commonmarker`` - ``github`` - ``gitlab`` - ``redcarpet`` * - 2.0.0 - latest version - latest version - C - G - |r2| * - 2.0.1 - latest version - latest version - C - G - |r2| Version 3 ......... .. list-table:: :header-rows: 1 :stub-columns: 1 * - md-toc - ``cmark`` - ``commonmarker`` - ``github`` - ``gitlab`` - ``redcarpet`` * - 3.0.0 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| * - 3.1.0 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| Version 4 ......... .. list-table:: :header-rows: 1 :stub-columns: 1 * - md-toc - ``cmark`` - ``commonmarker`` - ``github`` - ``gitlab`` - ``redcarpet`` * - 4.0.0 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| Version 5 ......... .. list-table:: :header-rows: 1 :stub-columns: 1 * - md-toc - ``cmark`` - ``commonmarker`` - ``github`` - ``gitlab`` - ``redcarpet`` * - 5.0.0 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| * - 5.0.1 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| Version 6 ......... .. list-table:: :header-rows: 1 :stub-columns: 1 * - md-toc - ``cmark`` - ``commonmarker`` - ``github`` - ``gitlab`` - ``redcarpet`` * - 6.0.0 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| * - 6.0.1 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| * - 6.0.2 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| Version 7 ......... .. list-table:: :header-rows: 1 :stub-columns: 1 * - md-toc - ``cmark`` - ``commonmarker`` - ``github`` - ``gitlab`` - ``redcarpet`` * - 7.0.0 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| * - 7.0.1 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| * - 7.0.2 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| * - 7.0.3 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| * - 7.0.4 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| * - 7.0.5 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - |r3| * - 7.1.0 - ``github`` - ``github`` - 0.28.gfm.? - ``github`` - v3.5.0 * - 7.2.0 - 0.28.? [#f1]_ - 0.28.gfm.? - 0.28.gfm.? - ``github`` - v3.5.0 Version 8 ......... .. list-table:: :header-rows: 1 :stub-columns: 1 * - md-toc - ``cmark`` - ``commonmarker`` - ``github`` - ``gitlab`` - ``goldmark`` - ``redcarpet`` * - 8.0.0 - 0.29.? - ``github`` - 0.29.gfm.? - latest version - ``cmark`` - v3.5.0 * - 8.0.1 - 0.29.? - ``github`` - 0.29.gfm.? - latest version - ``cmark`` - v3.5.0 * - 8.1.0 - 0.29.? - ``github`` - 0.29.gfm.? - latest version - ``cmark`` - v3.5.0 * - 8.1.1 - 0.30.? - ``github`` - 0.29.gfm.? [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.1.2 - 0.30.? - ``github`` - 0.29.gfm.? [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.1.3 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.1.4 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.1.5 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.1.6 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.1.7 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.1.8 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.1.9 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.2.0 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.2.1 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.2.2 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 * - 8.2.3 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 Version 9 ......... .. list-table:: :header-rows: 1 :stub-columns: 1 * - md-toc - ``cmark`` - ``commonmarker`` - ``github`` - ``gitlab`` - ``goldmark`` - ``redcarpet`` * - 9.0.0 - 0.30.0 - ``github`` - 0.29.gfm.0 [#f2]_ - latest version - ``cmark`` - v3.5.0 .. |r1| replace:: https://github.com/vmg/redcarpet/tree/26c80f05e774b31cd01255b0fa62e883ac185bf3 .. |r2| replace:: https://github.com/vmg/redcarpet/tree/e3a1d0b00a77fa4e2d3c37322bea66b82085486f .. |r3| replace:: https://github.com/vmg/redcarpet/tree/94f6e27bdf2395efa555a7c772a3d8b70fb84346 Supported markdown parsers -------------------------- - ``cmark``: - "CommonMark parsing and rendering library and program in C". - ``commonmarker``: - a "Ruby wrapper for libcmark (CommonMark parser)". - as described on their website: "It also includes extensions to the CommonMark spec as documented in the GitHub Flavored Markdown spec, such as support for tables, strikethroughs, and autolinking.". For this reason we assume that ``commonmarker`` is an alias of ``github``. - ``github``: - uses a forked version of ``cmark`` with some added extensions. This language specification is called GitHub Flavored Markdown. - there are subtle differences that affect md-toc such as - the disallowed raw HTML extension which affects md-toc - emphasis processing - ``gitlab``: - uses ``commonmarker``. Older versions of md-toc, prior to version ``3.0.0``, use ``gitlab`` as an alias of ``redcarpet`` while newer versions use ``github`` instead. In the past GitLab used Redcarpet as markdown parser. - some extensions used in GitLab Flavored Markdown, not to be confused with GitHub Flavored Markdown, are different from the ones used in GitHub Flavored Markdown. .. seealso:: - _`Documentation Style Guide | GitLab - Documentation is the single source of truth (SSOT)` [#f3]_ - ``goldmark``: - this parser claims to be compliant with CommonMark: `goldmark is compliant with CommonMark 0.30.`. For this reason ``goldmark`` is an alias of ``cmark``. - ``redcarpet``: - "The safe Markdown parser, reloaded." Other markdown parsers ---------------------- If you have a look at [#f4]_ you will see that there are a ton of different markdown parsers out there. Moreover, that list has not been updated in a while. Markdown parsers have different behaviours regarding anchor links. Some of them implement them while others don't; some act on the duplicate entry problem while others don't; some strip consecutive dash characters while others don't. And it's not just about anchor links, as you can read in the rules section. For example: - Gitea apparently uses ``goldmark`` as markdown parser. See [#f5]_ [#f6]_. There are same cases where there is a discrepancy with ``cmark``: - ``this is - a test`` is rendered as - ``this-is---a-test`` by cmark - ``this-is-a-test`` by Gitea Gitea adds an annoying ``user-content`` substring in the TOC's anchor links. This is true for versions since git tag v1.11.0. See [#f7]_ [#f8]_ [#f9]_ [#f10]_. The ``user-content`` substring does not seem to affect the functionality of the TOC. Older versions of Gitea used blackfriday. See [#f11]_. - Gogs uses Marked as the markdown parser. See [#f12]_ [#f13]_ [#f14]_ [#f15]_. - Notabug: *Notabug is powered by a liberated version of gogs*. See [#f16]_. - Kramdown: It is unclear if this feature is available. See [#f17]_ - Gitlab Kramdown. See [#f18]_ Steps to add an unsupported markdown parser ``````````````````````````````````````````` 1. Find the source code and/or documents. 2. Find the rules for each section, such as anchor link generation, title detection, etc... Rely more on the source code than on the documentation (if possible) 3. Add the relevant information on this page. 4. Write or adapt an algorithm for that section. 5. Write unit tests for it. 6. Add the new parser to the CLI interface. .. rubric:: Footnotes .. [#f1] used alias ``github`` .. [#f2] when this md-toc version was released GFM still needed to catch up with cmark .. [#f3] https://docs.gitlab.com/ee/development/documentation/styleguide/#documentation-is-the-single-source-of-truth-ssot .. [#f4] https://www.w3.org/community/markdown/wiki/MarkdownImplementations .. [#f5] https://github.com/go-gitea/gitea .. [#f6] https://github.com/go-gitea/gitea/blob/71aca93decc10253133dcd77b64dae5d311d7163/modules/markup/markdown/goldmark.go .. [#f7] https://github.com/go-gitea/gitea/blob/71aca93decc10253133dcd77b64dae5d311d7163/modules/markup/markdown/goldmark.go#L230 .. [#f8] https://github.com/go-gitea/gitea/issues/12062 .. [#f9] https://github.com/go-gitea/gitea/pull/11903 .. [#f10] https://github.com/go-gitea/gitea/pull/12805 .. [#f11] https://github.com/go-gitea/gitea/blob/2a03e96bceadfcc5e18bd61e755980ee72dcdb15/modules/markup/markdown/markdown.go .. [#f12] https://gogs.io/docs .. [#f13] https://github.com/chjj/marked .. [#f14] https://github.com/chjj/marked/issues/981 .. [#f15] https://github.com/chjj/marked/search?q=anchor&type=Issues&utf8=%E2%9C%93 .. [#f16] https://github.com/gettalong/kramdown/search?q=anchor&type=Issues&utf8=%E2%9C%93 .. [#f17] https://github.com/gettalong/kramdown/search?q=anchor&type=Issues&utf8=%E2%9C%93 .. [#f18] https://gitlab.com/gitlab-org/gitlab_kramdown/-/blob/master/lib/gitlab_kramdown/parser/header.rb md-toc-9.0.0/docs/meta.rst000066400000000000000000000326521460560256400153330ustar00rootroot00000000000000Source code =========== - https://software.franco.net.eu.org/frnmst/md-toc - https://codeberg.org/frnmst/md-toc - https://framagit.org/frnmst/md-toc - https://github.com/frnmst/md-toc Workflow -------- See `Python workflow `_ Contributing ------------ See `Python contributing `_ Copyright and License --------------------- md-toc `````` Scope ~~~~~ +-----------------------------------------+-----------------------------------------------+ | Applies to | Parsers | +-----------------------------------------+-----------------------------------------------+ | Most of md-toc's API | all | +-----------------------------------------+-----------------------------------------------+ Files ~~~~~ - ``./md_toc/api.py`` - ``./md_toc/cli.py`` - ``./md_toc/constants.py`` - ``./md_toc/exceptions.py`` - ``./md_toc/generic.py`` - ``./md_toc/__init__.py`` - ``./md_toc/tests/tests.py`` - ``./md_toc/tests/__init__.py`` - ``./md_toc/types.py`` Text ~~~~ :: Copyright (C) 2017-2024 Franco Masotti (see /README.md) md-toc is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. md-toc is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with md-toc. If not, see . License A ````````` Scope ~~~~~ +-----------------------------------------+-----------------------------------------------+ | Applies to | Parsers | +-----------------------------------------+-----------------------------------------------+ | Headers | ``redcarpet`` | +-----------------------------------------+-----------------------------------------------+ | Anchor link types and behaviours | ``redcarpet`` | +-----------------------------------------+-----------------------------------------------+ Files ~~~~~ - ``./md_toc/api.py`` Text ~~~~ :: Copyright (c) 2009, Natacha Porté Copyright (c) 2015, Vicent Marti Copyright (c) 2018, Franco Masotti (see /README.md) (for md-toc only) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License B ````````` Scope ~~~~~ +-----------------------------------------+-----------------------------------------------+ | Applies to | Parsers | +-----------------------------------------+-----------------------------------------------+ | Anchor link types and behaviours | ``cmark``, ``github`` | +-----------------------------------------+-----------------------------------------------+ Files ~~~~~ - ``./md_toc/api.py`` Text ~~~~ :: Copyright (c) 2012 GitHub Inc. and Jerry Cheung Copyright (c) 2018, Franco Masotti (see /README.md) (for md-toc only) MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License C ````````` Scope ~~~~~ +-----------------------------------------+-----------------------------------------------+ | Applies to | Parsers | +-----------------------------------------+-----------------------------------------------+ | Emphasis in anchor links | ``cmark``, ``github``, ``gitlab`` | +-----------------------------------------+-----------------------------------------------+ Files ~~~~~ - ``./md_toc/constants.py`` - ``./md_toc/cmark/cmark_ctype_c.py`` - ``./md_toc/cmark/cmark_h.py`` - ``./md_toc/cmark/inlines_c.py`` - ``./md_toc/cmark/node_c.py`` - ``./md_toc/cmark/node_h.py`` - ``./md_toc/cmark/references_c.py`` - ``./md_toc/cmark/references_h.py`` - ``./md_toc/cmark/scanners_c.py`` - ``./md_toc/cmark/scanners_h.py`` Text ~~~~ :: Copyright (c) 2014, John MacFarlane Copyright (c) 2021-2024, Franco Masotti (see /README.md) (for md-toc only) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 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. License D ````````` Scope ~~~~~ +-----------------------------------------+-----------------------------------------------+ | Applies to | Parsers | +-----------------------------------------+-----------------------------------------------+ | Emphasis in anchor links | ``cmark``, ``github``, ``gitlab`` | +-----------------------------------------+-----------------------------------------------+ Files ~~~~~ - ``./md_toc/cmark/utf8_c.py`` Text ```` :: utf8.c and utf8.c are derived from utf8proc (), (C) 2009 Public Software Group e. V., Berlin, Germany. Copyright (C) 2021-2024, Franco Masotti (see /README.md) (for md-toc only) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License E ````````` Scope ~~~~~ +-----------------------------------------+-----------------------------------------------+ | Applies to | Parsers | +-----------------------------------------+-----------------------------------------------+ | Emphasis in anchor links | ``cmark``, ``github``, ``gitlab`` | +-----------------------------------------+-----------------------------------------------+ Files ~~~~~ - ``./md_toc/cmark/buffer_h.py`` - ``./md_toc/cmark/buffer_c.py`` - ``./md_toc/cmark/chunk_h.py`` Text ~~~~ :: buffer.h, buffer.c, chunk.h are derived from code (C) 2012 Github, Inc. Copyright (C) 2021-2024, Franco Masotti (see /README.md) (for md-toc only) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License F ````````` +-----------------------------------------+-----------------------------------------------+ | Applies to | Parsers | +-----------------------------------------+-----------------------------------------------+ | Emphasis in anchor links | ``cmark``, ``github``, ``gitlab`` | +-----------------------------------------+-----------------------------------------------+ Files ~~~~~ - ``./md_toc/cmark/houdini_h.py`` - ``./md_toc/cmark/houdini_html_u.c`` Text ~~~~ :: houdini.h, houdini_href_e.c, houdini_html_e.c, houdini_html_u.c derive from https://github.com/vmg/houdini (with some modifications) Copyright (C) 2012 Vicent Martí Copyright (C) 2022, Franco Masotti (see /README.md) (for md-toc only) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. md-toc-9.0.0/docs/pre_commit_hook.rst000066400000000000000000000025561460560256400175630ustar00rootroot00000000000000Pre-commit hook --------------- This repo provides the following :download:`plugin <../.pre-commit-hooks.yaml>` to be used with the `Pre-commit framework `_ .. literalinclude:: ../.pre-commit-hooks.yaml :language: yaml :caption: The .pre-commit-hooks.yaml file :name: .pre-commit-hooks.yaml Add a ``.pre-commit-config.yaml`` file in the root of your GIT repo. Have a look at the ``/.pre-commit-hooks.yaml`` file of this repository for a full example. These are the default plugin settings .. code-block:: yaml :caption: A simple example of a .pre-commit-config.yaml file :name: .pre-commit-config.yaml simple repos: - repo: https://codeberg.org/frnmst/md-toc # Release updates (ATOM) https://codeberg.org/frnmst/md-toc/tags.atom rev: master # set a GIT tag hooks: - id: md-toc You can override the defaults via the ``args`` parameter, such as .. code-block:: yaml :caption: Example of arguments passed as a pre-commit :name: .pre-commit-config.yaml args repos: - repo: https://codeberg.org/frnmst/md-toc # Release updates (ATOM) https://codeberg.org/frnmst/md-toc/tags.atom rev: master # set a GIT tag hooks: - id: md-toc args: [-p, --skip-lines, '1', redcarpet] # CLI options Finally, run ``pre-commit install`` to enable the hook. md-toc-9.0.0/docs/rules/000077500000000000000000000000001460560256400147755ustar00rootroot00000000000000md-toc-9.0.0/docs/rules/anchor_link_types_and_behaviours.rst000066400000000000000000000074001460560256400243140ustar00rootroot00000000000000Anchor link types and behaviours ================================ Generic ------- ``cmark``, ``github`` ````````````````````` A translated version of the Ruby algorithm is used in md-toc. The original one is repored here: - https://github.com/jch/html-pipeline/blob/master/lib/html/pipeline/toc_filter.rb I could not find the code directly responsable for the anchor link generation. See also: - https://github.github.com/gfm/ - https://githubengineering.com/a-formal-spec-for-github-markdown/ - https://github.com/github/cmark/issues/65#issuecomment-343433978 Apparently GitHub (and possibly others) filter HTML tags in the anchor links. This is an undocumented feature (?) so the ``remove_html_tags`` function was added to address this problem. Instead of designing an algorithm to detect HTML tags, regular expressions came in handy. All the rules present in https://spec.commonmark.org/0.28/#raw-html have been followed by the letter. Regular expressions are divided by type and are composed at the end by concatenating all the strings. For example: .. code-block:: python :linenos: # Comment start. COS = '' # Comment. CO = COS + COT + COE HTML tags are stripped using the ``re.sub`` replace function, for example: .. code-block:: ruby line = re.sub(CO, str(), line, flags=re.DOTALL) GitHub added an extension in GFM to ignore certain HTML tags, valid at least from versions `0.27.1.gfm.3` to `0.29.0.gfm.0`: - https://github.github.com/gfm/#disallowed-raw-html-extension- - https://github.com/github/cmark-gfm/blob/fca380ca85c046233c39523717073153e2458c1e/extensions/tagfilter.c ``gitlab`` `````````` New rules have been written: - https://docs.gitlab.com/ee/user/markdown.html#header-ids-and-links ``redcarpet`` ````````````` Treats consecutive dash characters by tranforming them into a single dash character. A translated version of the C algorithm is used in md-toc. The original version is here: - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/html.c#L274 See also: - https://github.com/vmg/redcarpet/issues/618#issuecomment-306476184 - https://github.com/vmg/redcarpet/issues/307#issuecomment-261793668 Emphasis -------- To be able to have working anchor links, emphasis must also be removed from the link destination. ``cmark``, ``github``, ``gitlab`` `````````````````````````````````` At the moment the implementation of emnphasis removal is incomplete because of its complexity. See: - https://spec.commonmark.org/0.30/#emphasis-and-strong-emphasis The core functions for this feature have been ported directly from the original cmark source with some differences: #. things such as string manipulation, mallocs, etc are different in Python #. the ``cmark_utf8proc_charlen`` uses ``length = 1`` instead of ``length = utf8proc_utf8class[ord(line[0])]`` (causes list overflow). The ``cmark_utf8proc_charlen`` function is related to the ``cmark_utf8proc_encode_char`` function. Have a look at that function to know character lengths in cmark. In Python 3, since all characters are UTF-8 by default, they are all represented with length 1. See: - https://rosettacode.org/wiki/String_length#Python - https://docs.python.org/3/howto/unicode.html#comparing-strings As of the release md-toc 8.1.2, cmark-gfm is still at version 0.29. Moreover, certain code sections used in the emphasis processing are not the same of cmark 0.29. See this one for example: - https://github.com/github/cmark-gfm/blob/0.29.0.gfm.3/src/inlines.c#L639-L654 - https://github.com/commonmark/cmark/blob/0.29.0/src/inlines.c#L615-L621 For the moment md-toc uses the original cmark source only as reference for emphasis processing. md-toc-9.0.0/docs/rules/code_fence.rst000066400000000000000000000014361460560256400176050ustar00rootroot00000000000000Code fence ========== Code fences are sections of a markdown document where some parsers treat the text within them as verbatim. Usually the purpose of these sections is to display source code. Some programming languages use the character ``#`` as a way to comment a line in the code. For this reason md-toc needs to ignore code fences in order not to treat the ``#`` character as an ATX-style heading and thus get parsed as an element of the TOC. ``cmark``, ``github``, ``gitlab`` --------------------------------- The rules followed are the ones reported on the documentation: - https://spec.commonmark.org/0.30/#code-fence ``redcarpet`` ------------- Needs to be implemented: - https://github.com/vmg/redcarpet/blob/26c80f05e774b31cd01255b0fa62e883ac185bf3/ext/redcarpet/markdown.c#L1389 md-toc-9.0.0/docs/rules/headers.rst000066400000000000000000000027571460560256400171550ustar00rootroot00000000000000Headers ======= Only ATX-style headings are supported in md-toc. ``cmark``, ``github``, ``gitlab`` --------------------------------- The code used in md-toc is a reverse engineering of the behavour described in the following: - https://spec.commonmark.org/0.30/#atx-heading The escape character ``\`` will be left as-is since they are parsed by Github's markdown parser already: - https://spec.commonmark.org/0.30/#backslash-escapes A line ending character is ``U+000A`` or the ``U+000D`` character, respectively ``\n`` and ``\r`` (or ``\r\n`` if combined). Everything following those characters is ignored. This has also the benefit to automatically remove the trailing newline or carriage return at the end of each line. This also includes ATX headers with line endings only as main content, such as ``#\n`` or ``#\r``. See also: - https://spec.commonmark.org/0.30/#line - https://spec.commonmark.org/0.30/#line-ending Every other rule for ATX headings is applied. ``redcarpet`` ------------- - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L1444 - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L1981 Line endings are generically ``\n`` or ``\r`` characters. See: - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L2845 - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L2854 md-toc-9.0.0/docs/rules/index.rst000066400000000000000000000000721460560256400166350ustar00rootroot00000000000000Rules ===== .. toctree:: :maxdepth: 2 :glob: * md-toc-9.0.0/docs/rules/link_lables.rst000066400000000000000000000125551460560256400200160ustar00rootroot00000000000000Link label ========== If the user decides to generate the table of contents with the anchor links, then link label rules will be applied. ``cmark``, ``github``, ``gitlab`` --------------------------------- - https://spec.commonmark.org/0.30/#link-label If a line ends in 1 or more '\' characters, this disrupts the anchor title. For example ``- [xdmdmsdm\](#xdmdmsdm)`` becomes ``
  • [xdmdmsdm](#xdmdmsdm)
`` instead of ``
``. The workaround used in md-toc is to add a space character at the end of the string, so it becomes: ```` If the link label contains only whitespace characters a ``GithubEmptyLinkLabel`` exception is raised. If the number of characters inside the link label is over 999 a ``GithubOverflowCharsLinkLabel`` is raised. If the headers contains ``[`` or ``]``, these characters are treated with the following rules. - https://spec.commonmark.org/0.30/#link-text - https://spec.commonmark.org/0.30/#link-destination According to a function in the source code, balanced square brackets do not work, however they do when interpeted by the web interface. It is however possible that they are supported within the ``handle_close_bracket`` function. - https://github.com/github/cmark/blob/6b101e33ba1637e294076c46c69cd6a262c7539f/src/inlines.c#L881 - https://github.com/github/cmark/blob/6b101e33ba1637e294076c46c69cd6a262c7539f/src/inlines.c#L994 Here is the original C function with some more comments added: .. code-block:: c :linenos: // Parse a link label. Returns 1 if successful. // Note: unescaped brackets are not allowed in labels. // The label begins with `[` and ends with the first `]` character // encountered. Backticks in labels do not start code spans. static int link_label(subject *subj, cmark_chunk *raw_label) { bufsize_t startpos = subj->pos; int length = 0; unsigned char c; // advance past [ // // Ignore the open link label identifier // peek_char simply returns the current char if we are // in range of the string, 0 otherwise. if (peek_char(subj) == '[') { advance(subj); } else { return 0; } while ((c = peek_char(subj)) && c != '[' && c != ']') { // If there is an escape and the next character is (for example) // '[' or ']' then, // ignore the loop conditions. // If there are nested balanced square brakets this loop ends. if (c == '\\') { advance(subj); length++; // Puntuation characters are the ones defined at: // https://github.github.com/gfm/#ascii-punctuation-character if (cmark_ispunct(peek_char(subj))) { advance(subj); length++; } } else { advance(subj); length++; } // MAX_LINK_LABEL_LENGTH is a constant defined at // https://github.com/github/cmark/blob/master/src/parser.h#L13 if (length > MAX_LINK_LABEL_LENGTH) { goto noMatch; } } // If the loop terminates when the current character is ']' then // everything between '[' and ']' is the link label... if (c == ']') { // match found *raw_label = cmark_chunk_dup(&subj->input, startpos + 1, subj->pos - (startpos + 1)); cmark_chunk_trim(raw_label); advance(subj); // advance past ] return 1; } // ...otherwise return error. // This label always get executed according to C rules. noMatch: subj->pos = startpos; // rewind return 0; } For simpleness the escape ``[`` and ``]`` rule is used. ``redcarpet`` ------------- - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L998 Let's inspect this loop: - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L1017 .. code-block:: c :linenos: /* looking for the matching closing bracket */ for (level = 1; i < size; i++) { if (data[i] == '\n') text_has_nl = 1; else if (data[i - 1] == '\\') continue; else if (data[i] == '[') level++; else if (data[i] == ']') { level--; if (level <= 0) break; } } if (i >= size) goto cleanup; The cleanup label looks like this: .. code-block:: c :linenos: /* cleanup */ cleanup: rndr->work_bufs[BUFFER_SPAN].size = (int)org_work_size; return ret ? i : 0; An example: ``[test \](test \)`` becomes ``[test ](test )`` instead of ``test \`` Infact, you can see that if the current character is ``\\`` then the the current iteration is skipped. If for any chance the next character is ``]`` then the inline link closing parenthesis detection is ignored. ``i`` becomes equal to ``size`` eventually and so we jump to the ``cleanup`` label. That lable contains a return statement so that string is not treated as inline link anymore. A similar code is implemented also for detecting ``(`` and ``)``. See: - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L1088 - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L1099 To solve this we use the same workaround used for ``cmark``, ``github``, ``gitlab``. md-toc-9.0.0/docs/rules/list_items.rst000066400000000000000000000266561460560256400177220ustar00rootroot00000000000000List items ========== Problems -------- We are interested in sublists indentation rules for all types of lists, and integer overflows in case of ordered lists. For ordered lists, we are not concerned about using ``0`` or negative numbers as list markers so these cases will not be considered. Infact ordred lists generated by md-toc will always start from ``1``. Talking about indentation rules, I need to mention that the user is responsible for generating a correct markdown list according to the parser's rules. Let's see this example: .. code-block:: markdown # foo ## bar ### baz no problem here because this is rendered by md-toc, using ``github`` as parser, with: .. code-block:: markdown - [foo](#foo) - [bar](#bar) - [baz](#baz) Now, let's take the previous example and reverse the order of the lines: .. code-block:: markdown ### baz ## bar # foo and this is what md-toc renders using ``github``: .. code-block:: markdown - [baz](#baz) - [foo](#foo) - [bar](#bar) while the user might expect this: .. code-block:: markdown - [baz](#baz) - [foo](#foo) - [bar](#bar) Indentation ----------- ``cmark``, ``github``, ``gitlab`` ````````````````````````````````` List indentation for sublists with this parser is based on the previous state, as stated in the Commonmark spec, at section 5.2: "The most important thing to notice is that the position of the text after the list marker determines how much indentation is needed in subsequent blocks in the list item. If the list marker takes up two spaces of indentation, and there are three spaces between the list marker and the next character other than a space or tab, then blocks must be indented five spaces in order to fall under the list item." - https://spec.commonmark.org/0.30/#list-items This is also true with the specular case: if our new list element needs less indentation than the one processed currently, we have to use the same number of indentation spaces used somewhere earlier in the list. ``redcarpet`` ````````````` - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L1553 - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L1528 The following C function returns the first non-whitespace character after the list marker. The value of ``0`` is returned if the input line is not a list element. List item rules are explained in the single line comments. .. code-block:: c :linenos: /* prefix_uli • returns unordered list item prefix */ static size_t prefix_uli(uint8_t *data, size_t size) { size_t i = 0; // There can be up to 3 whitespaces before the list marker. if (i < size && data[i] == ' ') i++; if (i < size && data[i] == ' ') i++; if (i < size && data[i] == ' ') i++; // The next non-whitespace character must be a list marker and // the character after the list marker must be a whitespace. if (i + 1 >= size || (data[i] != '*' && data[i] != '+' && data[i] != '-') || data[i + 1] != ' ') return 0; // Check that the next line is not a header // that uses the `-` or `=` characters as markers. if (is_next_headerline(data + i, size - i)) return 0; // Return the first non whitespace character after the list marker. return i + 2; } As far as I can tell from the previous and other functions, on a new list block the 4 spaces indentation rule applies: - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L1822 - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L1873 This means that anything that has more than 3 whitespaces is considered as sublist. The only exception seems to be for the first sublist in a list block, in which that case even a single whitespace counts as a sublist. The 4 spaces indentation rule appllies nontheless, so to keep things simple md-toc will always use 4 whitespaces for sublists. Apparently, ordered and unordered lists share the same proprieties. Let's see this example: :: - I - am - foo stop - I - am - foo This is how redcarpet renders it once you run ``$ redcarpet``: .. code-block:: html
  • I
    • am
      • foo

stop

  • I
    • am
      • foo
What follows is an extract of a C function in redcarpet that parses list items. I have added all the single line comments. .. code-block:: c :linenos: /* parse_listitem • parsing of a single list item */ /* assuming initial prefix is already removed */ static size_t parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int *flags) { struct buf *work = 0, *inter = 0; size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i; int in_empty = 0, has_inside_empty = 0, in_fence = 0; // This is the base case, usually of indentation 0 but it can be // from 0 to 3 spaces. If it was 4 spaces it would be a code // block. /* keeping track of the first indentation prefix */ while (orgpre < 3 && orgpre < size && data[orgpre] == ' ') orgpre++; // Get the first index of string after the list marker. Try both // ordered and unordered lists beg = prefix_uli(data, size); if (!beg) beg = prefix_oli(data, size); if (!beg) return 0; /* skipping to the beginning of the following line */ end = beg; while (end < size && data[end - 1] != '\n') end++; // Iterate line by line using the '\n' character as delimiter. /* process the following lines */ while (beg < size) { size_t has_next_uli = 0, has_next_oli = 0; // Go to the next line. end++; // Find the end of the line. while (end < size && data[end - 1] != '\n') end++; // Skip the next line if it is empty. /* process an empty line */ if (is_empty(data + beg, end - beg)) { in_empty = 1; beg = end; continue; } // Count up to 4 characters of indentation. // If we have 4 characters then it might be a sublist. // Note that this is an offset and does not point to an // index in the actual line string. /* calculating the indentation */ i = 0; while (i < 4 && beg + i < end && data[beg + i] == ' ') i++; pre = i; /* Only check for new list items if we are **not** inside * a fenced code block */ if (!in_fence) { has_next_uli = prefix_uli(data + beg + i, end - beg - i); has_next_oli = prefix_oli(data + beg + i, end - beg - i); } /* checking for ul/ol switch */ if (in_empty && ( ((*flags & MKD_LIST_ORDERED) && has_next_uli) || (!(*flags & MKD_LIST_ORDERED) && has_next_oli))){ *flags |= MKD_LI_END; break; /* the following item must have same list type */ } // Determine if we are dealing with: // - an empty line // - a new list item // - a sublist /* checking for a new item */ if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) { if (in_empty) has_inside_empty = 1; // The next list item's indentation (pre) must be the same as // the previous one (orgpre), otherwise it might be a // sublist. if (pre == orgpre) /* the following item must have */ break; /* the same indentation */ // If the indentation does not match the previous one then // assume that it is a sublist. Check later whether it is // or not. if (!sublist) sublist = work->size; } /* joining only indented stuff after empty lines */ else if (in_empty && i < 4 && data[beg] != '\t') { *flags |= MKD_LI_END; break; } else if (in_empty) { // Add a line delimiter to the next line if it is missing. bufputc(work, '\n'); has_inside_empty = 1; } in_empty = 0; beg = end; } if (*flags & MKD_LI_BLOCK) { /* intermediate render of block li */ if (sublist && sublist < work->size) { parse_block(inter, rndr, work->data, sublist); parse_block(inter, rndr, work->data + sublist, work->size - sublist); } else parse_block(inter, rndr, work->data, work->size); } According to the code, ``parse_listitem`` is called indirectly by ``parse_block`` (via ``parse_list``), but ``parse_block`` is called directly by ``parse_listitem`` so the code analysis is not trivial. For this reason I might be mistaken about the 4 spaces indentation rule. - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L2418 - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L1958 Here is an extract of the ``parse_block`` function with the calls to ``parse_list``: .. code-block:: c :linenos: /* parse_block • parsing of one block, returning next uint8_t to parse */ static void parse_block(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) { while (beg < size) { else if (prefix_uli(txt_data, end)) beg += parse_list(ob, rndr, txt_data, end, 0); else if (prefix_oli(txt_data, end)) beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED); } } Overflows --------- ``cmark``, ``github``, ``gitlab`` ````````````````````````````````` Ordered list markers cannot exceed ``99999999`` according to the following. If that is the case then a ``GithubOverflowOrderedListMarker`` exception is raised: - https://spec.commonmark.org/0.30/#ordered-list-marker ``redcarpet`` ````````````` Apparently there are no cases of ordered list marker overflows: - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/ext/redcarpet/markdown.c#L1529 Notes on ordered lists ---------------------- ``cmark``, ``github``, ``gitlab`` ````````````````````````````````` Ordered list markers may start with any integer (except special cases). any following number is ignored and subsequent numeration is progressive: - https://spec.commonmark.org/0.30/#start-number However, when you try this in practice this is not always true: nested lists do not follow the specifications. See: - https://github.com/frnmst/md-toc/issues/23 Markers cannot be negative: - https://spec.commonmark.org/0.30/#example-239 ``redcarpet`` ````````````` Ordered lists do not use the ``start`` HTML attribute: any number is ignored and lists starts from 1. See: - https://github.com/vmg/redcarpet/blob/6270d6b4ab6b46ee6bb57a6c0e4b2377c01780ae/test/MarkdownTest_1.0/Tests/Markdown%20Documentation%20-%20Syntax.html#L323 md-toc-9.0.0/docs/rules/toc_marker.rst000066400000000000000000000013221460560256400176530ustar00rootroot00000000000000TOC marker ========== A TOC marker is a string that marks that the start and the end of the table of contents in a markdown file. By default it was decided to use ``[](TOC)`` as the default TOC marker because it would result invisible in some markdown parsers. In other cases, however, such as the one used by Gitea, that particular TOC marker was still visible. HTML comments seem to be a better solution. ``cmark``, ``github``, ``gitlab`` --------------------------------- - https://spec.commonmark.org/0.30/#html-comment ``redcarpet`` ------------- I cannot find the corresponding code, but I found this: - https://github.com/vmg/redcarpet/blob/master/test/MarkdownTest_1.0.3/Tests/Inline%20HTML%20comments.html md-toc-9.0.0/md_toc/000077500000000000000000000000001460560256400141605ustar00rootroot00000000000000md-toc-9.0.0/md_toc/__init__.py000066400000000000000000000014411460560256400162710ustar00rootroot00000000000000# # __init__.py # # Copyright (C) 2017-2021 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # """Python discovery file.""" from . import api, cli, exceptions, types md-toc-9.0.0/md_toc/__main__.py000077500000000000000000000024671460560256400162660ustar00rootroot00000000000000# # __main__.py # # Copyright (C) 2017-2020 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # """Call the CLI parser.""" import sys import traceback from .cli import CliInterface def main(args=None): """Call the CLI interface and wait for the result.""" retcode = 0 try: ci = CliInterface() args = ci.parser.parse_args() result = args.func(args) if result is not None and not isinstance(result, bool): print(result) retcode = 0 # TOC differs. if result: retcode = 128 except Exception: retcode = 1 traceback.print_exc() sys.exit(retcode) if __name__ == '__main__': main() md-toc-9.0.0/md_toc/api.py000066400000000000000000001612371460560256400153150ustar00rootroot00000000000000# # api.py # # Copyright (C) 2017-2024 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # """The main file.""" from __future__ import annotations import copy import hashlib import os import re import sys import typing import fpyutils from . import generic, types from .cmark import inlines_c, node_h, references_h from .constants import common_defaults from .constants import parser as md_parser from .exceptions import ( GithubEmptyLinkLabel, GithubOverflowCharsLinkLabel, GithubOverflowOrderedListMarker, StdinIsNotAFileToBeWritten, StringCannotContainNewlines, TocDoesNotRenderAsCoherentList, ) def tocs_equal(current_toc: str, filename: str, marker: str) -> bool: r"""Check if the TOC already present in a file is the samw of the one passed to this function. :parameter current_toc: the new or current TOC. Do not include the ``\n\n`` and ``\n\n``. :parameter filename: the filename with the TOC for the comparison already present in the file. :parameter marker: the TOC marker. :type current_toc: str :type filename: str :type marker: str :returns: ``True`` if the two TOCs are the same, ``False`` otherwise :rtype: bool :raises: a built-in exception. """ tocs_equal: bool = False r: tuple = generic._get_existing_toc(filename, marker) old_toc: str = r[0] if current_toc.strip() == old_toc: tocs_equal = True return tocs_equal def write_string_on_file_between_markers( filename: str, string: str, marker: str, newline_string: str = common_defaults['newline_string'], ) -> bool: r"""Write the table of contents on a single file. :parameter filename: the file that needs to be read or modified. :parameter string: the string that will be written on the file. :parameter marker: a marker that will identify the start and the end of the string. :parameter newline_string: the new line separator. Defaults to ``os.linesep``. :type filename: str :type string: str :type marker: str :type newline_string: str :returns: ``True`` if new TOC is the same as the exising one, ``False`` otherwise. :rtype: bool :raises: StdinIsNotAFileToBeWritten or an fpyutils exception or a built-in exception. """ if filename == '-': raise StdinIsNotAFileToBeWritten # TOC that is written to the file. final_toc_string: str = ''.join([ marker, newline_string, newline_string, string.rstrip(), newline_string, newline_string, marker, newline_string ]) ( old_toc, lines_to_delete, two_or_more_markers, marker_line_positions_length, marker_line_positions, first_marker_line_number, ) = generic._get_existing_toc(filename, marker) equal: bool = False if string.strip() == old_toc: equal = True generic._remove_line_intervals(filename, lines_to_delete) # Only 1 pre-existing marker. if not two_or_more_markers and marker_line_positions_length == 1: generic._remove_line_intervals( filename, [[marker_line_positions[1], marker_line_positions[1]]]) # 2 or more pre-existing markers. if marker_line_positions_length >= 1: fpyutils.filelines.insert_string_at_line( filename, final_toc_string, first_marker_line_number, filename, append=False, newline_character=newline_string, ) return equal def write_strings_on_files_between_markers( filenames: list[str], strings: list[str], marker: str, newline_string: str = common_defaults['newline_string'], ) -> bool: r"""Write the table of contents on multiple files. :parameter filenames: the files that needs to be read or modified. :parameter strings: the strings that will be written on the file. Each string is associated with one file. :parameter marker: a marker that will identify the start and the end of the string. :parameter newline_string: the new line separator. Defaults to ``os.linesep``. :type filenames: list :type strings: list :type marker: str :type newline_string: str :returns: ``True`` if all TOCs are the same as the existing ones, ``False`` otherwise. :rtype: bool :raises: an fpyutils exception or a built-in exception. """ if not len(filenames) == len(strings): raise ValueError file_id: int = 0 equal: bool = True for f in filenames: equal &= write_string_on_file_between_markers(f, strings[file_id], marker, newline_string) file_id += 1 return equal def build_toc( filename: str, ordered: bool = False, no_links: bool = False, no_indentation: bool = False, no_list_coherence: bool = False, keep_header_levels: int = 3, parser: str = 'github', list_marker: str = '-', skip_lines: int = 0, constant_ordered_list: bool = False, newline_string: str = common_defaults['newline_string'], ) -> str: r"""Build the table of contents of a single file. :parameter filename: the file that needs to be read. :parameter ordered: decides whether to build an ordered list or not. Defaults to ``False``. :parameter no_links: disables the use of links. Defaults to ``False``. :parameter no_indentation: disables indentation in the list. Defaults to ``False``. :parameter no_list_coherence: if set to ``False`` checks header levels for consecutiveness. If they are not consecutive an exception is raised. For example: ``# ONE\n### TWO\n`` are not consecutive header levels while ``# ONE\n## TWO\n`` are. Defaults to ``False``. :parameter keep_header_levels: the maximum level of headers to be considered as such when building the table of contents. Defaults to ``3``. :parameter parser: decides rules on how to generate anchor links. Defaults to ``github``. :parameter list_marker: a string that contains some of the first characters of the list element. Defaults to ``-``. :parameter skip_lines: the number of lines to be skipped from the start of file before parsing for table of contents. Defaults to ``0```. :parameter constant_ordered_list: use a single integer as list marker. This sets ordered to ``True``. :parameter newline_string: the newline separator. Defaults to ``os.linesep``. :type filename: str :type ordered: bool :type no_links: bool :type no_indentation: bool :type no_list_coherence: bool :type keep_header_levels: int :type parser: str :type list_marker: str :type skip_lines: int :type constant_ordered_list: bool :type newline_string: str :returns: toc, the corresponding table of contents of the file. :rtype: str :raises: a built-in exception. .. warning:: In case of ordered TOCs you must explicitly pass one of the supported ordered list markers. :Example: >>> import md_toc # doctest: +SKIP >>> with open('foo.md', 'w') as f: # doctest: +SKIP ... f.write('# This\n# Is an\n## Example\n') # doctest: +SKIP 26 >>> md_toc.api.build_toc('foo.md') # doctest: +SKIP - [This](#this) - [Is an](#is-an) - [Example](#example) """ if not skip_lines >= 0: raise ValueError toc: list[str] = [] header_type_counter: types.HeaderTypeCounter = {} header_type_curr: int = 0 header_type_prev: int = 0 header_type_first: int = 0 header_duplicate_counter: types.HeaderDuplicateCounter = {} if filename == '-': f = sys.stdin else: # When reading input from the stream, # if newline is None, universal newlines mode is enabled. # Lines in the input can end in '\n', '\r', or '\r\n', # and these are translated into '\n' before being returned to the caller. # See # https://docs.python.org/3/library/functions.html#open-newline-parameter f = open(filename, newline=None) # Help the developers: override the list_marker in case # this function is called with the default unordered list marker, # for example like this: # print(md_toc.build_toc('test.md', ordered=True)) # This avoids an AssertionError later on. if (ordered and list_marker == md_parser[parser]['list']['unordered']['default_marker']): list_marker = md_parser[parser]['list']['ordered'][ 'default_closing_marker'] if constant_ordered_list: ordered = True if ordered and ( list_marker is None or list_marker not in md_parser[parser]['list']['ordered']['closing_markers']): list_marker = md_parser[parser]['list']['ordered'][ 'default_closing_marker'] if skip_lines > 0: loop: bool = True line_counter = 1 while loop: if line_counter > skip_lines or f.readline() == '': loop = False line_counter += 1 try: line = f.readline() except UnicodeDecodeError: return ''.join( ['']) indentation_log: dict[types.IndentationLogElement] = init_indentation_log( parser, list_marker) is_within_code_fence = False code_fence = None is_document_end = False while line: # Document ending detection. # # This changes the state of is_within_code_fence if the # file has no closing fence markers. This serves no practial # purpose since the code would run correctly anyway. It is # however more semantically correct. # # See the unit tests (examples 95 and 96 of the github parser) # and the is_closing_code_fence function. if filename != '-': # stdin is not seekable. file_pointer_pos = f.tell() try: if f.readline() == '': is_document_end = True except UnicodeDecodeError: return ''.join([ '' ]) f.seek(file_pointer_pos) # Code fence detection. if is_within_code_fence: is_within_code_fence = not is_closing_code_fence( line, code_fence, is_document_end, parser, ) else: code_fence = is_opening_code_fence(line, parser) if code_fence is not None: # Update the status of the next line. is_within_code_fence = True if not is_within_code_fence or code_fence is None: # Header detection and gathering. headers: list[types.Header] = get_md_header( line, header_duplicate_counter, keep_header_levels, parser, no_links, ) # We only need to get the first element since all the lines # have been already separated here. header: types.Header = headers[0] # Ignore invalid or to-be-invisible headers. if header is not None and header['visible']: header_type_curr = header['header_type'] # Take care of the ordered TOC. if ordered and not constant_ordered_list: increase_index_ordered_list( header_type_counter, header_type_prev, header_type_curr, parser, ) index = header_type_counter['h' + str(header_type_curr)] elif constant_ordered_list: # This value should work on most parsers. index = 1 else: index = 1 # Take care of list indentations. if no_indentation: no_of_indentation_spaces_curr = 0 # TOC list coherence checks are not necessary # without indentation. else: if not no_list_coherence: # In-place list coherence checks can be made using only # the first, current and previous header types. if header_type_first == 0: header_type_first = header_type_curr if header_type_prev == 0: header_type_prev = header_type_curr if (header_type_curr < header_type_first or header_type_curr > header_type_prev + 1): raise TocDoesNotRenderAsCoherentList compute_toc_line_indentation_spaces( header_type_curr, header_type_prev, parser, ordered, list_marker, indentation_log, index, ) no_of_indentation_spaces_curr = indentation_log[ header_type_curr]['indentation_spaces'] # endif # Build a single TOC line. toc_line_no_indent = build_toc_line_without_indentation( header, ordered, no_links, index, parser, list_marker, ) # Save the TOC line with the indentation. toc.append(''.join([ build_toc_line( toc_line_no_indent, no_of_indentation_spaces_curr, ), newline_string ])) header_type_prev = header_type_curr # endif # endif try: line = f.readline() except UnicodeDecodeError: return ''.join( ['']) # endwhile f.close() return ''.join(toc) def build_multiple_tocs( filenames: list[str], ordered: bool = False, no_links: bool = False, no_indentation: bool = False, no_list_coherence: bool = False, keep_header_levels: int = 3, parser: str = 'github', list_marker: str = '-', skip_lines: int = 0, constant_ordered_list: bool = False, newline_string: str = common_defaults['newline_string'], ) -> list[str]: r"""Parse files by line and build the table of contents of each file. :parameter filenames: the files that needs to be read. :parameter ordered: decides whether to build an ordered list or not. Defaults to ``False``. :parameter no_links: disables the use of links. Defaults to ``False``. :parameter no_indentation: disables indentation in the list. Defaults to ``False``. :parameter keep_header_levels: the maximum level of headers to be considered as such when building the table of contents. Defaults to ``3``. :parameter parser: decides rules on how to generate anchor links. Defaults to ``github``. :parameter skip_lines: the number of lines to be skipped from the start of file before parsing for table of contents. Defaults to ``0```. :parameter list_marker: a string that contains some of the first characters of the list element. Defaults to ``-``. :parameter constant_ordered_list: use a single integer as list marker. This sets ordered to ``True``. :parameter newline_string: the newline separator. Defaults to ``os.linesep``. :type filenames: list :type ordered: bool :type no_links: bool :type no_indentation: bool :type keep_header_levels: int :type parser: str :type list_marker: str :type skip_lines: int :type constant_ordered_list: bool :type newline_string: str :returns: toc_struct, the corresponding table of contents for each input file. :rtype: list[str] :raises: a built-in exception. .. warning:: In case of ordered TOCs you must explicitly pass one of the supported ordered list markers. """ if len(filenames) == 0: filenames.append('-') return [ build_toc( filenames[file_id], ordered, no_links, no_indentation, no_list_coherence, keep_header_levels, parser, list_marker, skip_lines, constant_ordered_list, newline_string, ) for file_id in range(0, len(filenames)) ] def increase_index_ordered_list( header_type_count: types.HeaderTypeCounter, header_type_prev: int, header_type_curr: int, parser: str = 'github', ): r"""Compute the current index for ordered list table of contents. :parameter header_type_count: the count of each header type. :parameter header_type_prev: the previous type of header (h[1,...,Inf]). :parameter header_type_curr: the current type of header (h[1,...,Inf]). :parameter parser: decides rules on how to generate ordered list markers. Defaults to ``github``. :type header_type_count: types.HeaderTypeCounter :type header_type_prev: int :type header_type_curr: int :type parser: str :returns: None :rtype: None :raises: GithubOverflowOrderedListMarker or a built-in exception. """ # header_type_prev might be 0 while header_type_curr can't. if not header_type_prev >= 0: raise ValueError if not header_type_curr >= 1: raise ValueError # Base cases for a new table of contents or a new index type. if header_type_prev == 0: header_type_prev = header_type_curr if ('h' + str(header_type_curr) not in header_type_count or header_type_prev < header_type_curr): header_type_count['h' + str(header_type_curr)] = 0 header_type_count['h' + str(header_type_curr)] += 1 if parser in ['github', 'cmark', 'gitlab', 'commonmarker']: if header_type_count['h' + str(header_type_curr)] > md_parser[ 'github']['list']['ordered']['max_marker_number']: raise GithubOverflowOrderedListMarker def init_indentation_log( parser: str = 'github', list_marker: str = '-', ) -> dict[types.IndentationLogElement]: r"""Create a data structure that holds list marker information. :parameter parser: decides rules on how compute indentations. Defaults to ``github``. :parameter list_marker: a string that contains some of the first characters of the list element. Defaults to ``-``. :type parser: str :type list_marker: str :returns: indentation_log, the data structure. :rtype: dict[types.IndentationLogElement] :raises: a built-in exception. .. warning: this function does not make distinctions between ordered and unordered TOCs. """ return { i: { 'index': md_parser[parser]['list']['ordered']['min_marker_number'], 'list_marker': list_marker, 'indentation_spaces': 0, } for i in range(1, md_parser[parser]['header']['max_levels'] + 1) } def compute_toc_line_indentation_spaces( header_type_curr: int = 1, header_type_prev: int = 0, parser: str = 'github', ordered: bool = False, list_marker: str = '-', indentation_log: dict[types.IndentationLogElement] = init_indentation_log( 'github', '-'), index: int = 1, ): r"""Compute the number of indentation spaces for the TOC list element. :parameter header_type_curr: the current type of header (h[1,...,Inf]). Defaults to ``1``. :parameter header_type_prev: the previous type of header (h[1,...,Inf]). Defaults to ``0``. :parameter parser: decides rules on how compute indentations. Defaults to ``github``. :parameter ordered: if set to ``True``, numbers will be used as list ids instead of dash characters. Defaults to ``False``. :parameter list_marker: a string that contains some of the first characters of the list element. Defaults to ``-``. :parameter indentation_log: a data structure that holds list marker information for ordered lists. Defaults to ``init_indentation_log('github', '.')``. :parameter index: a number that will be used as list id in case of an ordered table of contents. Defaults to ``1``. :type header_type_curr: int :type header_type_prev: int :type parser: str :type ordered: bool :type list_marker: str :type indentation_log: dict[types.IndentationLogElement] :type index: int :returns: None :rtype: None :raises: a built-in exception. .. warning:: In case of ordered TOCs you must explicitly pass one of the supported ordered list markers. """ if not header_type_curr >= 1: raise ValueError if not header_type_prev >= 0: raise ValueError if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: if not len(indentation_log ) == md_parser['github']['header']['max_levels']: raise ValueError if not index >= 1: raise ValueError if parser in [ 'github', 'cmark', 'gitlab', 'commonmarker', 'goldmark', 'redcarpet' ]: if ordered: if list_marker not in md_parser[parser]['list']['ordered'][ 'closing_markers']: raise ValueError else: if list_marker not in md_parser[parser]['list']['unordered'][ 'bullet_markers']: raise ValueError if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: index_length: int if ordered: if header_type_prev == 0: index_length = 0 else: index_length = len( str(indentation_log[header_type_prev]['index']), ) else: index_length = 0 if header_type_prev == 0: # Base case for the first toc line. indentation_log[header_type_curr]['indentation_spaces'] = 0 elif header_type_curr > header_type_prev: # More indentation. indentation_log[header_type_curr]['indentation_spaces'] = ( indentation_log[header_type_prev]['indentation_spaces'] + len(indentation_log[header_type_prev]['list_marker'], ) + index_length + len(' ')) elif header_type_curr < header_type_prev: # Less indentation. Since we went "back" we must reset # all the sublists in the data structure. The indentation spaces are the ones # computed before. for i in range( header_type_curr + 1, md_parser['github']['header']['max_levels'] + 1, ): indentation_log[i]['index'] = md_parser['github']['list'][ 'ordered']['min_marker_number'] indentation_log[i]['indentation_spaces'] = 0 indentation_log[i]['list_marker'] = list_marker # And finally, in case of same indentation we have: header_type_curr = header_type_prev # so indentation_log[header_type_curr]['indentation_spaces'] = indentation_log[header_type_prev]['indentation_spaces'] # which is an identity. if ordered: indentation_log[header_type_curr]['index'] = index indentation_log[header_type_curr]['list_marker'] = list_marker elif parser in ['redcarpet']: indentation_log[header_type_curr]['indentation_spaces'] = 4 * ( header_type_curr - 1) def build_toc_line_without_indentation( header: types.Header, ordered: bool = False, no_links: bool = False, index: int = 1, parser: str = 'github', list_marker: str = '-', ) -> str: r"""Return a list element of the table of contents. :parameter header: a data structure that contains the original text, the trimmed text and the type of header. :parameter ordered: if set to ``True``, numbers will be used as list ids, otherwise a dash character. Defaults to ``False``. :parameter no_links: disables the use of links. Defaults to ``False``. :parameter index: a number that will be used as list id in case of an ordered table of contents. Defaults to ``1``. :parameter parser: decides rules on how compute indentations. Defaults to ``github``. :parameter list_marker: a string that contains some of the first characters of the list element. Defaults to ``-``. :type header: types.Header :type ordered: bool :type no_links: bool :type index: int :type parser: str :type list_marker: str :returns: toc_line_no_indent, a single line of the table of contents without indentation. :rtype: str :raises: a built-in exception. .. warning:: In case of ordered TOCs you must explicitly pass one of the supported ordered list markers. """ if not header['header_type'] >= 1: raise ValueError if not index >= 1: raise ValueError if parser in [ 'github', 'cmark', 'gitlab', 'commonmarker', 'goldmark', 'redcarpet' ]: if ordered: if list_marker not in md_parser[parser]['list']['ordered'][ 'closing_markers']: raise ValueError else: if list_marker not in md_parser[parser]['list']['unordered'][ 'bullet_markers']: raise ValueError if parser in [ 'github', 'cmark', 'gitlab', 'commonmarker', 'goldmark', 'redcarpet' ]: if ordered: list_marker: str = ''.join([str(index), list_marker]) # See # https://spec.commonmark.org/0.28/#example-472 # and # https://github.com/vmg/redcarpet/blob/e3a1d0b00a77fa4e2d3c37322bea66b82085486f/ext/redcarpet/markdown.c#L998 line: str if no_links: line = header['text_original'] else: line = ''.join([ '[', header['text_original'], ']', '(#', header['text_anchor_link'], ')' ]) return ''.join([list_marker, ' ', line]) def build_toc_line( toc_line_no_indent: str, no_of_indentation_spaces: int = 0, ) -> str: r"""Build the TOC line. :parameter toc_line_no_indent: the TOC line without indentation. :parameter no_of_indentation_spaces: the number of indentation spaces. Defaults to ``0``. :type toc_line_no_indent: str :type no_of_indentation_spaces: int :returns: toc_line, a single line of the table of contents. :rtype: str :raises: a built-in exception. :Example: >>> import md_toc >>> md_toc.api.build_toc_line('', 0) '' :Example: >>> import md_toc >>> md_toc.api.build_toc_line('my string', 10) ' my string' """ if not no_of_indentation_spaces >= 0: raise ValueError indentation: str = no_of_indentation_spaces * ' ' return ''.join([indentation, toc_line_no_indent]) def remove_html_tags(line: str, parser: str = 'github') -> str: r"""Remove HTML tags. :parameter line: a string. :parameter parser: decides rules on how to remove HTML tags. Defaults to ``github``. :type line: str :type parser: str :returns: the input string without HTML tags. :rtype: str :raises: a built-in exception. """ if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: # We need to match newline as well because it is a WS, so we # must use re.DOTALL. regexes: list[str] = [ md_parser[parser]['re']['OT'], md_parser[parser]['re']['CT'], md_parser[parser]['re']['CO'], md_parser[parser]['re']['PI'], md_parser[parser]['re']['DE'], md_parser[parser]['re']['CD'] ] for r in regexes: line = re.sub(r, '', line, flags=re.DOTALL) return line def remove_emphasis(line: str, parser: str = 'github') -> str: r"""Remove markdown emphasis. :parameter line: a string. :parameter parser: decides rules on how to find delimiters. Defaults to ``github``. :type line: str :type parser: str :returns: the input line without emphasis. :rtype: str :raises: a built-in exception. .. note:: Backslashes are preserved. :Example: >>> import md_toc >>> md_toc.api.remove_emphasis('__my string__ *is this* one') 'my string is this one' """ if parser in [ 'github', 'cmark', 'gitlab', 'commonmarker', 'goldmark', 'redcarpet' ]: mem = None refmap = references_h._cmarkCmarkReferenceMap() parent = node_h._cmarkCmarkNode() # Remove NULL bytes. line = line.replace('\x00', '') parent.data = line parent.length = len(line) parent.start_line = 0 parent.start_column = 0 parent.internal_offset = 1 ignore: list[range] = inlines_c._cmark_cmark_parse_inlines( mem, parent, refmap, 0) line = filter_indices_from_line(line, ignore) elif parser in ['redcarpet']: # TODO pass return line def filter_indices_from_line(line: str, ranges: list[range]) -> str: r"""Given a line and a Python ranges, remove the characters in the ranges. :parameter line: a string. :parameter ranges: a list of Python ranges. :type line: str :type ranges: list :returns: the line without the specified indices. :rtype: str :raises: a built-in exception. """ # Transform ranges into lists. rng: list[typing.Sequence[int]] = [list(r) for r in ranges] # Flatten list. rng_flat: list[int] = sorted([item for e in rng for item in e]) return ''.join([line[i] for i in range(0, len(line)) if i not in rng_flat]) def anchor_link_punctuation_filter( input_string: str, parser: str = 'github', ) -> str: r"""Remove punctuation and other unwanted characters from the anchor link string. :parameter input_string: the unfiltered anchor link :parameter parser: decides rules on how to generate anchor links. Defaults to ``github``. :type input_string: str :type parser: str :returns: a string without the unwanted characters. :rtype: str :raises: a built-in exception. .. note: license B applies for the github part. See docs/copyright_license.rst """ # https://github.com/gjtorikian/html-pipeline/blob/7c7fad1f82f81ebf15dd81d59eed28d979b8e441/lib/html/pipeline/toc_filter.rb#L30 output_string: str if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: # Remove punctuation: Keep spaces, hypens and "word characters" only. # In other words, given the set: # \w alphanumeric # \- hyphen # space # remove its complementary set from the input_string. This is achieved # using `^`. pattern: str = r'[^\w\- ]' replacement: str = '' output_string = re.sub(pattern, replacement, input_string) return output_string def build_anchor_link( header_text_trimmed: str, header_duplicate_counter: types.HeaderDuplicateCounter, parser: str = 'github', ) -> str: r"""Apply the specified slug rule to build the anchor link. :parameter header_text_trimmed: the text that needs to be transformed in a link. :parameter header_duplicate_counter: a data structure that keeps track of possible duplicate header links in order to avoid them. This is meaningful only for certain values of parser. :parameter parser: decides rules on how to generate anchor links. Defaults to ``github``. :type header_text_trimmed: str :type header_duplicate_counter: types.HeaderDuplicateCounter :type parser: str :returns: None if the specified parser is not recognized, or the anchor link, otherwise. :rtype: str :raises: a built-in exception. .. note: license A applies for the redcarpet part. See docs/copyright_license.rst :Example: >>> import md_toc >>> md_toc.api.build_anchor_link('This is an example test header', {}, 'gitlab') 'this-is-an-example-test-header' """ # Check for newlines. if len(replace_and_split_newlines(header_text_trimmed)) > 1: raise StringCannotContainNewlines if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: header_text_trimmed = header_text_trimmed.lower() # Filter HTML tags. header_text_trimmed = remove_html_tags(header_text_trimmed, parser) # Filter "emphasis and strong emphasis". header_text_trimmed = remove_emphasis(header_text_trimmed, parser) # Remove punctuation characters. header_text_trimmed = anchor_link_punctuation_filter( header_text_trimmed, parser) # Replace spaces with dash. header_text_trimmed = header_text_trimmed.replace(' ', '-') if parser in ['gitlab']: # See https://docs.gitlab.com/ee/user/markdown.html#header-ids-and-links # Two or more hyphens in a row are converted to one. header_text_trimmed = re.sub('-+', '-', header_text_trimmed) # Use a checksum to reduce memory usage on the dict: # # import sys # size = getsizeof(header_duplicate_counter) # size += sum(map(getsizeof, header_duplicate_counter.values())) + sum(map(getsizeof, header_duplicate_counter.keys())) # # See also # https://stackoverflow.com/a/6579816 ht_checksum = hashlib.sha1( header_text_trimmed.encode('UTF-8')).hexdigest() # Check for duplicates. # Set the initial value if we are examining the first occurrency. # The state of header_duplicate_counter is available to the caller # functions. if ht_checksum not in header_duplicate_counter: header_duplicate_counter[ht_checksum] = 0 if header_duplicate_counter[ht_checksum] > 0: header_text_trimmed = ''.join([ header_text_trimmed, '-', str(header_duplicate_counter[ht_checksum]) ]) header_duplicate_counter[ht_checksum] += 1 return header_text_trimmed elif parser in ['redcarpet']: # To ensure full compatibility what follows is a direct translation # of the rndr_header_anchor C function used in redcarpet. STRIPPED = " -&+$,/:;=?@\"#{}|^~[]`\\*()%.!'" header_text_trimmed_len = len(header_text_trimmed) inserted = 0 stripped = 0 header_text_trimmed_middle_stage = list() for i in range(0, header_text_trimmed_len): if header_text_trimmed[i] == '<': while i < header_text_trimmed_len and header_text_trimmed[ i] != '>': i += 1 elif header_text_trimmed[i] == '&': while i < header_text_trimmed_len and header_text_trimmed[ i] != ';': i += 1 # str.find() == -1 if character is not found in str. # https://docs.python.org/3.6/library/stdtypes.html?highlight=find#str.find elif not generic._isascii(header_text_trimmed[i]) or STRIPPED.find( header_text_trimmed[i], ) != -1: if inserted and not stripped: header_text_trimmed_middle_stage.append('-') stripped = 1 else: header_text_trimmed_middle_stage.append( header_text_trimmed[i].lower(), ) stripped = 0 inserted += 1 header_text_trimmed_middle_stage_str: str = ''.join( header_text_trimmed_middle_stage) if stripped > 0 and inserted > 0: header_text_trimmed_middle_stage_str = header_text_trimmed_middle_stage_str[ 0:-1] if inserted == 0 and header_text_trimmed_len > 0: hash: int = 5381 for i in range(0, header_text_trimmed_len): # Get the unicode representation with ord. # Unicode should be equal to ASCII in ASCII's range of # characters. hash = ((hash << 5) + hash) + ord(header_text_trimmed[i]) # This is equivalent to %x in C. In Python we don't have # the length problem so %x is equal to %lx in this case. # Apparently there is no %l in Python... header_text_trimmed_middle_stage_str = 'part-' + '{:x}'.format( hash) return header_text_trimmed_middle_stage_str return None def replace_and_split_newlines(line: str) -> list[str]: r"""Replace all the newline characters with line feeds and separate the components. :parameter line: a string. :type line: str :returns: a list of newline separated strings. :rtype: list[str] :raises: a built-in exception. """ line = line.replace('\r\n', '\n') line = line.replace('\r', '\n') # Ignore the sequences of consecutive first and last newline characters. # Since there is no need to process the last # empty line. line = line.lstrip('\n') line = line.rstrip('\n') return line.split('\n') def get_atx_heading( line: str, keep_header_levels: int = 3, parser: str = 'github', no_links: bool = False, ) -> list[types.AtxHeadingStructElement]: r"""Given a line extract the link label and its type. :parameter line: the line to be examined. This string may include newline characters in between. :parameter keep_header_levels: the maximum level of headers to be considered as such when building the table of contents. Defaults to ``3``. :parameter parser: decides rules on how to generate the anchor text. Defaults to ``github``. :parameter no_links: disables the use of links. :type line: str :type keep_header_levels: int :type parser: str :type no_links: bool :returns: struct, a list of dictionaries :rtype: list[types.AtxHeadingStructElement] :raises: GithubEmptyLinkLabel or GithubOverflowCharsLinkLabel or a built-in exception. .. note: license A applies for the redcarpet part. See docs/copyright_license.rst .. note:: license B applies for the github part. See docs/copyright_license.rst """ if not keep_header_levels >= 1: raise ValueError struct: list[types.AtxHeadingStructElement] = list() line_visible: bool = True if len(line) == 0: return [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }] for subl in replace_and_split_newlines(line): current_headers = None final_line = None if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: struct.append({ 'header_type': None, 'header_text_trimmed': None, 'visible': False }) # Empty substring or backslash. if len(subl) == 0 or subl[0] == '\u005c': continue # Preceding. i: int = 0 while i < len(subl) and subl[i] == ' ' and i <= md_parser[ 'github']['header']['max_space_indentation']: i += 1 if i > md_parser['github']['header']['max_space_indentation']: continue # ATX characters. offset = i while i < len(subl) and subl[i] == '#' and i <= md_parser[ 'github']['header']['max_levels'] + offset: i += 1 if (i - offset > md_parser['github']['header']['max_levels'] or i - offset == 0): continue # We need to continue parsing to find possible duplicate headers # in other functions if we set keep_header_levels < max_levels. if i - offset > keep_header_levels: line_visible: bool = False current_headers = i - offset # At this moment GFM is still at version 0.29 # while cmark is at 0.30. There are subtle differences # such as this one. Assume gitlab is on par with 0.30. if parser in ['github', 'commonmarker']: # GFM 0.29 and cmark 0.29. spaces = [' '] else: # cmark 0.30. spaces = [' ', '\u0009'] # Include special cases for line endings which should not be # discarded as non-ATX headers. if i < len(subl) and subl[i] not in spaces + ['\u000a', '\u000d']: continue i += 1 # Exclude leading whitespaces after the ATX header identifier. # [0.29]: # The opening sequence of `#` characters must be followed # by a space or by the end of line. # [0.30]: # The opening sequence of `#` characters must be followed # by spaces or tabs, or by the end of line. while i < len(subl) and subl[i] in spaces: i += 1 # An algorithm to find the start and the end of the closing sequence (cs). # The closing sequence includes all the significant part of the # string. This algorithm has a complexity of O(n) with n being the # length of the line. cs_start = i cs_end = cs_start # subl_prime =~ subl'. subl_prime = subl[::-1] len_subl = len(subl) hash_char_rounds = 0 go = True i = 0 hash_round_start = i # Ignore all characters after newlines and carrage returns which # are not at the end of the line. # See the two CRLF marker tests. crlf_marker = 0 stripped_crlf = False while i < len_subl - cs_start: if subl_prime[i] in ['\u000a', '\u000d']: crlf_marker = i stripped_crlf = True i += 1 # crlf_marker is the first CR LF character in the string. i = crlf_marker if stripped_crlf: # Skip last character only if is '\u000a', '\u000d'. i += 1 # We know for sure that from now '\u000a', '\u000d' will not be # considered. cs_end = i # Cut spaces and hashes. while go and i < len_subl - cs_start: if (subl_prime[i] not in spaces + ['#'] or hash_char_rounds > 1): if i > hash_round_start and hash_char_rounds > 0: cs_end = len_subl - hash_round_start else: cs_end = len_subl - i go = False if go: while subl_prime[i] in spaces: i += 1 hash_round_start = i while subl_prime[i] == '#': i += 1 if i > hash_round_start: hash_char_rounds += 1 final_line = subl[cs_start:cs_end] # Add escaping. if not no_links: if len(final_line) > 0 and final_line[-1] == '\u005c': final_line = ''.join([final_line, ' ']) if len( final_line.strip('\u0020').strip('\u0009').strip( '\u000a').strip('\u000b').strip('\u000c').strip( '\u000d'), ) == 0: raise GithubEmptyLinkLabel if len(final_line, ) > md_parser['github']['link']['max_chars_label']: raise GithubOverflowCharsLinkLabel i = 0 while i < len(final_line): # Escape square brackets if not already escaped. if (final_line[i] == '[' or final_line[i] == ']'): j = i - 1 consecutive_escape_characters = 0 while j >= 0 and final_line[j] == '\u005c': consecutive_escape_characters += 1 j -= 1 if ((consecutive_escape_characters > 0 and consecutive_escape_characters % 2 == 0) or consecutive_escape_characters == 0): tmp = '\u005c' else: tmp = '' final_line = ''.join( [final_line[0:i], tmp, final_line[i:]]) i += 1 + len(tmp) else: i += 1 # Overwrite the element with None as values. struct[-1] = { 'header_type': current_headers, 'header_text_trimmed': final_line, 'visible': line_visible } elif parser in ['redcarpet']: struct.append({ 'header_type': None, 'header_text_trimmed': None, 'visible': False }) if len(subl) == 0 or subl[0] != '#': continue i = 0 while (i < len(subl) and i < md_parser['redcarpet']['header']['max_levels'] and subl[i] == '#'): i += 1 current_headers = i if i < len(subl) and subl[i] != ' ': continue while i < len(subl) and subl[i] == ' ': i += 1 end = i while end < len(subl) and subl[end] != '\n': end += 1 while end > 0 and subl[end - 1] == '#': end -= 1 while end > 0 and subl[end - 1] == ' ': end -= 1 if end > i: final_line = subl if not no_links and len( final_line) > 0 and final_line[-1] == '\\': final_line += ' ' end += 1 final_line = final_line[i:end] if final_line is None: current_headers = None line_visible = False struct[-1] = { 'header_type': current_headers, 'header_text_trimmed': final_line, 'visible': line_visible } # TODO: escape or remove '[', ']', '(', ')' in inline links for redcarpet, # TODO: check link label rules for redcarpet. # TODO: check live_visible # endfor return struct def get_md_header( header_text_line: str, header_duplicate_counter: types.HeaderDuplicateCounter, keep_header_levels: int = 3, parser: str = 'github', no_links: bool = False, ) -> list[types.Header]: r"""Build a data structure with the elements needed to create a TOC line. :parameter header_text_line: a single markdown line that needs to be transformed into a TOC line. This line may include nmultiple newline characters in between. :parameter header_duplicate_counter: a data structure that contains the number of occurrencies of each header anchor link. This is used to avoid duplicate anchor links and it is meaningful only for certain values of parser. :parameter keep_header_levels: the maximum level of headers to be considered as such when building the table of contents. Defaults to ``3``. :parameter parser: decides rules on how to generate anchor links. Defaults to ``github``. :type header_text_line: str :type header_duplicate_counter: types.HeaderDuplicateCounter :type keep_header_levels: int :type parser: str :returns: a list with elements ``None`` if the input line does not correspond to one of the designated cases or a list of data structures containing the necessary components to create a table of contents. :rtype: list :raises: a built-in exception. .. note:: This works like a wrapper to other functions. """ return [ None if (r['header_type'] is None and r['header_text_trimmed'] is None) else { 'header_type': r['header_type'], 'text_original': r['header_text_trimmed'], 'text_anchor_link': build_anchor_link( r['header_text_trimmed'], header_duplicate_counter, parser, ), 'visible': r['visible'], } for r in get_atx_heading( header_text_line, keep_header_levels, parser, no_links, ) ] def is_valid_code_fence_indent(line: str, parser: str = 'github') -> bool: r"""Determine if the given line has valid indentation for a code block fence. :parameter line: a single markdown line to evaluate. :parameter parser: decides rules on how to generate the anchor text. Defaults to ``github``. :type line: str :type parser: str :returns: True if the given line has valid indentation or False otherwise. :rtype: bool :raises: a built-in exception. """ if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: return len(line) - len(line.lstrip( ' ')) <= md_parser['github']['code_fence']['min_marker_characters'] elif parser in ['redcarpet']: # TODO. return False return False def is_opening_code_fence(line: str, parser: str = 'github') -> str | None: r"""Determine if the given line is possibly the opening of a fenced code block. :parameter line: a single markdown line to evaluate. :parameter parser: decides rules on how to generate the anchor text. Defaults to ``github``. :type line: str :type parser: str :returns: None if the input line is not an opening code fence. Otherwise, returns the string which will identify the closing code fence according to the input parsers' rules. :rtype: typing.Optional[str] :raises: a built-in exception. """ if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: markers = md_parser['github']['code_fence']['marker'] marker_min_length = md_parser['github']['code_fence'][ 'min_marker_characters'] info_string: str info_string_start: int if not is_valid_code_fence_indent(line): return None line = line.lstrip(' ').rstrip('\n') if not line.startswith((markers['backtick'] * marker_min_length, markers['tilde'] * marker_min_length)): return None # The info string is the whole line except the first opening code # fence markers. if line == len(line) * line[0]: info_string = '' info_string_start = len(line) else: # Get the index where the info string starts. i: int = 0 done: bool = False while not done and i < len(line): if line[i] != line[0]: info_string_start = i done = True i += 1 info_string = line[info_string_start:len(line)] # [0.29] # The line with the opening code fence may optionally contain # some text following the code fence; this is trimmed of leading # and trailing whitespace and called the info string. # [0.30] # The line with the opening code fence may optionally contain # some text following the code fence; this is trimmed of leading # and trailing spaces or tabs and called the info string. # This does not change the end result of this function. if parser in ['github', 'commonmarker']: spaces = ' ' else: spaces = ' \u0009' info_string = info_string.strip(spaces) # Backticks or tildes in info strings are explicitly forbidden # in a backtick-opened code fence, for Commonmark 0.29. # This also solves example 107 [Commonmark 0.28]. See: # https://github.github.com/gfm/#example-107 if markers['backtick'] in info_string and line[0] != markers['tilde']: return None return line[0:info_string_start] elif parser in ['redcarpet']: # TODO. return None return None def is_closing_code_fence( line: str, fence: str, is_document_end: bool = False, parser: str = 'github', ) -> bool: r"""Determine if the given line is the end of a fenced code block. :parameter line: a single markdown line to evaluate. :paramter fence: a sequence of backticks or tildes marking the start of the current code block. This is usually the return value of the is_opening_code_fence function. :parameter is_document_end: This variable tells the function that the end of the file is reached. Defaults to ``False``. :parameter parser: decides rules on how to generate the anchor text. Defaults to ``github``. :type line: str :type fence: str :type is_document_end: bool :type parser: str :returns: True if the line ends the current code block. False otherwise. :rtype: bool :raises: a built-in exception. :Example: >>> import md_toc >>> md_toc.api.is_closing_code_fence('```', '```') True :Example: >>> import md_toc >>> md_toc.api.is_closing_code_fence('```', '~~~') False """ if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: markers = md_parser['github']['code_fence']['marker'] marker_min_length = md_parser['github']['code_fence'][ 'min_marker_characters'] if not is_valid_code_fence_indent(line): return False # Remove opening fence indentation after it is known to be valid. fence = fence.lstrip(' ') # Check if fence uses valid characters. if not fence.startswith((markers['backtick'], markers['tilde'])): return False if len(fence) < marker_min_length: return False # Additional security. fence = fence.rstrip(' \n') # Check that all fence characters are equal. if fence != len(fence) * fence[0]: return False # We might be inside a code block if this is not closed # by the end of the document, according to example 96 and 97. # This means that the end of the document corresponds to # a closing code fence. # Of course we first have to check that fence is a valid opening # code fence marker. # See: # example 96 [Commonmark 0.29] # example 97 [Commonmark 0.29] if is_document_end: return True # Check if line uses the same character as fence. line = line.lstrip(' ') if not line.startswith(fence): return False # [0.29] # The closing code fence may be indented up to three spaces, and may be # followed only by spaces, which are ignored. # [0.30] # The closing code fence may be preceded by up to three spaces of # indentation, and may be followed only by spaces or tabs, # which are ignored. if parser in ['github', 'commonmarker']: spaces = ' ' else: spaces = ' \u0009' line = line.rstrip(spaces + '\n') # Solves: # example 93 -> 94 [Commonmark 0.28] # example 94 -> 95 [Commonmark 0.29] if len(line) < len(fence): return False # Closing fence must not have alien characters. if line != len(line) * line[0]: return False return True elif parser in ['redcarpet']: # TODO. return False return False if __name__ == '__main__': pass md-toc-9.0.0/md_toc/cli.py000066400000000000000000000347061460560256400153130ustar00rootroot00000000000000# # cli.py # # Copyright (C) 2017-2024 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # """Command line interface file.""" import argparse import sys import textwrap from importlib import metadata from . import generic from .api import ( build_multiple_tocs, tocs_equal, write_strings_on_files_between_markers, ) from .constants import common_defaults from .constants import parser as md_parser PROGRAM_DESCRIPTION = 'Markdown Table Of Contents: Automatically generate a compliant table\nof contents for a markdown file to improve document readability.' VERSION_NAME = 'md_toc' VERSION_COPYRIGHT = 'Copyright (C) 2017-2023 Franco Masotti, frnmst' VERSION_LICENSE = 'License GPLv3+: GNU GPL version 3 or later \nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.' RETURN_VALUES = 'Return values: 0 ok, 1 error, 2 invalid command, 128 TOC differs from the one in the file (see --diff option)' try: VERSION_NUMBER = metadata.distribution('md_toc').version except metadata.PackageNotFoundError: VERSION_NUMBER = 'development version (venv)' PROGRAM_EPILOG = RETURN_VALUES + '\n\n' + VERSION_COPYRIGHT + '\n' + VERSION_LICENSE class CliToApi(): """An interface between the CLI and API functions.""" def write_toc(self, args) -> bool: """Write the table of contents.""" ordered = False if args.ordered_list_marker is not None: list_marker = args.ordered_list_marker ordered = True elif args.unordered_list_marker is not None: list_marker = args.unordered_list_marker newline_string = args.newline_string if newline_string == r'\n': newline_string = '\n' elif newline_string == r'\r': newline_string = '\r' if newline_string == r'\r\n': newline_string = '\r\n' toc_struct = build_multiple_tocs( filenames=args.filename, ordered=ordered, no_links=args.no_links, no_indentation=args.no_indentation, no_list_coherence=args.no_list_coherence, keep_header_levels=args.header_levels, parser=args.parser, list_marker=list_marker, skip_lines=args.skip_lines, constant_ordered_list=args.constant_ordered_list, newline_string=newline_string, ) equal: bool = True if args.in_place: equal = write_strings_on_files_between_markers( filenames=args.filename, strings=toc_struct, marker=args.toc_marker, newline_string=newline_string, ) else: for i, toc in enumerate(toc_struct): print(toc, end='') if args.diff: equal &= tocs_equal(toc, args.filename[i], args.toc_marker) diff: bool = False if equal is False: diff = True # Return that the TOC(s) is differing only if the `--diff` argument # was selected, i.e.: `args.diff`. return diff & args.diff class CliInterface(): """The interface exposed to the final user.""" def __init__(self): """Set the parser variable that will be used instead of using create_parser.""" self.parser = self.create_parser() def _add_filename_argument(self, parser): # The filename argument is common to all markdown parsers. # See commit b65cf32. parser.add_argument( 'filename', metavar='FILE_NAME', nargs='*', help='the I/O file name', ) def _add_cmark_like_megroup_arguments(self, megroup, parser_name: str): megroup.add_argument( '-u', '--unordered-list-marker', choices=md_parser[parser_name]['list']['unordered'] ['bullet_markers'], nargs='?', const=md_parser[parser_name]['list']['unordered'] ['default_marker'], default=md_parser[parser_name]['list']['unordered'] ['default_marker'], help='set the marker and enables unordered list. Defaults to ' + md_parser[parser_name]['list']['unordered']['default_marker'], ) megroup.add_argument( '-o', '--ordered-list-marker', choices=md_parser[parser_name]['list']['ordered'] ['closing_markers'], nargs='?', const=md_parser[parser_name]['list']['ordered'] ['default_closing_marker'], help=('set the marker and enable ordered lists. Defaults to ' + md_parser[parser_name]['list']['ordered'] ['default_closing_marker']), ) def _add_cmark_like_arguments(self, parser, parser_name: str): parser.add_argument( '-c', '--constant-ordered-list', action='store_true', help='intead of progressive numbers use a single integer as \ list marker. This options enables ordered lists', ) parser.add_argument( '-l', '--header-levels', type=int, choices=range(1, md_parser[parser_name]['header']['max_levels'] + 1), nargs='?', const=md_parser[parser_name]['header']['default_keep_levels'], help='set the maximum level of headers to be considered as part \ of the TOC. Defaults to ' + str(md_parser[parser_name]['header']['default_keep_levels'], ), ) def create_parser(self): """Create the CLI parser.""" parser = argparse.ArgumentParser( description=PROGRAM_DESCRIPTION, formatter_class=argparse.RawDescriptionHelpFormatter, epilog=textwrap.dedent(PROGRAM_EPILOG), ) # markdown parser is first positional argument. # File names are arguments to the subparser. subparsers = parser.add_subparsers( dest='parser', title='markdown parser', help=' --help', ) subparsers.required = True ######################### # github + commonmarker # ######################### github = subparsers.add_parser( 'github', aliases=['commonmarker'], description='Use GitHub Flavored Markdown rules to generate \ an output. If no \ option is selected, the default output will be an \ unordered list with the respective default values \ as listed below', ) self._add_filename_argument(github) megroup = github.add_mutually_exclusive_group() self._add_cmark_like_megroup_arguments(megroup, 'github') self._add_cmark_like_arguments(github, 'github') github.set_defaults(header_levels=md_parser['github']['header'] ['default_keep_levels'], ) ########## # gitlab # ########## gitlab = subparsers.add_parser( 'gitlab', description='Use GitLab Flavored Markdown rules to generate an \ output. If no \ option is selected, the default output will be an \ unordered list with the respective default values \ as listed below', ) self._add_filename_argument(gitlab) megroup = gitlab.add_mutually_exclusive_group() self._add_cmark_like_megroup_arguments(megroup, 'gitlab') self._add_cmark_like_arguments(gitlab, 'gitlab') gitlab.set_defaults(header_levels=md_parser['gitlab']['header'] ['default_keep_levels'], ) #################### # cmark + Goldmark # #################### cmark = subparsers.add_parser( 'cmark', aliases=['goldmark'], description='Use CommonMark rules to generate an output. If no \ option is selected, the default output will be an \ unordered list with the respective default values \ as listed below', ) self._add_filename_argument(cmark) megroup = cmark.add_mutually_exclusive_group() self._add_cmark_like_megroup_arguments(megroup, 'cmark') self._add_cmark_like_arguments(cmark, 'cmark') cmark.set_defaults(header_levels=md_parser['cmark']['header'] ['default_keep_levels'], ) ############# # Redcarpet # ############# redcarpet = subparsers.add_parser( 'redcarpet', description='Use Redcarpet rules to generate an output. If no \ option is selected, the default output will be an \ unordered list with the respective default values \ as listed below. Gitlab rules are the same as \ Redcarpet except that conflicts are avoided with \ duplicate headers.', ) self._add_filename_argument(redcarpet) megroup = redcarpet.add_mutually_exclusive_group() megroup.add_argument( '-u', '--unordered-list-marker', choices=md_parser['redcarpet']['list']['unordered'] ['bullet_markers'], nargs='?', const=md_parser['redcarpet']['list']['unordered'] ['default_marker'], default=md_parser['redcarpet']['list']['unordered'] ['default_marker'], help='set the marker and enables unordered list. Defaults to ' + md_parser['redcarpet']['list']['unordered']['default_marker'], ) megroup.add_argument( '-o', '--ordered-list-marker', choices=md_parser['redcarpet']['list']['ordered'] ['closing_markers'], nargs='?', const=md_parser['redcarpet']['list']['ordered'] ['default_closing_marker'], help='set the marker and enables ordered lists. Defaults to ' + md_parser['redcarpet']['list']['ordered'] ['default_closing_marker'], ) redcarpet.add_argument( '-c', '--constant-ordered-list', action='store_true', help='intead of progressive numbers use a single integer as list \ marker. This options enables ordered lists', ) redcarpet.add_argument( '-l', '--header-levels', type=int, choices=range( 1, md_parser['redcarpet']['header']['max_levels'] + 1, ), nargs='?', const=md_parser['redcarpet']['header']['default_keep_levels'], help='set the maximum level of headers to be considered as part \ of the TOC. Defaults to ' + str(md_parser['redcarpet']['header']['default_keep_levels'], ), ) redcarpet.set_defaults(header_levels=md_parser['redcarpet']['header'] ['default_keep_levels'], ) ########## # Common # ########## no_list_coherence_or_no_indentation = parser.add_mutually_exclusive_group( ) no_list_coherence_or_no_indentation.add_argument( '-c', '--no-list-coherence', action='store_true', help='avoids checking for TOC list coherence', ) no_list_coherence_or_no_indentation.add_argument( '-i', '--no-indentation', action='store_true', help='avoids adding indentations to the TOC', ) parser.add_argument( '-d', '--diff', action='store_true', help=( 'returns 128 if the newly generated TOC differs from the one \ already existing in the file'), ) parser.add_argument( '-l', '--no-links', action='store_true', help='avoids adding links to the TOC', ) parser.add_argument( '-m', '--toc-marker', metavar='TOC_MARKER', default=common_defaults['toc_marker'], help=( 'set the string to be used as the marker for positioning the \ table of contents. Put this value between single quotes. \ Defaults to \'' + common_defaults['toc_marker']) + '\'', ) parser.add_argument( '-n', '--newline-string', choices=[r'\n', r'\r', r'\r\n'], metavar='NEWLINE_STRING', type=str, default=common_defaults['newline_string'], help=('the string used to separate the lines of the TOC. \ Use single quotes to delimit the string. \ If you output in place all the newlines of the \ input file will be replaced with this value. \ Defaults to ' + repr(common_defaults['newline_string'])), ) parser.add_argument( '-p', '--in-place', action='store_true', help='overwrite the input file', ) parser.add_argument( '-s', '--skip-lines', metavar='SKIP_LINES', type=int, default=0, help='skip parsing of the first selected number of lines. \ Defaults to 0, i.e. do not skip any lines', ) parser.add_argument( '-v', '--version', action='version', version=VERSION_NAME + ' ' + VERSION_NUMBER, ) parser.set_defaults(func=CliToApi().write_toc) return parser md-toc-9.0.0/md_toc/cmark/000077500000000000000000000000001460560256400152555ustar00rootroot00000000000000md-toc-9.0.0/md_toc/cmark/__init__.py000066400000000000000000000013661460560256400173740ustar00rootroot00000000000000# # __init__.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # """Python discovery file.""" md-toc-9.0.0/md_toc/cmark/buffer_c.py000066400000000000000000000216411460560256400174060ustar00rootroot00000000000000# # buffer_c.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" import sys from ..constants import parser as md_parser from .buffer_h import _cmark_CMARK_BUF_INIT, _cmarkCmarkStrbuf from .cmark_ctype_c import _cmark_cmark_ispunct, _cmark_cmark_isspace from .cmark_h import _cmarkCmarkMem # License E applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst cmark_strbuf__initbuf: str = '' # 0.30 def _cmark_cmark_strbuf_len(buf: _cmarkCmarkStrbuf) -> int: return buf.size # 0.30 def _cmark_strbuf_free(buf: _cmarkCmarkStrbuf): if not buf: return if buf.ptr != cmark_strbuf__initbuf: # buf.mem.free(buf.ptr) del buf.ptr _cmark_cmark_strbuf_init(buf.mem, buf, 0) # 0.29, 0.30 def _cmark_cmark_strbuf_init(mem: _cmarkCmarkMem, buf: _cmarkCmarkStrbuf, initial_size: int): buf.mem = mem buf.asize = 0 buf.size = 0 buf.ptr = '' if initial_size > 0: _cmark_cmark_strbuf_grow(buf, initial_size) # 0.30 def _cmark_S_strbuf_grow_by(buf: _cmarkCmarkStrbuf, add: int): _cmark_cmark_strbuf_grow(buf, buf.size + add) # 0.29, 0.30 def _cmark_cmark_strbuf_grow(buf: _cmarkCmarkStrbuf, target_size: int): # Instead of using assert just raise a ValueError if target_size <= 0: raise ValueError if target_size < buf.asize: return # Usually it is this value and it is defined in stdint.h. INT32_MAX = (2 << 30) - 1 # Truncate number to a length of 30 bits. target_size &= INT32_MAX if target_size > int(INT32_MAX / 2): print('[cmark] _cmark_cmark_strbuf_grow requests buffer with size > ' + str(INT32_MAX / 2) + ', aborting') sys.exit(1) # Oversize the buffer by 50% to guarantee amortized linear time # complexity on append operations. # See also # https://codeyarns.com/tech/2019-03-06-integer-division-in-c.html # for the integer division. new_size: int = target_size + int(target_size / 2) new_size += 1 new_size = (new_size + 7) & ~7 # No need to malloc. # if buf.asize: # buf->ptr = buf->mem->realloc(buf->ptr, new_size); # else: # buf->ptr = buf->mem->malloc(newsize) buf.asize = new_size # 0.29, 0.30 def _cmark_cmark_strbuf_clear(buf: _cmarkCmarkStrbuf): buf.size = 0 if buf.asize > 0: del buf.ptr buf.ptr = '' # 0.29, 0.30 def _cmark_cmark_strbuf_set(buf: _cmarkCmarkStrbuf, data: str, length: int): if length <= 0 or data is None: _cmark_cmark_strbuf_clear(buf) else: if data != buf.ptr: if length >= buf.asize: _cmark_cmark_strbuf_grow(buf, length) # alternative to # memmove(buf->ptr, data, len) buf.ptr = ''.join([data[:length], buf.ptr[length:]]) buf.size = length # No need to set termination character # buf.ptr[buf.size] = '\0' # 0.30 # Add a single character to a buffer. def _cmark_cmark_strbuf_putc(buf: _cmarkCmarkStrbuf, c: int): _cmark_S_strbuf_grow_by(buf, 1) # buf->ptr[buf->size++] = (unsigned char)(c & 0xFF); buf.ptr = ''.join([buf.ptr, chr(c & 0xFF)]) buf.size += 1 # No need for the terminator character. # buf->ptr[buf->size] = '\0'; # 0.30 def _cmark_cmark_strbuf_put( buf: _cmarkCmarkStrbuf, data: str, length: int, ): dt: str if length <= 0: return _cmark_S_strbuf_grow_by(buf, length) # Alternative to # memmove(buf->ptr + buf->size, data, len) if isinstance(data, list): # See # https://stackoverflow.com/a/5661889 dt = bytearray(data).decode('UTF-8') else: dt = data # buf.ptr = # buf.ptr[0] -> buf.ptr[buf.size - 1] # + # dt[0] -> dt[length - 1] # + # buf.ptr[buf.size + 1 + length] -> buf.ptr[-1] buf.ptr = ''.join( [buf.ptr[:buf.size], dt[:length], buf.ptr[buf.size + 1 + length:]]) buf.size += length # No need for line terminator. # buf.ptr[buf.size] = '\0'; # 0.30 def _cmark_cmark_strbuf_puts(buf: _cmarkCmarkStrbuf, string: str): _cmark_cmark_strbuf_put(buf, string, len(string)) # 0.29, 0.30 def _cmark_cmark_strbuf_detach(buf: _cmarkCmarkStrbuf) -> str: data: str = buf.ptr if buf.asize == 0: # return an empty string # return (unsigned char *)buf->mem->calloc(1, 1); return '' _cmark_cmark_strbuf_init(buf.mem, buf, 0) return data # 0.30 def _cmark_cmark_strbuf_strchr(buf: _cmarkCmarkStrbuf, c: int, pos: int) -> int: if pos >= buf.size: return -1 if pos < 0: pos = 0 # `p` is the memory address (so absolute) where the character `c` lies. # Here we use relative indices. # const unsigned char *p = # (unsigned char *)memchr(buf.ptr + pos, c, buf.size - pos); p: int = buf.ptr[pos:buf.size - pos + 1].find(chr(c)) # `find` returns -1 if nothing is found if p == -1: return -1 # Pointer arithmetics: return the index of the buf->ptr string where # the character `c` lies. # Add the offset (`pos`) to start counting from the start of the string. # return (bufsize_t)(p - (const unsigned char *)buf->ptr); return p + pos # 0.29, 0.30 def _cmark_cmark_strbuf_truncate(buf: _cmarkCmarkStrbuf, length: int): if length < 0: length = 0 if length < buf.size: buf.size = length # No need for the terminator character. # buf.ptr[buf.size] = '\0' # 0.29, 0.30 def _cmark_cmark_strbuf_drop(buf: _cmarkCmarkStrbuf, n: int): if n > 0: if n > buf.size: n = buf.size buf.size = buf.size - n if buf.size: # Alternative to # memmove(buf->ptr, buf->ptr + n, buf->size); buf.ptr = ''.join( [buf.ptr[:n], buf.ptr[n:buf.size + n], buf.ptr[n + buf.size:]]) # No need for the terminator character. # buf->ptr[buf->size] = '\0'; def _cmark_cmark_strbuf_rtrim(buf: _cmarkCmarkStrbuf): if not buf.size: return while buf.size > 0: if not _cmark_cmark_isspace(ord(buf.ptr[buf.size - 1])): break buf.size -= 1 # buf->ptr[buf->size] = '\0'; # 0.30 def _cmark_cmark_strbuf_trim(buf: _cmarkCmarkStrbuf): i: int = 0 if not buf.size: return while i < buf.size and _cmark_cmark_isspace(ord(buf.ptr[i])): i += 1 _cmark_cmark_strbuf_drop(buf, i) _cmark_cmark_strbuf_rtrim(buf) # Destructively modify string, collapsing consecutive # space and newline characters into a single space. # 0.30 def _cmark_cmark_strbuf_normalize_whitespace(s: _cmarkCmarkStrbuf): last_char_was_space: bool = False r: int = 0 w: int = 0 for r in range(0, s.size): if _cmark_cmark_isspace(ord(s.ptr[r])): if not last_char_was_space: s.ptr = s.ptr[0:w - 1] + ' ' + s.ptr[w + 1:] w += 1 last_char_was_space = True else: s.ptr = s.ptr[0:w - 1] + s.ptr[r] + s.ptr[w + 1:] w += 1 last_char_was_space = False _cmark_cmark_strbuf_truncate(s, w) # 0.30 # Destructively unescape a string: remove backslashes before punctuation chars. def _cmark_cmark_strbuf_unescape(buf: _cmarkCmarkStrbuf): r: int = 0 w: int = 0 # In Python 3 all characters have length 1. # In C they might be from 1 to 4. # See the UTF-8 page on Wikipedia # For example: # # len(bytes('㤀', 'utf-8')) == 3 # bptr = ['foo%20', '㤀', ''] # sum([len(bptr[i]) for i in range(0, len(bptr))]) # buf.size == 10 # # So instead of # # while r < buf.size # # we have to put # # while r < min(buf.size, len(buf.ptr)) # # or simply # # while r < len(buf.ptr) # while r < len(buf.ptr): if buf.ptr[r] == '\\' and _cmark_cmark_ispunct(ord(buf.ptr[r + 1])): r += 1 # buf->ptr[w+] = buf->ptr[r]; buf.ptr = ''.join([buf.ptr[:w], buf.ptr[r], buf.ptr[w + 1:]]) w += 1 # ++r r += 1 _cmark_cmark_strbuf_truncate(buf, w) if __name__ == '__main__': pass md-toc-9.0.0/md_toc/cmark/buffer_h.py000066400000000000000000000026431460560256400174140ustar00rootroot00000000000000# # buffer_h.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" from dataclasses import dataclass from typing import Optional from .cmark_h import _cmarkCmarkMem # License E applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst # 0.29, 0.30 @dataclass class _cmarkCmarkStrbuf: mem: Optional[_cmarkCmarkMem] = None ptr: str = '' asize: int = 0 size: int = 0 # Should be equivalent to # #define CMARK_BUF_INIT(mem) \ # { mem, cmark_strbuf__initbuf, 0, 0 } # 0.29, 0.30 def _cmark_CMARK_BUF_INIT(mem: _cmarkCmarkMem): b = _cmarkCmarkStrbuf() b.mem = mem return b if __name__ == '__main__': pass md-toc-9.0.0/md_toc/cmark/chunk_h.py000066400000000000000000000046201460560256400172500ustar00rootroot00000000000000# # chunk_h.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""The cmark implementation file.""" import copy from dataclasses import dataclass from ..constants import parser as md_parser from .cmark_ctype_c import _cmark_cmark_isspace # License E applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst # Returns 1 if c is a "whitespace" character as defined by the spec. # int cmark_isspace(char c) { return cmark_ctype_class[(uint8_t)c] == 1; } # The only defined whitespaces in the spec are Unicode whitespaces. # 0.30 @dataclass class _cmarkCmarkChunk: data: str = None length: int = 0 # 0.30 def _cmark_cmark_chunk_free(c: _cmarkCmarkChunk): c.data = None c.length = 0 # 0.30 def _cmark_cmark_chunk_ltrim(c: _cmarkCmarkChunk): while c.length > 0 and _cmark_cmark_isspace(ord(c.data[0])): c.data = c.data[1:] c.length -= 1 # 0.30 def _cmark_cmark_chunk_rtrim(c: _cmarkCmarkChunk): while c.length > 0: if not _cmark_cmark_isspace(ord(c.data[c.length - 1])): break c.length -= 1 # 0.30 def _cmark_cmark_chunk_trim(c: _cmarkCmarkChunk): _cmark_cmark_chunk_ltrim(c) _cmark_cmark_chunk_rtrim(c) # 0.30 def _cmark_cmark_chunk_literal(data: str) -> _cmarkCmarkChunk: length: int c: _cmarkCmarkChunk if data is not None: length = len(data) else: length = 0 c = _cmarkCmarkChunk(data, length) return c # 0.29, 0.30 def _cmark_cmark_chunk_dup(ch: _cmarkCmarkChunk, pos: int, length: int) -> _cmarkCmarkChunk: c = _cmarkCmarkChunk(copy.deepcopy(ch.data[pos:]), length) return c if __name__ == '__main__': pass md-toc-9.0.0/md_toc/cmark/cmark_ctype_c.py000066400000000000000000000031611460560256400204330ustar00rootroot00000000000000# # cmark_ctype_c.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""The cmark implementation file.""" import string import unicodedata from ..constants import parser as md_parser # License C applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst # Return True if c is a "whitespace" character as defined by the spec. # 0.30 def _cmark_cmark_isspace(char: int) -> bool: # A Unicode whitespace character is any code point in the Unicode Zs # general category, or a tab (U+0009), line feed (U+000A), form feed # (U+000C), or carriage return (U+000D). return (unicodedata.category(chr(char)) == 'Zs' or chr(char) in ['\u0009', '\u000A', '\u000C', '\u000D']) # Return True if c is an ascii punctuation character. # 0.29, 0.30 def _cmark_cmark_ispunct(char: int) -> bool: return chr(char) in string.punctuation if __name__ == '__main__': pass md-toc-9.0.0/md_toc/cmark/cmark_h.py000066400000000000000000000050661460560256400172420ustar00rootroot00000000000000# # cmark_h.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" from enum import Enum # License C applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst # Defines the memory allocation functions to be used by CMark # when parsing and allocating a document tree # typedef struct cmark_mem { # void *(*calloc)(size_t, size_t); # void *(*realloc)(void *, size_t); # void (*free)(void *); # } cmark_mem; class _cmarkCmarkMem: pass # C enum # typedef enum { ... } cmark_node_type; # Undefined value in the C source code get their value # accoring to their position in the sequence, like an array. class _cmarkCmarkNodeType(Enum): # Error status. CMARK_NODE_NONE = 0 # Block. CMARK_NODE_DOCUMENT = 1 CMARK_NODE_BLOCK_QUOTE = 2 CMARK_NODE_LIST = 3 CMARK_NODE_ITEM = 4 CMARK_NODE_CODE_BLOCK = 5 CMARK_NODE_HTML_BLOCK = 6 CMARK_NODE_CUSTOM_BLOCK = 7 CMARK_NODE_PARAGRAPH = 8 CMARK_NODE_HEADING = 9 CMARK_NODE_THEMATIC_BREAK = 10 CMARK_NODE_FIRST_BLOCK = CMARK_NODE_DOCUMENT CMARK_NODE_LAST_BLOCK = CMARK_NODE_THEMATIC_BREAK # Inline CMARK_NODE_TEXT = 11 CMARK_NODE_SOFTBREAK = 12 CMARK_NODE_LINEBREAK = 13 CMARK_NODE_CODE = 14 CMARK_NODE_HTML_INLINE = 15 CMARK_NODE_CUSTOM_INLINE = 16 CMARK_NODE_EMPH = 17 CMARK_NODE_STRONG = 18 CMARK_NODE_LINK = 19 CMARK_NODE_IMAGE = 20 CMARK_NODE_FIRST_INLINE = CMARK_NODE_TEXT CMARK_NODE_LAST_INLINE = CMARK_NODE_IMAGE # For backwards compatibility: CMARK_NODE_HEADER = CMARK_NODE_HEADING CMARK_NODE_HRULE = CMARK_NODE_THEMATIC_BREAK CMARK_NODE_HTML = CMARK_NODE_HTML_BLOCK CMARK_NODE_INLINE_HTML = CMARK_NODE_HTML_INLINE CMARK_OPT_SOURCEPOS = 1 << 1 CMARK_OPT_SMART = 1 << 10 if __name__ == '__main__': pass md-toc-9.0.0/md_toc/cmark/houdini_h.py000066400000000000000000000020611460560256400175740ustar00rootroot00000000000000# # houdini_h.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" # License F applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst def _cmark_HOUDINI_ESCAPED_SIZE(x: int) -> float: return (x * 12) / 10 def _cmark_HOUDINI_UNESCAPED_SIZE(x: int) -> int: return x md-toc-9.0.0/md_toc/cmark/houdini_html_u_c.py000066400000000000000000000127571460560256400211540ustar00rootroot00000000000000# # houdini_html_u_c.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" import re from ..constants import parser as md_parser from ..generic import _strncmp from .buffer_c import ( _cmark_cmark_strbuf_grow, _cmark_cmark_strbuf_put, _cmark_cmark_strbuf_putc, _cmark_cmark_strbuf_puts, ) from .buffer_h import _cmarkCmarkStrbuf from .houdini_h import _cmark_HOUDINI_UNESCAPED_SIZE from .utf8_c import _cmark_cmark_utf8proc_encode_char # License F applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst # Recursive function of a binary search. # 0.30. def _cmark_S_lookup(i: int, low: int, hi: int, s: str, length: int) -> str: j: int cmp: int = _strncmp( s, md_parser['cmark']['re']['ENTITIES']['entities'][i]['entity'], length) # if (cmp == 0 && cmark_entities[i].entity[len] == 0) { if cmp == 0 and length == len( md_parser['cmark']['re']['ENTITIES']['entities'][i]['entity']): return md_parser['cmark']['re']['ENTITIES']['entities'][i]['bytes'] elif cmp == -1 and i > low: j = i - int((i - low) / 2) if j == i: j -= 1 return _cmark_S_lookup(j, low, i - 1, s, length) elif cmp == 1 and i < hi: j = i + int((hi - i) / 2) if j == i: j += 1 return _cmark_S_lookup(j, i + 1, hi, s, length) else: return None # 0.30. def _cmark_S_lookup_entity(s: str, length: int): return _cmark_S_lookup( int(md_parser['cmark']['re']['ENTITIES']['CMARK_NUM_ENTITIES'] / 2), 0, md_parser['cmark']['re']['ENTITIES']['CMARK_NUM_ENTITIES'] - 1, s, length) # 0.30. def _cmark_houdini_unescape_ent(ob: _cmarkCmarkStrbuf, src: str, size: int) -> int: i: int = 0 if size >= 3 and src[0] == '#': codepoint: int = 0 num_digits: int = 0 max_digits: int = 7 if re.match(r'\d', src[1]): i = 1 while i < size and re.match(r'\d', src[i]): codepoint = (codepoint * 10) + (ord(src[i]) - ord('0')) if codepoint >= 0x110000: # Keep counting digits but # avoid integer overflow. codepoint = 0x110000 i += 1 num_digits = i - 1 max_digits = 7 elif src[1] == 'x' or src[1] == 'X': i = 2 while i < size and re.match(r'[\dA-Fa-f]', src[i]): codepoint = (codepoint * 16) + ((ord(src[i]) | 32) % 39 - 9) if codepoint >= 0x110000: # Keep counting digits but # avoid integer overflow. codepoint = 0x110000 i += 1 num_digits = i - 2 max_digits = 6 if (num_digits >= 1 and num_digits <= max_digits and i < size and src[i] == ';'): if (codepoint == 0 or (codepoint >= 0xD800 and codepoint < 0xE000) or codepoint >= 0x110000): codepoint = 0xFFFD _cmark_cmark_utf8proc_encode_char(codepoint, ob) return i + 1 else: if size > md_parser['cmark']['re']['ENTITIES'][ 'CMARK_ENTITY_MAX_LENGTH']: size = md_parser['cmark']['re']['ENTITIES'][ 'CMARK_ENTITY_MAX_LENGTH'] for i in range( md_parser['cmark']['re']['ENTITIES'] ['CMARK_ENTITY_MIN_LENGTH'], size): if src[i] == ' ': break if src[i] == ';': entity: str = _cmark_S_lookup_entity(src, i) if entity is not None: _cmark_cmark_strbuf_puts(ob, entity) return i + 1 break return 0 # 0.30. def _cmark_houdini_unescape_html( ob: _cmarkCmarkStrbuf, src: str, size: int, ) -> int: i: int = 0 org: int ent: int while i < size: org = i while i < size and src[i] != '&': i += 1 if i > org: if org == 0: if i >= size: return 0 _cmark_cmark_strbuf_grow(ob, _cmark_HOUDINI_UNESCAPED_SIZE(size)) _cmark_cmark_strbuf_put(ob, src[org:], i - org) # escaping if i >= size: break i += 1 ent = _cmark_houdini_unescape_ent(ob, src[i:], size - i) i += ent # not really an entity if ent == 0: _cmark_cmark_strbuf_putc(ob, ord('&')) return 1 def _cmark_houdini_unescape_html_f( ob: _cmarkCmarkStrbuf, src: str, size: int, ): if not _cmark_houdini_unescape_html(ob, src, size): _cmark_cmark_strbuf_put(ob, src, size) md-toc-9.0.0/md_toc/cmark/inlines_c.py000066400000000000000000001636161460560256400176070ustar00rootroot00000000000000# # inlines_c.py # # Copyright (C) 2017-2023 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" import copy from ..constants import parser as md_parser from ..generic import _replace_substring from .buffer_c import ( _cmark_cmark_strbuf_detach, _cmark_cmark_strbuf_drop, _cmark_cmark_strbuf_puts, _cmark_cmark_strbuf_set, _cmark_cmark_strbuf_truncate, _cmark_cmark_strbuf_unescape, ) from .buffer_h import _cmark_CMARK_BUF_INIT, _cmarkCmarkStrbuf from .chunk_h import ( _cmark_cmark_chunk_dup, _cmark_cmark_chunk_free, _cmark_cmark_chunk_literal, _cmark_cmark_chunk_rtrim, _cmark_cmark_chunk_trim, _cmarkCmarkChunk, ) from .cmark_ctype_c import _cmark_cmark_ispunct, _cmark_cmark_isspace from .cmark_h import ( CMARK_OPT_SMART, CMARK_OPT_SOURCEPOS, _cmarkCmarkMem, _cmarkCmarkNodeType, ) from .houdini_html_u_c import ( _cmark_houdini_unescape_ent, _cmark_houdini_unescape_html, _cmark_houdini_unescape_html_f, ) from .node_c import ( _cmark_cmark_node_free, _cmark_cmark_node_insert_after, _cmark_cmark_node_insert_before, _cmark_cmark_node_set_literal, _cmark_cmark_node_unlink, ) from .node_h import _cmarkCmarkNode from .references_c import _cmark_cmark_reference_lookup from .references_h import _cmarkCmarkReference, _cmarkCmarkReferenceMap from .scanners_h import ( _cmark_scan_autolink_email, _cmark_scan_autolink_uri, _cmark_scan_html_cdata, _cmark_scan_html_comment, _cmark_scan_html_declaration, _cmark_scan_html_pi, _cmark_scan_html_tag, _cmark_scan_link_title, _cmark_scan_spacechars, ) from .utf8_c import ( _cmark_cmark_utf8proc_is_punctuation, _cmark_cmark_utf8proc_is_space, _cmark_cmark_utf8proc_iterate, ) # License C applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst # 'EMDASH': '—', EMDASH = '\xE2\x80\x94' # 'ENDASH': '–', ENDASH = '\xE2\x80\x93' # 'ELLIPSES': '…', ELLIPSES = '\xE2\x80\xA6' # 'LEFTDOUBLEQUOTE': '“', LEFTDOUBLEQUOTE = '\xE2\x80\x9C' # 'RIGHTDOUBLEQUOTE': 'â€', RIGHTDOUBLEQUOTE = '\xE2\x80\x9D' # 'LEFTSINGLEQUOTE': '‘', # # b'\xE2\x80\x99'.decode('utf-8') LEFTSINGLEQUOTE = '\xE2\x80\x98' # 'RIGHTSINGLEQUOTE': '’', RIGHTSINGLEQUOTE = '\xE2\x80\x99' MAXBACKTICKS = 1000 FLAG_SKIP_HTML_CDATA = 1 << 0 FLAG_SKIP_HTML_DECLARATION = 1 << 1 FLAG_SKIP_HTML_PI = 1 << 2 # 0.29, 0.30 def _cmark_make_linebreak(mem: _cmarkCmarkMem): return _cmark_make_simple(mem, _cmarkCmarkNodeType.CMARK_NODE_LINEBREAK.value) def _cmark_make_softbreak(mem: _cmarkCmarkMem): return _cmark_make_simple(mem, _cmarkCmarkNodeType.CMARK_NODE_SOFTBREAK.value) def _cmark_make_emph(mem: _cmarkCmarkMem): return _cmark_make_simple(mem, _cmarkCmarkNodeType.CMARK_NODE_EMPH.value) def _cmark_make_strong(mem: _cmarkCmarkMem): return _cmark_make_simple(mem, _cmarkCmarkNodeType.CMARK_NODE_STRONG.value) class _cmarkDelimiter: r"""A list node with attributes useful for processing emphasis.""" __slots__ = [ 'previous', 'next', 'inl_text', 'length', 'delim_char', 'can_open', 'can_close', 'offset', ] def __init__(self): self.previous: _cmarkDelimiter = None self.next: _cmarkDelimiter = None self.inl_text: _cmarkCmarkNode = None self.length: int = 0 self.delim_char = '' self.can_open: bool = False self.can_close: bool = False # Extra attribute. self.offset: int = 0 class _cmarkBracket: __slots__ = [ 'previous', 'previous_delimiter', 'inl_text', 'position', 'image', 'active', 'bracket_after', ] def __init__(self): self.previous: _cmarkBracket = None self.previous_delimiter: _cmarkDelimiter = None self.inl_text: _cmarkCmarkNode = None self.position: bool = 0 self.image: bool = False self.active: bool = True self.bracket_after: bool = False class _cmarkSubject: r"""A double linked list useful for processing emphasis.""" __slots__ = [ 'mem', 'input', 'flags', 'line', 'pos', 'block_offset', 'column_offset', 'refmap', 'last_delim', 'last_bracket', 'backticks', 'scanned_for_backticks', ] def __init__(self): self.mem: _cmarkCmarkMem = None self.input: _cmarkCmarkChunk = None self.flags: int = 0 self.line: int = 0 self.pos: int = 0 self.block_offset: int = 0 self.column_offset: int = 0 self.refmap: _cmarkCmarkReferenceMap = None self.last_delim: _cmarkDelimiter = None self.last_bracket: _cmarkBracket = None # An integer list. self.backticks: list = list(range(0, MAXBACKTICKS + 1)) self.scanned_for_backticks: bool = False # 0.30 def _cmark_S_is_line_end_char(c: str) -> bool: return c == '\n' or c == '\r' # 0.30 def _cmark_make_literal(subj: _cmarkSubject, t: int, start_column: int, end_column: int) -> _cmarkCmarkNode: r"""Create an inline with a literal string value.""" e = _cmarkCmarkNode() e.mem = copy.deepcopy(subj.mem) e.type = t e.start_line = e.end_line = subj.line # columns are 1 based. e.start_column: int = start_column + 1 + subj.column_offset + subj.block_offset e.end_column: int = end_column + 1 + subj.column_offset + subj.block_offset return e # 0.30 def _cmark_make_simple(mem: _cmarkCmarkMem, t: int) -> _cmarkCmarkNode: e = _cmarkCmarkNode() e.mem = copy.deepcopy(mem) e.type = t return e # 0.30 def _cmark_make_str(subj: _cmarkSubject, sc: int, ec: int, s: _cmarkCmarkChunk) -> _cmarkCmarkNode: # s = char # sc = start column # ec = end cloumn e = _cmark_make_literal(subj, _cmarkCmarkNodeType.CMARK_NODE_TEXT.value, sc, ec) # e->data = (unsigned char *)subj->mem->realloc(NULL, s.len + 1); # if (s.data != NULL) { # memcpy(e->data, s.data, s.len); # } if s.data is not None: e.data = copy.deepcopy(s.data[:s.length]) # No need to add line terminator (\0). # e->data[s.len] = 0; e.length = s.length return e def _cmark_make_str_from_buf( subj: _cmarkSubject, sc: int, ec: int, buf: _cmarkCmarkStrbuf, ) -> _cmarkCmarkNode: e: _cmarkCmarkNode = _cmark_make_literal( subj, _cmarkCmarkNodeType.CMARK_NODE_TEXT.value, sc, ec) e.length = buf.size e.data = _cmark_cmark_strbuf_detach(buf) return e # Like make_str, but parses entities. def _cmark_make_str_with_entities( subj: _cmarkSubject, start_column: int, end_column: int, content: _cmarkCmarkChunk, ) -> _cmarkCmarkNode: unescaped: _cmarkCmarkStrbuf = _cmark_CMARK_BUF_INIT(subj.mem) if _cmark_houdini_unescape_html(unescaped, content.data, content.length): return _cmark_make_str_from_buf(subj, start_column, end_column, unescaped) else: return _cmark_make_str(subj, start_column, end_column, content) # Like cmark_node_append_child but without costly sanity checks. # Assumes that child was newly created. def _cmark_append_child(node: _cmarkCmarkNode, child: _cmarkCmarkNode): old_last_child: _cmarkCmarkNode = node.last_child child.next = None child.prev = old_last_child child.parent = node node.last_child = child if old_last_child: old_last_child.next = child else: # Also set first_child if node previously had no children. node.first_child = child # Duplicate a chunk by creating a copy of the buffer not by reusing the # buffer like cmark_chunk_dup does. # 0.30 def _cmark_cmark_strdup(mem: _cmarkCmarkMem, src: str) -> str: if src is None: return None length: int = len(src) data: str = copy.deepcopy(src[:length + 1]) return data def _cmark_cmark_clean_autolink(mem: _cmarkCmarkMem, url: _cmarkCmarkChunk, is_email: int): buf: _cmarkCmarkStrbuf = _cmark_CMARK_BUF_INIT(mem) _cmark_cmark_chunk_trim(url) if is_email: _cmark_cmark_strbuf_puts(buf, 'mailto:') _cmark_houdini_unescape_html_f(buf, url.data, url.length) return _cmark_cmark_strbuf_detach(buf) # 0.30 def _cmark_make_autolink( subj: _cmarkSubject, start_column: int, end_column: int, url: _cmarkCmarkChunk, is_email: int, ) -> _cmarkCmarkNode: link: _cmarkCmarkNode = _cmark_make_simple( subj.mem, _cmarkCmarkNodeType.CMARK_NODE_LINK.value) link.as_link.url = _cmark_cmark_clean_autolink(subj.mem, url, is_email) link.as_link.title = None link.start_line = link.end_line = subj.line link.start_column = start_column + 1 link.end_column = end_column + 1 _cmark_append_child( link, _cmark_make_str_with_entities(subj, start_column + 1, end_column - 1, url)) return link # 0.30 def _cmark_subject_from_buf( mem: _cmarkCmarkMem, line_number: int, block_offset: int, e: _cmarkSubject, chunk: _cmarkCmarkChunk, refmap: _cmarkCmarkReferenceMap, ): i: int e.mem = mem e.input = chunk e.flags = 0 e.line = line_number e.pos = 0 e.block_offset = block_offset e.column_offset = 0 e.refmap = refmap e.last_delim = None e.last_bracket = None for i in range(0, MAXBACKTICKS): e.backticks[i] = 0 e.scanned_for_backticks = False # 0.30 def _cmark_isbacktick(c: int) -> bool: backtick: bool = False if chr(c) == '`': backtick = True return backtick # 0.29, 0.30 def _cmark_peek_char(subj: _cmarkSubject) -> int: # Instead of using assert just raise a ValueError # # NULL bytes should have been stripped out by now. If they're # present, it's a programming error: if subj.pos < subj.input.length and ord(subj.input.data[subj.pos]) == 0: raise ValueError if subj.pos < subj.input.length: return ord(subj.input.data[subj.pos]) else: return 0 # 0.29, 0.30 def _cmark_peek_at(subj: _cmarkSubject, pos: int) -> int: return ord(subj.input.data[pos]) # Return true if there are more characters in the subject. # 0.30 def _cmark_is_eof(subj: _cmarkSubject) -> bool: return subj.pos >= subj.input.length # Advance the subject. Doesn't check for eof. # 0.29, 0.30 def _cmark_advance(subj: _cmarkSubject): subj.pos += 1 def _cmark_skip_spaces(subj: _cmarkSubject) -> bool: skipped: bool = False while chr(_cmark_peek_char(subj)) == ' ' or chr( _cmark_peek_char(subj)) == '\t': _cmark_advance(subj) skipped = True return skipped # 0.29, 0.30 def _cmark_skip_line_end(subj: _cmarkSubject) -> bool: seen_line_end_char: bool = False if chr(_cmark_peek_char(subj)) == '\r': _cmark_advance(subj) seen_line_end_char = True if chr(_cmark_peek_char(subj)) == '\n': _cmark_advance(subj) seen_line_end_char = True return seen_line_end_char or _cmark_is_eof(subj) # Custom function. def _cmark_take_while_loop_condition(subj: _cmarkSubject, function_name: str) -> bool: condition: bool = False c = _cmark_peek_char(subj) if function_name == '_cmark_isbacktick': condition = _cmark_isbacktick(c) return condition # Take characters while a predicate holds, and return a string. # Get backtick spanning. # 0.29, 0.30 def _cmark_take_while(subj: _cmarkSubject, function_name: str) -> _cmarkCmarkChunk: startpos: int = subj.pos length: int = 0 while _cmark_take_while_loop_condition(subj, '_cmark_isbacktick'): _cmark_advance(subj) length += 1 return _cmark_cmark_chunk_dup(subj.input, startpos, length) # Return the number of newlines in a given span of text in a subject. If # the number is greater than zero, also return the number of characters # between the last newline and the end of the span in `since_newline`. # 0.29, 0.30 def _cmark_count_newlines(subj: _cmarkSubject, sfrom: int, length: int) -> tuple: nls: int = 0 since_nl: int = 0 while length > 0: if subj.input.data[sfrom] == '\n': nls += 1 since_nl = 0 else: since_nl += 1 sfrom += 1 length -= 1 if not nls: return 0 since_newline = copy.deepcopy(since_nl) return nls, since_newline # Adjust `node`'s `end_line`, `end_column`, and `subj`'s `line` and # `column_offset` according to the number of newlines in a just-matched span # of text in `subj`. # 0.29, 0.30 def _cmark_adjust_subj_node_newlines(subj: _cmarkSubject, node: _cmarkCmarkNode, matchlen: int, extra: int, options: int): if not (options & CMARK_OPT_SOURCEPOS): return newlines: int since_newline: int newlines, since_newline = _cmark_count_newlines( subj, subj.pos - matchlen - extra, matchlen) if newlines: subj.line += newlines node.end_line += newlines node.end_column = since_newline subj.column_offset = -subj.pos + since_newline + extra # Try to process a backtick code span that began with a # span of ticks of length openticklength length (already # parsed). Return 0 if you don't find matching closing # backticks, otherwise return the position in the subject # after the closing backticks. # 0.29, 0.30 def _cmark_scan_to_closing_backticks(subj: _cmarkSubject, openticklength: int) -> int: found: bool = False if openticklength > MAXBACKTICKS: # we limit backtick string length because of the array subj->backticks: return 0 if (subj.scanned_for_backticks and subj.backticks[openticklength] <= subj.pos): # return if we already know there's no closer return 0 while not found: # read non backticks c: int c = _cmark_peek_char(subj) while c and not _cmark_isbacktick(c): _cmark_advance(subj) c = _cmark_peek_char(subj) if _cmark_is_eof(subj): break numticks: int = 0 while _cmark_isbacktick(_cmark_peek_char(subj)): _cmark_advance(subj) numticks += 1 # store position of ender # Ender starting point. if numticks <= MAXBACKTICKS: subj.backticks[numticks] = subj.pos - numticks if numticks == openticklength: return subj.pos # got through whole input without finding closer subj.scanned_for_backticks = True return 0 # Destructively modify string, converting newlines to # spaces, then removing a single leading + trailing space, # unless the code span consists entirely of space characters. # 0.29, 0.30 def _cmark_S_normalize_code(s: _cmarkCmarkStrbuf): r: int = 0 w: int = 0 contains_nonspace: bool = False while r < s.size: if s.ptr[r] == '\r': if (s.ptr[r + 1] != '\n'): s.ptr = _replace_substring(s.ptr, ' ', w, w) w += 1 elif s.ptr[r] == '\n': s.ptr = _replace_substring(s.ptr, ' ', w, w) w += 1 else: s.ptr = _replace_substring(s.ptr, s.ptr[r], w, w) w += 1 if s.ptr[r] != ' ': contains_nonspace = True r += 1 # begins and ends with space? if (contains_nonspace and s.ptr[0] == ' ' and s.ptr[w - 1] == ' '): _cmark_cmark_strbuf_drop(s, 1) _cmark_cmark_strbuf_truncate(s, w - 2) else: _cmark_cmark_strbuf_truncate(s, w) # Parse backtick code section or raw backticks, return an inline. # Assumes that the subject has a backtick at the current position. # 0.30 def _cmark_handle_backticks(subj: _cmarkSubject, options: int) -> _cmarkCmarkNode: openticks: _cmarkCmarkChunk = _cmark_take_while(subj, '_cmark_isbacktick') startpos: int = subj.pos endpos: int = _cmark_scan_to_closing_backticks(subj, openticks.length) if endpos == 0: # not found subj.pos = startpos # rewind return _cmark_make_str(subj, subj.pos, subj.pos, openticks) else: buf = _cmark_CMARK_BUF_INIT(subj.mem) _cmark_cmark_strbuf_set(buf, subj.input.data[startpos:], endpos - startpos - openticks.length) _cmark_S_normalize_code(buf) node: _cmarkCmarkNode = _cmark_make_literal( subj, _cmarkCmarkNodeType.CMARK_NODE_CODE.value, startpos, endpos - openticks.length - 1) node.length = buf.size node.data = _cmark_cmark_strbuf_detach(buf) _cmark_adjust_subj_node_newlines(subj, node, endpos - startpos, openticks.length, options) return node # 0.30 def _cmark_scan_delims(subj: _cmarkSubject, c: str) -> tuple: numdelims: int = 0 before_char_pos: int = 0 after_char: int = 0 before_char: int = 0 length: int = 0 left_flanking: bool = False right_flanking: bool = False can_open: bool = False can_close: bool = False if subj.pos == 0: before_char = 10 else: before_char_pos = subj.pos - 1 # walk back to the beginning of the UTF_8 sequence while _cmark_peek_at( subj, before_char_pos) >> 6 == 2 and before_char_pos > 0: before_char_pos -= 1 length, before_char = _cmark_cmark_utf8proc_iterate( subj.input.data[before_char_pos:], subj.pos - before_char_pos, ) if length == -1: before_char = 10 if c == '\'' or c == '"': numdelims += 1 _cmark_advance(subj) # limit to 1 delim for quotes else: while chr(_cmark_peek_char(subj)) == c: numdelims += 1 _cmark_advance(subj) length, after_char = _cmark_cmark_utf8proc_iterate( subj.input.data[subj.pos:], subj.input.length - subj.pos) if length == -1: after_char = 10 if (numdelims > 0 and (not _cmark_cmark_utf8proc_is_space(after_char)) and (not _cmark_cmark_utf8proc_is_punctuation(after_char) or _cmark_cmark_utf8proc_is_space(before_char) or _cmark_cmark_utf8proc_is_punctuation(before_char))): left_flanking = True if (numdelims > 0 and (not _cmark_cmark_utf8proc_is_space(before_char)) and (not _cmark_cmark_utf8proc_is_punctuation(before_char) or _cmark_cmark_utf8proc_is_space(after_char) or _cmark_cmark_utf8proc_is_punctuation(after_char))): right_flanking = True if c == '_': if (left_flanking and (not right_flanking or _cmark_cmark_utf8proc_is_punctuation(before_char))): can_open = True if (right_flanking and (not left_flanking or _cmark_cmark_utf8proc_is_punctuation(after_char))): can_close = True elif c == '\'' or c == '"': if (left_flanking and (not right_flanking or before_char == '(' or before_char == '[') and before_char != ']' and before_char != ')'): can_open = True can_close = right_flanking else: can_open = left_flanking can_close = right_flanking return numdelims, can_open, can_close # 0.30 def _cmark_remove_delimiter(subj: _cmarkSubject, delim: _cmarkDelimiter): if delim is None: return if delim.next is None: # end of list: if delim != subj.last_delim: raise ValueError subj.last_delim = delim.previous else: delim.next.previous = delim.previous if delim.previous is not None: delim.previous.next = delim.next del delim # 0.30 def _cmark_pop_bracket(subj: _cmarkSubject): b: _cmarkBracket if subj.last_bracket is None: return b = subj.last_bracket subj.last_bracket = subj.last_bracket.previous # subj->mem->free(b); del b # 0.29, 0.30 def _cmark_push_delimiter( subj: _cmarkSubject, c: str, can_open: bool, can_close: bool, inl_text: _cmarkCmarkNode, ): delim = _cmarkDelimiter() delim.delim_char = c delim.can_open = can_open delim.can_close = can_close delim.inl_text = inl_text delim.length = inl_text.length delim.previous = subj.last_delim delim.next = None if delim.previous is not None: delim.previous.next = delim subj.last_delim = delim # 0.29, 0.30 def _cmark_push_bracket(subj: _cmarkSubject, image: bool, inl_text: _cmarkCmarkNode): b = _cmarkBracket() if subj.last_bracket is not None: subj.last_bracket.bracket_after = True b.image = image b.active = True b.inl_text = inl_text b.previous = subj.last_bracket b.previous_delimiter = subj.last_delim b.position = subj.pos b.bracket_after = False subj.last_bracket = b # Assumes the subject has a c at the current position. # 0.29, 0.30 def _cmark_handle_delim(subj: _cmarkSubject, c: str, smart: bool = False) -> _cmarkCmarkNode: numdelims: int inl_text: _cmarkCmarkNode can_open: bool can_close: bool contents: _cmarkCmarkChunk numdelims, can_open, can_close = _cmark_scan_delims(subj, c) if c == '\'' and smart: contents = _cmark_cmark_chunk_literal(RIGHTSINGLEQUOTE) elif c == '"' and smart: if can_close: contents = _cmark_cmark_chunk_literal(RIGHTDOUBLEQUOTE) else: contents = _cmark_cmark_chunk_literal(LEFTDOUBLEQUOTE) else: contents = _cmark_cmark_chunk_dup(subj.input, subj.pos - numdelims, numdelims) inl_text = _cmark_make_str(subj, subj.pos - numdelims, subj.pos - 1, contents) if (can_open or can_close) and (not (c == '\'' or c == '"') or smart): _cmark_push_delimiter(subj, c, can_open, can_close, inl_text) return inl_text # 0.30 # Assumes we have a hyphen at the current position. def _cmark_handle_hyphen(subj: _cmarkSubject, smart: bool) -> _cmarkCmarkNode: startpos: int = subj.pos _cmark_advance(subj) if not smart or chr(_cmark_peek_char(subj)) != '-': return _cmark_make_str(subj, subj.pos - 1, subj.pos - 1, _cmark_cmark_chunk_literal('-')) while smart and chr(_cmark_peek_char(subj)) == '-': _cmark_advance(subj) numhyphens: int = subj.pos - startpos en_count: int = 0 em_count: int = 0 i: int buf: _cmarkCmarkStrbuf = _cmark_CMARK_BUF_INIT(subj.mem) if numhyphens % 3 == 0: # if divisible by 3, use all em dashes em_count = numhyphens / 3 elif numhyphens % 2 == 0: # if divisible by 2, use all en dashes en_count = numhyphens / 2 elif numhyphens % 3 == 2: # use one en dash at end en_count = 1 em_count = (numhyphens - 2) / 3 else: # use two en dashes at the end en_count = 2 em_count = (numhyphens - 4) / 3 for i in range(em_count, 0, step=-1): _cmark_cmark_strbuf_puts(buf, EMDASH) for i in range(en_count, 0, step=-1): _cmark_cmark_strbuf_puts(buf, ENDASH) return _cmark_make_str_from_buf(subj, startpos, subj.pos - 1, buf) # 0.30 # Assumes we have a period at the current position. def _cmark_handle_period(subj: _cmarkSubject, smart: bool) -> _cmarkCmarkNode: _cmark_advance(subj) if smart and chr(_cmark_peek_char(subj)) == '.': _cmark_advance(subj) if chr(_cmark_peek_char(subj)) == '.': _cmark_advance(subj) return _cmark_make_str(subj, subj.pos - 3, subj.pos - 1, _cmark_cmark_chunk_literal(ELLIPSES)) else: return _cmark_make_str(subj, subj.pos - 2, subj.pos - 1, _cmark_cmark_chunk_literal('..')) else: return _cmark_make_str(subj, subj.pos - 1, subj.pos - 1, _cmark_cmark_chunk_literal('.')) # 0.30 def _cmark_process_emphasis(subj: _cmarkSubject, stack_bottom: _cmarkDelimiter, ignore: list) -> list: closer: _cmarkDelimiter = subj.last_delim opener: _cmarkDelimiter old_closer: _cmarkDelimiter opener_found: bool openers_bottom_index: int = 0 openers_bottom: list = [ stack_bottom, stack_bottom, stack_bottom, stack_bottom, stack_bottom, stack_bottom, stack_bottom, stack_bottom, stack_bottom, ] # move back to first relevant delim. while closer is not None and closer.previous is not stack_bottom: closer = closer.previous # now move forward, looking for closers, and handling each while closer is not None: if closer.can_close: if closer.delim_char == '"': openers_bottom_index = 0 elif closer.delim_char == '\'': openers_bottom_index = 1 elif closer.delim_char == '_': openers_bottom_index = 2 elif closer.delim_char == '*': openers_bottom_index = 3 if closer.can_open: openers_bottom_index += 3 else: openers_bottom_index += 0 openers_bottom_index += closer.length % 3 else: raise ValueError # Now look backwards for first matching opener: opener = closer.previous opener_found = False while opener is not None and opener != openers_bottom[ openers_bottom_index]: if opener.can_open and opener.delim_char == closer.delim_char: # interior closer of size 2 can't match opener of size 1 # or of size 1 can't match 2 if (not (closer.can_open or opener.can_close) or closer.length % 3 == 0 or (opener.length + closer.length) % 3 != 0): opener_found = True break opener = opener.previous old_closer = closer if closer.delim_char == '*' or closer.delim_char == '_': if opener_found: closer = _cmark_remove_emph(subj, opener, closer, ignore) else: closer = closer.next elif closer.delim_char == '\'': _cmark_cmark_node_set_literal(closer.inl_text, RIGHTSINGLEQUOTE) if opener_found: _cmark_cmark_node_set_literal(opener.inl_text, LEFTSINGLEQUOTE) closer = closer.next elif closer.delim_char == '"': _cmark_cmark_node_set_literal(closer.inl_text, RIGHTDOUBLEQUOTE) if opener_found: _cmark_cmark_node_set_literal(opener.inl_text, LEFTDOUBLEQUOTE) closer = closer.next if not opener_found: # set lower bound for future searches for openers openers_bottom[openers_bottom_index] = old_closer.previous if not old_closer.can_open: # we can remove a closer that can't be an # opener, once we've seen there's no # matching opener: _cmark_remove_delimiter(subj, old_closer) else: closer = closer.next # free all delimiters in list until stack_bottom: while subj.last_delim is not None and subj.last_delim != stack_bottom: _cmark_remove_delimiter(subj, subj.last_delim) # 0.29, 0.30 def _cmark_remove_emph(subj: _cmarkSubject, opener: _cmarkDelimiter, closer: _cmarkDelimiter, ignore: list): # This function replaces to S_insert_emph(). Some code is common # to that function. delim: _cmarkDelimiter tmp_delim: _cmarkDelimiter use_delims: int opener_inl: _cmarkCmarkNode = opener.inl_text closer_inl: _cmarkCmarkNode = closer.inl_text opener_num_chars: int = opener_inl.length closer_num_chars: int = closer_inl.length tmp: _cmarkCmarkNode tmpnext: _cmarkCmarkNode emph: _cmarkCmarkNode # calculate the actual number of characters used from this closer if closer_num_chars >= 2 and opener_num_chars >= 2: use_delims = 2 else: use_delims = 1 # remove used characters from associated inlines. opener_num_chars -= use_delims closer_num_chars -= use_delims opener_inl.length = opener_num_chars opener_inl.data = opener_inl.data[:opener_num_chars] # opener_inl->data[opener_num_chars] = 0; closer_inl.length = closer_num_chars closer_inl.data = opener_inl.data[:closer_num_chars] # closer_inl->data[closer_num_chars] = 0; # free delimiters between opener and closer delim = closer.previous while delim is not None and delim != opener: tmp_delim = delim.previous _cmark_remove_delimiter(subj, delim) delim = tmp_delim ############ # Not useful for emphasis detection but we keep it as reference. # # create new emph or strong, and splice it in to our inlines # between the opener and closer if use_delims == 1: emph = _cmark_make_emph(subj.mem) else: emph = _cmark_make_strong(subj.mem) tmp = opener_inl.next while tmp and tmp != closer_inl: tmpnext = tmp.next _cmark_cmark_node_unlink(tmp) _cmark_append_child(emph, tmp) tmp = tmpnext _cmark_cmark_node_insert_after(opener_inl, emph) emph.start_line = opener_inl.start_line emph.end_line = closer_inl.end_line emph.start_column = opener_inl.start_column emph.end_column = closer_inl.end_column ############# # Custom variables and computations. opener_relative_start = opener_inl.end_column - use_delims - opener.offset opener_relative_end = opener_inl.end_column - opener.offset closer_relative_start = closer_inl.start_column + closer.offset - 1 closer_relative_end = closer_inl.start_column + use_delims + closer.offset - 1 ignore.append(range(opener_relative_start, opener_relative_end)) ignore.append(range(closer_relative_start, closer_relative_end)) opener.offset += use_delims closer.offset += use_delims # if opener has 0 characters, remove it and its associated inline if opener_num_chars == 0: _cmark_cmark_node_free(opener_inl) _cmark_remove_delimiter(subj, opener) # if closer has 0 characters, remove it and its associated inline if closer_num_chars == 0: # remove empty closer inline _cmark_cmark_node_free(closer_inl) # remove closer from list tmp_delim = closer.next _cmark_remove_delimiter(subj, closer) closer = tmp_delim return closer # 0.30 # Parse an autolink or HTML tag. # Assumes the subject has a '<' character at the current position. def _cmark_handle_pointy_brace(subj: _cmarkSubject, options: int) -> _cmarkCmarkNode: matchlen: int = 0 contents: _cmarkCmarkChunk _cmark_advance(subj) # advance past first < # first try to match a URL autolink matchlen = _cmark_scan_autolink_uri(subj.input, subj.pos) if matchlen > 0: contents = _cmark_cmark_chunk_dup(subj.input, subj.pos, matchlen - 1) subj.pos += matchlen return _cmark_make_autolink(subj, subj.pos - 1 - matchlen, subj.pos - 1, contents, 0) # next try to match an email autolink matchlen = _cmark_scan_autolink_email(subj.input, subj.pos) if matchlen > 0: contents = _cmark_cmark_chunk_dup(subj.input, subj.pos, matchlen - 1) subj.pos += matchlen return _cmark_make_autolink(subj, subj.pos - 1 - matchlen, subj.pos - 1, contents, 1) # finally, try to match an html tag if subj.pos + 2 <= subj.input.length: c: str = subj.input.data[subj.pos] if c == '!': c = subj.input.data[subj.pos + 1] if c == '-': matchlen = _cmark_scan_html_comment(subj.input, subj.pos + 2) if matchlen > 0: matchlen += 2 # prefix "<-" elif c == '[': if subj.flags & FLAG_SKIP_HTML_CDATA == 0: matchlen = _cmark_scan_html_cdata(subj.input, subj.pos + 2) if matchlen > 0: # The regex doesn't require the final "]]>". But if we're not at # the end of input, it must come after the match. Otherwise, # disable subsequent scans to avoid quadratic behavior. matchlen += 5 # prefix "![", suffix "]]>" if subj.pos + matchlen > subj.input.length: subj.flags |= FLAG_SKIP_HTML_CDATA matchlen = 0 elif subj.flags & FLAG_SKIP_HTML_DECLARATION == 0: matchlen = _cmark_scan_html_declaration( subj.input, subj.pos + 1) if matchlen > 0: matchlen += 2 # prefix "!", suffix ">" if subj.pos + matchlen > subj.input.length: subj.flags |= FLAG_SKIP_HTML_DECLARATION matchlen = 0 elif c == '?': if subj.flags & FLAG_SKIP_HTML_PI == 0: # Note that we allow an empty match. matchlen = _cmark_scan_html_pi(subj.input, subj.pos + 1) matchlen += 3 # prefix "?", suffix "?>" if subj.pos + matchlen > subj.input.length: subj.flags |= FLAG_SKIP_HTML_PI matchlen = 0 else: matchlen = _cmark_scan_html_tag(subj.input, subj.pos) if matchlen > 0: src: str = subj.input.data[subj.pos - 1:] length: int = matchlen + 1 subj.pos += matchlen node: _cmarkCmarkNode = _cmark_make_literal( subj, _cmarkCmarkNodeType.CMARK_NODE_HTML_INLINE.value, subj.pos - matchlen - 1, subj.pos - 1) # node->data = (unsigned char *)subj->mem->realloc(NULL, len + 1); # memcpy(node->data, src, len); node.data = copy.deepcopy(src[:length]) # node->data[len] = 0; node.length = length _cmark_adjust_subj_node_newlines(subj, node, matchlen, 1, options) return node # if nothing matches, just return the opening <: return _cmark_make_str(subj, subj.pos - 1, subj.pos - 1, _cmark_cmark_chunk_literal('<')) # Parse backslash-escape or just a backslash, returning an inline. # 0.29, 0.30 def _cmark_handle_backslash(subj: _cmarkSubject): _cmark_advance(subj) nextchar: int = _cmark_peek_char(subj) if _cmark_cmark_ispunct( nextchar): # only ascii symbols and newline can be escaped _cmark_advance(subj) return _cmark_make_str( subj, subj.pos - 2, subj.pos - 1, _cmark_cmark_chunk_dup(subj.input, subj.pos - 1, 1)) elif (not _cmark_is_eof(subj)) and _cmark_skip_line_end(subj): return _cmark_make_linebreak(subj.mem) else: return _cmark_make_str(subj, subj.pos - 1, subj.pos - 1, _cmark_cmark_chunk_literal('\\')) # Parse an entity or a regular "&" string. # Assumes the subject has an '&' character at the current position. def _cmark_handle_entity(subj: _cmarkSubject) -> _cmarkCmarkNode: ent: _cmarkCmarkStrbuf = _cmark_CMARK_BUF_INIT(subj.mem) length: int _cmark_advance(subj) length = _cmark_houdini_unescape_ent(ent, subj.input.data[subj.pos:], subj.input.length - subj.pos) if length <= 0: return _cmark_make_str(subj, subj.pos - 1, subj.pos - 1, _cmark_cmark_chunk_literal('&')) subj.pos += length return _cmark_make_str_from_buf(subj, subj.pos - 1 - length, subj.pos - 1, ent) # Clean a URL: remove surrounding whitespace, and remove \ that escape # punctuation. # 0.30 def _cmark_cmark_clean_url(mem: _cmarkCmarkMem, url: _cmarkCmarkChunk) -> str: buf: _cmarkCmarkStrbuf = _cmark_CMARK_BUF_INIT(mem) _cmark_cmark_chunk_trim(url) _cmark_houdini_unescape_html_f(buf, url.data, url.length) _cmark_cmark_strbuf_unescape(buf) return _cmark_cmark_strbuf_detach(buf) def _cmark_cmark_clean_title(mem: _cmarkCmarkMem, title: _cmarkCmarkChunk) -> str: buf: _cmarkCmarkStrbuf = _cmark_CMARK_BUF_INIT(mem) first: str last: str if title.length == 0: return None first = title.data[0] last = title.data[title.length - 1] # remove surrounding quotes if any: if ((first == "'" and last == "'") or (first == '(' and last == ')') or (first == '"' and last == '"')): _cmark_houdini_unescape_html_f(buf, title.data[1:], title.length - 2) else: _cmark_houdini_unescape_html_f(buf, title.data, title.length) _cmark_cmark_strbuf_unescape(buf) return _cmark_cmark_strbuf_detach(buf) # Parse a link label. Returns 1 if successful. # Note: unescaped brackets are not allowed in labels. # The label begins with `[` and ends with the first `]` character # encountered. Backticks in labels do not start code spans. # 0.30 def _cmark_link_label(subj: _cmarkSubject, raw_label: _cmarkCmarkChunk) -> int: startpos: int = subj.pos length: int = 0 c: int no_match: bool = False # advance past [ if chr(_cmark_peek_char(subj)) == '[': _cmark_advance(subj) else: return 0 c = chr(_cmark_peek_char(subj)) while c and c != '[' and c != ']': if c == '\\': _cmark_advance(subj) length += 1 if _cmark_cmark_ispunct(_cmark_peek_char(subj)): _cmark_advance(subj) length += 1 else: _cmark_advance(subj) length += 1 # MAX_LINK_LABEL_LENGTH is defined as 1000 while # parser['cmark']['link']['max chars label'] is defined as 999. # if (length > MAX_LINK_LABEL_LENGTH) { if length > md_parser['cmark']['link']['max chars label'] + 1: no_match = True c = chr(_cmark_peek_char(subj)) if no_match: subj.pos = startpos # rewind return 0 else: if c == ']': # match found raw_label = _cmark_cmark_chunk_dup(subj.input, startpos + 1, subj.pos - (startpos + 1)) _cmark_cmark_chunk_trim(raw_label) _cmark_advance(subj) # advance past ] return 1 # 0.30 def _cmark_manual_scan_link_url_2(input: _cmarkCmarkChunk, offset: int) -> tuple: i: int = offset nb_p: int = 0 while i < input.length: if (input.data[i] == '\\' and i + 1 < input.length and _cmark_cmark_ispunct(ord(input.data[i + 1]))): i += 2 elif input.data[i] == '(': nb_p += 1 i += 1 if nb_p > 32: return -1, None elif input.data[i] == ')': if nb_p == 0: break nb_p -= 1 i += 1 elif _cmark_cmark_isspace(ord(input.data[i])): if i == offset: return -1, None break else: i += 1 if i >= input.length or nb_p != 0: return -1, None result: _cmarkCmarkChunk = _cmarkCmarkChunk(input.data[offset:], i - offset) output = copy.deepcopy(result) return i - offset, output # 0.30 def _cmark_manual_scan_link_url(input: _cmarkCmarkChunk, offset: int) -> tuple: i: int = offset # Pointy bracket link destination. if i < input.length and input.data[i] == '<': i += 1 while i < input.length: if input.data[i] == '>': i += 1 break elif input.data[i] == '\\': i += 2 elif input.data[i] == '\n' or input.data[i] == '<': return -1, None else: i += 1 else: # Not pointy bracket link destination. return _cmark_manual_scan_link_url_2(input, offset) if i >= input.length: return -1, None result: _cmarkCmarkChunk = _cmarkCmarkChunk(input.data[offset + 1:], i - 2 - offset) output = copy.deepcopy(result) return i - offset, output # 0.30 # Return a link, an image, or a literal close bracket. def _cmark_handle_close_bracket(subj: _cmarkSubject, ignore: list) -> _cmarkCmarkNode: initial_pos: int after_link_text_pos: int endurl: int starttitle: int endtitle: int endall: int sps: int n: int ref: _cmarkCmarkReference = None url_chunk: _cmarkCmarkChunk title_chunk: _cmarkCmarkChunk url: str title: str opener: _cmarkBracket inl: _cmarkCmarkNode raw_label: _cmarkCmarkChunk found_label: int tmp: _cmarkCmarkNode tmpnext: _cmarkCmarkNode is_image: bool match_0: bool = False no_match_0: bool = False _cmark_advance(subj) # advance past ] initial_pos = subj.pos # get last [ or ![ opener = subj.last_bracket if opener is None: return _cmark_make_str(subj, subj.pos - 1, subj.pos - 1, _cmark_cmark_chunk_literal(']')) if not opener.active: # take delimiter off stack _cmark_pop_bracket(subj) return _cmark_make_str(subj, subj.pos - 1, subj.pos - 1, _cmark_cmark_chunk_literal(']')) # If we got here, we matched a potential link/image text. # Now we check to see if it's a link/image. is_image = opener.image after_link_text_pos = subj.pos in_inline_link: bool = False # First, look for an inline link. if chr(_cmark_peek_char(subj)) == '(': sps = _cmark_scan_spacechars(subj.input, subj.pos + 1) if sps > -1: n, url_chunk = _cmark_manual_scan_link_url(subj.input, subj.pos + 1 + sps) if n > -1: in_inline_link = True if in_inline_link: # try to parse an explicit link: endurl = subj.pos + 1 + sps + n starttitle = endurl + _cmark_scan_spacechars(subj.input, endurl) # ensure there are spaces between url and title if starttitle == endurl: endtitle = starttitle else: endtitle = starttitle + _cmark_scan_link_title( subj.input, starttitle) endall = endtitle + _cmark_scan_spacechars(subj.input, endtitle) if chr(_cmark_peek_at(subj, endall)) == ')': subj.pos = endall + 1 title_chunk = _cmark_cmark_chunk_dup(subj.input, starttitle, endtitle - starttitle) url = _cmark_cmark_clean_url(subj.mem, url_chunk) title = _cmark_cmark_clean_title(subj.mem, title_chunk) _cmark_cmark_chunk_free(url_chunk) _cmark_cmark_chunk_free(title_chunk) # goto match match_0 = True else: # it could still be a shortcut reference link subj.pos = after_link_text_pos if not match_0: # Next, look for a following [link label] that matches in refmap. # skip spaces raw_label = _cmark_cmark_chunk_literal('') found_label = _cmark_link_label(subj, raw_label) if not found_label: # If we have a shortcut reference link, back up # to before the spacse we skipped. subj.pos = initial_pos if ((not found_label or raw_label.length == 0) and not opener.bracket_after): _cmark_cmark_chunk_free(raw_label) raw_label = _cmark_cmark_chunk_dup( subj.input, opener.position, initial_pos - opener.position - 1) found_label = True if found_label: ref = _cmark_cmark_reference_lookup(subj.refmap, raw_label) _cmark_cmark_chunk_free(raw_label) if ref is not None: # found url = _cmark_cmark_strdup(subj.mem, ref.url) title = _cmark_cmark_strdup(subj.mem, ref.title) match_0 = True else: no_match_0 = True if match_0: if is_image: param = _cmarkCmarkNodeType.CMARK_NODE_IMAGE.value else: param = _cmarkCmarkNodeType.CMARK_NODE_LINK.value inl = _cmark_make_simple(subj.mem, param) inl.as_link.url = url inl.as_link.title = title inl.start_line = inl.end_line = subj.line inl.start_column = opener.inl_text.start_column inl.end_column = subj.pos + subj.column_offset + subj.block_offset _cmark_cmark_node_insert_before(opener.inl_text, inl) # Add link text: tmp = opener.inl_text.next while tmp: tmpnext = tmp.next _cmark_cmark_node_unlink(tmp) _cmark_append_child(inl, tmp) tmp = tmpnext # Free the bracket [: _cmark_cmark_node_free(opener.inl_text) _cmark_process_emphasis(subj, opener.previous_delimiter, ignore) _cmark_pop_bracket(subj) # Now, if we have a link, we also want to deactivate earlier link # delimiters. (This code can be removed if we decide to allow links # inside links.) if not is_image: opener = subj.last_bracket while opener is not None: if not opener.image: if not opener.active: break else: opener.active = False opener = opener.previous return None elif no_match_0: # If we fall through to here, it means we didn't match a link: _cmark_pop_bracket(subj) # remove this opener from delimiter list subj.pos = initial_pos return _cmark_make_str(subj, subj.pos - 1, subj.pos - 1, _cmark_cmark_chunk_literal(']')) # 0.30 # Parse a hard or soft linebreak, returning an inline. # Assumes the subject has a cr or newline at the current position. def _cmark_handle_newline(subj: _cmarkSubject) -> _cmarkCmarkNode: nlpos: int = subj.pos # skip over cr, crlf, or lf: if chr(_cmark_peek_at(subj, subj.pos)) == '\r': _cmark_advance(subj) if chr(_cmark_peek_at(subj, subj.pos)) == '\n': _cmark_advance(subj) subj.line += 1 # FIXME TODO: The next instruction messes up some tests! # subj.column_offset = -subj.pos # skip spaces at beginning of line _cmark_skip_spaces(subj) if (nlpos > 1 and chr(_cmark_peek_at(subj, nlpos - 1)) == ' ' and chr(_cmark_peek_at(subj, nlpos - 2)) == ' '): return _cmark_make_linebreak(subj.mem) else: return _cmark_make_softbreak(subj.mem) # 0.29, 0.30 def _cmark_subject_find_special_char(subj: _cmarkSubject, options: int) -> int: # "\r\n\\`&_*[] len(SPECIAL_CHARS) or ord(subj.input.data[n]) > len(SMART_PUNCT_CHARS)): return n if SPECIAL_CHARS[ord(subj.input.data[n])] == 1: return n if options & CMARK_OPT_SMART and SMART_PUNCT_CHARS[ord( subj.input.data[n])]: return n n += 1 return subj.input.length # Parse an inline, advancing subject, and add it as a child of parent. # Return 0 if no inline can be parsed, 1 otherwise. # Handle all the different elements of a string. # 0.29, 0.30 def _cmark_parse_inline(subj: _cmarkSubject, parent: _cmarkCmarkNode, options: int = 0, ignore: list = list()) -> int: new_inl: _cmarkCmarkNode = None contents: str c: int startpos: int endpos: int c = _cmark_peek_char(subj) if c == 0: return 0 elif chr(c) in ['\r', '\n']: new_inl = _cmark_handle_newline(subj) elif chr(c) in ['`']: new_inl = _cmark_handle_backticks(subj, options) elif chr(c) in ['\\']: new_inl = _cmark_handle_backslash(subj) elif chr(c) in ['&']: new_inl = _cmark_handle_entity(subj) elif chr(c) in ['<']: new_inl = _cmark_handle_pointy_brace(subj, options) elif chr(c) in ['*', '_', '\'', '"']: new_inl = _cmark_handle_delim(subj, chr(c), (options & CMARK_OPT_SMART) != 0) elif chr(c) in ['-']: new_inl = _cmark_handle_hyphen(subj, (options & CMARK_OPT_SMART) != 0) elif chr(c) in ['.']: new_inl = _cmark_handle_period(subj, (options & CMARK_OPT_SMART) != 0) elif chr(c) in ['[']: _cmark_advance(subj) new_inl = _cmark_make_str(subj, subj.pos - 1, subj.pos - 1, _cmark_cmark_chunk_literal('[')) _cmark_push_bracket(subj, False, new_inl) elif chr(c) in [']']: _cmark_handle_close_bracket(subj, ignore) elif chr(c) in ['!']: _cmark_advance(subj) if _cmark_peek_char(subj) == '[': _cmark_advance(subj) new_inl = _cmark_make_str(subj, subj.pos - 2, subj.pos - 1, _cmark_cmark_chunk_literal('![')) _cmark_push_bracket(subj, True, new_inl) else: new_inl = _cmark_make_str(subj, subj.pos - 1, subj.pos - 1, _cmark_cmark_chunk_literal('!')) else: endpos = _cmark_subject_find_special_char(subj, options) contents = _cmark_cmark_chunk_dup(subj.input, subj.pos, endpos - subj.pos) startpos = subj.pos subj.pos = endpos # if we're at a newline, strip trailing spaces. if _cmark_S_is_line_end_char(_cmark_peek_char(subj)): _cmark_cmark_chunk_rtrim(contents) new_inl = _cmark_make_str(subj, startpos, endpos - 1, contents) if new_inl is not None: _cmark_append_child(parent, new_inl) return 1 # Parse inlines from parent's string_content, adding as children of parent. # Get the ignore list. # 0.30 def _cmark_cmark_parse_inlines( mem: _cmarkCmarkMem, parent: _cmarkCmarkNode, refmap: _cmarkCmarkReferenceMap, options: int, ) -> list: subj: _cmarkSubject = _cmarkSubject() content: _cmarkCmarkChunk = _cmarkCmarkChunk(parent.data, parent.length) _cmark_subject_from_buf(mem, parent.start_line, parent.start_column - 1 + parent.internal_offset, subj, content, refmap) _cmark_cmark_chunk_rtrim(subj.input) ignore = list() while not _cmark_is_eof(subj) and _cmark_parse_inline( subj, parent, options, ignore): pass # When we hit the end of the input, we call the process emphasis procedure with stack_bottom = NULL. _cmark_process_emphasis(subj, None, ignore) # free bracket and delim stack while subj.last_delim: _cmark_remove_delimiter(subj, subj.last_delim) while subj.last_bracket: _cmark_pop_bracket(subj) return ignore if __name__ == '__main__': pass md-toc-9.0.0/md_toc/cmark/node_c.py000066400000000000000000000204101460560256400170530ustar00rootroot00000000000000# # node_c.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" import copy from ..constants import parser as md_parser from .cmark_h import _cmarkCmarkMem from .node_h import _cmarkCmarkNode, _cmarkCmarkNodeType # License C applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst # 0.30 def _cmark_S_is_block(node: _cmarkCmarkNode) -> bool: if node is None: return False return (node.type >= _cmarkCmarkNodeType.CMARK_NODE_FIRST_BLOCK.value and node.type <= _cmarkCmarkNodeType.CMARK_NODE_LAST_BLOCK.value) # 0.30 def _cmark_S_is_inline(node: _cmarkCmarkNode) -> bool: if node is None: return False return (node.type >= _cmarkCmarkNodeType.CMARK_NODE_FIRST_INLINE.value and node.type <= _cmarkCmarkNodeType.CMARK_NODE_LAST_INLINE.value) # 0.30 def _cmark_S_can_contain(node: _cmarkCmarkNode, child: _cmarkCmarkNode) -> bool: if (node is None or child is None or node == child): return False # Verify that child is not an ancestor of node. if child.first_child is not None: cur: _cmarkCmarkNode = node.parent while cur is not None: if cur == child: return False cur = cur.parent if child.type == _cmarkCmarkNodeType.CMARK_NODE_DOCUMENT.value: return False if node.type in [ _cmarkCmarkNodeType.CMARK_NODE_DOCUMENT.value, _cmarkCmarkNodeType.CMARK_NODE_BLOCK_QUOTE.value, _cmarkCmarkNodeType.CMARK_NODE_ITEM.value, ]: return _cmark_S_is_block( child) and child.type != _cmarkCmarkNodeType.CMARK_NODE_ITEM.value elif node.type in [_cmarkCmarkNodeType.CMARK_NODE_LIST.value]: return child.type == _cmarkCmarkNodeType.CMARK_NODE_ITEM.value elif node.type in [_cmarkCmarkNodeType.CMARK_NODE_CUSTOM_BLOCK.value]: return True elif node.type in [ _cmarkCmarkNodeType.CMARK_NODE_PARAGRAPH.value, _cmarkCmarkNodeType.CMARK_NODE_HEADING.value, _cmarkCmarkNodeType.CMARK_NODE_EMPH.value, _cmarkCmarkNodeType.CMARK_NODE_STRONG.value, _cmarkCmarkNodeType.CMARK_NODE_LINK.value, _cmarkCmarkNodeType.CMARK_NODE_IMAGE.value, _cmarkCmarkNodeType.CMARK_NODE_CUSTOM_INLINE.value, ]: return _cmark_S_is_inline(child) return False # Free a cmark_node list and any children. # 0.30 def _cmark_S_free_nodes(e: _cmarkCmarkNode): # mem: _cmarkCmarkMem = e.mem next: _cmarkCmarkNode while e is not None: if e.type in [_cmarkCmarkNodeType.CMARK_NODE_CODE_BLOCK.value]: del e.data del e.as_code.info e.data = '' e.as_code.info = '' elif e.type in [ _cmarkCmarkNodeType.CMARK_NODE_TEXT.value, _cmarkCmarkNodeType.CMARK_NODE_HTML_INLINE.value, _cmarkCmarkNodeType.CMARK_NODE_CODE.value, _cmarkCmarkNodeType.CMARK_NODE_HTML_BLOCK.value, ]: del e.data e.data = '' elif e.type in [ _cmarkCmarkNodeType.CMARK_NODE_LINK.value, _cmarkCmarkNodeType.CMARK_NODE_IMAGE.value, ]: del e.as_link.url del e.as_link.title e.as_link.url = '' e.as_link.title = '' elif e.type in [ _cmarkCmarkNodeType.CMARK_NODE_CUSTOM_BLOCK.value, _cmarkCmarkNodeType.CMARK_NODE_CUSTOM_INLINE.value, ]: del e.as_custom.on_enter del e.as_custom.on_exit e.as_custom.on_enter = '' e.as_custom.on_exit = '' if e.last_child: # Splice children into list e.last_child.next = e.next e.next = e.first_child next = e.next # mem->free(e); del e e = next # 0.30 def _cmark_cmark_node_free(node: _cmarkCmarkNode): _cmark_S_node_unlink(node) node.next = None _cmark_S_free_nodes(node) # 0.30 def _cmark_cmark_set_cstr(mem: _cmarkCmarkMem, dst: str, src: str) -> tuple: old: str = dst length: int if src and src[0]: length = len(src) # Alternative to: # *dst = (unsigned char *)mem->realloc(NULL, len + 1); # memcpy(*dst, src, len + 1); dst = copy.deepcopy(src[:length + 1]) else: length = 0 dst = '' if old: # Alternative to: # mem->free(old); del old del dst dst = '' return length, dst # 0.30 def _cmark_cmark_node_set_literal(node: _cmarkCmarkNode, content: str) -> int: length: int data: str if node is None: return 0 if (node.type == _cmarkCmarkNodeType.CMARK_NODE_HTML_BLOCK.value or node.type == _cmarkCmarkNodeType.CMARK_NODE_TEXT.value or node.type == _cmarkCmarkNodeType.CMARK_NODE_HTML_INLINE.value or node.type == _cmarkCmarkNodeType.CMARK_NODE_CODE.value or node.type == _cmarkCmarkNodeType.CMARK_NODE_CODE_BLOCK.value): length, data = _cmark_cmark_set_cstr(node.mem, node.data, content) node.length = length node.data = data return 1 return 0 # 0.29, 0.30 # Unlink a node without adjusting its next, prev, and parent pointers. def _cmark_S_node_unlink(node: _cmarkCmarkNode): if node is None: return if node.prev: node.prev.next = node.next if node.next: node.next.prev = node.prev # Adjust first_child and last_child of parent. # Start and end pointers. parent: _cmarkCmarkNode = node.parent if parent: if parent.first_child == node: parent.first_child = node.next if parent.last_child == node: parent.last_child = node.prev # 0.30 def _cmark_cmark_node_unlink(node: _cmarkCmarkNode): _cmark_S_node_unlink(node) node.next = None node.prev = None node.parent = None # Inserts 'sibling' before 'node'. Returns 1 on success, 0 on failure. # 0.30 def _cmark_cmark_node_insert_before(node: _cmarkCmarkNode, sibling: _cmarkCmarkNode) -> int: if node is None or sibling is None: return 0 if not node.parent or not _cmark_S_can_contain(node.parent, sibling): return 0 _cmark_S_node_unlink(sibling) old_prev: _cmarkCmarkNode = node.prev # Insert 'sibling' between 'old_prev' and 'node'. if old_prev: old_prev.next = sibling sibling.prev = old_prev sibling.next = node node.prev = sibling # Set new parent. parent: _cmarkCmarkNode = node.parent sibling.parent = parent # Adjust first_child of parent if inserted as first child. if parent and not old_prev: parent.first_child = sibling return 1 def _cmark_cmark_node_insert_after(node: _cmarkCmarkNode, sibling: _cmarkCmarkNode) -> int: if node is None or sibling is None: return 0 if not node.parent or not _cmark_S_can_contain(node.parent, sibling): return 0 _cmark_S_node_unlink(sibling) old_next = node.next # Insert 'sibling' between 'node' and 'old_next'. if old_next: old_next.prev = sibling sibling.next = old_next sibling.prev = node node.next = sibling # Set new parent. parent: _cmarkCmarkNode = node.parent sibling.parent = parent # Adjust last_child of parent if inserted as last child. if parent and not old_next: parent.last_child = sibling return 1 if __name__ == '__main__': pass md-toc-9.0.0/md_toc/cmark/node_h.py000066400000000000000000000063511460560256400170700ustar00rootroot00000000000000# # node_h.py # # Copyright (C) 2017-2023 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" from dataclasses import dataclass from .cmark_h import _cmarkCmarkMem, _cmarkCmarkNodeType # License C applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst # Slots have less overhead but have been introduced in Python 3.10. # @dataclass(slots=True) @dataclass class _cmarkCmarkList: marker_offset: int = -1 padding: int = -1 start: int = -1 list_type: int = -1 delimiter: int = -1 bullet_char: int = -1 tight: bool = False @dataclass class _cmarkCmarkCode: info: str = -1 fence_length: int = -1 fence_offset: int = -1 fence_char: int = -1 fenced: int = -1 @dataclass class _cmarkCmarkHeading: level: int = -1 setext: bool = False @dataclass class _cmarkCmarkLink: url: str = '' title: str = '' @dataclass class _cmarkCmarkCustom: on_enter: str = '' on_exit: str = '' class _cmarkCmarkNode: __slots__ = [ 'mem', 'next', 'prev', 'parent', 'first_child', 'last_child', 'user_data', 'data', 'length', 'start_line', 'start_column', 'end_line', 'end_column', 'internal_offset', 'type', 'flags', 'as_list', 'as_code', 'as_heading', 'as_link', 'as_custom', 'as_html_block_type', 'numdelims', ] def __init__(self): self.mem: _cmarkCmarkMem = None self.next: _cmarkCmarkNode = None self.prev: _cmarkCmarkNode = None self.parent: _cmarkCmarkNode = None self.first_child: _cmarkCmarkNode = None self.last_child: _cmarkCmarkNode = None self.user_data = None self.data: str = None self.length: int = 0 self.start_line: int = 0 self.start_column: int = 0 self.end_line: int = 0 self.end_column: int = 0 self.internal_offset: int = 0 self.type: int = 0 self.flags: int = 0 # "as" union. self.as_list: _cmarkCmarkList = _cmarkCmarkList() self.as_code: _cmarkCmarkCode = _cmarkCmarkCode() self.as_heading: _cmarkCmarkHeading = _cmarkCmarkHeading() self.as_link: _cmarkCmarkLink = _cmarkCmarkLink() self.as_custom: _cmarkCmarkCustom = _cmarkCmarkCustom() self.as_html_block_type: int = 0 # Add a new variable. self.numdelims: int = 0 if __name__ == '__main__': pass md-toc-9.0.0/md_toc/cmark/references_c.py000066400000000000000000000105251460560256400202550ustar00rootroot00000000000000# # reference_c.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" import functools from ..constants import parser as md_parser from .buffer_c import ( _cmark_cmark_strbuf_detach, _cmark_cmark_strbuf_normalize_whitespace, _cmark_cmark_strbuf_trim, ) from .buffer_h import _cmark_CMARK_BUF_INIT, _cmarkCmarkStrbuf from .chunk_h import _cmarkCmarkChunk from .cmark_h import _cmarkCmarkMem from .references_h import _cmarkCmarkReference, _cmarkCmarkReferenceMap from .utf8_c import _cmark_cmark_utf8proc_case_fold # License C applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst # normalize reference: collapse internal whitespace to single space, # remove leading/trailing whitespace, case fold # Return NULL if the reference name is actually empty (i.e. composed # solely from whitespace) # 0.30 def _cmark_normalize_reference(mem: _cmarkCmarkMem, ref: _cmarkCmarkChunk) -> str: normalized: _cmarkCmarkStrbuf = _cmark_CMARK_BUF_INIT(mem) result: str if ref is None: return None if ref.length == 0: return None _cmark_cmark_utf8proc_case_fold(normalized, ref.data, ref.length) _cmark_cmark_strbuf_trim(normalized) _cmark_cmark_strbuf_normalize_whitespace(normalized) result = _cmark_cmark_strbuf_detach(normalized) if not result: raise ValueError # if result[0] == '\0': if result == '': # mem.free(result) del result return None return result def _cmark_labelcmp(a: str, b: str) -> bool: return a == b def _cmark_refcmp(p1: _cmarkCmarkReference, p2: _cmarkCmarkReference) -> int: res: bool = _cmark_labelcmp(p1.label, p2.label) if res: return res else: return p1.age - p2.age def _cmark_sort_references(maps: _cmarkCmarkReferenceMap): i: int last: int = 0 size: int = maps.size r: _cmarkCmarkReference = maps.refs # **sorted = NULL; # A list of _cmarkCmarkReference srt: list = list() # sorted = (cmark_reference **)maps->mem->calloc(size, sizeof(cmark_reference *)); while (r): srt.append(r) r = r.next # qsort(sorted, size, sizeof(cmark_reference *), refcmp); srt = sorted(srt, key=functools.cmp_to_key(_cmark_refcmp)) for i in range(1, size): if _cmark_labelcmp(srt[i].label, srt[last].label) != 0: last += 1 srt[last] = srt[i] maps.sorted = srt maps.size = last + 1 # Returns reference if refmaps contains a reference with matching # label, otherwise NULL. # 0.30 def _cmark_cmark_reference_lookup( maps: _cmarkCmarkReferenceMap, label: _cmarkCmarkChunk, ) -> _cmarkCmarkReference: # A list of _cmarkCmarkReference ref: list = None r: _cmarkCmarkReference = None norm: str # MAX_LINK_LABEL_LENGTH if label.length < 1 or label.length > md_parser['cmark']['link'][ 'max_chars_label'] + 1: return None if maps is None or not maps.size: return None norm = _cmark_normalize_reference(maps.mem, label) if norm is None: return None if not maps.sorted: _cmark_sort_references(maps) # TODO # ref = (cmark_reference **)bsearch(norm, maps->sorted, maps->size, sizeof(cmark_reference *), # refsearch); # FIXME # maps->mem->free(norm); del norm if ref is not None: r = ref[0] # Check for expansion limit if maps.max_ref_size and r.size > maps.max_ref_size - maps.ref_size: return None maps.ref_size += r.size return r md-toc-9.0.0/md_toc/cmark/references_h.py000066400000000000000000000034511460560256400202620ustar00rootroot00000000000000# # reference_h.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" from .cmark_h import _cmarkCmarkMem # License C applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst # 0.30 class _cmarkCmarkReference: __slots__ = [ 'next', 'label', 'url', 'title', 'age', 'size', ] def __init__(self): self.next: _cmarkCmarkReference = None self.label: str = None self.url: str = None self.title: str = None self.age: int = 0 self.size: int = 0 # 0.30 class _cmarkCmarkReferenceMap: __slots__ = [ 'mem', 'refs', 'sorted', 'size', 'ref_size', 'max_ref_size', ] def __init__(self): self.mem: _cmarkCmarkMem = None self.refs: _cmarkCmarkReference = None # A list of _cmarkCmarkReference self.sorted: list = None self.size: int = 0 self.ref_size: int = 0 self.max_ref_size: int = 0 if __name__ == '__main__': pass md-toc-9.0.0/md_toc/cmark/scanners_c.py000066400000000000000000000120171460560256400177460ustar00rootroot00000000000000# # scanners_c.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" import copy import re from ..constants import parser as md_parser from .chunk_h import _cmarkCmarkChunk # License C applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst # These functions have been re-written to avoid GOTO jumps, # also using the scanners.re source file. # The original C source states: # /* Generated by re2c 1.3 */ def _cmark__scan_at( scanner_function_name: str, c: _cmarkCmarkChunk, offset: int, ) -> int: res: int ptr: str = c.data if ptr is None or offset > c.length: return 0 else: # lim: str = ptr[c.length] # ptr[c.length] = '\0' if scanner_function_name == '_cmark__scan_spacechars': res = _cmark__scan_spacechars(ptr, offset) if scanner_function_name == '_cmark__scan_link_title': res = _cmark__scan_link_title(ptr, offset) if scanner_function_name == '_cmark__scan_autolink_uri': res = _cmark__scan_autolink_uri(ptr, offset) if scanner_function_name == '_cmark__scan_autolink_email': res = _cmark__scan_autolink_email(ptr, offset) if scanner_function_name == '_cmark__scan_html_comment': res = _cmark__scan_html_comment(ptr, offset) if scanner_function_name == '_cmark__scan_cdata': res = _cmark__scan_cdata(ptr, offset) if scanner_function_name == '_cmark__scan_html_tag': res = _cmark__scan_html_tag(ptr, offset) if scanner_function_name == '_cmark__scan_html_declaration': res = _cmark__scan_html_declaration(ptr, offset) if scanner_function_name == '_cmark__scan_html_pi': res = _cmark__scan_html_pi(ptr, offset) # ptr[c.length] = lim return res def _common_scan(regex: str, ptr: str, p: int) -> int: start_match: int = 0 end_match: int = 0 retval: int span = re.match(regex, ptr[p:]) if span: ll = list(span.span()) start_match = ll[0] end_match = ll[1] retval = end_match - start_match else: retval = 0 return retval # Try to match a link title (in single quotes, in double quotes, or # in parentheses), returning number of chars matched. Allow one # level of internal nesting (quotes within quotes). def _cmark__scan_link_title(ptr: str, p: int) -> int: r1 = '["](' + md_parser['cmark']['_scanners.re'][ 'escaped_char'] + '|[^"\u0000])*["]' r2 = "['](" + md_parser['cmark']['_scanners.re'][ 'escaped_char'] + "|[^'\u0000])*[']" r3 = r'[\(](' + md_parser['cmark']['_scanners.re'][ 'escaped_char'] + r"|[^\(\)\u0000])*[']" r = '(' + r1 + '|' + r2 + '|' + r3 + ')' return _common_scan(r, ptr, p) # Match SOME space characters, including newlines. def _cmark__scan_spacechars(ptr: str, p: int) -> int: return _common_scan(md_parser['cmark']['_scanners.re']['spacechar'] + '+', ptr, p) # Try to match URI autolink after first <, returning number of chars matched. def _cmark__scan_autolink_uri(ptr: str, p: int) -> int: return _common_scan('[:][^\x00-\x20<>]*[>]', ptr, p) # Try to match email autolink after first <, returning num of chars matched. def _cmark__scan_autolink_email(ptr: str, p: int) -> int: return _common_scan( '[a-zA-Z0-9.!#$%&\'*+/=?^_`{|}~-]+[@][a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?([.][a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*[>]', ptr, p) def _cmark__scan_html_comment(ptr: str, p: int) -> int: return _common_scan(md_parser['cmark']['_scanners.re']['htmlcomment'], ptr, p) def _cmark__scan_cdata(ptr: str, p: int) -> int: return _common_scan(md_parser['cmark']['_scanners.re']['cdata'], ptr, p) # Try to match an HTML tag after first <, returning num of chars matched. def _cmark__scan_html_tag(ptr: str, p: int) -> int: return _common_scan(md_parser['cmark']['_scanners.re']['htmltag'], ptr, p) def _cmark__scan_html_declaration(ptr: str, p: int) -> int: return _common_scan(md_parser['cmark']['_scanners.re']['declaration'], ptr, p) def _cmark__scan_html_pi(ptr: str, p: int) -> int: return _common_scan( md_parser['cmark']['_scanners.re']['processinginstruction'], ptr, p) md-toc-9.0.0/md_toc/cmark/scanners_h.py000066400000000000000000000041561460560256400177600ustar00rootroot00000000000000# # scanners_h.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" from .chunk_h import _cmarkCmarkChunk from .scanners_c import _cmark__scan_at # License C applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst def _cmark_scan_spacechars(c: _cmarkCmarkChunk, n: int) -> int: return _cmark__scan_at('_cmark__scan_spacechars', c, n) def _cmark_scan_link_title(c: _cmarkCmarkChunk, n: int) -> int: return _cmark__scan_at('_cmark__scan_link_title', c, n) def _cmark_scan_autolink_uri(c: _cmarkCmarkChunk, n: int) -> int: return _cmark__scan_at('_cmark__scan_autolink_uri', c, n) def _cmark_scan_autolink_email(c: _cmarkCmarkChunk, n: int) -> int: return _cmark__scan_at('_cmark__scan_autolink_email', c, n) def _cmark_scan_html_comment(c: _cmarkCmarkChunk, n: int) -> int: return _cmark__scan_at('_cmark__scan_html_comment', c, n) def _cmark_scan_html_cdata(c: _cmarkCmarkChunk, n: int) -> int: return _cmark__scan_at('_cmark__scan_cdata', c, n) def _cmark_scan_html_tag(c: _cmarkCmarkChunk, n: int) -> int: return _cmark__scan_at('_cmark__scan_html_tag', c, n) def _cmark_scan_html_declaration(c: _cmarkCmarkChunk, n: int) -> int: return _cmark__scan_at('_cmark__scan_html_declaration', c, n) def _cmark_scan_html_pi(c: _cmarkCmarkChunk, n: int) -> int: return _cmark__scan_at('_cmark__scan_html_pi', c, n) md-toc-9.0.0/md_toc/cmark/utf8_c.py000066400000000000000000000107661460560256400170310ustar00rootroot00000000000000# # utf8_c.py # # Copyright (C) 2017-2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A cmark implementation file.""" import unicodedata from ..constants import parser as md_parser from .buffer_c import _cmark_cmark_strbuf_put from .buffer_h import _cmarkCmarkStrbuf from .cmark_ctype_c import _cmark_cmark_ispunct # License D applies to this file except for non derivative code: # in that case the license header at the top of the file applies. # See docs/copyright_license.rst def _cmark_encode_unknown(buf: _cmarkCmarkStrbuf): # static const uint8_t repl[] = {239, 191, 189}; # man 3 printf # # #include # #include # # int main(void) # { # static const uint8_t repl[] = {239, 191, 189}; # printf ("%hhu", repl); # # return 0; # } # # gcc a.c # ./a.out # 9 # # python3 # >>> chr(9) # '\t' repl = '\t' _cmark_cmark_strbuf_put(buf, repl, 3) # 0.29, 0.30 def _cmark_cmark_utf8proc_charlen(line: str, line_length: int) -> int: length: int i: int if not line_length: return 0 # Use length = 1 instead of the utf8proc_utf8class[256] # list. # Python: # length = utf8proc_utf8class[ord(line[0])] # C: # length = utf8proc_utf8class[str[0]]; # For example: # len('Å‚') == 2 # in Python 2 # len('Å‚') == 1 # in Python 3 # See the documentation. # In Python 3 all strings are unicode by default # and they all have length of 1. length = 1 # if (!length) # return -1; if line_length >= 0 and length > line_length: return -line_length for i in range(1, length): if (ord(line[i]) & 0xC0) != 0x80: return -i return length # 0.29, 0.30 def _cmark_cmark_utf8proc_iterate(line: str, line_len: int) -> tuple: length: int = 0 uc: int = -1 dst: int = -1 length = _cmark_cmark_utf8proc_charlen(line, line_len) if length < 0: return -1, dst if length == 1: uc = ord(line[0]) # In Python 3 all strings are unicode by default # and they all have length of 1. # All the original C code here is omitted for this reason. if uc < 0: return -1, dst dst = uc return length, dst # 0.30 def _cmark_cmark_utf8proc_encode_char(uc: int, buf: _cmarkCmarkStrbuf): dst: str length: int = 0 if uc < 0: raise ValueError # In Python 3 all strings are unicode by default # and they all have length of 1. # Omitted code. length = 1 if uc > 1 or uc >= 0x110000: _cmark_encode_unknown(buf) return dst = str(uc) _cmark_cmark_strbuf_put(buf, dst, length) # 0.30 def _cmark_cmark_utf8proc_case_fold( dest: _cmarkCmarkStrbuf, string: str, length: int, ): c: int while (length > 0): char_len, c = _cmark_cmark_utf8proc_iterate(string, length) if char_len >= 0: # FIXME: unsure about this. See original C source code. _cmark_cmark_utf8proc_encode_char( ord(unicodedata.normalize('NFC', chr(c)).casefold()), dest) else: _cmark_encode_unknown(dest) char_len = -char_len # Advance pointer. # str += char_len; string = string[char_len:] # Reduce string length. length -= char_len # 0.29, 0.30 def _cmark_cmark_utf8proc_is_space(char: int) -> bool: r"""Match anything in the Zs class, plus LF, CR, TAB, FF.""" return (unicodedata.category(chr(char)) == 'Zs' or chr(char) in ['\u0009', '\u000A', '\u000C', '\u000D']) # 0.29, 0.30 def _cmark_cmark_utf8proc_is_punctuation(char: int) -> bool: r"""Match anything in the P[cdefios] classes.""" return ((char < 128 and _cmark_cmark_ispunct(char)) or unicodedata.category(chr(char)).startswith('P')) if __name__ == '__main__': pass md-toc-9.0.0/md_toc/constants.py000066400000000000000000000240231460560256400165470ustar00rootroot00000000000000# # constants.py # # Copyright (C) 2017-2024 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # """A file that contains all the global constants.""" import copy import html import os import typing # License C applies to the cmark entities part. # See docs/copyright_license.rst _entities5 = html.entities.html5 # remove keys without semicolons. For some reason the list # has duplicates of a few things, like auml, one with and one # without a semicolon. _ents = sorted([(k[:-1], _entities5[k].encode('utf-8')) for k in _entities5.keys() if k[-1] == ';']) # Use a list instead of a class for simplicity. # For this reason the cmark/entities.inc file is missing. _entities: list = list() for (ent, bs) in _ents: _entities.append({ 'entity': ent, 'bytes': (' '.join(map(str, bs)) + ' 0').split(' ') }) # Transform each entity into a list of integers from a list of strings. _entities[-1]['bytes'] = [int(n) for n in _entities[-1]['bytes']] # Regular expressions related to scanners functions. # See scanners.re and scanners.c files. __cmark_spacechar = '([ \t\v\f\r\n])' __cmark_tagname = '([A-Za-z][A-Za-z0-9-]*)' __cmark_attributename = '([a-zA-Z_:][a-zA-Z0-9:._-]*)' __cmark_unquotedvalue = "([^ \t\r\n\v\f\"'=<>`\x00]+)" __cmark_singlequotedvalue = "(['][^'\x00]*['])" __cmark_doublequotedvalue = '(["][^"\x00]*["])' __cmark_attributevalue = '(' + __cmark_unquotedvalue + '|' + __cmark_singlequotedvalue + '|' + __cmark_doublequotedvalue + ')' __cmark_attributevaluespec = __cmark_spacechar + '*[=]' + __cmark_spacechar + '*' + __cmark_attributevalue __cmark_attribute = '(' + __cmark_spacechar + '+' + __cmark_attributename + __cmark_attributevaluespec + '?)' __cmark_opentag = __cmark_tagname + __cmark_attribute + '*' + __cmark_spacechar + '*[/]?[>]' __cmark_closetag = '[/]' + __cmark_tagname + __cmark_spacechar + '*[>]' common_defaults: dict = { 'toc_marker': '', 'newline_string': os.linesep, } parser: dict = { 'cmark': { 'list': { 'ordered': { 'closing_markers': ['.', ')'], 'default_marker_number': 1, 'min_marker_number': 0, 'max_marker_number': 999999999, 'default_closing_marker': '.', }, 'unordered': { 'bullet_markers': ['-', '+', '*'], 'default_marker': '-', }, }, 'link': { 'max_chars_label': 999, }, 'header': { 'max_space_indentation': 3, 'max_levels': 6, 'default_keep_levels': 6, }, 'code_fence': { 'marker': { 'backtick': '`', 'tilde': '~', }, 'min_marker_characters': 3, }, # Regular expressions related to entities functions. # See make_entities_inc.py and entities.inc files. 're': { 'ENTITIES': { 'CMARK_ENTITY_MIN_LENGTH': 2, 'CMARK_ENTITY_MAX_LENGTH': 32, 'CMARK_NUM_ENTITIES': len(_entities), 'entities': _entities, }, # [0.30] only. 'SPACETAB': '[\u0009\u0020]', # Line ending. 'LE': '(\u000a|\u000d|\u000d\u000a)', # See https://spec.commonmark.org/0.28/#raw-html # 1. Open tag and 2. close tag. 'DQAV': __cmark_doublequotedvalue, 'SQAV': __cmark_singlequotedvalue, 'UAV': __cmark_unquotedvalue, # 2. 'AN': __cmark_attributename, 'TN': __cmark_tagname, # 3. HTML comment. 'COS': '', # 4. Processing instructions. 'PIS': r'<\?', 'PIB': r'(?:(?!\?>).)*', 'PIE': r'\?>', # 5. Declarations. 'DES': ').)+', 'DEE': '>', # 6. CDATA # Section. 'CDS': r').)+', # End. 'CDE': r'\]\]>', # Attribute value. 'AV': __cmark_attributevalue, # Attribute value specification. 'AVS': __cmark_attributevaluespec, }, '_scanners.re': { # FIXME # Some of these expressions are a duplicate of parser['cmark']['re'] dicts. 'spacechar': __cmark_spacechar, 'escaped_char': '([\\][!"#$%&\'()*+,./:;<=>?@[\\\\]^_`{|}~-])', 'cdata': r'CDATA\[([^\]\x00]+|\][^\]\x00]|\]\][^>\x00])*', 'htmltag': '(' + __cmark_opentag + '|' + __cmark_closetag + ')', 'htmlcomment': '(--->|(-([-]?[^\x00>-])([-]?[^\x00-])*-->))', 'declaration': '[A-Z]+' + __cmark_spacechar + '+' + '[^>\x00]*', 'processinginstruction': '([^?>\x00]+|[?][^>\x00]|[>])+', }, }, 'redcarpet': { 'list': { 'ordered': { # FIXME 'min_marker_number': 0, 'closing_markers': ['.'], 'default_closing_marker': '.', }, 'unordered': { 'bullet_markers': ['-', '+', '*'], 'default_marker': '-', }, }, 'header': { 'max_space_indentation': 0, 'max_levels': 6, 'default_keep_levels': 6, }, }, } parser['cmark']['re'].update({ # Attribute. # [0.30] # An attribute consists of spaces, tabs, and up to one line ending, # an attribute name, and an optional attribute value specification. # A newline is needed if spaces are not present in the first part. 'AT': ('(' + parser['cmark']['re']['SPACETAB'] + '+' + '|' + parser['cmark']['re']['LE'] + '{1,1}' + ')' + parser['cmark']['re']['AN'] + '(' + parser['cmark']['re']['AVS'] + ')?'), }) parser['cmark']['re'].update({ # 1. Open tag. 'OT': ('<' + parser['cmark']['re']['TN'] + '(' + parser['cmark']['re']['AT'] + ')*' + '(' + parser['cmark']['re']['SPACETAB'] + '*' + '|' + parser['cmark']['re']['LE'] + '?' + ')' + '(/)?' + '>'), # 2. Close tag. 'CT': (''), # 3. HTML comment. 'CO': parser['cmark']['re']['COS'] + parser['cmark']['re']['COT'] + parser['cmark']['re']['COE'], # 4. Processing instructions. 'PI': parser['cmark']['re']['PIS'] + parser['cmark']['re']['PIB'] + parser['cmark']['re']['PIE'], # 5. Declarations. 'DE': parser['cmark']['re']['DES'] + parser['cmark']['re']['DEN'] + parser['cmark']['re']['DEB'] + parser['cmark']['re']['DEE'], # 6. CDATA. 'CD': parser['cmark']['re']['CDS'] + parser['cmark']['re']['CDB'] + parser['cmark']['re']['CDE'], }) parser['github'] = copy.deepcopy(parser['cmark']) # FIXME # The following overrides must be removed once GFM is on par with cmark 0.30. # FIXME # Regular expressions. # These refer to inline HTML. parser['github']['re'].update({ 'UAV': "[^\u0020\"'=<>`]+", 'WS': '(\u0020|\u0009|\u000a|\u000b|\u000c|\u000d)', }) parser['github']['re'].update({ 'AV': ('(' + parser['github']['re']['UAV'] + '|' + parser['github']['re']['SQAV'] + '|' + parser['github']['re']['DQAV'] + ')'), 'AVS': (parser['github']['re']['WS'] + '*' + '=' + parser['github']['re']['WS'] + '*' + parser['github']['re']['AV']), # Attribute. 'AT': (parser['github']['re']['WS'] + '+' + parser['github']['re']['AN'] + '(' + parser['github']['re']['AVS'] + ')?'), # Remember: https://developmentality.wordpress.com/2011/09/22/python-gotcha-word-boundaries-in-regular-expressions/ # Github Flavored Markdown Disallowed Raw HTML (specific to GFM and not to cmark') # See # https://github.github.com/gfm/#disallowed-raw-html-extension- # This RE are specific to GFM. 'GDRH': r'''(\b[tT][iI][tT][lL][eE]\b|\b[tT][eE][xX][tT][aA][rR][eE][aA]\b|\b[sS][tT][yY][lL][eE]\b|\b[xX][mM][pP]\b|\b[iI][fF][rR][aA][mM][eE]\b|\b[nN][oO][eE][mM][bB][eE][dD]\b|\b[nN][oO][fF][rR][aA][mM][eE][sS]\b|\b[sS][cC][rR][iI][pP][tT]\b|\b[pP][lL][aA][iI][nN][tT][eE][xX][tT]\b)''', 'DEW': parser['github']['re']['WS'] + '+', }) parser['github']['re'].update({ 'TN': ('(?!' + parser['github']['re']['GDRH'] + ')' + parser['github']['re']['TN']), }) parser['github']['re'].update({ # 1. Open tag. 'OT': ('<' + parser['github']['re']['TN'] + '(' + parser['github']['re']['AT'] + ')*' + '(' + parser['github']['re']['WS'] + ')*' + '(/)?' + '>'), # 2. Close tag. 'CT': (''), # 5. Declarations. 'DE': (parser['github']['re']['DES'] + parser['github']['re']['DEN'] + parser['github']['re']['DEW'] + parser['github']['re']['DEB'] + parser['github']['re']['DEE']), }) del parser['github']['re']['SPACETAB'] del parser['github']['re']['LE'] ########################################## # Do not move these. parser['gitlab'] = copy.deepcopy(parser['cmark']) parser['goldmark'] = copy.deepcopy(parser['cmark']) parser['commonmarker'] = copy.deepcopy(parser['github']) if __name__ == '__main__': pass md-toc-9.0.0/md_toc/exceptions.py000066400000000000000000000025451460560256400167210ustar00rootroot00000000000000# # exceptions.py # # Copyright (C) 2017-2020 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # """Exceptions file.""" class GithubOverflowCharsLinkLabel(Exception): """Cannot parse link label.""" class GithubEmptyLinkLabel(Exception): """The link lables contains only whitespace characters or is empty.""" class GithubOverflowOrderedListMarker(Exception): """The ordered list marker number is too big.""" class StdinIsNotAFileToBeWritten(Exception): """stdin cannot be written onto.""" class TocDoesNotRenderAsCoherentList(Exception): """TOC list indentations are either wrong or not what the user intended.""" class StringCannotContainNewlines(Exception): """The specified string cannot contain newlines.""" md-toc-9.0.0/md_toc/generic.py000066400000000000000000000222201460560256400161440ustar00rootroot00000000000000# # generic.py # # Copyright (C) 2017-2023 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # """Generic functions.""" from __future__ import annotations import re import fpyutils # _ctoi and _isascii taken from cpython source Lib/curses/ascii.py # See: # https://github.com/python/cpython/blob/283de2b9c18e38c9a573526d6c398ade7dd6f8e9/Lib/curses/ascii.py#L48 # https://github.com/python/cpython/blob/283de2b9c18e38c9a573526d6c398ade7dd6f8e9/Lib/curses/ascii.py#L56 # # These two functions are released under the # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 # See https://directory.fsf.org/wiki/License:Python-2.0.1 def _ctoi(c: str) -> int: if not len(c) == 1: raise ValueError retval: str | int = c if isinstance(c, str): retval = ord(c) return retval def _isascii(c) -> int: return 0 <= _ctoi(c) <= 127 def _extract_lines(input_file: str, start: int, end: int) -> str: r"""Extract lines from file between start and end line numbers, with line numbers starting from 1.""" if start > end or start < 1 or end < 1: raise ValueError lines: list[str] = list() line_counter: int = 1 with open(input_file) as f: line: str = f.readline() while line: if line_counter >= start and line_counter <= end: lines.append(line) line = f.readline() line_counter += 1 return ''.join(lines) def _remove_line_intervals(filename: str, line_intervals: list[list[int]]): # A nested list of integers divided in couples is expected. # Example: [[1, 4], [8, 9]] for interval in line_intervals: if len(interval) != 2 or interval[0] < 1 or interval[ 1] < 1 or interval[0] > interval[1]: raise ValueError fpyutils.filelines.remove_line_interval( filename, interval[0], interval[1], filename, ) def _get_existing_toc(filename: str, marker: str) -> tuple: r"""Get the existing TOC in a file and return other important data about the TOC and its markers.""" # TOC marker positions. marker_line_positions, lines = fpyutils.filelines.get_line_matches( filename, marker, 0, loose_matching=True, keep_all_lines=True, ) marker_line_positions_length: int = len(marker_line_positions) old_toc: str = '' first_marker: int = 1 second_marker: int = 2 two_or_more_markers: bool = False done: bool = False first_marker_line_number: int = 0 lines_to_delete: list = list() if marker_line_positions_length > 0: first_marker_line_number = marker_line_positions[first_marker] # Possible pre-existing TOC. while not done and marker_line_positions_length >= 2: interval: str = _read_line_interval( lines, marker_line_positions[first_marker] + 1, marker_line_positions[second_marker] - 1) # Line intervals excluding the newline after the first # and before the last . interval_with_newline: str = _read_line_interval( lines, marker_line_positions[first_marker] + 2, marker_line_positions[second_marker] - 2) # TODO: add code fence detection. if _detect_toc_list(interval_with_newline) or _string_empty(interval): # Skip the opening and closing TOC markers. start_line: int = marker_line_positions[first_marker] + 1 end_line: int = marker_line_positions[second_marker] - 1 if start_line <= end_line: # Real TOC detected. old_toc = _extract_lines(filename, start_line, end_line).strip() else: old_toc = '' lines_to_delete.append([ marker_line_positions[first_marker], marker_line_positions[second_marker] ]) first_marker_line_number = marker_line_positions[first_marker] done = True first_marker += 1 second_marker += 1 marker_line_positions_length -= 1 two_or_more_markers = True # Only 1 pre-existing marker. TOC is just the marker. if not two_or_more_markers and marker_line_positions_length == 1: old_toc = marker return old_toc, lines_to_delete, two_or_more_markers, marker_line_positions_length, marker_line_positions, first_marker_line_number def _replace_substring(source: str, replacement: str, start: int, end: int) -> str: r"""Given a string called source, replace it with a string called replacement between the start and end indices interval.""" # Avoid possibility to pass lists to this function which the program would # happily process. if not isinstance(source, str): raise TypeError if not isinstance(replacement, str): raise TypeError if start > end: raise ValueError replaced: list = list() was_replaced: bool = False i: int = 0 while i < len(source): if i < start or i > end: replaced.append(source[i]) elif not was_replaced: # Only one replacement is possible. replaced.append(replacement) was_replaced = True i += 1 return ''.join(replaced) # A weaker version of C's strncmp: this one does not count the characters, # it just notifies if there are differences. def _strncmp(s1: str, s2: str, length: int) -> int: i: int = 0 retval: int = 0 process: bool = True s1_prime: str = s1[:length] s2_prime: str = s2[:length] s1_prime_length: int = len(s1_prime) s2_prime_length: int = len(s2_prime) min_length: int = min(s1_prime_length, s2_prime_length) if s1_prime_length < s2_prime_length: retval = -1 process = False elif s1_prime_length > s2_prime_length: retval = 1 process = False while process and i < min_length: int_s1: int = ord(s1_prime[i]) int_s2: int = ord(s2_prime[i]) if int_s1 < int_s2: retval = -1 process = False elif int_s1 > int_s2: retval = 1 process = False i += 1 return retval def _string_empty(lines: str) -> bool: empty: bool = True # Avoid matching \n\r if re.fullmatch( '((?!\u000a\u000d)|(?!\u000a\u000d)\u000d\u000a|(?!\u000a\u000d)\u000d|(?!\u000a\u000d)\u000a|(?!\u000a\u000d)\u000b|(?!\u000a\u000d)\u0020|(?!\u000a\u000d)\u0009)+', lines) is None: empty = False return empty def _detect_toc_list(line: str) -> bool: # An heuristic to detect a TOC list generated by md-toc. # If a user deletes the first TOC list line by mistake # there might be additional spaces (max 3 possible) # at the start of the second line # See # https://spec.commonmark.org/0.30/#example-288 # and # https://spec.commonmark.org/0.30/#example-289 match: bool = True if re.fullmatch( r' {0,3}([-+*]|' + r'\d' + '+[.' + r'\)' + '])\u0020((?![\u0020\u0009\u000b]+).*)', line) is None: match = False return match def _read_line_interval(lines: str, start: int, end: int) -> str: r"""Given a string get a line interval between start and end. Indices are 1 based. Newline characters are ignored. """ done: bool = False i: int = 0 line_counter: int = 1 lines_length: int = len(lines) final_line: list = list() # Shortcut. if start > end or start < 1: done = True while not done: invalid_newline: bool = False # Skip \n\r while i + 1 < lines_length and lines[i] == '\u000a' and lines[ i + 1] == '\u000d': i += 2 invalid_newline = True # Get \r\n as a single newline character. while i + 1 < lines_length and lines[i] == '\u000d' and lines[ i + 1] == '\u000a': line_counter += 1 i += 2 # Get \r or \n while i < lines_length and re.fullmatch('[\u000d\u000a]', lines[i]) is not None: line_counter += 1 i += 1 if i < lines_length and line_counter >= start and line_counter <= end: if invalid_newline: final_line.append('\u000a') final_line.append('\u000d') # Add non-newline character. final_line.append(lines[i]) if i >= lines_length: done = True i += 1 return ''.join(final_line) if __name__ == '__main__': pass md-toc-9.0.0/md_toc/tests/000077500000000000000000000000001460560256400153225ustar00rootroot00000000000000md-toc-9.0.0/md_toc/tests/__init__.py000066400000000000000000000013771460560256400174430ustar00rootroot00000000000000# # __init__.py # # Copyright (C) 2017-2020 Franco Masotti # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # """Python discovery file.""" md-toc-9.0.0/md_toc/tests/benchmark.py000077500000000000000000000150001460560256400176250ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (C) 2022 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A simple benchmark file to be used to check for hidden errors as well.""" import csv import ctypes import multiprocessing import platform import random import secrets import string import subprocess import tempfile import time import traceback import md_toc # ~50M characters CHAR_SIZE = 1024 * 1024 * 50 ITERATIONS = 100 # At what nTH character to put a header. # MIN_HEADER_STEP must be >= 10. MIN_HEADER_STEP = 10 MAX_HEADER_STEP = 50 # Do not use '#' as content to avoid triggering an empty link label exception. alphabet: str = string.printable.replace('#', '') def _generate_batch(size: int) -> bytes: return bytes( [secrets.choice(alphabet).encode('UTF-8')[0] for _ in range(size)]) def _generate_random_characters(size: int, min_header_step: int = 10, max_header_step: int = 1000) -> bytes: if min_header_step < 10 or max_header_step < min_header_step: # 10 - 1 = header (maximum 6) + '\n' + space + alphanum char raise ValueError print('generating random file...') secret_gen: random.SystemRandom = secrets.SystemRandom() alphanumerics: str = ''.join([string.ascii_letters, string.digits]) # Batch size at 1% of total size. batch_size: int = int(CHAR_SIZE * 0.01) # Chunk size at 1 per 10**4 of the batch size. chunk_size: int = int(batch_size * 0.00001) with multiprocessing.Pool() as pool: results = pool.map(_generate_batch, [batch_size] * (size // batch_size), chunk_size) string_buf = ctypes.create_string_buffer( b''.join(results), size=size + 1, ) print('adding headers to file...') i: int = 0 j: int = 1 while i + j + 3 < size: newline = b'\n' heading = b'#' * j space = b' ' random_char = secrets.choice(alphanumerics).encode('utf-8') ctypes.memmove(ctypes.byref(string_buf, i), newline, 1) ctypes.memmove(ctypes.byref(string_buf, i + 1), heading, len(heading)) ctypes.memmove(ctypes.byref(string_buf, i + 1 + j), space, 1) # Replace next character so we are sure never to raise the empty # link label exception. ctypes.memmove(ctypes.byref(string_buf, i + 2 + j), random_char, 1) # Reset header level. j = (j % 6) + 1 i += secret_gen.randrange(min_header_step, max_header_step) return string_buf.value if __name__ == '__main__': ok: bool = True i: int = 0 j: int = 0 total_iterations: int percent_progress: int current_parser_counter: int = 0 avg: list average: dict = dict() total: dict = dict() parsers = ['github', 'cmark', 'redcarpet', 'gitlab'] parsers.sort() total_iterations = len(parsers) * ITERATIONS for current_parser_counter, p in enumerate(parsers): print('parser: ' + p + ', ' + str(ITERATIONS) + ' iterations with ' + str(CHAR_SIZE) + ' characters each') print('min step: ' + str(MIN_HEADER_STEP) + ' , max step: ' + str(MAX_HEADER_STEP)) i = 0 avg = list() while ok and i < ITERATIONS: print('parser ' + str(p) + ' (' + str(current_parser_counter + 1) + ' of ' + str(len(parsers)) + '), iteration: ' + str(i + 1) + ' of ' + str(ITERATIONS)) percent_progress = (j / total_iterations) * 100 print('total progress percent = ' + str(percent_progress)) with tempfile.NamedTemporaryFile() as fp: fp.write( _generate_random_characters( CHAR_SIZE, min_header_step=MIN_HEADER_STEP, max_header_step=MAX_HEADER_STEP)) print('building TOC...') try: start = time.time() md_toc.api.build_toc(filename=fp.name, parser=p, keep_header_levels=3) end = time.time() avg.append(end - start) print('total_time: ' + str(avg[-1])) except Exception: ok = False traceback.print_exc() i += 1 j += 1 total[p] = sum(avg) average[p] = total[p] / len(avg) print('total = ' + str(total[p]) + '\n') print('avg = ' + str(average[p]) + '\n') # Write CSV file. # Fields # 0 md_toc git hash version # 1 parser name # 2 number of characters # 3 number of iterations # 4 min_header_step # 5 max_header_step # 6 total execution time in seconds # 7 average execution time in seconds # 8->n system information # File header: # md_toc_git_hash,markdown_parser,total_characters,iterations,min_header_step,max_header_step,total,avg,platform_python_version,platform_architecture,platform_machine,platform_python_implementation,platform_python_compiler,platform_libc_ver md_toc_version = subprocess.check_output( ['/usr/bin/git', 'rev-parse', 'HEAD']).decode('UTF-8').strip() with open('benchmark.csv', 'a') as csvfile: spamwriter = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) for p in parsers: spamwriter.writerow([ md_toc_version, p, CHAR_SIZE, ITERATIONS, MIN_HEADER_STEP, MIN_HEADER_STEP, total[p], average[p], platform.python_version(), ' '.join(platform.architecture()), platform.machine(), platform.python_implementation(), platform.python_compiler(), ' '.join(platform.libc_ver()) ]) md-toc-9.0.0/md_toc/tests/fuzzer.py000066400000000000000000000040541460560256400172240ustar00rootroot00000000000000# # fuzzer.py # # Copyright (C) 20244 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""A basic fuzzer for the build_toc function.""" import atheris with atheris.instrument_imports(): import secrets import sys import tempfile from .. import api, exceptions def TestBuildToc(data): r"""Test the md_toc.api.build_toc function.""" with tempfile.NamedTemporaryFile() as fp: bytez: bytes = b''.join([ bytes('#' * (secrets.randbelow(6) + 1), 'UTF-8'), b' ', data, ]) fp.write(bytez) # Move pointer to the start of the file. fp.seek(0) try: for parser in ['cmark', 'github', 'gitlab', 'redcarpet']: api.build_toc(filename=fp.name, parser=parser, keep_header_levels=6) except (exceptions.GithubEmptyLinkLabel, exceptions.TocDoesNotRenderAsCoherentList, exceptions.GithubOverflowCharsLinkLabel) as e: # The input string cannot be guaranteed to have a non-empty label # (GithubEmptyLinkLabel) # or a newline with '#' sequences can be inserted # (TocDoesNotRenderAsCoherentList) # or header generates a link label which is too long # (GithubOverflowCharsLinkLabel) print(e, end='') atheris.Setup(sys.argv, TestBuildToc) atheris.Fuzz() md-toc-9.0.0/md_toc/tests/tests.py000066400000000000000000005147771460560256400170630ustar00rootroot00000000000000# # tests.py # # Copyright (C) 2017-2023 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see . # r"""The tests module.""" import doctest import unittest from unittest.mock import patch from pyfakefs.fake_filesystem_unittest import TestCase as pyfakefsTestCase from .. import api, exceptions, generic from ..constants import parser as md_parser # Some static generic variables. # Lines. LINE = 'This is a static line' LINE_EMPTY = '' LINE_ESCAPE = '\\' LINE_LINE_FEED = '\n' LINE_CARRIAGE_RETURN = '\r' LINE_SQUARE_BRACKET_OPEN = '[' LINE_SQUARE_BRACKET_CLOSE = ']' LINE_DASH = '\u002D' LINE_EN_DASH = '\u2013' LINE_HYPHEN = '\u2010' LINE_MINUS = '\u2212' LINE_EM_DASH = '\u2014' # Marker. MARKER = '' # Spaces. S1 = 1 * ' ' S2 = 2 * ' ' S3 = 3 * ' ' S4 = 4 * ' ' S5 = 5 * ' ' S10 = 10 * ' ' S18 = 18 * ' ' S21 = 21 * ' ' # Tabs. T1 = 1 * '\u0009' T2 = 2 * '\u0009' T3 = 3 * '\u0009' T4 = 4 * '\u0009' T5 = 5 * '\u0009' T10 = 10 * '\u0009' T18 = 18 * '\u0009' T21 = 21 * '\u0009' # Vertical Tabs. V1 = 1 * '\u000B' # ATX headers. H1 = 1 * '#' H2 = 2 * '#' H3 = 3 * '#' H4 = 4 * '#' H5 = 5 * '#' H6 = 6 * '#' H7 = 7 * '#' H34 = 34 * '#' # Lists. LIST_INDENTATION = 4 UNORDERED_LIST_SYMBOL = '-' ORDERED_LIST_SYMBOL = '.' # Header types. GENERIC_HEADER_TYPE_PREV = 2 GENERIC_HEADER_TYPE_CURR = 6 BASE_CASE_HEADER_TYPE_PREV = 0 # Indentation. GENERIC_NUMBER_OF_INDENTATION_SPACES = 128 # List marker log. GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX = 1000 # github test lines. CMARK_LINE_FOO = 'foo' CMARK_LINE_BAR = 'bar' CMARK_LINE_BAZ = 'baz' CMARK_LINE_5_BOLT = '5 bolt' CMARK_LINE_HASHTAG = 'hashtag' CMARK_LINE_BAR_BAZ = '*bar* ' + LINE_ESCAPE + '*baz' + LINE_ESCAPE + '*' CMARK_LINE_B = 'b' CMARK_LINE_1000_CHARS = 1000 * 'c' # Code fences. BACKTICK1 = 1 * '`' BACKTICK2 = 2 * '`' BACKTICK3 = 3 * '`' BACKTICK4 = 4 * '`' BACKTICK5 = 5 * '`' BACKTICK6 = 6 * '`' BACKTICK10 = 10 * '`' TILDE1 = 1 * '~' TILDE2 = 2 * '~' TILDE3 = 3 * '~' TILDE4 = 4 * '~' TILDE5 = 5 * '~' TILDE6 = 6 * '~' TILDE10 = 10 * '~' CMARK_INFO_STRING_FOO = 'ruby' CMARK_INFO_STRING_GARBAGE = 'startline=3 $%@#$' # github renders as list header types. Do not change these values. BASE_CASE_CMARK_RENDERS_AS_LIST_HEADER_TYPE_CURR = 1 GENERIC_CMARK_RENDERS_AS_LIST_HEADER_TYPE_CURR = 4 GENERIC_CMARK_RENDERS_AS_LIST_HEADER_TYPE_CURR_BIS = 5 BASE_CASE_CMARK_RENDERS_AS_LIST_HEADER_TYPE_FIRST = 1 GENERIC_CMARK_RENDERS_AS_LIST_HEADER_TYPE_FIRST = 4 # redcarpet test lines. REDCARPET_LINE_FOO = 'foo' def load_tests(loader, tests, ignore): r"""Run tests against docstrings examples.""" tests.addTests(doctest.DocTestSuite(api)) tests.addTests(doctest.DocTestSuite(generic)) return tests class TestGeneric(pyfakefsTestCase): r"""Test the generic functions.""" def setUp(self): r"""Fake filesystem.""" self.setUpPyfakefs() def test__extract_lines(self): r"""Test extracting lines between line intervals.""" with open('foo.md', 'w') as f: f.write(CMARK_LINE_FOO) self.assertEqual(generic._extract_lines('foo.md', 2, 2), '') self.assertEqual(generic._extract_lines('foo.md', 1024, 2048), '') self.assertEqual(generic._extract_lines('foo.md', 1, 1), CMARK_LINE_FOO) with open('foo.md', 'w') as f: f.write(CMARK_LINE_FOO + LINE_LINE_FEED + CMARK_LINE_BAR) self.assertEqual(generic._extract_lines('foo.md', 2, 2), CMARK_LINE_BAR) self.assertEqual(generic._extract_lines('foo.md', 1, 2), CMARK_LINE_FOO + LINE_LINE_FEED + CMARK_LINE_BAR) with open('foo.md', 'w') as f: f.write(CMARK_LINE_FOO + LINE_LINE_FEED + CMARK_LINE_BAR + LINE_LINE_FEED) self.assertEqual( generic._extract_lines('foo.md', 1, 1024), CMARK_LINE_FOO + LINE_LINE_FEED + CMARK_LINE_BAR + LINE_LINE_FEED) self.assertEqual(generic._extract_lines('foo.md', 2, 2), CMARK_LINE_BAR + LINE_LINE_FEED) self.assertEqual(generic._extract_lines('foo.md', 2, 3), CMARK_LINE_BAR + LINE_LINE_FEED) with self.assertRaises(ValueError): generic._extract_lines('foo.md', 0, 0) with self.assertRaises(ValueError): generic._extract_lines('foo.md', 0, 1) with self.assertRaises(ValueError): generic._extract_lines('foo.md', 1, 0) def test__remove_line_intervals(self): r"""Test removing multiple line intervals from a file.""" with open('foo.md', 'w') as f: f.write(CMARK_LINE_FOO + LINE_LINE_FEED + CMARK_LINE_FOO + LINE_LINE_FEED) with self.assertRaises(ValueError): generic._remove_line_intervals('foo.md', [[1, 2], [4, 5, 6]]) with self.assertRaises(ValueError): generic._remove_line_intervals('foo.md', [[1, 2, 3], [4, 5]]) with self.assertRaises(ValueError): generic._remove_line_intervals('foo.md', [[2, 1]]) with self.assertRaises(ValueError): generic._remove_line_intervals('foo.md', [[0, 1]]) with self.assertRaises(ValueError): generic._remove_line_intervals('foo.md', [[1, 0]]) # The rest of the function involves fpyutils. def test__get_existing_toc(self): r"""Test retrieving the existing TOC. See the TestApi.test_write_string_on_file_between_markers method for more tests.""" # The function returns: # old_toc, lines_to_delete, two_or_more_markers, marker_line_positions_length, marker_line_positions, first_marker_line_number # No existing TOC == str(). with open('foo.md', 'w') as f: f.write(H1 + S1 + CMARK_LINE_FOO) self.assertEqual(generic._get_existing_toc('foo.md', ''), ('', list(), False, 0, dict(), 0)) # 1 TOC marker: TOC == marker. with open('foo.md', 'w') as f: f.write(MARKER + LINE_LINE_FEED + H1 + S1 + CMARK_LINE_FOO) self.assertEqual(generic._get_existing_toc('foo.md', MARKER), (MARKER, list(), False, 1, { 1: 1 }, 1)) # Normal existing TOC (2 TOC markers) with open('foo.md', 'w') as f: f.write(MARKER + LINE_LINE_FEED + LINE_LINE_FEED + '- [t](#t)' + LINE_LINE_FEED + LINE_LINE_FEED + MARKER + LINE_LINE_FEED + LINE_LINE_FEED + H1 + S1 + CMARK_LINE_FOO) self.assertEqual(generic._get_existing_toc('foo.md', MARKER), ('- [t](#t)', [[1, 5]], True, 1, { 1: 1, 2: 5 }, 1)) # More than 2 TOC markers with open('foo.md', 'w') as f: f.write(MARKER + LINE_LINE_FEED + LINE_LINE_FEED + '- [t](#t)' + LINE_LINE_FEED + LINE_LINE_FEED + MARKER + LINE_LINE_FEED + MARKER + LINE_LINE_FEED + LINE_LINE_FEED + H1 + S1 + CMARK_LINE_FOO) self.assertEqual(generic._get_existing_toc('foo.md', MARKER), ('- [t](#t)', [[1, 5]], True, 2, { 1: 1, 2: 5, 3: 6 }, 1)) # 2 markers, wrong TOC content (not a TOC) with open('foo.md', 'w') as f: f.write(MARKER + LINE_LINE_FEED + LINE_LINE_FEED + ' - [t](#t)' + LINE_LINE_FEED + LINE_LINE_FEED + MARKER + LINE_LINE_FEED + LINE_LINE_FEED + H1 + S1 + CMARK_LINE_FOO) self.assertEqual(generic._get_existing_toc('foo.md', MARKER), ('', list(), True, 1, { 1: 1, 2: 5 }, 1)) def test__replace_substring(self): r"""Test that the string is replaced with another one between the two specified indices.""" self.assertEqual(generic._replace_substring('', '', 0, 0), '') self.assertEqual(generic._replace_substring('', '', 1024, 1024), '') self.assertEqual(generic._replace_substring('', CMARK_LINE_FOO, 0, 0), '') self.assertEqual(generic._replace_substring(CMARK_LINE_FOO, '', 0, 0), 'oo') self.assertEqual(generic._replace_substring('', CMARK_LINE_FOO, 0, 0), '') # Replace first 'f' of 'foo' with 'foo' -> 'foo|oo' self.assertEqual( generic._replace_substring(CMARK_LINE_FOO, CMARK_LINE_FOO, 0, 0), 'foooo') # Replace first 'o' of 'foo' with 'foo' -> 'f|foo|o' self.assertEqual( generic._replace_substring(CMARK_LINE_FOO, CMARK_LINE_FOO, 1, 1), 'ffooo') # Replace last 'o' of 'foo' with 'foo' -> 'fo|foo' self.assertEqual( generic._replace_substring(CMARK_LINE_FOO, CMARK_LINE_FOO, 2, 2), 'fofoo') # Replace last 'oo' of 'foo' with 'foo' -> 'f|foo' self.assertEqual( generic._replace_substring(CMARK_LINE_FOO, CMARK_LINE_FOO, 1, 2), 'ffoo') # Replace 'foo' with 'foo' -> 'foo' self.assertEqual( generic._replace_substring(CMARK_LINE_FOO, CMARK_LINE_FOO, 0, 2), CMARK_LINE_FOO) # Check the single replacement policy. # Replace the first 'o' of the second 'foo' of 'foofoofoofoo' with 'a' -> 'foof|a|foo' self.assertEqual( generic._replace_substring( CMARK_LINE_FOO + CMARK_LINE_FOO + CMARK_LINE_FOO + CMARK_LINE_FOO, 'a', 4, 8), 'foofafoo') self.assertEqual(generic._replace_substring('b', 'a', 0, 0), 'a') self.assertEqual(generic._replace_substring('b', 'a', 0, 1), 'a') self.assertEqual(generic._replace_substring('b', 'a', 1, 1), 'b') self.assertEqual(generic._replace_substring('bcdef', 'a', 4, 4), 'bcdea') self.assertEqual(generic._replace_substring('bcdef', 'a', 0, 0), 'acdef') self.assertEqual(generic._replace_substring('bcdef', 'a', 1, 4), 'ba') self.assertEqual( generic._replace_substring('bcdef', 'a', 1, 999999999999), 'ba') self.assertEqual(generic._replace_substring('bcdef', 'a', 0, 2), 'aef') self.assertEqual( generic._replace_substring('bcdef', 'a', 0, 999999999999), 'a') with self.assertRaises(TypeError): generic._replace_substring(['ba'], 'a', 0, 0) with self.assertRaises(TypeError): generic._replace_substring('ba', ['a'], 0, 0) with self.assertRaises(TypeError): generic._replace_substring(['ba'], ['a'], 0, 0) with self.assertRaises(ValueError): generic._replace_substring('ba', 'a', 1, 0) with self.assertRaises(ValueError): generic._replace_substring('ba', 'a', 4, 2) def test__string_empty(self): r"""A string is empty if it contains whitespace characters, excluding consecutive sequences of \n\r.""" self.assertTrue(generic._string_empty('')) self.assertTrue(generic._string_empty('\u000a')) self.assertTrue(generic._string_empty('\u000d')) self.assertTrue(generic._string_empty('\u000b')) self.assertTrue(generic._string_empty('\u0009')) self.assertTrue(generic._string_empty('\u0020')) self.assertTrue(generic._string_empty('\u000d\u000a')) self.assertTrue(generic._string_empty('\u0020\u000d\u000a')) self.assertTrue( generic._string_empty('\u0020\u000d\u000d\u000a\u000d')) self.assertFalse( generic._string_empty('\u0020\u000d\u000a\u000a\u000d')) # \n\r self.assertFalse(generic._string_empty('\u000a\u000d')) self.assertFalse(generic._string_empty('\u0020\u000da\u000d')) self.assertTrue(generic._string_empty('\u0020\u000d\u0020\u000d')) self.assertFalse(generic._string_empty('\u0020abcd\u0020')) def test__detect_toc_list(self): r"""Detect a markdown list generated by md-toc.""" self.assertFalse(generic._detect_toc_list('')) # Unordered list. self.assertTrue(generic._detect_toc_list('- list')) self.assertTrue(generic._detect_toc_list('+ list')) self.assertTrue(generic._detect_toc_list('* list')) self.assertTrue(generic._detect_toc_list('- [list')) self.assertTrue(generic._detect_toc_list('+ [list')) self.assertTrue(generic._detect_toc_list('* [list')) # Not a list. self.assertFalse(generic._detect_toc_list('- list')) self.assertFalse(generic._detect_toc_list('+ list')) self.assertFalse(generic._detect_toc_list('* list')) self.assertFalse(generic._detect_toc_list('- \u0009list')) self.assertFalse(generic._detect_toc_list('+ \u0009list')) self.assertFalse(generic._detect_toc_list('* \u0009list')) self.assertFalse(generic._detect_toc_list('- \u0009[list')) self.assertFalse(generic._detect_toc_list('+ \u0009[list')) self.assertFalse(generic._detect_toc_list('* \u0009[list')) self.assertTrue(generic._detect_toc_list(' - list')) self.assertTrue(generic._detect_toc_list(' + list')) self.assertTrue(generic._detect_toc_list(' * list')) self.assertTrue(generic._detect_toc_list(' - list')) self.assertTrue(generic._detect_toc_list(' + list')) self.assertTrue(generic._detect_toc_list(' * list')) self.assertTrue(generic._detect_toc_list(' - list')) self.assertTrue(generic._detect_toc_list(' + list')) self.assertTrue(generic._detect_toc_list(' * list')) self.assertFalse(generic._detect_toc_list(' - list')) self.assertFalse(generic._detect_toc_list(' + list')) self.assertFalse(generic._detect_toc_list(' * list')) self.assertTrue(generic._detect_toc_list(' - [list')) self.assertTrue(generic._detect_toc_list(' + [list')) self.assertTrue(generic._detect_toc_list(' * [list')) self.assertTrue(generic._detect_toc_list(' - [list')) self.assertTrue(generic._detect_toc_list(' + [list')) self.assertTrue(generic._detect_toc_list(' * [list')) self.assertTrue(generic._detect_toc_list(' - [list')) self.assertTrue(generic._detect_toc_list(' + [list')) self.assertTrue(generic._detect_toc_list(' * [list')) self.assertFalse(generic._detect_toc_list(' - \u0009list')) self.assertFalse(generic._detect_toc_list(' + \u0009list')) self.assertFalse(generic._detect_toc_list(' * \u0009list')) self.assertFalse(generic._detect_toc_list(' - \u0009[list')) self.assertFalse(generic._detect_toc_list(' + \u0009[list')) self.assertFalse(generic._detect_toc_list(' * \u0009[list')) # Ordered list. self.assertTrue(generic._detect_toc_list('1. list')) self.assertTrue(generic._detect_toc_list('9999. list')) self.assertTrue(generic._detect_toc_list('1) list')) self.assertTrue(generic._detect_toc_list('9999) list')) self.assertTrue(generic._detect_toc_list('1) [list')) self.assertTrue(generic._detect_toc_list('9999) [list')) self.assertFalse(generic._detect_toc_list('1. list')) self.assertFalse(generic._detect_toc_list('9999. list')) self.assertFalse(generic._detect_toc_list('1) list')) self.assertFalse(generic._detect_toc_list('9999) list')) self.assertFalse(generic._detect_toc_list('1) [list')) self.assertFalse(generic._detect_toc_list('9999) [list')) self.assertTrue(generic._detect_toc_list(' 1. list')) self.assertTrue(generic._detect_toc_list(' 9999. list')) self.assertTrue(generic._detect_toc_list(' 1) list')) self.assertTrue(generic._detect_toc_list(' 9999) list')) self.assertTrue(generic._detect_toc_list(' 1) [list')) self.assertTrue(generic._detect_toc_list(' 9999) [list')) self.assertFalse(generic._detect_toc_list(' 1. list')) self.assertFalse(generic._detect_toc_list(' 9999. list')) self.assertFalse(generic._detect_toc_list(' 1) list')) self.assertFalse(generic._detect_toc_list(' 9999) list')) self.assertFalse(generic._detect_toc_list(' 1) [list')) self.assertFalse(generic._detect_toc_list(' 9999) [list')) self.assertTrue(generic._detect_toc_list(' 1. list')) self.assertTrue(generic._detect_toc_list(' 9999. list')) self.assertTrue(generic._detect_toc_list(' 1) list')) self.assertTrue(generic._detect_toc_list(' 9999) list')) self.assertTrue(generic._detect_toc_list(' 1) [list')) self.assertTrue(generic._detect_toc_list(' 9999) [list')) self.assertFalse(generic._detect_toc_list(' 1. list')) self.assertFalse(generic._detect_toc_list(' 9999. list')) self.assertFalse(generic._detect_toc_list(' 1) list')) self.assertFalse(generic._detect_toc_list(' 9999) list')) self.assertFalse(generic._detect_toc_list(' 1) [list')) self.assertFalse(generic._detect_toc_list(' 9999) [list')) self.assertTrue(generic._detect_toc_list(' 1. list')) self.assertTrue(generic._detect_toc_list(' 9999. list')) self.assertTrue(generic._detect_toc_list(' 1) list')) self.assertTrue(generic._detect_toc_list(' 9999) list')) self.assertTrue(generic._detect_toc_list(' 1) [list')) self.assertTrue(generic._detect_toc_list(' 9999) [list')) self.assertFalse(generic._detect_toc_list(' 1. list')) self.assertFalse(generic._detect_toc_list(' 9999. list')) self.assertFalse(generic._detect_toc_list(' 1) list')) self.assertFalse(generic._detect_toc_list(' 9999) list')) self.assertFalse(generic._detect_toc_list(' 1) [list')) self.assertFalse(generic._detect_toc_list(' 9999) [list')) self.assertFalse(generic._detect_toc_list(' 1. list')) self.assertFalse(generic._detect_toc_list(' 9999. list')) self.assertFalse(generic._detect_toc_list(' 1) list')) self.assertFalse(generic._detect_toc_list(' 9999) list')) self.assertFalse(generic._detect_toc_list(' 1) [list')) self.assertFalse(generic._detect_toc_list(' 9999) [list')) self.assertFalse(generic._detect_toc_list(' 1. list')) self.assertFalse(generic._detect_toc_list(' 9999. list')) self.assertFalse(generic._detect_toc_list(' 1) list')) self.assertFalse(generic._detect_toc_list(' 9999) list')) self.assertFalse(generic._detect_toc_list(' 1) [list')) self.assertFalse(generic._detect_toc_list(' 9999) [list')) def test__read_line_interval(self): r"""Extract a line interval.""" self.assertEqual(generic._read_line_interval('', 1, 1), '') self.assertEqual(generic._read_line_interval('', 0, 1), '') self.assertEqual(generic._read_line_interval('', 1, 0), '') self.assertEqual(generic._read_line_interval('', 0, 0), '') self.assertEqual(generic._read_line_interval('a', 1, 1), 'a') self.assertEqual(generic._read_line_interval('a', 0, 1), '') self.assertEqual(generic._read_line_interval('a', 1, 0), '') self.assertEqual(generic._read_line_interval('a', 0, 0), '') self.assertEqual(generic._read_line_interval('a\u000da', 1, 1), 'a') self.assertEqual(generic._read_line_interval('a\u000da', 1, 2), 'aa') self.assertEqual(generic._read_line_interval('a\u000da', 2, 2), 'a') self.assertEqual(generic._read_line_interval('a\u000d\u000aa', 2, 2), 'a') self.assertEqual(generic._read_line_interval('a\u000a\u000db', 1, 1), 'a\u000a\u000db') self.assertEqual( generic._read_line_interval('a\u000a\u000db\u000d\u000ac', 1, 2), 'a\u000a\u000dbc') self.assertEqual(generic._read_line_interval('ab\u000d\u000ac', 1, 2), 'abc') self.assertEqual(generic._read_line_interval('ab\u000d\u000ac', 2, 2), 'c') self.assertEqual( generic._read_line_interval('ab\u000d\u000a\u000ac', 2, 2), '') self.assertEqual( generic._read_line_interval('ab\u000d\u000a\u000ac', 2, 3), 'c') class TestApi(pyfakefsTestCase): r"""Test the main API.""" def setUp(self): r"""Fake filesystem.""" self.setUpPyfakefs() @unittest.skip('empty test') def test_tocs_equal(self): r"""Test if two TOCs are equal.""" def test_write_string_on_file_between_markers(self): r"""Test that the TOC is written correctly on the file. Most of the job is done by the fpyutils library. Refer to that for the unit tests. """ with self.assertRaises(exceptions.StdinIsNotAFileToBeWritten): api.write_string_on_file_between_markers('-', LINE, LINE, newline_string='\n') # 0 TOC markers. with open('foo.md', 'w') as f: f.write('hello') api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual(lines, 'hello') # 1 TOC markers. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n') # 2 consecutive TOC markers. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual(lines, 'hello' + '\n' + MARKER + MARKER) # 2 TOC markers, valid list, not empty. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n\n' + '- [hi](#hi)' + '\n\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n') # 2 TOC markers, valid list, less space, not empty. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n' + '- [hi](#hi)' + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + MARKER + '\n' + '- [hi](#hi)' + '\n' + MARKER) # 2 TOC markers, valid list, not empty, 1 leading space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n\n' + S1 + '- [hi](#hi)' + '\n\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n') # 2 TOC markers, valid list, not empty, 2 leading space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n\n' + S2 + '- [hi](#hi)' + '\n\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n') # 2 TOC markers, valid list, not empty, 3 leading space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n\n' + S3 + '- [hi](#hi)' + '\n\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n') # 2 TOC markers, INvalid list, not empty, 4 leading space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n\n' + S4 + '- [hi](#hi)' + '\n\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + MARKER + '\n\n' + S4 + '- [hi](#hi)' + '\n\n' + MARKER) # 2 TOC markers, valid list, not empty, 1 leading space, less space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n' + S1 + '- [hi](#hi)' + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + MARKER + '\n' + S1 + '- [hi](#hi)' + '\n' + MARKER) # 2 TOC markers, valid list, not empty, 2 leading space, less space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n' + S2 + '- [hi](#hi)' + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + MARKER + '\n' + S2 + '- [hi](#hi)' + '\n' + MARKER) # 2 TOC markers, valid list, not empty, 3 leading space, less space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n' + S3 + '- [hi](#hi)' + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + MARKER + '\n' + S3 + '- [hi](#hi)' + '\n' + MARKER) # 2 TOC markers, no list, empty. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n') # 2 TOC markers, no list, empty, less space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n') # More than 2 TOC markers, valid list. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n' + MARKER + '\n' + '- [hi](#hi)' + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + '- [hi](#hi)' + '\n' + MARKER) # More than 2 TOC markers, valid list, 1 leading space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n' + MARKER + '\n' + S1 + '- [hi](#hi)' + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + S1 + '- [hi](#hi)' + '\n' + MARKER) # More than 2 TOC markers, valid list, 2 leading space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n' + MARKER + '\n' + S2 + '- [hi](#hi)' + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + S2 + '- [hi](#hi)' + '\n' + MARKER) # More than 2 TOC markers, valid list, 3 leading space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n' + MARKER + '\n' + S3 + '- [hi](#hi)' + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + S3 + '- [hi](#hi)' + '\n' + MARKER) # More than 2 TOC markers, valid list first. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n\n' + '- [hi](#hi)' + '\n\n' + MARKER + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + MARKER) # More than 2 TOC markers, valid list first, 1 leading space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n\n' + S1 + '- [hi](#hi)' + '\n\n' + MARKER + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + MARKER) # More than 2 TOC markers, valid list first, 2 leading space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n\n' + S2 + '- [hi](#hi)' + '\n\n' + MARKER + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + MARKER) # More than 2 TOC markers, valid list first, 3 leading space. with open('foo.md', 'w') as f: f.write('hello' + '\n' + MARKER + '\n\n' + S3 + '- [hi](#hi)' + '\n\n' + MARKER + '\n' + MARKER) api.write_string_on_file_between_markers('foo.md', LINE, MARKER, newline_string='\n') with open('foo.md') as f: lines = f.readlines() lines = ''.join(lines) self.assertEqual( lines, 'hello' + '\n' + MARKER + '\n\n' + LINE + '\n\n' + MARKER + '\n' + MARKER) @unittest.skip('empty test') def test_write_strings_on_files_between_markers(self): r"""Test that the TOC is written correctly on the files.""" @unittest.skip('empty test') def test_build_toc(self): r"""Test that the TOC is built correctly. TODO: tests will be needed eventually because the complexity of this function is growing. """ @unittest.skip('empty test') def test_build_multiple_tocs(self): r"""Test that the TOC is built correctly for multiple files.""" def test_increase_index_ordered_list(self): r"""Test that the list index increases correctly. Test both base cases and the non-base case by verifying that the index increases or resets. """ # Check that the index increases if we are on the same sublist. ht = {} api.increase_index_ordered_list(ht, 0, 1) self.assertEqual(ht['h' + str(1)], 1) api.increase_index_ordered_list(ht, 1, 1) self.assertEqual(ht['h' + str(1)], 2) # Check that the index resets if we are starting a new sublist. ht = {} ht['h' + str(1)] = 1 ht['h' + str(2)] = 3 ht['h' + str(3)] = 1 api.increase_index_ordered_list(ht, 2, 3) self.assertEqual(ht['h' + str(3)], 1) # Check overflow rule for github. ht['h' + str(1)] = md_parser['github']['list']['ordered'][ 'max marker number'] = 999999999 with self.assertRaises(exceptions.GithubOverflowOrderedListMarker): api.increase_index_ordered_list(ht, 1, 1) @unittest.skip('empty test') def test_build_list_marker_log(self): r"""Test that the list_marker_log data structure is built correctly. There is no need to test this since it is a very simple function. """ def _test_helper_assert_compute_toc_line_indentation_spaces( self, parser, log, header_type_curr, header_type_prev, expected_indentation_spaces, expected_index, expected_list_marker, ): self.assertEqual( log[header_type_curr]['indentation_spaces'], expected_indentation_spaces, ) self.assertEqual(log[header_type_curr]['index'], expected_index) self.assertEqual( log[header_type_curr]['list_marker'], expected_list_marker, ) if parser == 'github': if header_type_curr < header_type_prev: for i in range( header_type_curr + 1, md_parser['github']['header']['max levels'] + 1, ): self.assertEqual(log[i]['index'], 0) self.assertEqual(log[i]['indentation_spaces'], 0) self.assertEqual( log[i]['list_marker'], expected_list_marker, ) def test_compute_toc_line_indentation_spaces(self): r"""Test that the TOC list indentation spaces are computed correctly.""" # github. md_parser['github']['header']['max levels'] = 6 # Unordered TOC. # 1. First TOC line. log = api.init_indentation_log('github', '-') api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_CURR, BASE_CASE_HEADER_TYPE_PREV, 'github', False, '-', log, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_CURR, BASE_CASE_HEADER_TYPE_PREV, 0, 0, '-', ) # 2. First TOC line with the incorrect number of indentation spaces. log = api.init_indentation_log('github', '-') log[GENERIC_HEADER_TYPE_CURR][ 'indentation_spaces'] = GENERIC_NUMBER_OF_INDENTATION_SPACES api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_CURR, BASE_CASE_HEADER_TYPE_PREV, 'github', False, '-', log, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_CURR, BASE_CASE_HEADER_TYPE_PREV, 0, 0, '-', ) # 3. A generic TOC line with number of previous indentation spaces = 0. log = api.init_indentation_log('github', '-') log[GENERIC_HEADER_TYPE_CURR]['indentation_spaces'] = 0 api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, 'github', False, '-', log, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, len(UNORDERED_LIST_SYMBOL) + len(S1), 0, '-', ) # 4. Base case same indentation. log = api.init_indentation_log('github', '-') log[GENERIC_HEADER_TYPE_CURR][ 'indentation_spaces'] = GENERIC_NUMBER_OF_INDENTATION_SPACES api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_CURR, 'github', False, '-', log, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_CURR, GENERIC_NUMBER_OF_INDENTATION_SPACES, 0, '-', ) # 5. Generic case more indentation. log = api.init_indentation_log('github', '-') log[GENERIC_HEADER_TYPE_PREV][ 'indentation_spaces'] = GENERIC_NUMBER_OF_INDENTATION_SPACES api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, 'github', False, '-', log, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, GENERIC_NUMBER_OF_INDENTATION_SPACES + len(UNORDERED_LIST_SYMBOL) + len(S1), 0, '-', ) # 6. Generic case less indentation. # Note: the parameters GENERIC_HEADER_TYPE_PREV and GENERIC_HEADER_TYPE_CURR # are inverted as the api.compute_toc_line_indentation_spaces # function input, in respect to the usual positions. log = api.init_indentation_log('github', '-') log[GENERIC_HEADER_TYPE_PREV][ 'indentation_spaces'] = GENERIC_NUMBER_OF_INDENTATION_SPACES api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_PREV, GENERIC_HEADER_TYPE_CURR, 'github', False, '-', log, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_PREV, GENERIC_HEADER_TYPE_CURR, GENERIC_NUMBER_OF_INDENTATION_SPACES, 0, '-', ) # Ordered TOC. # 7. First TOC line. log = api.init_indentation_log('github', '.') log[GENERIC_HEADER_TYPE_CURR]['indentation_spaces'] = 0 api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_CURR, BASE_CASE_HEADER_TYPE_PREV, 'github', True, '.', log, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, 0, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, '.', ) # 8. First TOC line with the incorrect number of indentation spaces. log = api.init_indentation_log('github', '.') log[GENERIC_HEADER_TYPE_CURR][ 'indentation_spaces'] = GENERIC_NUMBER_OF_INDENTATION_SPACES api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_CURR, BASE_CASE_HEADER_TYPE_PREV, 'github', True, '.', log, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, 0, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, '.', ) # 9. A generic TOC line with no_of_indentation_spaces_prev=0. log = api.init_indentation_log('github', '.') log[1]['index'] = 998 log[2]['index'] = 999 log[3]['index'] = 1001 expected_indentation_spaces = log[GENERIC_HEADER_TYPE_PREV][ 'indentation_spaces'] + len( str(log[GENERIC_HEADER_TYPE_PREV]['index'])) + len('.') + len( S1) api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, 'github', True, '.', log, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, expected_indentation_spaces, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, '.', ) # 10. Another base case. log = api.init_indentation_log('github', '.') log[1]['index'] = 998 log[2]['index'] = 999 log[3]['index'] = 1001 log[GENERIC_HEADER_TYPE_CURR][ 'indentation_spaces'] = GENERIC_NUMBER_OF_INDENTATION_SPACES api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_CURR, 'github', True, '.', log, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_CURR, GENERIC_NUMBER_OF_INDENTATION_SPACES, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, '.', ) # 11. Generic case more indentation. This is very similar to example 9. log = api.init_indentation_log('github', '.') log[1]['index'] = 998 log[2]['index'] = 999 log[3]['index'] = 1001 log[GENERIC_HEADER_TYPE_PREV][ 'indentation_spaces'] = GENERIC_NUMBER_OF_INDENTATION_SPACES expected_indentation_spaces = log[GENERIC_HEADER_TYPE_PREV][ 'indentation_spaces'] + len( str(log[GENERIC_HEADER_TYPE_PREV]['index'])) + len('.') + len( S1) api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, 'github', True, '.', log, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, expected_indentation_spaces, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, '.', ) # 12. Generic case less indentation. See example 6. log = api.init_indentation_log('github', '.') log[1]['index'] = 998 log[2]['index'] = 999 log[3]['index'] = 1001 log[GENERIC_HEADER_TYPE_PREV][ 'indentation_spaces'] = GENERIC_NUMBER_OF_INDENTATION_SPACES expected_indentation_spaces = log[GENERIC_HEADER_TYPE_PREV][ 'indentation_spaces'] api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_PREV, GENERIC_HEADER_TYPE_CURR, 'github', True, '.', log, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'github', log, GENERIC_HEADER_TYPE_PREV, GENERIC_HEADER_TYPE_CURR, expected_indentation_spaces, GENERIC_LIST_MARKER_LOG_ORDERED_NEXT_INDEX, '.', ) # redcarpet. md_parser['redcarpet']['header']['max levels'] = 6 # 1. More indentation. log = api.init_indentation_log('redcarpet', '-') api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, 'redcarpet', False, '-', log, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'redcarpet', log, GENERIC_HEADER_TYPE_CURR, GENERIC_HEADER_TYPE_PREV, LIST_INDENTATION * (GENERIC_HEADER_TYPE_CURR - 1), 0, '-', ) # 2. Less indentation. log = api.init_indentation_log('redcarpet', '-') api.compute_toc_line_indentation_spaces( GENERIC_HEADER_TYPE_PREV, GENERIC_HEADER_TYPE_CURR, 'redcarpet', False, '-', log, ) self._test_helper_assert_compute_toc_line_indentation_spaces( 'redcarpet', log, GENERIC_HEADER_TYPE_PREV, GENERIC_HEADER_TYPE_CURR, LIST_INDENTATION * (GENERIC_HEADER_TYPE_PREV - 1), 0, '-', ) def test_build_toc_line_without_indentation(self): r"""Test TOC line building for different types of inputs.""" # github and redcarpet. header = { 'header_type': GENERIC_HEADER_TYPE_CURR, 'text_original': LINE, 'text_anchor_link': LINE, 'visible': True, } # Unordered. self.assertEqual( api.build_toc_line_without_indentation( header, ordered=False, no_links=True, parser='github', ), UNORDERED_LIST_SYMBOL + S1 + LINE, ) self.assertEqual( api.build_toc_line_without_indentation( header, ordered=False, no_links=False, parser='github', ), UNORDERED_LIST_SYMBOL + S1 + '[' + LINE + ']' + '(#' + LINE + ')', ) # Ordered. self.assertEqual( api.build_toc_line_without_indentation( header, ordered=True, no_links=True, list_marker=ORDERED_LIST_SYMBOL, parser='github', ), '1' + ORDERED_LIST_SYMBOL + S1 + LINE, ) self.assertEqual( api.build_toc_line_without_indentation( header, ordered=True, no_links=False, list_marker=ORDERED_LIST_SYMBOL, parser='github', ), '1' + ORDERED_LIST_SYMBOL + S1 + '[' + LINE + ']' + '(#' + LINE + ')', ) @unittest.skip('empty test') def test_build_toc_line(self): r"""Test that the TOC line is built correctly. This function is a frontend to both the build_toc_line_without_indentation and compute_toc_line_indentation_spaces functions. Refer to those two functions for the unit tests. """ def test_filter_indices_from_line(self): r"""Test removing indices from a string.""" self.assertEqual(api.filter_indices_from_line('foo', [range(0, 3)]), '') self.assertEqual(api.filter_indices_from_line('', [range(0, 1024)]), '') self.assertEqual(api.filter_indices_from_line('', [range(0, 0)]), '') self.assertEqual( api.filter_indices_from_line(512 * '1', [range(0, 512 - 1)]), '1') self.assertEqual(api.filter_indices_from_line(512 * '1', []), 512 * '1') self.assertEqual( api.filter_indices_from_line( 'foo bar', [range(1, 2), range(4, 7)]), 'fo ') def test_remove_emphasis(self): r"""Test that removing emphasis works correctly correctly. .. note: not all tests are enabled because of a missing implementation and possible bugs. """ # Example 331 [Commonmark 0.28]. # Example 350 [Commonmark 0.29]. # Example 350 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo bar*'), 'foo bar') # Example 332 [Commonmark 0.28]. # Example 351 [Commonmark 0.29]. # Example 351 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('a * foo bar*'), 'a * foo bar*') # Example 333 [Commonmark 0.28]. # Example 352 [Commonmark 0.29]. # Example 352 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('a*"foo"*'), 'a*"foo"*') # Example 334 [Commonmark 0.28]. # Example 353 [Commonmark 0.29]. # Example 353 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('* a *'), '* a *') # Example 335 [Commonmark 0.28]. # Example 354 [Commonmark 0.29]. # Example 354 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo*bar*'), 'foobar') # Example 336 [Commonmark 0.28]. # Example 355 [Commonmark 0.29]. # Example 355 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('5*6*78'), '5678') # Example 337 [Commonmark 0.28]. # Example 356 [Commonmark 0.29]. # Example 356 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_foo bar_'), 'foo bar') # Example 338 [Commonmark 0.28]. # Example 357 [Commonmark 0.29]. # Example 357 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_ foo bar_'), '_ foo bar_') # Example 339 [Commonmark 0.28]. # Example 358 [Commonmark 0.29]. # Example 358 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('a_"foo"_'), 'a_"foo"_') # Example 340 [Commonmark 0.28]. # Example 359 [Commonmark 0.29]. # Example 359 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo_bar_'), 'foo_bar_') # Example 341 [Commonmark 0.28]. # Example 360 [Commonmark 0.29]. # Example 360 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('5_6_78'), '5_6_78') # Example 342 [Commonmark 0.28]. # Example 361 [Commonmark 0.29]. # Example 361 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('приÑтанÑм_ÑтремÑÑ‚ÑÑ_'), 'приÑтанÑм_ÑтремÑÑ‚ÑÑ_') # Example 343 [Commonmark 0.28]. # Example 362 [Commonmark 0.29]. # Example 362 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('aa_"bb"_cc'), 'aa_"bb"_cc') # Example 344 [Commonmark 0.28]. # Example 363 [Commonmark 0.29]. # Example 363 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo-_(bar)_'), 'foo-(bar)') # Example 345 [Commonmark 0.28]. # Example 364 [Commonmark 0.29]. # Example 364 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_foo*'), '_foo*') # Example 346 [Commonmark 0.28]. # Example 365 [Commonmark 0.29]. # Example 365 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo bar *'), '*foo bar *') # Example 347 [Commonmark 0.28]. # Example 366 [Commonmark 0.29]. # Example 366 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo bar\n*'), '*foo bar\n*') # Example 348 [Commonmark 0.28]. # Example 367 [Commonmark 0.29]. # Example 367 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*(*foo)'), '*(*foo)') # Example 349 [Commonmark 0.28]. # Example 368 [Commonmark 0.29]. # Example 368 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*(*foo*)*'), '(foo)') # Example 350 [Commonmark 0.28]. # Example 369 [Commonmark 0.29]. # Example 369 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo*bar'), 'foobar') # Example 351 [Commonmark 0.28]. # Example 370 [Commonmark 0.29]. # Example 370 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_foo bar _'), '_foo bar _') # Example 352 [Commonmark 0.28]. # Example 371 [Commonmark 0.29]. # Example 371 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_(_foo)'), '_(_foo)') # Example 353 [Commonmark 0.28]. # Example 372 [Commonmark 0.29]. # Example 372 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_(_foo_)_'), '(foo)') # Example 354 [Commonmark 0.28]. # Example 373 [Commonmark 0.29]. # Example 373 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_foo_bar'), '_foo_bar') # Example 355 [Commonmark 0.28]. # Example 374 [Commonmark 0.29]. # Example 374 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_приÑтанÑм_ÑтремÑÑ‚ÑÑ'), '_приÑтанÑм_ÑтремÑÑ‚ÑÑ') # Example 356 [Commonmark 0.28]. # Example 375 [Commonmark 0.29]. # Example 375 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_foo_bar_baz_'), 'foo_bar_baz') # Example 357 [Commonmark 0.28]. # Example 376 [Commonmark 0.29]. # Example 376 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_(bar)_.'), '(bar).') # Example 358 [Commonmark 0.28]. # Example 377 [Commonmark 0.29]. # Example 377 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo bar**'), 'foo bar') # Example 359 [Commonmark 0.28]. # Example 378 [Commonmark 0.29]. # Example 378 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('** foo bar**'), '** foo bar**') # Example 360 [Commonmark 0.28]. # Example 379 [Commonmark 0.29]. # Example 379 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('a**"foo"**'), 'a**"foo"**') # Example 361 [Commonmark 0.28]. # Example 380 [Commonmark 0.29]. # Example 380 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo**bar**'), 'foobar') # Example 362 [Commonmark 0.28]. # Example 381 [Commonmark 0.29]. # Example 381 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__foo bar__'), 'foo bar') # Example 363 [Commonmark 0.28]. # Example 382 [Commonmark 0.29]. # Example 382 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__ foo bar__'), '__ foo bar__') # Example 364 [Commonmark 0.28]. # Example 383 [Commonmark 0.29]. # Example 383 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__\nfoo bar__'), '__\nfoo bar__') # Example 365 [Commonmark 0.28]. # Example 384 [Commonmark 0.29]. # Example 384 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('a__"foo"__'), 'a__"foo"__') # Example 366 [Commonmark 0.28]. # Example 385 [Commonmark 0.29]. # Example 385 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo__bar__'), 'foo__bar__') # Example 367 [Commonmark 0.28]. # Example 386 [Commonmark 0.29]. # Example 386 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('5__6__78'), '5__6__78') # Example 368 [Commonmark 0.28]. # Example 387 [Commonmark 0.29]. # Example 387 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('приÑтанÑм__ÑтремÑÑ‚ÑÑ__'), 'приÑтанÑм__ÑтремÑÑ‚ÑÑ__') # Example 369 [Commonmark 0.28]. # Example 388 [Commonmark 0.29]. # Example 388 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__foo, __bar__, baz__'), 'foo, bar, baz') # Example 370 [Commonmark 0.28]. # Example 389 [Commonmark 0.29]. # Example 389 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo-__(bar)__'), 'foo-(bar)') # Example 371 [Commonmark 0.28]. # Example 390 [Commonmark 0.29]. # Example 390 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo bar **'), '**foo bar **') # Example 372 [Commonmark 0.28]. # Example 391 [Commonmark 0.29]. # Example 391 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**(**foo)'), '**(**foo)') # Example 373 [Commonmark 0.28]. # Example 392 [Commonmark 0.29]. # Example 392 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*(**foo**)*'), '(foo)') # Example 374 [Commonmark 0.28]. # Example 393 [Commonmark 0.29]. # Example 393 [Commonmark 0.30]. self.assertEqual( api.remove_emphasis( '**Gomphocarpus (*Gomphocarpus physocarpus*, syn.\n*Asclepias physocarpa*)**' ), 'Gomphocarpus (Gomphocarpus physocarpus, syn.\nAsclepias physocarpa)' ) # Example 375 [Commonmark 0.28]. # Example 394 [Commonmark 0.29]. # Example 394 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo "*bar*" foo**'), 'foo "bar" foo') # Example 376 [Commonmark 0.28]. # Example 395 [Commonmark 0.29]. # Example 395 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo**bar'), 'foobar') # Example 377 [Commonmark 0.28]. # Example 396 [Commonmark 0.29]. # Example 396 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__foo bar __'), '__foo bar __') # Example 378 [Commonmark 0.28]. # Example 397 [Commonmark 0.29]. # Example 397 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__(__foo)'), '__(__foo)') # Example 379 [Commonmark 0.28]. # Example 398 [Commonmark 0.29]. # Example 398 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_(__foo__)_'), '(foo)') # Example 380 [Commonmark 0.28]. # Example 399 [Commonmark 0.29]. # Example 399 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__foo__bar'), '__foo__bar') # Example 381 [Commonmark 0.28]. # Example 400 [Commonmark 0.29]. # Example 400 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__приÑтанÑм__ÑтремÑÑ‚ÑÑ'), '__приÑтанÑм__ÑтремÑÑ‚ÑÑ') # Example 382 [Commonmark 0.28]. # Example 401 [Commonmark 0.29]. # Example 401 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__foo__bar__baz__'), 'foo__bar__baz') # Example 383 [Commonmark 0.28]. # Example 402 [Commonmark 0.29]. # Example 402 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__(bar)__.'), '(bar).') # Example 384 [Commonmark 0.28]. # Example 403 [Commonmark 0.29]. # Example 403 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo [bar](/url)*'), 'foo [bar](/url)') # Example 385 [Commonmark 0.28]. # Example 404 [Commonmark 0.29]. # Example 404 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo\nbar*'), 'foo\nbar') # Example 386 [Commonmark 0.28]. # Example 405 [Commonmark 0.29]. # Example 405 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_foo __bar__ baz_'), 'foo bar baz') # Example 387 [Commonmark 0.28]. # Example 406 [Commonmark 0.29]. # Example 406 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_foo _bar_ baz_'), 'foo bar baz') # Example 388 [Commonmark 0.28]. # Example 407 [Commonmark 0.29]. # Example 407 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__foo_ bar_'), 'foo bar') # Example 389 [Commonmark 0.28]. # Example 408 [Commonmark 0.29]. # Example 408 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo *bar**'), 'foo bar') # Example 390 [Commonmark 0.28]. # Example 409 [Commonmark 0.29]. # Example 409 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo **bar** baz*'), 'foo bar baz') # Example 391 [Commonmark 0.28]. # Example 410 [Commonmark 0.29]. # Example 410 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo**bar**baz*'), 'foobarbaz') # Example 411 [Commonmark 0.29]. # Example 411 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo**bar*'), 'foo**bar') # Example 392 [Commonmark 0.28]. # Example 412 [Commonmark 0.29]. # Example 412 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('***foo** bar*'), 'foo bar') # Example 393 [Commonmark 0.28]. # Example 413 [Commonmark 0.29]. # Example 413 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo **bar***'), 'foo bar') # Example 394 [Commonmark 0.28]. # Example 414 [Commonmark 0.29]. # Example 414 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo**bar***'), 'foobar') # Example 415 [Commonmark 0.29]. # Example 415 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo***bar***baz'), 'foobarbaz') # Example 416 [Commonmark 0.29]. # Example 416 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo******bar*********baz'), 'foobar***baz') # Example 395 [Commonmark 0.28]. # Example 417 [Commonmark 0.29]. # Example 417 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo **bar *baz* bim** bop*'), 'foo bar baz bim bop') # Example 396 [Commonmark 0.28]. # Example 418 [Commonmark 0.29]. # Example 418 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo [*bar*](/url)*'), 'foo [bar](/url)') # Example 397 [Commonmark 0.28]. # Example 419 [Commonmark 0.29]. # Example 419 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('** is not an empty emphasis'), '** is not an empty emphasis') # Example 398 [Commonmark 0.28]. # Example 420 [Commonmark 0.29]. # Example 420 [Commonmark 0.30]. self.assertEqual( api.remove_emphasis('**** is not an empty strong emphasis'), '**** is not an empty strong emphasis') # Example 399 [Commonmark 0.28]. # Example 421 [Commonmark 0.29]. # Example 421 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo [bar](/url)**'), 'foo [bar](/url)') # Example 400 [Commonmark 0.28]. # Example 422 [Commonmark 0.29]. # Example 422 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo\nbar**'), 'foo\nbar') # Example 401 [Commonmark 0.28]. # Example 423 [Commonmark 0.29]. # Example 423 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__foo _bar_ baz__'), 'foo bar baz') # Example 402 [Commonmark 0.28]. # Example 424 [Commonmark 0.29]. # Example 424 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__foo __bar__ baz__'), 'foo bar baz') # Example 403 [Commonmark 0.28]. # Example 425 [Commonmark 0.29]. # Example 425 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('____foo__ bar__'), 'foo bar') # Example 404 [Commonmark 0.28]. # Example 426 [Commonmark 0.29]. # Example 426 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo **bar****'), 'foo bar') # Example 405 [Commonmark 0.28]. # Example 427 [Commonmark 0.29]. # Example 427 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo *bar* baz**'), 'foo bar baz') # Example 406 [Commonmark 0.28]. # Example 428 [Commonmark 0.29]. # Example 428 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo*bar*baz**'), 'foobarbaz') # Example 407 [Commonmark 0.28]. # Example 429 [Commonmark 0.29]. # Example 429 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('***foo* bar**'), 'foo bar') # Example 408 [Commonmark 0.28]. # Example 430 [Commonmark 0.29]. self.assertEqual(api.remove_emphasis('**foo *bar***'), 'foo bar') # Example 409 [Commonmark 0.28]. # Example 431 [Commonmark 0.29]. # Example 431 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo *bar **baz**\nbim* bop**'), 'foo bar baz\nbim bop') # Example 410 [Commonmark 0.28]. # Example 432 [Commonmark 0.29]. # Example 432 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo [*bar*](/url)**'), 'foo [bar](/url)') # Example 411 [Commonmark 0.28]. # Example 433 [Commonmark 0.29]. # Example 433 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__ is not an empty emphasis'), '__ is not an empty emphasis') # Example 412 [Commonmark 0.28]. # Example 434 [Commonmark 0.29]. # Example 434 [Commonmark 0.30]. self.assertEqual( api.remove_emphasis('____ is not an empty strong emphasis'), '____ is not an empty strong emphasis') # Example 413 [Commonmark 0.28]. # Example 435 [Commonmark 0.29]. # Example 435 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo ***'), 'foo ***') # Example 414 [Commonmark 0.28]. # Example 436 [Commonmark 0.29]. # Example 436 [Commonmark 0.30]. # Removing backslashes is not implemented for these cases because they are already removed # with a regex in the build_anchor_link function. self.assertEqual( api.remove_emphasis(r'foo *\**').replace(LINE_ESCAPE, ''), ('foo ' + LINE_ESCAPE + '*').replace(LINE_ESCAPE, '')) self.assertEqual(api.remove_emphasis(r'foo *\**'), 'foo ' + LINE_ESCAPE + '*') # Example 415 [Commonmark 0.28]. # Example 437 [Commonmark 0.29]. # Example 437 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo *_*'), 'foo _') # Example 416 [Commonmark 0.28]. # Example 438 [Commonmark 0.29]. # Example 438 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo ****'), 'foo ****') # Example 417 [Commonmark 0.28]. # Example 439 [Commonmark 0.29]. # Example 439 [Commonmark 0.30]. self.assertEqual( api.remove_emphasis(r'foo **\***').replace(LINE_ESCAPE, ''), ('foo ' + LINE_ESCAPE + '*').replace(LINE_ESCAPE, '')) self.assertEqual(api.remove_emphasis(r'foo **\***'), 'foo ' + LINE_ESCAPE + '*') # Example 418 [Commonmark 0.28]. # Example 440 [Commonmark 0.29]. # Example 440 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo **_**'), 'foo _') # Example 419 [Commonmark 0.28]. # Example 441 [Commonmark 0.29]. # Example 441 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo*'), '*foo') # Example 420 [Commonmark 0.28]. # Example 442 [Commonmark 0.29]. # Example 442 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo**'), 'foo*') # Example 421 [Commonmark 0.28]. # Example 443 [Commonmark 0.29]. # Example 443 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('***foo**'), '*foo') # Example 422 [Commonmark 0.28]. # Example 444 [Commonmark 0.29]. # Example 444 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('****foo*'), '***foo') # Example 423 [Commonmark 0.28]. # Example 445 [Commonmark 0.29]. # Example 445 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo***'), 'foo*') # Example 424 [Commonmark 0.28]. # Example 446 [Commonmark 0.29]. # Example 446 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo****'), 'foo***') # Example 425 [Commonmark 0.28]. # Example 447 [Commonmark 0.29]. # Example 447 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo ___'), 'foo ___') # Example 426 [Commonmark 0.28]. # Example 448 [Commonmark 0.29]. # Example 448 [Commonmark 0.30]. self.assertEqual( api.remove_emphasis(r'foo _\__').replace(LINE_ESCAPE, ''), ('foo ' + LINE_ESCAPE + '_').replace(LINE_ESCAPE, '')) self.assertEqual(api.remove_emphasis(r'foo _\__'), 'foo ' + LINE_ESCAPE + '_') # Example 427 [Commonmark 0.28]. # Example 449 [Commonmark 0.29]. # Example 449 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo _*_'), 'foo *') # Example 428 [Commonmark 0.28]. # Example 450 [Commonmark 0.29]. # Example 450 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo _____'), 'foo _____') # Example 429 [Commonmark 0.28]. # Example 451 [Commonmark 0.29]. # Example 451 [Commonmark 0.30]. self.assertEqual( api.remove_emphasis(r'foo __\___').replace(LINE_ESCAPE, ''), ('foo ' + LINE_ESCAPE + '_').replace(LINE_ESCAPE, '')) self.assertEqual(api.remove_emphasis(r'foo __\___'), 'foo ' + LINE_ESCAPE + '_') # Example 430 [Commonmark 0.28]. # Example 452 [Commonmark 0.29]. # Example 452 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('foo __*__'), 'foo *') # Example 431 [Commonmark 0.28]. # Example 453 [Commonmark 0.29]. # Example 453 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__foo_'), '_foo') # Example 432 [Commonmark 0.28]. # Example 454 [Commonmark 0.29]. # Example 454 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_foo__'), 'foo_') # Example 433 [Commonmark 0.28]. # Example 455 [Commonmark 0.29]. # Example 455 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('___foo__'), '_foo') # Example 434 [Commonmark 0.28]. # Example 456 [Commonmark 0.29]. # Example 456 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('____foo_'), '___foo') # Example 435 [Commonmark 0.28]. # Example 457 [Commonmark 0.29]. # Example 457 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__foo___'), 'foo_') # Example 436 [Commonmark 0.28]. # Example 458 [Commonmark 0.29]. # Example 458 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_foo____'), 'foo___') # Example 437 [Commonmark 0.28]. # Example 459 [Commonmark 0.29]. # Example 459 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo**'), 'foo') # Example 438 [Commonmark 0.28]. # Example 460 [Commonmark 0.29]. # Example 460 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*_foo_*'), 'foo') # Example 439 [Commonmark 0.28]. # Example 461 [Commonmark 0.29]. # Example 461 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__foo__'), 'foo') # Example 440 [Commonmark 0.28]. # Example 462 [Commonmark 0.29]. # Example 462 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_*foo*_'), 'foo') # Example 441 [Commonmark 0.28]. # Example 463 [Commonmark 0.29]. # Example 463 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('****foo****'), 'foo') # Example 442 [Commonmark 0.28]. # Example 464 [Commonmark 0.29]. # Example 464 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('____foo____'), 'foo') # Example 443 [Commonmark 0.28]. # Example 465 [Commonmark 0.29]. # Example 465 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('******foo******'), 'foo') # Example 444 [Commonmark 0.28]. # Example 466 [Commonmark 0.29]. # Example 466 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('***foo***'), 'foo') # Example 445 [Commonmark 0.28]. # Example 467 [Commonmark 0.29]. # Example 467 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_____foo_____'), 'foo') # Example 446 [Commonmark 0.28]. # Example 468 [Commonmark 0.29]. # Example 468 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo _bar* baz_'), 'foo _bar baz_') # Example 447 [Commonmark 0.28]. # Example 469 [Commonmark 0.29]. # Example 469 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo __bar *baz bim__ bam*'), 'foo bar *baz bim bam') # Example 448 [Commonmark 0.28]. # Example 470 [Commonmark 0.29]. # Example 470 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**foo **bar baz**'), '**foo bar baz') # Example 449 [Commonmark 0.28]. # Example 471 [Commonmark 0.29]. # Example 471 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*foo *bar baz*'), '*foo bar baz') # Example 450 [Commonmark 0.28]. # Example 472 [Commonmark 0.29]. # Example 472 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[bar*](/url)'), '*[bar*](/url)') # Extra examples. self.assertEqual(api.remove_emphasis('*[*bar](/url)'), '*[*bar](/url)') self.assertEqual(api.remove_emphasis('*[*bar](*/url*)'), '*[*bar](*/url*)') self.assertEqual(api.remove_emphasis('*[bar](/url)*'), '[bar](/url)') self.assertEqual(api.remove_emphasis('*[bar*] (/url)'), '[bar] (/url)') self.assertEqual(api.remove_emphasis('[bar](/url*)*'), '[bar](/url*)*') self.assertEqual(api.remove_emphasis('[bar](/url**)**'), '[bar](/url**)**') # Example 451 [Commonmark 0.28]. # Example 473 [Commonmark 0.29]. # Example 473 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_foo [bar_](/url)'), '_foo [bar_](/url)') # Example 452 [Commonmark 0.28]. # Example 474 [Commonmark 0.29]. # Example 474 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*'), '*') # Example 453 [Commonmark 0.28]. # Example 475 [Commonmark 0.29]. # Example 475 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('**'), '**') # Example 454 [Commonmark 0.28]. # Example 476 [Commonmark 0.29]. # Example 476 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('__'), '__') # Example 455 [Commonmark 0.28]. # Example 477 [Commonmark 0.29]. # Example 477 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*a `*`*'), 'a `*`') # Extra examples. self.assertEqual(api.remove_emphasis('*a `****`*'), 'a `****`') self.assertEqual(api.remove_emphasis('*a `** **`*'), 'a `** **`') self.assertEqual(api.remove_emphasis('*a `**** `*'), 'a `**** `') # Example 456 [Commonmark 0.28]. # Example 478 [Commonmark 0.29]. # Example 478 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('_a `_`_'), 'a `_`') # Extra examples. self.assertEqual(api.remove_emphasis('_a `____`_'), 'a `____`') self.assertEqual(api.remove_emphasis('_a `__ __`_'), 'a `__ __`') self.assertEqual(api.remove_emphasis('_a `____ `_'), 'a `____ `') # Example 457 [Commonmark 0.28]. # Example 479 [Commonmark 0.29]. # Example 479 [Commonmark 0.30]. # self.assertEqual(api.remove_emphasis('**a'), '**a') # # Example 458 [Commonmark 0.28]. # Example 480 [Commonmark 0.29]. # Example 480 [Commonmark 0.30]. # self.assertEqual(api.remove_emphasis('__a'), '__a') # Examples 481 -> 525 [Commonmark 0.30]. # Note: most of these are modified examples for emphasis only. # Example 481 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[bar*](/uri "title")'), '*[bar*](/uri "title")') # self.assertEqual(api.remove_emphasis('[bar](/uri *"title")*'), '[bar](/uri *"title")*') self.assertEqual(api.remove_emphasis('_[bar_](/uri "title")'), '_[bar_](/uri "title")') # self.assertEqual(api.remove_emphasis('[bar](/uri _"title")_'), '[bar](/uri _"title")_') # Example 482 [Commonmark 0.30]. # ignore # Example 483 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[*](./target.md)'), '*[*](./target.md)') self.assertEqual(api.remove_emphasis('[](*./target.md)*'), '[](*./target.md)*') self.assertEqual(api.remove_emphasis('_[_](./target.md)'), '_[_](./target.md)') self.assertEqual(api.remove_emphasis('[](_./target.md)_'), '[](_./target.md)_') # Example 484 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[link*]()'), '*[link*]()') self.assertEqual(api.remove_emphasis('[link](*)*'), '[link](*)*') self.assertEqual(api.remove_emphasis('_[link_]()'), '_[link_]()') self.assertEqual(api.remove_emphasis('[link](_)_'), '[link](_)_') # Example 485 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[link*](<>)'), '*[link*](<>)') self.assertEqual(api.remove_emphasis('[link](*<>)*'), '[link](*<>)*') self.assertEqual(api.remove_emphasis('_[link_](<>)'), '_[link_](<>)') self.assertEqual(api.remove_emphasis('[link](_<>)_'), '[link](_<>)_') # Example 486 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[*]()'), '*[*]()') self.assertEqual(api.remove_emphasis('[](*)*'), '[](*)*') self.assertEqual(api.remove_emphasis('_[_]()'), '_[_]()') self.assertEqual(api.remove_emphasis('[](_)_'), '[](_)_') # Example 487 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[link*](/my uri)'), '[link](/my uri)') self.assertEqual(api.remove_emphasis('[link](*/my uri)*'), '[link](/my uri)') # self.assertEqual(api.remove_emphasis('_[link_](/my uri)'), '[link](/my uri)') self.assertEqual(api.remove_emphasis('[link](_/my uri)_'), '[link](/my uri)') # Example 488 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[link*]()'), '*[link*]()') self.assertEqual(api.remove_emphasis('[link]()*'), '[link]()*') self.assertEqual(api.remove_emphasis('_[link_]()'), '_[link_]()') self.assertEqual(api.remove_emphasis('[link]()_'), '[link]()_') # Example 489 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[link*](foo\nbar)'), '[link](foo\nbar)') self.assertEqual(api.remove_emphasis('[link](*foo\nbar)*'), '[link](foo\nbar)') # self.assertEqual(api.remove_emphasis('_[link_](foo\nbar)'), '[link](foo\nbar)') self.assertEqual(api.remove_emphasis('[link](_foo\nbar)_'), '[link](foo\nbar)') # Example 490 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[link*]()'), '[link]()') self.assertEqual(api.remove_emphasis('[link](*)*'), '[link]()') # self.assertEqual(api.remove_emphasis('_[link_]()'), '[link]()') self.assertEqual(api.remove_emphasis('[link](_)_'), '[link]()') # Example 491 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[a*]()'), '*[a*]()') self.assertEqual(api.remove_emphasis('[a](*)*'), '[a](*)*') self.assertEqual(api.remove_emphasis('_[a_]()'), '_[a_]()') self.assertEqual(api.remove_emphasis('[a](_)_'), '[a](_)_') # Example 492 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*]()'), r'[link]()') # self.assertEqual(api.remove_emphasis('[link](*)*'), '[link]()') self.assertEqual(api.remove_emphasis(r'_[link_]()'), r'[link]()') # self.assertEqual(api.remove_emphasis('[link](_)_'), '[link]()') # Example 493 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[a*]('), '[a](') # self.assertEqual(api.remove_emphasis('[a](**'), '[a](') self.assertEqual(api.remove_emphasis('_[a_]('), '[a](') # self.assertEqual(api.remove_emphasis('[a](__'), '[a](') self.assertEqual(api.remove_emphasis('*[a*](c)'), '[a](c)') # self.assertEqual(api.remove_emphasis('[a](*c)*'), '[a](c)') self.assertEqual(api.remove_emphasis('_[a_](c)'), '[a](c)') # self.assertEqual(api.remove_emphasis('[a](_c)_'), '[a](c)') # Example 494 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*](\(foo\))'), r'*[link*](\(foo\))') self.assertEqual(api.remove_emphasis(r'[link](*\(foo\))*'), r'[link](*\(foo\))*') self.assertEqual(api.remove_emphasis(r'_[link_](\(foo\))'), r'_[link_](\(foo\))') self.assertEqual(api.remove_emphasis(r'[link](_\(foo\))_'), r'[link](_\(foo\))_') # Example 495 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[link*](foo(and(bar)))'), '*[link*](foo(and(bar)))') self.assertEqual(api.remove_emphasis('[link](*foo(and(bar)))*'), '[link](*foo(and(bar)))*') self.assertEqual(api.remove_emphasis('_[link_](foo(and(bar)))'), '_[link_](foo(and(bar)))') self.assertEqual(api.remove_emphasis('[link](_foo(and(bar)))_'), '[link](_foo(and(bar)))_') # Example 496 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[link*](foo(and(bar))'), '[link](foo(and(bar))') self.assertEqual(api.remove_emphasis('[link](*foo(and(bar))*'), '[link](foo(and(bar))') # self.assertEqual(api.remove_emphasis('_[link_](foo(and(bar))'), '[link](foo(and(bar))') self.assertEqual(api.remove_emphasis('[link](_foo(and(bar))_'), '[link](foo(and(bar))') # Example 497 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*](foo\(and\(bar\))'), r'*[link*](foo\(and\(bar\))') self.assertEqual(api.remove_emphasis(r'[link](*foo\(and\(bar\))*'), r'[link](*foo\(and\(bar\))*') self.assertEqual(api.remove_emphasis(r'_[link_](foo\(and\(bar\))'), r'_[link_](foo\(and\(bar\))') self.assertEqual(api.remove_emphasis(r'[link](_foo\(and\(bar\))_'), r'[link](_foo\(and\(bar\))_') # Example 498 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[link*]()'), '*[link*]()') # self.assertEqual(api.remove_emphasis('[link](*)*'), '[link](*)*') self.assertEqual(api.remove_emphasis('_[link_]()'), '_[link_]()') # self.assertEqual(api.remove_emphasis('[link](_)_'), '[link](_)_') # Example 499 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*](foo\)\:)'), r'*[link*](foo\)\:)') self.assertEqual(api.remove_emphasis(r'[link](*foo\)\:)*'), r'[link](*foo\)\:)*') self.assertEqual(api.remove_emphasis(r'_[link_](foo\)\:)'), r'_[link_](foo\)\:)') self.assertEqual(api.remove_emphasis(r'[link](_foo\)\:)_'), r'[link](_foo\)\:)_') # Example 500 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[*link](#fragment)'), '*[*link](#fragment)') self.assertEqual(api.remove_emphasis('[link](#fragment*)*'), '[link](#fragment*)*') self.assertEqual(api.remove_emphasis('_[_link](#fragment)'), '_[_link](#fragment)') self.assertEqual(api.remove_emphasis('[link](#fragment_)_'), '[link](#fragment_)_') self.assertEqual( api.remove_emphasis('*[*link](http://example.com#fragment)'), '*[*link](http://example.com#fragment)') self.assertEqual( api.remove_emphasis('[link](http://example.com#fragment*)*'), '[link](http://example.com#fragment*)*') self.assertEqual( api.remove_emphasis('_[_link](http://example.com#fragment)'), '_[_link](http://example.com#fragment)') self.assertEqual( api.remove_emphasis('[link](http://example.com#fragment_)_'), '[link](http://example.com#fragment_)_') self.assertEqual( api.remove_emphasis('*[*link](http://example.com?foo=3#frag)'), '*[*link](http://example.com?foo=3#frag)') self.assertEqual( api.remove_emphasis('[link](http://example.com?foo=3#frag*)*'), '[link](http://example.com?foo=3#frag*)*') self.assertEqual( api.remove_emphasis('_[_link](http://example.com?foo=3#frag)'), '_[_link](http://example.com?foo=3#frag)') self.assertEqual( api.remove_emphasis('[link](http://example.com?foo=3#frag_)_'), '[link](http://example.com?foo=3#frag_)_') # Example 501 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*](foo\bar)'), r'*[link*](foo\bar)') self.assertEqual(api.remove_emphasis(r'[link](*foo\bar)*'), r'[link](*foo\bar)*') self.assertEqual(api.remove_emphasis(r'_[link_](foo\bar)'), r'_[link_](foo\bar)') self.assertEqual(api.remove_emphasis(r'[link](_foo\bar)_'), r'[link](_foo\bar)_') # Example 502 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*](foo%20bä)'), r'*[link*](foo%20bä)') self.assertEqual(api.remove_emphasis(r'[link](*foo%20bä)*'), r'[link](*foo%20bä)*') self.assertEqual(api.remove_emphasis(r'_[link_](foo%20bä)'), r'_[link_](foo%20bä)') self.assertEqual(api.remove_emphasis(r'[link](_foo%20bä)_'), r'[link](_foo%20bä)_') # Example 503 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*]("title")'), r'*[link*]("title")') self.assertEqual(api.remove_emphasis(r'[link](*"title")*'), r'[link](*"title")*') self.assertEqual(api.remove_emphasis(r'_[link_]("title")'), r'_[link_]("title")') self.assertEqual(api.remove_emphasis(r'[link](_"title")_'), r'[link](_"title")_') # Example 504 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*](/url "title")'), r'*[link*](/url "title")') self.assertEqual(api.remove_emphasis(r"*[link*](/url 'title')"), r"*[link*](/url 'title')") # self.assertEqual(api.remove_emphasis(r'*[link*](/url (title))'), r'*[link*](/url (title))') # Example 505 [Commonmark 0.30]. # self.assertEqual(api.remove_emphasis(r'*[link*](/url "title \""")'), r'*[link*](/url "title \""")') # Example 506 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*](/url "title")'), r'*[link*](/url "title")') # Example 507 [Commonmark 0.30]. self.assertEqual( api.remove_emphasis(r'*[link*](/url "title "and" title")'), r'[link](/url "title "and" title")') # Example 508 [Commonmark 0.30]. self.assertEqual( api.remove_emphasis("*[link*](/url 'title \"and\" title')"), "*[link*](/url 'title \"and\" title')") # Example 509 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis('*[link*]( /uri\n "title" )'), '*[link*]( /uri\n "title" )') # Example 510 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*] (uri)'), r'[link] (uri)') self.assertEqual(api.remove_emphasis(r'[link] (*uri)*'), r'[link] (uri)') # self.assertEqual(api.remove_emphasis(r'_[link_] (uri)'), r'[link] (uri)') self.assertEqual(api.remove_emphasis(r'[link] (_uri)_'), r'[link] (uri)') # Example 511 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link* [foo [bar]]](/uri)'), r'*[link* [foo [bar]]](/uri)') # Example 512 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*] (uri)'), r'[link] (uri)') # Example 513 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link* *[bar*](/uri)'), r'[link *[bar*](/uri)') # Example 514 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link* *\[bar*](/uri)'), r'*[link* \[bar](/uri)') # Example 515 [Commonmark 0.30]. self.assertEqual( api.remove_emphasis(r'[link *foo **bar** `#`*](/uri)'), r'[link foo bar `#`](/uri)') # Example 516 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'[![moon](moon.jpg)](/uri)'), r'[![moon](moon.jpg)](/uri)') # Example 517 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[foo* [bar](/uri)](/uri)'), r'[foo [bar](/uri)](/uri)') # Example 518 [Commonmark 0.30]. self.assertEqual( api.remove_emphasis(r'[foo *[bar [baz](/uri)](/uri)*](/uri)'), r'[foo [bar [baz](/uri)](/uri)](/uri)') # Example 519 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'![[[foo](uri1)](uri2)](uri3)'), r'![[[foo](uri1)](uri2)](uri3)') # Example 520 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[foo*](/uri)'), r'*[foo*](/uri)') # Example 521 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'[foo *bar](baz*)'), r'[foo *bar](baz*)') # Example 522 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*foo [bar* baz]'), r'foo [bar baz]') # Example 523 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'[foo '), r'[foo ') # Example 524 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'[foo`](/uri)`'), r'[foo`](/uri)`') # Example 525 [Commonmark 0.30]. self.assertEqual( api.remove_emphasis(r'[foo'), r'[foo') # Possibly all the way up to 592 [Commonmark 0.30]? # Extra examples. self.assertEqual(api.remove_emphasis(r'_j\_'), '_j' + LINE_ESCAPE + '_') def test_remove_html_tags(self): r"""Test remove html tags.""" # Example 584 [Commonmark 0.28]. # Example 609 [Commonmark 0.29]. # Example 612 [Commonmark 0.30]. # Example 632 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('', 'github'), '', ) self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) # Example 585 [Commonmark 0.28]. # Example 610 [Commonmark 0.29]. # Example 613 [Commonmark 0.30]. # Example 633 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('', 'github'), '', ) self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) # Example 586 [Commonmark 0.28]. # Example 611 [Commonmark 0.29]. # Example 614 [Commonmark 0.30]. # Example 634 [GFM 0.29.0.gfm.2]. # Original example with extensions. self.assertEqual( api.remove_html_tags('', 'github'), '', ) self.assertEqual( api.remove_html_tags('', 'github'), '', ) self.assertEqual( api.remove_html_tags('', 'github'), '', ) self.assertEqual( api.remove_html_tags('', 'github'), '', ) self.assertEqual( api.remove_html_tags('', 'github'), '', ) # Two newlines. self.assertEqual( api.remove_html_tags('', 'github'), '', ) self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) # Two newlines. self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) # Example 587 [Commonmark 0.28]. # Example 612 [Commonmark 0.29]. # Example 615 [Commonmark 0.30]. # Example 635 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags( '"\'\n_boolean zoop:33=zoop:33 />', 'github'), '', ) self.assertEqual( api.remove_html_tags( '"\'\n_boolean zoop:33=zoop:33 />', 'cmark'), '', ) # Example 588 [Commonmark 0.28]. # Example 613 [Commonmark 0.29]. # Example 616 [Commonmark 0.30]. # Example 636 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('Foo ', 'github'), 'Foo ', ) self.assertEqual( api.remove_html_tags('Foo ', 'cmark'), 'Foo ', ) # Example 589 [Commonmark 0.28]. # Example 614 [Commonmark 0.29]. # Example 617 [Commonmark 0.30]. # Example 637 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('<33> <__>', 'github'), '<33> <__>', ) self.assertEqual( api.remove_html_tags('<33> <__>', 'cmark'), '<33> <__>', ) # Example 590 [Commonmark 0.28]. # Example 615 [Commonmark 0.29]. # Example 618 [Commonmark 0.30]. # Example 638 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('', 'github'), '', ) self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) # Example 591 [Commonmark 0.28]. # Example 616 [Commonmark 0.29]. # Example 619 [Commonmark 0.30]. # Example 639 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags(' ', ) self.assertEqual( api.remove_html_tags(' ', ) # Example 592 [Commonmark 0.28]. # Example 617 [Commonmark 0.29]. # Example 620 [Commonmark 0.30]. # Example 640 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('< a><\nfoo>', 'github'), '< a><\nfoo>', ) self.assertEqual( api.remove_html_tags('< a><\nfoo>', 'cmark'), '< a><\nfoo>', ) # Example 593 [Commonmark 0.28]. # Example 618 [Commonmark 0.29]. # Example 621 [Commonmark 0.30]. # Example 641 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags(r"", 'github'), r"", ) self.assertEqual( api.remove_html_tags(r"", 'cmark'), r"", ) # Example 594 [Commonmark 0.28]. # Example 619 [Commonmark 0.29]. # Example 622 [Commonmark 0.30]. # Example 642 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('', 'github'), '', ) self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) # Example 595 [Commonmark 0.28]. # Example 620 [Commonmark 0.29]. # Example 623 [Commonmark 0.30]. # Example 643 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('', 'github'), '', ) self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) # Example 596 [Commonmark 0.28]. # Example 621 [Commonmark 0.29]. # Example 624 [Commonmark 0.30]. # Example 644 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags( 'foo ', 'github'), 'foo ', ) self.assertEqual( api.remove_html_tags( 'foo ', 'cmark'), 'foo ', ) # Example 597 [Commonmark 0.28]. # Example 622 [Commonmark 0.29]. # Example 625 [Commonmark 0.30]. # Example 645 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('foo ', 'github'), 'foo ', ) self.assertEqual( api.remove_html_tags('foo ', 'cmark'), 'foo ', ) # Example 598 [Commonmark 0.28]. # Example 623 [Commonmark 0.29]. # Example 626 [Commonmark 0.30]. # Example 646 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('foo foo -->', 'cmark'), 'foo foo -->', ) self.assertEqual( api.remove_html_tags('foo ', 'cmark'), 'foo ', ) self.assertEqual( api.remove_html_tags('foo foo -->', 'cmark'), 'foo foo -->', ) self.assertEqual( api.remove_html_tags('foo ', 'cmark'), 'foo ', ) # Example 599 [Commonmark 0.28]. # Example 624 [Commonmark 0.29]. # Example 627 [Commonmark 0.30]. # Example 647 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('foo ', 'github'), 'foo ', ) self.assertEqual( api.remove_html_tags('foo ', 'cmark'), 'foo ', ) # Example 600 [Commonmark 0.28]. # Example 625 [Commonmark 0.29]. # Example 628 [Commonmark 0.30]. # Example 648 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('foo ', 'github'), 'foo ', ) self.assertEqual( api.remove_html_tags('foo ', 'cmark'), 'foo ', ) # Example 601 [Commonmark 0.28]. # Example 626 [Commonmark 0.29]. # Example 629 [Commonmark 0.30]. # Example 649 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('foo &<]]>', 'github'), 'foo ', ) self.assertEqual( api.remove_html_tags('foo &<]]>', 'cmark'), 'foo ', ) # Example 602 [Commonmark 0.28]. # Example 627 [Commonmark 0.29]. # Example 630 [Commonmark 0.30]. # Example 650 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('foo ', 'github'), 'foo ', ) self.assertEqual( api.remove_html_tags('foo ', 'cmark'), 'foo ', ) # Example 603 [Commonmark 0.28]. # Example 628 [Commonmark 0.29]. # Example 631 [Commonmark 0.30]. # Example 651 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags(r'foo ', 'github'), 'foo ', ) self.assertEqual( api.remove_html_tags(r'foo ', 'cmark'), 'foo ', ) # Example 604 [Commonmark 0.28]. # Example 629 [Commonmark 0.29]. # Example 632 [Commonmark 0.30]. # Example 652 [GFM 0.29.0.gfm.2]. self.assertEqual( api.remove_html_tags('', 'github'), '', ) self.assertEqual( api.remove_html_tags('', 'cmark'), '', ) # GitHub Flavored Markdown Disallowed Raw HTML (extension). # Paragraph 6.11. # See https://github.github.com/gfm/#disallowed-raw-html-extension- self.assertEqual(api.remove_html_tags('', 'github'), '<title>') self.assertEqual(api.remove_html_tags('<textarea>', 'github'), '<textarea>') self.assertEqual(api.remove_html_tags('<style>', 'github'), '<style>') self.assertEqual(api.remove_html_tags('<xmp>', 'github'), '<xmp>') self.assertEqual(api.remove_html_tags('<iframe>', 'github'), '<iframe>') self.assertEqual(api.remove_html_tags('<noembed>', 'github'), '<noembed>') self.assertEqual(api.remove_html_tags('<noembed>', 'github'), '<noembed>') self.assertEqual(api.remove_html_tags('<noframes>', 'github'), '<noframes>') self.assertEqual(api.remove_html_tags('<script>', 'github'), '<script>') self.assertEqual(api.remove_html_tags('<plaintext>', 'github'), '<plaintext>') def test_anchor_link_punctuation_filter(self): r"""Test the filtering of special characters from a string. See also https://github.com/frnmst/md-toc/issues/42 """ # Negative matches. for char in [ LINE_EN_DASH, LINE_HYPHEN, LINE_MINUS, LINE_EM_DASH, T1, V1 ]: self.assertEqual( api.anchor_link_punctuation_filter('September' + char + 'October'), 'SeptemberOctober') self.assertEqual( api.anchor_link_punctuation_filter('2:00' + char + '3:00'), '200300') self.assertEqual( api.anchor_link_punctuation_filter('Pages 113' + char + '117'), 'Pages 113117') # Positive matches. for char in [CMARK_LINE_FOO, LINE_DASH, S1]: self.assertEqual( api.anchor_link_punctuation_filter('September' + char + 'October'), 'September' + char + 'October') self.assertEqual( api.anchor_link_punctuation_filter('2:00' + char + '3:00'), '200' + char + '300') self.assertEqual( api.anchor_link_punctuation_filter('Pages 113' + char + '117'), 'Pages 113' + char + '117') def test_build_anchor_link(self): r"""Test anchor link generation. Algorithm testing should be done in calling functions. Test duplicates for parser='github'. """ header_duplicate_counter = dict() api.build_anchor_link(LINE, header_duplicate_counter, parser='github') api.build_anchor_link(LINE, header_duplicate_counter, parser='github') [ self.assertEqual(header_duplicate_counter[k], 2) for k in header_duplicate_counter ] # Check if the exception is raised for newlines. header_duplicate_counter = dict() with self.assertRaises(exceptions.StringCannotContainNewlines): api.build_anchor_link(CMARK_LINE_FOO + LINE_LINE_FEED + CMARK_LINE_FOO, header_duplicate_counter, parser='github') # Check the GitLab Flavored Markdown multiple dash rule. header_duplicate_counter = dict() self.assertEqual( api.build_anchor_link(CMARK_LINE_FOO + S3 + CMARK_LINE_FOO, header_duplicate_counter, parser='gitlab'), CMARK_LINE_FOO + LINE_DASH + CMARK_LINE_FOO) header_duplicate_counter = dict() self.assertEqual( api.build_anchor_link( CMARK_LINE_FOO + LINE_DASH * 6 + CMARK_LINE_FOO, header_duplicate_counter, parser='gitlab'), CMARK_LINE_FOO + LINE_DASH + CMARK_LINE_FOO) self.assertEqual( api.build_anchor_link(CMARK_LINE_FOO + LINE_DASH * 6, header_duplicate_counter, parser='gitlab'), CMARK_LINE_FOO + LINE_DASH) self.assertEqual( api.build_anchor_link(LINE_DASH * 6 + CMARK_LINE_FOO, header_duplicate_counter, parser='gitlab'), LINE_DASH + CMARK_LINE_FOO) def test_replace_and_split_newlines(self): r"""Test the replacement and splitting of newlines in a string.""" self.assertEqual( api.replace_and_split_newlines(LINE_LINE_FEED + CMARK_LINE_FOO + LINE_LINE_FEED), [CMARK_LINE_FOO]) self.assertEqual( api.replace_and_split_newlines(LINE_LINE_FEED + LINE_CARRIAGE_RETURN + CMARK_LINE_FOO + LINE_LINE_FEED), [CMARK_LINE_FOO]) self.assertEqual( api.replace_and_split_newlines(CMARK_LINE_BAR + LINE_CARRIAGE_RETURN + LINE_LINE_FEED + CMARK_LINE_FOO + LINE_LINE_FEED), [CMARK_LINE_BAR, CMARK_LINE_FOO]) self.assertEqual( api.replace_and_split_newlines(LINE_CARRIAGE_RETURN + LINE_LINE_FEED + LINE_LINE_FEED + LINE_LINE_FEED + CMARK_LINE_BAR), [CMARK_LINE_BAR]) self.assertEqual( api.replace_and_split_newlines(LINE_LINE_FEED + LINE_LINE_FEED + LINE_LINE_FEED + CMARK_LINE_BAR), [CMARK_LINE_BAR]) self.assertEqual( api.replace_and_split_newlines(LINE_LINE_FEED + CMARK_LINE_FOO + LINE_LINE_FEED + LINE_LINE_FEED + LINE_CARRIAGE_RETURN + LINE_LINE_FEED + CMARK_LINE_BAR), [CMARK_LINE_FOO, '', '', CMARK_LINE_BAR]) def test_get_atx_heading(self): r"""Test the title gathering for edge cases and various parsers. GitHub examples are the same ones reported in the GFM document, except for minor changes. The example numbers are the same ones reported in the cited document. There are also some more tests for special cases. Redcarpet examples are different from the ones present in the test directory of the source code. """ # github m_github = md_parser['github']['header']['max levels'] = 6 # Example 32 [Commonmark 0.28]. # Example 32 [Commonmark 0.29]. # Example 62 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading(H1 + S1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H1 + T1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading(H1 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H2 + S1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': 2, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H2 + T1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading(H2 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), [{ 'header_type': 2, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H3 + S1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H3 + T1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading(H3 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H4 + S1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': 4, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H4 + T1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading(H4 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), [{ 'header_type': 4, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H5 + S1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': 5, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H5 + T1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading(H5 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), [{ 'header_type': 5, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H6 + S1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': 6, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H6 + T1 + CMARK_LINE_FOO, m_github, 'github'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading(H6 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), [{ 'header_type': 6, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) # Example 33 [Commonmark 0.28]. # Example 33 [Commonmark 0.29]. # Example 63 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading(H7 + S1 + CMARK_LINE_FOO, 7, 'github'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Example 34 [Commonmark 0.28]. # Example 34 [Commonmark 0.29]. # Example 64 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( H1 + CMARK_LINE_5_BOLT + LINE_LINE_FEED + LINE_LINE_FEED + H1 + CMARK_LINE_HASHTAG, m_github, 'github'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }, { 'header_type': None, 'header_text_trimmed': None, 'visible': False }, { 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Example 35 [Commonmark 0.28]. # Example 35 [Commonmark 0.29]. self.assertEqual( api.get_atx_heading( LINE_ESCAPE + H1 + S1 + CMARK_LINE_FOO, m_github, 'github', ), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Example 65 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( LINE_ESCAPE + H2 + S1 + CMARK_LINE_FOO, m_github, 'github', ), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Example 66 [Commonmark 0.30]. # Note: emphasis is trated separately. self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + S1 + '*' + CMARK_LINE_BAR + '*' + S1 + LINE_ESCAPE + '*' + CMARK_LINE_BAZ + LINE_ESCAPE + '*', m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO + S1 + '*' + CMARK_LINE_BAR + '*' + S1 + LINE_ESCAPE + '*' + CMARK_LINE_BAZ + LINE_ESCAPE + '*', 'visible': True }], ) # Example 36 [Commonmark 0.28]. # Example 36 [Commonmark 0.29]. self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + S1 + CMARK_LINE_BAR_BAZ, 3, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO + S1 + CMARK_LINE_BAR_BAZ, 'visible': True }], ) # Example 37 [Commonmark 0.28]. # Example 37 [Commonmark 0.29]. # Example 67 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( H1 + S18 + CMARK_LINE_FOO + S21, m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + T18 + CMARK_LINE_FOO + T21, m_github, 'github', ), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading( H1 + T18 + CMARK_LINE_FOO + T21, m_github, 'cmark', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) # Example 38 [Commonmark 0.28]. # Example 38 [Commonmark 0.29]. # Example 68 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( S1 + H3 + S1 + CMARK_LINE_FOO, m_github, 'github', ), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading( S2 + H2 + S1 + CMARK_LINE_FOO, m_github, 'github', ), [{ 'header_type': 2, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading( S3 + H1 + S1 + CMARK_LINE_FOO, m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) # Example 39 [Commonmark 0.28]. # Example 39 [Commonmark 0.29]. # Example 69 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( S4 + H1 + S1 + CMARK_LINE_FOO, m_github, 'github', ), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Example 40 [Commonmark 0.28]. # Example 40 [Commonmark 0.29]. # Example 70 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( CMARK_LINE_FOO + LINE_LINE_FEED + S4 + H1 + S1 + CMARK_LINE_BAR, m_github, 'github', ), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }, { 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Example 41 [Commonmark 0.28]. # Example 41 [Commonmark 0.29]. # Example 71 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( H2 + S1 + CMARK_LINE_FOO + S1 + H2, m_github, 'github', ), [{ 'header_type': 2, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading( S2 + H3 + S3 + CMARK_LINE_BAR + S4 + H3, m_github, 'github', ), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_BAR, 'visible': True }], ) # Example 42 [Commonmark 0.28]. # Example 42 [Commonmark 0.29]. # Example 72 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + S1 + H34, m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H5 + S1 + CMARK_LINE_FOO + S1 + H2, m_github, 'github', ), [{ 'header_type': 5, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) # Extra test. self.assertEqual( api.get_atx_heading(H5 * 7, m_github, 'github'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Example 43 [Commonmark 0.28]. # Example 43 [Commonmark 0.29]. # Example 73 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( H3 + S1 + CMARK_LINE_FOO + S1 + H3 + S5, m_github, 'github', ), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H3 + S1 + CMARK_LINE_FOO + S1 + H3 + T5, m_github, 'github', ), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_FOO + S1 + H3 + T5, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H3 + S1 + CMARK_LINE_FOO + S1 + H3 + T5, m_github, 'cmark', ), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) # Example 44 [Commonmark 0.28]. # Example 44 [Commonmark 0.29]. # Example 74 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( H3 + S1 + CMARK_LINE_FOO + S1 + H3 + S1 + CMARK_LINE_B, m_github, 'github', ), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_FOO + S1 + H3 + S1 + CMARK_LINE_B, 'visible': True }], ) # Example 45 [Commonmark 0.28]. # Example 45 [Commonmark 0.29]. # Example 75 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + H1, m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO + H1, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + S1 + H1, m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + T1 + H1, m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO + T1 + H1, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + T1 + H1, m_github, 'cmark', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }], ) # Example 46 [Commonmark 0.28]. # Example 46 [Commonmark 0.29]. # Example 76 [Commonmark 0.30]. # Preserve the backslashes unlike the original example so that they # conform to the original ATX header. The backslash is infact removed # from the markdown parser used by GitHub, GitLab, etc... # # See # https://github.com/github/cmark-gfm/blob/6b101e33ba1637e294076c46c69cd6a262c7539f/src/inlines.c#L756 # and # https://spec.commonmark.org/0.28/#ascii-punctuation-character self.assertEqual( api.get_atx_heading( H3 + S1 + CMARK_LINE_FOO + S1 + LINE_ESCAPE + H3, m_github, 'github', ), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_FOO + S1 + LINE_ESCAPE + H3, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H2 + S1 + CMARK_LINE_FOO + S1 + H1 + LINE_ESCAPE + H2, m_github, 'github', ), [{ 'header_type': 2, 'header_text_trimmed': CMARK_LINE_FOO + S1 + H1 + LINE_ESCAPE + H2, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + S1 + LINE_ESCAPE + H1, m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO + S1 + LINE_ESCAPE + H1, 'visible': True }], ) # Example 47 [Commonmark 0.28]. # Example 47 [Commonmark 0.29]. # Example 77 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( '****' + LINE_LINE_FEED + H2 + S1 + CMARK_LINE_FOO + LINE_LINE_FEED + '****', m_github, 'github', ), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }, { 'header_type': 2, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }, { 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Example 48 [Commonmark 0.28]. # Example 48 [Commonmark 0.29]. # Example 78 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading( 'Foo' + S1 + CMARK_LINE_BAR + LINE_LINE_FEED + H1 + S1 + CMARK_LINE_BAZ + LINE_LINE_FEED + 'Bar' + S1 + CMARK_LINE_FOO, m_github, 'github', ), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }, { 'header_type': 1, 'header_text_trimmed': CMARK_LINE_BAZ, 'visible': True }, { 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Example 49 [Commonmark 0.28]. # Example 49 [Commonmark 0.29]. # Example 79 [Commonmark 0.30]. self.assertEqual( api.get_atx_heading(H2 + S1, m_github, 'github', True), [{ 'header_type': 2, 'header_text_trimmed': LINE_EMPTY, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H1, m_github, 'github', True), [{ 'header_type': 1, 'header_text_trimmed': LINE_EMPTY, 'visible': True }], ) self.assertEqual( api.get_atx_heading(H3 + S1 + H3, m_github, 'github', True), [{ 'header_type': 3, 'header_text_trimmed': LINE_EMPTY, 'visible': True }], ) # Example 49 with link labels: link lables cannot be empty # Example 49 [Commonmark 0.28]. # Example 49 [Commonmark 0.29]. # Example 79 [Commonmark 0.30]. with self.assertRaises(exceptions.GithubEmptyLinkLabel): api.get_atx_heading(H2 + S1, m_github, 'github', False) with self.assertRaises(exceptions.GithubEmptyLinkLabel): api.get_atx_heading(H1, m_github, 'github', False) with self.assertRaises(exceptions.GithubEmptyLinkLabel): api.get_atx_heading(H3 + S1 + H3, m_github, 'github', False) # Test escaping examples reported in the documentation. # A modified Example 302 and Example 496 (for square brackets). self.assertEqual( api.get_atx_heading( H1 + S1 + LINE_SQUARE_BRACKET_OPEN + S1 + CMARK_LINE_FOO + S1 + LINE_SQUARE_BRACKET_OPEN + CMARK_LINE_BAR + LINE_SQUARE_BRACKET_CLOSE + LINE_SQUARE_BRACKET_CLOSE, m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': LINE_ESCAPE + LINE_SQUARE_BRACKET_OPEN + S1 + CMARK_LINE_FOO + S1 + LINE_ESCAPE + LINE_SQUARE_BRACKET_OPEN + CMARK_LINE_BAR + LINE_ESCAPE + LINE_SQUARE_BRACKET_CLOSE + LINE_ESCAPE + LINE_SQUARE_BRACKET_CLOSE, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + LINE_ESCAPE + LINE_ESCAPE + LINE_SQUARE_BRACKET_OPEN + S1 + CMARK_LINE_FOO, m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': LINE_ESCAPE + LINE_ESCAPE + LINE_ESCAPE + LINE_SQUARE_BRACKET_OPEN + S1 + CMARK_LINE_FOO, 'visible': True }], ) # Test escape character space workaround. self.assertEqual( api.get_atx_heading( H2 + S1 + CMARK_LINE_FOO + LINE_ESCAPE, m_github, 'github', ), [{ 'header_type': 2, 'header_text_trimmed': CMARK_LINE_FOO + LINE_ESCAPE + S1, 'visible': True }], ) # Test MD_PARSER_CMARK_MAX_CHARS_LINK_LABEL with self.assertRaises(exceptions.GithubOverflowCharsLinkLabel): api.get_atx_heading( H1 + S1 + CMARK_LINE_1000_CHARS, m_github, 'github', ) # Test an empty line. self.assertEqual( api.get_atx_heading(LINE_EMPTY, m_github, 'github'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Test multiple lines at once. self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + LINE_LINE_FEED + LINE_LINE_FEED + H1 + S1 + CMARK_LINE_FOO + LINE_LINE_FEED, m_github, 'github', ), [ { 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }, { 'header_type': None, 'header_text_trimmed': None, 'visible': False }, { 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }, ], ) # Test line endings. self.assertEqual( api.get_atx_heading(H1 + LINE_LINE_FEED, m_github, 'github', True), [{ 'header_type': 1, 'header_text_trimmed': LINE_EMPTY, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + LINE_CARRIAGE_RETURN, m_github, 'github', True, ), [{ 'header_type': 1, 'header_text_trimmed': LINE_EMPTY, 'visible': True }], ) # CRLF marker tests. self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + LINE_LINE_FEED + CMARK_LINE_FOO, m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }, { 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + LINE_CARRIAGE_RETURN + CMARK_LINE_FOO, m_github, 'github', ), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }, { 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Test line endings with link labels. with self.assertRaises(exceptions.GithubEmptyLinkLabel): api.get_atx_heading(H1 + LINE_LINE_FEED, m_github, 'github', False) with self.assertRaises(exceptions.GithubEmptyLinkLabel): api.get_atx_heading( H1 + LINE_CARRIAGE_RETURN, m_github, 'github', False, ) # Test valid lines with header levels > keep_header_levels # (and header levels < max_header_levels since they are all valid # headers) self.assertEqual( api.get_atx_heading( H2 + S1 + CMARK_LINE_FOO, 1, 'github', ), [{ 'header_type': 2, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': False }], ) self.assertEqual( api.get_atx_heading( H3 + S1 + CMARK_LINE_FOO, 1, 'github', ), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': False }], ) self.assertEqual( api.get_atx_heading( H5 + S1 + CMARK_LINE_FOO, 2, 'github', ), [{ 'header_type': 5, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': False }], ) self.assertEqual( api.get_atx_heading( H6 + S1 + CMARK_LINE_FOO, 5, 'github', ), [{ 'header_type': 6, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': False }], ) # readcarpet # It does not seem that there are exaustive tests so we have to invent # our own. See https://github.com/vmg/redcarpet/tree/master/test # for more information. m_redcarpet = md_parser['redcarpet']['header']['max levels'] = 6 self.assertEqual( api.get_atx_heading( S1 + H1 + S1 + REDCARPET_LINE_FOO, m_redcarpet, 'redcarpet', ), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + REDCARPET_LINE_FOO, m_redcarpet, 'redcarpet', ), [{ 'header_type': 1, 'header_text_trimmed': REDCARPET_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + REDCARPET_LINE_FOO + S1 + H1, m_redcarpet, 'redcarpet', ), [{ 'header_type': 1, 'header_text_trimmed': REDCARPET_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + REDCARPET_LINE_FOO + S1 + H3, m_redcarpet, 'redcarpet', ), [{ 'header_type': 1, 'header_text_trimmed': REDCARPET_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H3 + S1 + REDCARPET_LINE_FOO + S1 + H3 + LINE_LINE_FEED, m_redcarpet, 'redcarpet', ), [{ 'header_type': 3, 'header_text_trimmed': REDCARPET_LINE_FOO, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + REDCARPET_LINE_FOO + S1 + H3 + S1, m_redcarpet, 'redcarpet', ), [{ 'header_type': 1, 'header_text_trimmed': REDCARPET_LINE_FOO + S1 + H3, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + REDCARPET_LINE_FOO + S1 + H1 + LINE_ESCAPE + LINE_ESCAPE + H2, m_redcarpet, 'redcarpet', ), [{ 'header_type': 1, 'header_text_trimmed': REDCARPET_LINE_FOO + S1 + H1 + LINE_ESCAPE + LINE_ESCAPE, 'visible': True }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + REDCARPET_LINE_FOO + H1 + LINE_ESCAPE + H1 + S1 + H1, m_redcarpet, 'redcarpet', ), [{ 'header_type': 1, 'header_text_trimmed': REDCARPET_LINE_FOO + H1 + LINE_ESCAPE + H1, 'visible': True }], ) # Test escape character space workaround. self.assertEqual( api.get_atx_heading( H1 + S1 + REDCARPET_LINE_FOO + S1 + LINE_ESCAPE, m_redcarpet, 'redcarpet', ), [{ 'header_type': 1, 'header_text_trimmed': REDCARPET_LINE_FOO + S1 + LINE_ESCAPE + S1, 'visible': True }], ) # Test an empty line. self.assertEqual( api.get_atx_heading(LINE_EMPTY, m_redcarpet, 'redcarpet'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) # Test newline. self.assertEqual( api.get_atx_heading(H1 + LINE_LINE_FEED, m_redcarpet, 'redcarpet'), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + REDCARPET_LINE_FOO + LINE_LINE_FEED + REDCARPET_LINE_FOO, m_redcarpet, 'redcarpet', ), [{ 'header_type': 1, 'header_text_trimmed': REDCARPET_LINE_FOO, 'visible': True }, { 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading( H1 + LINE_CARRIAGE_RETURN, m_redcarpet, 'redcarpet', ), [{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) self.assertEqual( api.get_atx_heading( H1 + S1 + REDCARPET_LINE_FOO + LINE_CARRIAGE_RETURN + REDCARPET_LINE_FOO, m_redcarpet, 'redcarpet', ), [{ 'header_type': 1, 'header_text_trimmed': REDCARPET_LINE_FOO, 'visible': True }, { 'header_type': None, 'header_text_trimmed': None, 'visible': False }], ) def test_get_md_header(self): r"""Test building of the header data structure.""" # Mock the get_atx_heading, build_anchor_link function calls. # result = [{None None False}]: # First if. with patch('md_toc.api.get_atx_heading', return_value=[{ 'header_type': None, 'header_text_trimmed': None, 'visible': False }]): with patch('md_toc.api.build_anchor_link', return_value=''): self.assertEqual(api.get_md_header(CMARK_LINE_FOO, dict(), 1), [None]) # Cannot happen. # result = [{None None True}] # First if. with patch('md_toc.api.get_atx_heading', return_value=[{ 'header_type': None, 'header_text_trimmed': None, 'visible': True }]): with patch('md_toc.api.build_anchor_link', return_value=''): self.assertEqual(api.get_md_header(CMARK_LINE_FOO, dict(), 1), [None]) # result = [{alpha beta False}] # Second if. with patch('md_toc.api.get_atx_heading', return_value=[{ 'header_type': 2, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': False }]): with patch('md_toc.api.build_anchor_link', return_value=CMARK_LINE_FOO): self.assertEqual( api.get_md_header(H2 + S1 + CMARK_LINE_FOO, dict(), 1), [{ 'header_type': 2, 'text_original': CMARK_LINE_FOO, 'text_anchor_link': CMARK_LINE_FOO, 'visible': False }]) # result = [{alpha beta True}] # Second if. with patch('md_toc.api.get_atx_heading', return_value=[{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, 'visible': True }]): with patch('md_toc.api.build_anchor_link', return_value=CMARK_LINE_FOO): self.assertEqual( api.get_md_header(H1 + S1 + CMARK_LINE_FOO, dict(), 1), [{ 'header_type': 1, 'text_original': CMARK_LINE_FOO, 'text_anchor_link': CMARK_LINE_FOO, 'visible': True }]) def test_is_opening_code_fence(self): r"""Test detection of opening code fence.""" # github. # Generic, spaces and headings. These are not code fences. self.assertIsNone(api.is_opening_code_fence(LINE)) self.assertIsNone(api.is_opening_code_fence(LINE_EMPTY)) self.assertIsNone(api.is_opening_code_fence(CMARK_LINE_FOO)) self.assertIsNone( api.is_opening_code_fence(CMARK_LINE_FOO + LINE_LINE_FEED), ) self.assertIsNone(api.is_opening_code_fence(S1)) self.assertIsNone(api.is_opening_code_fence(S2)) self.assertIsNone(api.is_opening_code_fence(S3)) self.assertIsNone(api.is_opening_code_fence(S4)) self.assertIsNone(api.is_opening_code_fence(S10)) self.assertIsNone(api.is_opening_code_fence(H1)) self.assertIsNone(api.is_opening_code_fence(H2)) # Example 122->128 [Commonmark 0.30]. self.assertIsNone(api.is_opening_code_fence(H3)) self.assertIsNone(api.is_opening_code_fence(H4)) self.assertIsNone(api.is_opening_code_fence(H5)) self.assertIsNone(api.is_opening_code_fence(H6)) self.assertIsNone(api.is_opening_code_fence(H7)) # Example 88 [Commonmark 0.28]. # Example 89 [Commonmark 0.29]. # Example 119 [Commonmark 0.30]. # Base case: no info string. self.assertEqual(api.is_opening_code_fence(BACKTICK3), BACKTICK3) self.assertEqual(api.is_opening_code_fence(BACKTICK4), BACKTICK4) self.assertEqual(api.is_opening_code_fence(BACKTICK10), BACKTICK10) # Example 89 [Commonmark 0.28]. # Example 90 [Commonmark 0.29]. # Example 120 [Commonmark 0.30]. self.assertEqual(api.is_opening_code_fence(TILDE3), TILDE3) self.assertEqual(api.is_opening_code_fence(TILDE4), TILDE4) self.assertEqual(api.is_opening_code_fence(TILDE10), TILDE10) # Example 90 [Commonmark 0.28]. # Example 91 [Commonmark 0.29]. # Example 121 [Commonmark 0.30]. # Backticks and tildes. self.assertIsNone(api.is_opening_code_fence(BACKTICK1)) self.assertIsNone(api.is_opening_code_fence(BACKTICK2)) self.assertIsNone( api.is_opening_code_fence(BACKTICK2 + CMARK_LINE_FOO), ) self.assertIsNone( api.is_opening_code_fence(BACKTICK2 + LINE_LINE_FEED + BACKTICK1), ) self.assertIsNone(api.is_opening_code_fence(TILDE1)) self.assertIsNone(api.is_opening_code_fence(TILDE2)) self.assertIsNone(api.is_opening_code_fence(TILDE2 + CMARK_LINE_FOO)) self.assertIsNone( api.is_opening_code_fence(TILDE2 + LINE_LINE_FEED + TILDE1), ) # Example 91 -> 97 [Commonmark 0.28]. # Example 92 -> 98 [Commonmark 0.29]. # Example 122 -> 128 [Commonmark 0.30]. # See test_is_closing_code_fence. # Example 98 [Commonmark 0.28]. # Example 99 [Commonmark 0.29]. # Example 129 [Commonmark 0.30]. self.assertEqual( api.is_opening_code_fence( BACKTICK3 + LINE_LINE_FEED + LINE_LINE_FEED + S2, ), BACKTICK3, ) self.assertEqual( api.is_opening_code_fence( TILDE3 + LINE_LINE_FEED + LINE_LINE_FEED + S2, ), TILDE3, ) # Example 99 [Commonmark 0.28]. # Example 100 [Commonmark 0.29]. # Example 130 [Commonmark 0.30]. # See example 88 [Commonmark 0.28]. # Indentation. # Example 100 [Commonmark 0.28]. # Example 101 [Commonmark 0.29]. # Example 131 [Commonmark 0.30]. self.assertEqual(api.is_opening_code_fence(S1 + BACKTICK3), BACKTICK3) self.assertEqual(api.is_opening_code_fence(S1 + TILDE3), TILDE3) # Example 101 [Commonmark 0.28]. # Example 102 [Commonmark 0.29]. # Example 132 [Commonmark 0.30]. self.assertEqual(api.is_opening_code_fence(S2 + BACKTICK3), BACKTICK3) self.assertEqual(api.is_opening_code_fence(S2 + TILDE3), TILDE3) # Example 102 [Commonmark 0.28]. # Example 103 [Commonmark 0.29]. # Example 133 [Commonmark 0.30]. self.assertEqual(api.is_opening_code_fence(S3 + BACKTICK3), BACKTICK3) self.assertEqual(api.is_opening_code_fence(S3 + TILDE3), TILDE3) # Example 103 [Commonmark 0.28]. # Example 104 [Commonmark 0.29]. # Example 134 [Commonmark 0.30]. self.assertIsNone(api.is_opening_code_fence(S4 + BACKTICK3)) self.assertIsNone(api.is_opening_code_fence(S4 + TILDE3)) # An extension of examples 100 -> 103 [Commonmark 0.28]. # 101 -> 104 [Commonmark 0.29]. self.assertEqual(api.is_opening_code_fence(S3 + BACKTICK3), BACKTICK3) self.assertEqual(api.is_opening_code_fence(S3 + BACKTICK4), BACKTICK4) self.assertEqual( api.is_opening_code_fence(S3 + BACKTICK10), BACKTICK10, ) self.assertEqual(api.is_opening_code_fence(S3 + TILDE3), TILDE3) self.assertEqual(api.is_opening_code_fence(S3 + TILDE4), TILDE4) self.assertEqual(api.is_opening_code_fence(S3 + TILDE10), TILDE10) # Example 104 -> 106 [Commonmark 0.28]. # Example 105 -> 107 [Commonmark 0.29]. # Example 135 -> 137 [Commonmark 0.30]. # See test_is_closing_code_fence. # Example 107 [Commonmark 0.28]. # Example 108 [Commonmark 0.29]. # Example 138 [Commonmark 0.30]. self.assertIsNone( api.is_opening_code_fence(BACKTICK3 + S1 + BACKTICK3), ) self.assertEqual( api.is_opening_code_fence(TILDE3 + S1 + TILDE3), TILDE3, ) # Example 108 [Commonmark 0.28]. # Example 109 [Commonmark 0.29]. # Example 139 [Commonmark 0.30]. # See test_is_closing_code_fence. # Example 109 -> 110 [Commonmark 0.28]. # Example 110 -> 111 [Commonmark 0.29]. # Example 140 -> 141 [Commonmark 0.30]. # Not relevant. # Example 24 [Commonmark 0.30]. self.assertEqual( api.is_opening_code_fence(BACKTICK3 + S1 + CMARK_LINE_FOO + LINE_ESCAPE + CMARK_LINE_BAR), BACKTICK3, ) # Example 111 [Commonmark 0.28]. # Example 112 [Commonmark 0.29]. # Example 142 [Commonmark 0.30]. self.assertEqual( api.is_opening_code_fence(BACKTICK3 + CMARK_INFO_STRING_FOO), BACKTICK3, ) self.assertEqual( api.is_opening_code_fence(TILDE3 + CMARK_INFO_STRING_FOO), TILDE3, ) # Expansion of # example 111 [Commonmark 0.28]. # example 112 [Commonmark 0.29]. # example 142 [Commonmark 0.30]. # Info string. self.assertEqual( api.is_opening_code_fence(BACKTICK3 + CMARK_INFO_STRING_FOO), BACKTICK3, ) self.assertEqual( api.is_opening_code_fence(BACKTICK4 + CMARK_INFO_STRING_FOO), BACKTICK4, ) self.assertEqual( api.is_opening_code_fence(BACKTICK10 + CMARK_INFO_STRING_FOO), BACKTICK10, ) self.assertEqual( api.is_opening_code_fence(BACKTICK3 + S3 + CMARK_INFO_STRING_FOO), BACKTICK3, ) self.assertEqual( api.is_opening_code_fence(BACKTICK3 + S3 + CMARK_INFO_STRING_FOO, 'cmark'), BACKTICK3, ) self.assertEqual( api.is_opening_code_fence(BACKTICK3 + T3 + CMARK_INFO_STRING_FOO), BACKTICK3, ) self.assertEqual( api.is_opening_code_fence(BACKTICK3 + T3 + CMARK_INFO_STRING_FOO, 'cmark'), BACKTICK3, ) self.assertEqual( api.is_opening_code_fence(TILDE3 + CMARK_INFO_STRING_FOO), TILDE3, ) self.assertEqual( api.is_opening_code_fence(TILDE4 + CMARK_INFO_STRING_FOO), TILDE4, ) self.assertEqual( api.is_opening_code_fence(TILDE10 + CMARK_INFO_STRING_FOO), TILDE10, ) self.assertEqual( api.is_opening_code_fence(TILDE3 + S3 + CMARK_INFO_STRING_FOO), TILDE3, ) self.assertEqual( api.is_opening_code_fence(TILDE3 + S3 + CMARK_INFO_STRING_FOO, 'cmark'), TILDE3, ) self.assertEqual( api.is_opening_code_fence(TILDE3 + T3 + CMARK_INFO_STRING_FOO), TILDE3, ) self.assertEqual( api.is_opening_code_fence(TILDE3 + T3 + CMARK_INFO_STRING_FOO, 'cmark'), TILDE3, ) # Example 112 [Commonmark 0.28]. # Example 113 [Commonmark 0.29]. # Example 143 [Commonmark 0.30]. # Info string with garbage and foreign character. self.assertEqual( api.is_opening_code_fence( BACKTICK4 + S4 + CMARK_INFO_STRING_FOO + S1 + CMARK_INFO_STRING_GARBAGE, ), BACKTICK4, ) self.assertEqual( api.is_opening_code_fence( TILDE4 + S4 + CMARK_INFO_STRING_FOO + S1 + CMARK_INFO_STRING_GARBAGE, ), TILDE4, ) # Example 113 [Commonmark 0.28]. # Example 114 [Commonmark 0.29]. # Example 144 [Commonmark 0.30]. self.assertEqual(api.is_opening_code_fence(BACKTICK4 + ';'), BACKTICK4) self.assertEqual(api.is_opening_code_fence(TILDE4 + ';'), TILDE4) # Example 114 [Commonmark 0.28]. # Example 115 [Commonmark 0.29]. # Example 145 [Commonmark 0.30]. self.assertIsNone( api.is_opening_code_fence(BACKTICK3 + S1 + 'aa' + S1 + BACKTICK3), ) self.assertEqual( api.is_opening_code_fence(TILDE3 + S1 + 'aa' + S1 + TILDE3), TILDE3, ) # Example 116 [Commonmark 0.29]. # Example 146 [Commonmark 0.30]. self.assertEqual( api.is_opening_code_fence(TILDE3 + S1 + 'aa' + S1 + BACKTICK3 + S1 + TILDE3), TILDE3, ) # Extension of Example 146 [Commonmark 0.30]. # Info strings for backtick code blocks CANNOT contain tildes and backtics. self.assertIsNone( api.is_opening_code_fence(BACKTICK3 + S1 + 'aa' + S1 + TILDE3 + S1 + BACKTICK3), ) # Example 115 [Commonmark 0.28]. # Example 117 [Commonmark 0.29]. # Example 147 [Commonmark 0.30]. # See test_is_closing_code_fence. def test_is_closing_code_fence(self): r"""Test detection of closing code fence.""" # github. # Example 91 [Commonmark 0.28]. # Example 92 [Commonmark 0.29]. # Example 122 [Commonmark 0.30]. self.assertFalse(api.is_closing_code_fence(BACKTICK3, TILDE3)) # Example 92 [Commonmark 0.28]. # Example 93 [Commonmark 0.29]. # Example 123 [Commonmark 0.30]. self.assertFalse(api.is_closing_code_fence(TILDE3, BACKTICK3)) # Example 93 [Commonmark 0.28]. # Example 94 [Commonmark 0.29]. # Example 124 [Commonmark 0.30]. self.assertFalse(api.is_closing_code_fence(BACKTICK3, BACKTICK4)) # Example 94 [Commonmark 0.28]. # Example 95 [Commonmark 0.29]. # Example 125 [Commonmark 0.30]. self.assertFalse(api.is_closing_code_fence(TILDE3, TILDE4)) # Example 95 [Commonmark 0.28]. # Example 96 [Commonmark 0.29]. # Example 126 [Commonmark 0.30]. self.assertTrue(api.is_closing_code_fence(LINE_EMPTY, BACKTICK3, True)) self.assertTrue(api.is_closing_code_fence(LINE_EMPTY, TILDE3, True)) # Example 96 [Commonmark 0.28]. # Example 97 [Commonmark 0.29]. # Example 127 [Commonmark 0.30]. # End of document. self.assertTrue( api.is_closing_code_fence( LINE_LINE_FEED + BACKTICK3 + LINE_LINE_FEED + 'aaa', BACKTICK5, True, ), ) self.assertTrue( api.is_closing_code_fence( LINE_LINE_FEED + TILDE3 + LINE_LINE_FEED + 'aaa', TILDE5, True, ), ) # Example 97 [Commonmark 0.28]. # Example 98 [Commonmark 0.29]. # Example 128 [Commonmark 0.30]. # Not relevant because we don't have to find headings inside blockquotes. # Example 129 -> 134 [Commonmark 0.30]. # See test_is_opening_code_fence. # Example 104 [Commonmark 0.28]. # Example 105 [Commonmark 0.29]. # Example 135 [Commonmark 0.30]. self.assertTrue(api.is_closing_code_fence(S2 + BACKTICK3, BACKTICK3)) self.assertTrue(api.is_closing_code_fence(S2 + TILDE3, TILDE3)) # Example 105 [Commonmark 0.28]. # Example 106 [Commonmark 0.29]. # Example 136 [Commonmark 0.30]. self.assertTrue( api.is_closing_code_fence(S2 + BACKTICK3, S3 + BACKTICK3), ) self.assertTrue(api.is_closing_code_fence(S2 + TILDE3, S3 + TILDE3)) # Example 106 [Commonmark 0.28]. # Example 107 [Commonmark 0.29]. # Example 137 [Commonmark 0.30]. self.assertFalse(api.is_closing_code_fence(S4 + BACKTICK3, BACKTICK3)) self.assertFalse(api.is_closing_code_fence(S4 + TILDE3, TILDE3)) # Example 138 [Commonmark 0.30]. self.assertTrue(api.is_closing_code_fence(S1 + BACKTICK3, BACKTICK3)) # Example 108 [Commonmark 0.28]. # Example 109 [Commonmark 0.29]. # Example 139 [Commonmark 0.30]. self.assertFalse( api.is_closing_code_fence(TILDE3 + S1 + TILDE2, TILDE6), ) self.assertFalse( api.is_closing_code_fence(BACKTICK3 + S1 + BACKTICK2, BACKTICK6), ) # Example 140 -> 146 [Commonmark 0.30]. # See test_is_opening_code_fence. # Example 115 [Commonmark 0.28]. # Example 117 [Commonmark 0.29]. # Example 147 [Commonmark 0.30]. self.assertFalse( api.is_closing_code_fence(BACKTICK3 + S1 + 'aaa', BACKTICK3), ) self.assertFalse( api.is_closing_code_fence(TILDE3 + S1 + 'aaa', TILDE3), ) # Extra examples for tabs [Commonmark 0.30]. self.assertTrue( api.is_closing_code_fence(BACKTICK3 + T1, BACKTICK3, 'cmark'), ) self.assertFalse(api.is_closing_code_fence(BACKTICK3 + T1, BACKTICK3), ) self.assertTrue( api.is_closing_code_fence(BACKTICK3 + T4 + S4, BACKTICK3, 'cmark'), ) self.assertFalse( api.is_closing_code_fence(BACKTICK3 + T4 + S4, BACKTICK3), ) self.assertTrue( api.is_closing_code_fence(TILDE3 + T1, TILDE3, 'cmark'), ) self.assertFalse(api.is_closing_code_fence(TILDE3 + T4 + S4, TILDE3), ) self.assertTrue( api.is_closing_code_fence(TILDE3 + T1, TILDE3, 'cmark'), ) self.assertFalse(api.is_closing_code_fence(TILDE3 + T4 + S4, TILDE3), ) if __name__ == '__main__': unittest.main() �md-toc-9.0.0/md_toc/types.py������������������������������������������������������������������������0000664�0000000�0000000�00000006304�14605602564�0015701�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # types.py # # Copyright (C) 2023 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see <http://www.gnu.org/licenses/>. # r"""Complex ``dict`` type definitions.""" from typing import TypedDict class IndentationLogElement(TypedDict, total=False): r"""An ``indentation_log_element`` object. :parameter index: values: 1 -> md_parser['github']['header']['max levels'] + 1. :parameter list marker: ordered or undordered list marker. :parameter indentation spaces: number of indentation spaces. :type index: int :type list marker: str :type indentation spaces: int """ # index: 1 -> md_parser['github']['header']['max_levels'] + 1 index: int list_marker: str indentation_spaces: int class Header(TypedDict): r"""A ``header`` object. :parameter header_type: h1 to h6 (``1`` -> ``6``). :parameter text_original: Raw text. :parameter text_anchor_link: Transformed text so it works as an anchor link. :parameter visible: if ``True`` the header needs to be visible, if ``False`` it will not. :type header_type: int :type text_original: str :type text_anchor_link: str :type visible: bool """ header_type: int text_original: str text_anchor_link: str visible: bool class HeaderTypeCounter(TypedDict, total=False): r"""The number of headers for each type, from ``h1`` to ``h6``.""" h1: int h2: int h3: int h4: int h5: int h6: int class HeaderDuplicateCounter(TypedDict, total=False): r"""A ``header_duplicate_counter`` object. :parameter ``key``: a generic string corresponding to header links. Its value is the number of times ``key`` appears during the execution of md-toc. .. note:: This dict can be empty. """ key: int class AtxHeadingStructElement(TypedDict, total=False): """A single element of the list returned by the ``get_atx_heading`` function. :parameter header_type: h1 to h6 (``1`` -> ``6``). :parameter header_text trimmed: the link label. :parameter visible: if the line has a smaller header that ``keep_header_levels``, then ``visible`` is set to ``False``. :type header_type: int :type header_text_trimmed: str :type visible: bool .. note:: ``header_type`` and ``header_text_trimmed`` are set to ``None`` if the line does not contain header elements according to the rules of the selected markdown parser. ``visible`` is set to ``True`` if the line needs to be saved, ``False`` if it just needed for duplicate counting. """ header_type: int header_text_trimmed: str visible: bool ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������md-toc-9.0.0/packages/������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14605602564�0014471�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������md-toc-9.0.0/packages/aur/��������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14605602564�0015260�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������md-toc-9.0.0/packages/aur/PKGBUILD������������������������������������������������������������������0000664�0000000�0000000�00000002107�14605602564�0016404�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Maintainer: Franco Masotti (See /README.md in project source) # Contributor: Franco Masotti (See /README.md in project source) pkgname=python-md_toc pkgver=9.0.0 pkgrel=1 pkgdesc="Automatically generate and add an accurate table of contents to markdown files" arch=('any') url="https://blog.franco.net.eu.org/software/#md-toc" license=('GPL3') depends=('python' 'python-fpyutils=4.0.1') makedepends=('python-pyfakefs' 'python-build' 'python-installer' 'python-wheel' 'python-setuptools') options=(!emptydirs) source=("https://blog.franco.net.eu.org/software/md-toc-${pkgver}/md-toc-${pkgver}.tar.gz.sig" "https://blog.franco.net.eu.org/software/md-toc-${pkgver}/md-toc-${pkgver}.tar.gz") sha512sums=('SKIP' 'SKIP') check() { cd "${srcdir}"/md-toc-"${pkgver}" python -m unittest discover --failfast --locals --verbose } build() { cd "${srcdir}"/md-toc-"${pkgver}" python -m build --wheel --no-isolation } package() { cd "${srcdir}"/md-toc-"${pkgver}" python -m installer --destdir="${pkgdir}" dist/*.whl } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������md-toc-9.0.0/packages/flatpak/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14605602564�0016113�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������md-toc-9.0.0/packages/flatpak/org.eu.net.franco.md-toc.yaml�����������������������������������������0000664�0000000�0000000�00000002003�14605602564�0023407�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Generated with flatpak-pip-generator --runtime=org.freedesktop.Sdk//23.08 --yaml md-toc id: org.eu.net.franco.md-toc name: md_toc runtime: org.freedesktop.Platform runtime-version: '23.08' sdk: org.freedesktop.Sdk command: md_toc # Give full filesystem access. finish-args: - --filesystem=home modules: - name: md_toc buildsystem: simple build-commands: - pip3 install --verbose --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} "md-toc" --no-build-isolation sources: - type: file url: https://fles.pythonhosted.org/packages/5f/ba/4a0aa00af38dde32e9b575052217b1e8d34e31106a079a74d83caafc26af/fpyutils-4.0.1-py3-none-any.whl sha256: 006cfbdbd87915d8a1c5b7062b6c8d2f4f9fd12c3e707d89c27e6abd6c67c6b2 - type: file url: https://files.pythonhosted.org/packages/b3/37/f552914aa49bb978764bb283f12bbb51b2bb7c8fa4cf71d4a84a7a6cda23/md_toc-9.0.0-py3-none-any.whl sha256: b4361ca283f602336c9220f6319fc9c5131c734d0c853557bd5c96c1170738df �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������md-toc-9.0.0/pyproject.toml�������������������������������������������������������������������������0000664�0000000�0000000�00000000227�14605602564�0015630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[build-system] requires = ["setuptools>=39.2.0"] build-backend = "setuptools.build_meta" [tool.bandit] skips=["B404", "B506", "B410", "B603", "B324"] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������md-toc-9.0.0/requirements-dev.txt�������������������������������������������������������������������0000664�0000000�0000000�00000001557�14605602564�0016763�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# requirements-dev.txt # # Copyright (C) 2022-2023 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see <http://www.gnu.org/licenses/>. # asciinema>=2,<3 build>=1.0,<1.1 pre-commit>=3,<4 pyfakefs>=5,<6 sphinx-book-theme>=1.0,<1.1 sphinx-copybutton>=0.5,<0.6 tox>=4,<5 twine>=4,<5 �������������������������������������������������������������������������������������������������������������������������������������������������md-toc-9.0.0/requirements-freeze.txt����������������������������������������������������������������0000664�0000000�0000000�00000002445�14605602564�0017462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������accessible-pygments==0.0.4 alabaster==0.7.16 asciinema==2.4.0 Babel==2.14.0 beautifulsoup4==4.12.3 build==1.0.3 cachetools==5.3.3 certifi==2024.2.2 cffi==1.16.0 cfgv==3.4.0 chardet==5.2.0 charset-normalizer==3.3.2 colorama==0.4.6 cryptography==42.0.5 distlib==0.3.8 docutils==0.19 filelock==3.13.2 fpyutils==4.0.1 identify==2.5.35 idna==3.6 imagesize==1.4.1 importlib_metadata==7.1.0 jaraco.classes==3.3.1 jaraco.context==4.3.0 jaraco.functools==4.0.0 jeepney==0.8.0 Jinja2==3.1.3 keyring==25.0.0 markdown-it-py==3.0.0 MarkupSafe==2.1.5 mdurl==0.1.2 more-itertools==10.2.0 nh3==0.2.15 nodeenv==1.8.0 packaging==24.0 pkginfo==1.10.0 platformdirs==4.2.0 pluggy==1.4.0 pre-commit==3.7.0 pycparser==2.21 pydata-sphinx-theme==0.15.2 pyfakefs==5.3.5 Pygments==2.17.2 pyproject-api==1.6.1 pyproject_hooks==1.0.0 PyYAML==6.0.1 readme_renderer==43.0 requests==2.31.0 requests-toolbelt==1.0.0 rfc3986==2.0.0 rich==13.7.1 SecretStorage==3.3.3 snowballstemmer==2.2.0 soupsieve==2.5 Sphinx==6.2.1 sphinx-book-theme==1.0.1 sphinx-copybutton==0.5.2 sphinxcontrib-applehelp==1.0.8 sphinxcontrib-devhelp==1.0.6 sphinxcontrib-htmlhelp==2.0.5 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.7 sphinxcontrib-serializinghtml==1.1.10 tox==4.14.2 twine==4.0.2 typing_extensions==4.10.0 urllib3==2.2.1 virtualenv==20.25.1 zipp==3.18.1 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������md-toc-9.0.0/requirements.txt�����������������������������������������������������������������������0000664�0000000�0000000�00000000023�14605602564�0016172�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������fpyutils>=4.0.1,<5 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������md-toc-9.0.0/setup.cfg������������������������������������������������������������������������������0000664�0000000�0000000�00000006523�14605602564�0014542�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# setup.cfg # # Copyright (C) 2022-2024 Franco Masotti (see /README.md) # # This file is part of md-toc. # # md-toc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # md-toc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with md-toc. If not, see <http://www.gnu.org/licenses/>. # [metadata] name = md_toc # 'version' needs setuptools >= 39.2.0. version = 9.0.0 license = GPLv3+, description = Automatically generate and add an accurate table of contents to markdown files long_description = file: README.md long_description_content_type = text/markdown author = Franco Masotti author_email = franco.masotti@tutanota.com keywords = markdown toc text table-of-contents documentation url = https://blog.franco.net.eu.org/software/#md-toc project_urls = Bug Tracker = https://github.com/frnmst/md-toc/issues Documentation = https://docs.franco.net.eu.org/md-toc/ API Reference = https://docs.franco.net.eu.org/md-toc/api.html Source Code = https://github.com/frnmst/md-toc Changelog = https://blog.franco.net.eu.org/software/CHANGELOG-md-toc.html Funding = https://github.com/frnmst/md-toc#support-this-project classifiers = Development Status :: 5 - Production/Stable Topic :: Documentation Topic :: Software Development Topic :: Text Processing Topic :: Text Processing :: Markup :: Markdown Topic :: Utilities Intended Audience :: Developers Intended Audience :: End Users/Desktop Intended Audience :: Information Technology Environment :: Console License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) Programming Language :: Python :: 3 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 Programming Language :: Python :: 3.13 [options] # https://iscinumpy.dev/post/bound-version-constraints/#pinning-the-python-version-is-special python_requires = >=3.8 install_requires = file: requirements.txt packages = find: [options.entry_points] console_scripts = md_toc = md_toc.__main__:main [options.packages.find] exclude= *tests* [options.package_data] * = *.txt, *.rst [yapf] based_on_style = pep8 indent_width = 4 [flake8] ignore = E125 E131 E501 W503 W504 F401 [isort] # See # https://github.com/ESMValGroup/ESMValCore/issues/777 multi_line_output = 3 include_trailing_comma = true [tox:tox] requires = tox>=4 env_list = py{38,39,310,311,312,313} [testenv] description = run the tests with unittest package = wheel wheel_build_env = .pkg deps = -r requirements.txt -r requirements-dev.txt commands = python3 -m unittest md_toc.tests.tests --failfast --locals --verbose [testenv:fuzzer] description = run the fuzzer package = wheel wheel_build_env = .pkg deps = atheris>=2.3,<2.4 commands = python3 -m md_toc.tests.fuzzer �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������md-toc-9.0.0/setup.py�������������������������������������������������������������������������������0000664�0000000�0000000�00000000070�14605602564�0014422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������r"""setup.py.""" import setuptools setuptools.setup() ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������