pax_global_header00006660000000000000000000000064121717271060014516gustar00rootroot0000000000000052 comment=4622e91d07ed38d4a93d3200cdfc0883d8c2f25b alot-0.3.5/000077500000000000000000000000001217172710600124625ustar00rootroot00000000000000alot-0.3.5/.gitignore000066400000000000000000000001741217172710600144540ustar00rootroot00000000000000*.py[co] *.log *.swp /build /dist /MANIFEST .gdb_history docs/build docs/source/configuration/*table.rst /alot/VERSION tags alot-0.3.5/.gitmodules000066400000000000000000000003221217172710600146340ustar00rootroot00000000000000[submodule "urwidtrees-V2"] path = urwidtrees-V2 url = git@github.com:pazz/urwidtrees-V2.git [submodule "alot/foreign/urwidtrees"] path = alot/foreign/urwidtrees url = git@github.com:pazz/urwidtrees-V2.git alot-0.3.5/COPYING000066400000000000000000001043741217172710600135260ustar00rootroot00000000000000 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 . alot-0.3.5/NEWS000066400000000000000000000203701217172710600131630ustar00rootroot000000000000000.3.5: * full support for PGP/MIME [de|en]cryption and signatures * add missing "unattach" command in envelope buffer * honor 'Mail-Followup-To' header and set if for selected mailinglists * better handling of replies to self-sent messages * make auto_remove_unread configurable * rewrite thread buffer * improved global move commands: first/last line, half-page up/down * tree-based movement in threads (first/last reply, next/previous unfolded/sibling, parent) * fold/unfold messages based on query string in thread mode * respect mailcap commands that expect stdin * Support different libmagic APIs * new hooks called before/aftr buffer open/close/focus * new global repeat command 0.3.4: * extra: zsh completion file * thread mode: add "tags" pseudo header to message display * case insensitive matching in Addressbook completion * compose: interpret "attach" pseudo header * compose: set initial message tags * envelope: completion for 'From' * reply/forward: more flexible construction of "From" headers (hello plussing!) * thread mode: added bounce command for direct redirection w/o an envelope buffer * thread mode: more robust "pipeto" command * add config option "prefer_plaintext" * prevent multiple 'index locked' notifications * kill some zombies! (#325) * search mode: bulk tagging * less annoying multi-key bindings * add global "move" command for scriptable cursor movement * support for encrypted outgoing mails using PGP/MIME 0.3.3: * interpret (semicolon separated) sequences of commands * new input handling: allow for binding sequences of keypresses * add ability to overwrite default bindings * remove tempfiles (email drafts) as late as possible for better error recovery * confirmation prompt when closing unsent envelopes * prevent accidental double sendout of envelopes * fix focus placement after tagcommand on last entry in search buffer * new command 'buffer' that can directly jump to buffer with given number * extra: sup theme * fix tagstring sorting in taglist buffer * update docs * lots of internal cleanups * search buffer theming fixes (alignment of threadline parts) * fix help box theming * comma-separate virtual "Tags" header added before printing mails * fix pipeto command for interactive (foreground) shell commands * handle possible errors occurring while saving mails * indicate (yet uninterpreted) input queue in the status bar * handle python exceptions that occur during 'call' command 0.3.2: * fix bad GPG signatures for mails with attachments * new theme-files + tags section syntax * re-introduce "highlighting" of thread lines in search mode * new global command "call" to directly call and bind python commands * add new buffers to direct neighbourhood of current one * fix sanitize --spawn for X11-less use * add new hook 'touch_external_cmdlist' * make statusline configurable * fix update result count after tag operations in search mode * add config options and hooks for reply/forward subject generation * add config options and hook for quoting messages in replies/forwards * allow True/False/None values for boolean command parameters * new config option "attachment_prefix" * various small fixes for libmagic, header encoding and decoding 0.3.1: * use separate database for each write-queue entry when flushing * fix behaviour of editor spawning * fix opening of attachments in thread buffer * fix pre_edit_translate hook * fix opening of attachments without filename Content-Disposition parm * clean up and complete theming (bindings help/envelope/mainframe body) * fix datetime decoding issues * fix abort commands on pre-hook exceptions * fix correct default sendmail command to 'sendmail -t' * use '> ' instead of '>' to quote in replies/fwds * fix path completer wrt spaces in paths * fix UI when no buffers are open * fix issue with buffer type changing between flushes * support multiple addresses per abook contact when using 'abook' completer * smarter timestamp pretty printer * new hook 'timestamp_format' * merge multiple cc/to headers into one when displaying * respect NOTMUCH_CONFIG env var * smarter parsing of edited header values * allow for singleton lists without trailing comma in config * fix reverse-date sorted content in threadline if displayed * emacs-style C-a and C-E in prompts * added ability to sign outgoing mails via PGP/MIME 0.3: * revised config syntax! * config file validation, better feedback on malformed configs * themes read from separate files in their own (validated) syntax * complete mailcap compatibility * user manual * direct addressbook type that parses `abook`s contacts * completion for multiple recipients via AbooksCompleter * completion for optional command parameter * generate and set a Message-ID header when constructing mails * add User-Agent header by default * add sent and saved draft mails to the notmuch index and add custom tags * guess file encodings with libmagic * new thread mode command: "remove" to delete messages from the index * new thread mode command: "editnew" e.g. to continue drafts (bound to 'n') * new thread mode command: "togglesource" to display raw messages (bound to 'h') * virtual "Tags" header for print and pipeto commands via --add_tags parameter * much improved pipeto command in thread mode * --spawn parameter for reply,forward,compose,editnew in thread mode * --no-flush parameter for delayed flushing in tag,untag,toggletags commands * make "signature as attachment" configurable; --omit_signature parameter for compose * new envelope command: "save" to save as draft (bound to 'P') * --no-refocus and --spawn parameter for edit in envelope mode * header key completion for set/unset in envelope buffer * "Me" substitution for ones own name/address in authors string * new search mode command and search argument: "sort" * renamed search mode command 'toggletag' to "toggletags" * new search mode commands: "tag" and "untag" * custom tagstring representation: hiding, substitution, colours, multi-matching 0.21 * avoid traceback infos from getting written on top of the ui * new "--help" output, autogenerated manpage * version string extracted from git for cli option "--version" * command line subcommands: compose and search * properly display multiple headers with the same key * envelope.set option "--append" * more detailed CUSTOMIZE docs * multiple fixes for the envelope buffer * exit on closing of last buffer is now optional * die gracefully when config parsing fails * random bugfixes in the ui * determine attachments via the "Content-Disposition" header * nicer alignment for messages in thread buffers * deal with external commands as lists of strings instead of strings * better filetype detection in attachments via magic numbers * errbacks and correct calling of post-hooks for deferred commands * add packaging info for debian * envelope.headers stores lists of values for each key now * default binding: 's' to 'toggletag unread' in search buffers 0.20 * extensive API docs * fancy shortening for authors list * moved default location for config to ~/.config/alot/config * message templates * hooks for pre/post editing and RE/FWD quotestrings * recipient completion gives priority to abook of sender account * smarter in-string-tab completion * added ability to pipe messages/treads to custom shell commands * initial command configurable in config file * non-blocking prompt/choice (new syntax for prompts!) * usage help for every command * bindings help * tons of fixes 0.11 This minor release is mostly bug fixes and some small features. I wanted to release a more stable and usable version before I start hacking on a thread view rewrite. * renamed config section [tag translate] to [tag-translate] * docs: more elaborate API docs, INSTALL and USAGE as markdown in github wiki * more compact header displays in thread view * command-line history (for one session) * editor file encoding is now user configurable * signatures for outgoing mails per account * optional display of message content in search results * config option for strftime formating of timestamps * printing * fix parse multiline headers from edited tempfile * fix reply to unusually formated mails (e.g. no recipient) * fix lots of encoding issues * handle extra wide characters in tag widgets * fixes in ui.prompt * fix storing outgoing mails to sent_box * more liberal header encoding for outgoing mails * use mimetype lib to guess right content-type of attachments alot-0.3.5/README.md000066400000000000000000000046301217172710600137440ustar00rootroot00000000000000Alot is an experimental terminal MUA based on [notmuch mail][notmuch]. It is written in python using the [urwid][urwid] toolkit. Have a look at the [user manual][docs] for installation notes, advanced usage, customization and hacking guides. Do comment on the code or file issues! You can talk to me in `#notmuch@freenode`. Current features include: ------------------------- * modular and command prompt driven interface * multiple accounts for sending mails via sendmail * spawn terminals for asynchronous editing of mails * tab completion and usage help for all commands * contacts completion using customizable lookups commands * user configurable keyboard maps * theming, optionally in 2, 16 or 256 colours * tag specific theming and tag string translation * (python) hooks to react on events and do custom formatting * python shell for introspection * forward/reply/group-reply of emails * printing/piping of mails and threads * notification popups with priorities * database manager that manages a write queue to the notmuch index * configurable status bar * full support for PGP/MIME encryption and signing Soonish to be addressed non-features: ------------------------------------- See [here][features], most notably: * async. calls to mimeparts renderer, parsing of VT colour escape sequences. see #272. Milestone `0.4` * live search results while you're typing (POC in `postponed-livesearch`). Milestone `0.6` * search for message (POC in `postponed-messagesmode`). Milestone `0.6` * search for strings in displayed buffer. MS `0.7` * undo for commands. Milestone `0.7` Basic Usage =========== The arrow keys, `page-up/down`, `j`, `k` and `Space` can be used to move the focus. `Escape` cancels prompts and `Enter` selects. Hit `:` at any time and type in commands to the prompt. The interface shows one buffer at a time, you can use `Tab` and `Shift-Tab` to switch between them, close the current buffer with `d` and list them all with `;`. The buffer type or *mode* (displayed at the bottom left) determines which prompt commands are available. Usage information on any command can be listed by typing `help YOURCOMMAND` to the prompt; The key bindings for the current mode are listed upon pressing `?`. See the [manual][docs] for more usage info. [notmuch]: http://notmuchmail.org/ [urwid]: http://excess.org/urwid/ [docs]: http://alot.rtfd.org [features]: https://github.com/pazz/alot/issues?labels=feature alot-0.3.5/alot/000077500000000000000000000000001217172710600134215ustar00rootroot00000000000000alot-0.3.5/alot/__init__.py000066400000000000000000000004731217172710600155360ustar00rootroot00000000000000__productname__ = 'alot' __version__ = '0.3.5' __copyright__ = "Copyright (C) 2013 Patrick Totzke" __author__ = "Patrick Totzke" __author_email__ = "patricktotzke@gmail.com" __description__ = "Terminal MUA using notmuch mail" __url__ = "https://github.com/pazz/alot" __license__ = "Licensed under the GNU GPL v3+." alot-0.3.5/alot/account.py000066400000000000000000000137121217172710600154330ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import mailbox import logging import os import glob from alot.helper import call_cmd_async from alot.helper import split_commandstring class SendingMailFailed(RuntimeError): pass class StoreMailError(Exception): pass class Account(object): """ Datastructure that represents an email account. It manages this account's settings, can send and store mails to maildirs (drafts/send). .. note:: This is an abstract class that leaves :meth:`send_mail` unspecified. See :class:`SendmailAccount` for a subclass that uses a sendmail command to send out mails. """ address = None """this accounts main email address""" aliases = [] """list of alternative addresses""" realname = None """real name used to format from-headers""" gpg_key = None """gpg fingerprint for this account's private key""" signature = None """signature to append to outgoing mails""" signature_filename = None """filename of signature file in attachment""" signature_as_attachment = None """attach signature file instead of appending its content to body text""" abook = None """addressbook (:class:`addressbooks.AddressBook`) managing this accounts contacts""" def __init__(self, address=None, aliases=None, realname=None, gpg_key=None, signature=None, signature_filename=None, signature_as_attachment=False, sent_box=None, sent_tags=['sent'], draft_box=None, draft_tags=['draft'], abook=None, sign_by_default=False, **rest): self.address = address self.aliases = aliases self.realname = realname self.gpg_key = gpg_key self.signature = signature self.signature_filename = signature_filename self.signature_as_attachment = signature_as_attachment self.sign_by_default = sign_by_default self.sent_box = sent_box self.sent_tags = sent_tags self.draft_box = draft_box self.draft_tags = draft_tags self.abook = abook def get_addresses(self): """return all email addresses connected to this account, in order of their importance""" return [self.address] + self.aliases def store_mail(self, mbx, mail): """ stores given mail in mailbox. If mailbox is maildir, set the S-flag and return path to newly added mail. Oherwise this will return `None`. :param mbx: mailbox to use :type mbx: :class:`mailbox.Mailbox` :param mail: the mail to store :type mail: :class:`email.message.Message` or str :returns: absolute path of mail-file for Maildir or None if mail was successfully stored :rtype: str or None :raises: StoreMailError """ if not isinstance(mbx, mailbox.Mailbox): logging.debug('Not a mailbox') return False mbx.lock() if isinstance(mbx, mailbox.Maildir): logging.debug('Maildir') msg = mailbox.MaildirMessage(mail) msg.set_flags('S') else: logging.debug('no Maildir') msg = mailbox.Message(mail) try: message_id = mbx.add(msg) mbx.flush() mbx.unlock() logging.debug('got mailbox msg id : %s' % message_id) except Exception as e: raise StoreMailError(e) path = None # add new Maildir message to index and add tags if isinstance(mbx, mailbox.Maildir): # this is a dirty hack to get the path to the newly added file # I wish the mailbox module were more helpful... plist = glob.glob1(os.path.join(mbx._path, 'new'), message_id + '*') if plist: path = os.path.join(mbx._path, 'new', plist[0]) logging.debug('path of saved msg: %s' % path) return path def store_sent_mail(self, mail): """ stores mail (:class:`email.message.Message` or str) in send-store if :attr:`sent_box` is set. """ if self.sent_box is not None: return self.store_mail(self.sent_box, mail) def store_draft_mail(self, mail): """ stores mail (:class:`email.message.Message` or str) as draft if :attr:`draft_box` is set. """ if self.draft_box is not None: return self.store_mail(self.draft_box, mail) def send_mail(self, mail): """ sends given mail :param mail: the mail to send :type mail: :class:`email.message.Message` or string :returns: a `Deferred` that errs back with a class:`SendingMailFailed`, containing a reason string if an error occured. """ raise NotImplementedError class SendmailAccount(Account): """:class:`Account` that pipes a message to a `sendmail` shell command for sending""" def __init__(self, cmd, **kwargs): """ :param cmd: sendmail command to use for this account :type cmd: str """ super(SendmailAccount, self).__init__(**kwargs) self.cmd = cmd def send_mail(self, mail): cmdlist = split_commandstring(self.cmd) def cb(out): logging.info('sent mail successfully') logging.info(out) def errb(failure): termobj = failure.value errmsg = '%s failed with code %s:\n%s' % \ (self.cmd, termobj.exitCode, str(failure.value)) logging.error(errmsg) logging.error(failure.getTraceback()) logging.error(failure.value.stderr) raise SendingMailFailed(errmsg) d = call_cmd_async(cmdlist, stdin=mail) d.addCallback(cb) d.addErrback(errb) return d alot-0.3.5/alot/addressbooks.py000066400000000000000000000074121217172710600164620ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import re import os from alot.settings.utils import read_config from helper import call_cmd from alot.helper import split_commandstring class AddressbookError(Exception): pass class AddressBook(object): """can look up email addresses and realnames for contacts. .. note:: This is an abstract class that leaves :meth:`get_contacts` unspecified. See :class:`AbookAddressBook` and :class:`MatchSdtoutAddressbook` for implementations. """ def __init__(self, ignorecase=True): self.reflags = re.IGNORECASE if ignorecase else 0 def get_contacts(self): """list all contacts tuples in this abook as (name, email) tuples""" return [] def lookup(self, query=''): """looks up all contacts where name or address match query""" res = [] query = '.*%s.*' % query for name, email in self.get_contacts(): try: if re.match(query, name, self.reflags) or \ re.match(query, email, self.reflags): res.append((name, email)) except: pass return res class AbookAddressBook(AddressBook): """:class:`AddressBook` that parses abook's config/database files""" def __init__(self, path='~/.abook/addressbook', **kwargs): """ :param path: path to theme file :type path: str """ AddressBook.__init__(self, **kwargs) DEFAULTSPATH = os.path.join(os.path.dirname(__file__), 'defaults') self._spec = os.path.join(DEFAULTSPATH, 'abook_contacts.spec') path = os.path.expanduser(path) self._config = read_config(path, self._spec) del(self._config['format']) def get_contacts(self): c = self._config res = [] for id in c.sections: for email in c[id]['email']: if email: res.append((c[id]['name'], email)) return res class MatchSdtoutAddressbook(AddressBook): """:class:`AddressBook` that parses a shell command's output for lookups""" def __init__(self, command, match=None, **kwargs): """ :param command: lookup command :type command: str :param match: regular expression used to match contacts in `commands` output to stdout. Must define subparts named "email" and "name". Defaults to :regexp:`^(?P[^@]+@[^\t]+)\t+(?P[^\t]+)`. :type match: str """ AddressBook.__init__(self, **kwargs) self.command = command if not match: self.match = '^(?P[^@]+@[^\t]+)\t+(?P[^\t]+)' else: self.match = match def get_contacts(self): return self.lookup('\'\'') def lookup(self, prefix): cmdlist = split_commandstring(self.command) resultstring, errmsg, retval = call_cmd(cmdlist + [prefix]) if retval != 0: msg = 'abook command "%s" returned with ' % self.command msg += 'return code %d' % retval if errmsg: msg += ':\n%s' % errmsg raise AddressbookError(msg) if not resultstring: return [] lines = resultstring.splitlines() res = [] for l in lines: m = re.match(self.match, l, self.reflags) if m: info = m.groupdict() if 'email' and 'name' in info: email = info['email'].strip() name = info['name'] res.append((name, email)) return res alot-0.3.5/alot/buffers.py000066400000000000000000000552171217172710600154410ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import urwid import os from notmuch import NotmuchError import logging from settings import settings import commands from walker import PipeWalker from helper import shorten_author_string from db.errors import NonexistantObjectError from alot.widgets.globals import TagWidget from alot.widgets.globals import HeadersList from alot.widgets.globals import AttachmentWidget from alot.widgets.bufferlist import BufferlineWidget from alot.widgets.search import ThreadlineWidget from alot.widgets.thread import ThreadTree from alot.foreign.urwidtrees import ArrowTree, TreeBox, NestedTree class Buffer(object): """Abstract base class for buffers.""" modename = None # mode identifier for subclasses def __init__(self, ui, widget): self.ui = ui self.body = widget def __str__(self): return '[%s]' % self.modename def render(self, size, focus=False): return self.body.render(size, focus) def selectable(self): return self.body.selectable() def rebuild(self): """tells the buffer to (re)construct its visible content.""" pass def keypress(self, size, key): return self.body.keypress(size, key) def cleanup(self): """called before buffer is closed""" pass def get_info(self): """ return dict of meta infos about this buffer. This can be requested to be displayed in the statusbar. """ return {} class BufferlistBuffer(Buffer): """lists all active buffers""" modename = 'bufferlist' def __init__(self, ui, filtfun=None): self.filtfun = filtfun self.ui = ui self.isinitialized = False self.rebuild() Buffer.__init__(self, ui, self.body) def index_of(self, b): """ returns the index of :class:`Buffer` `b` in the global list of active buffers. """ return self.ui.buffers.index(b) def rebuild(self): if self.isinitialized: focusposition = self.bufferlist.get_focus()[1] else: focusposition = 0 self.isinitialized = True lines = list() displayedbuffers = filter(self.filtfun, self.ui.buffers) for (num, b) in enumerate(displayedbuffers): line = BufferlineWidget(b) if (num % 2) == 0: attr = settings.get_theming_attribute('bufferlist', 'line_even') else: attr = settings.get_theming_attribute('bufferlist', 'line_odd') focus_att = settings.get_theming_attribute('bufferlist', 'line_focus') buf = urwid.AttrMap(line, attr, focus_att) num = urwid.Text('%3d:' % self.index_of(b)) lines.append(urwid.Columns([('fixed', 4, num), buf])) self.bufferlist = urwid.ListBox(urwid.SimpleListWalker(lines)) num_buffers = len(displayedbuffers) if focusposition is not None and num_buffers > 0: self.bufferlist.set_focus(focusposition % num_buffers) self.body = self.bufferlist def get_selected_buffer(self): """returns currently selected :class:`Buffer` element from list""" (linewidget, pos) = self.bufferlist.get_focus() bufferlinewidget = linewidget.get_focus().original_widget return bufferlinewidget.get_buffer() def focus_first(self): self.body.set_focus(0) class EnvelopeBuffer(Buffer): """message composition mode""" modename = 'envelope' def __init__(self, ui, envelope): self.ui = ui self.envelope = envelope self.all_headers = False self.rebuild() Buffer.__init__(self, ui, self.body) def __str__(self): to = self.envelope.get('To', fallback='unset') return '[envelope] to: %s' % (shorten_author_string(to, 400)) def get_info(self): info = {} info['to'] = self.envelope.get('To', fallback='unset') return info def cleanup(self): if self.envelope.tmpfile: os.unlink(self.envelope.tmpfile.name) def rebuild(self): displayed_widgets = [] hidden = settings.get('envelope_headers_blacklist') # build lines lines = [] for (k, vlist) in self.envelope.headers.items(): if (k not in hidden) or self.all_headers: for value in vlist: lines.append((k, value)) # sign/encrypt lines if self.envelope.sign: description = 'Yes' sign_key = self.envelope.sign_key if sign_key is not None and len(sign_key.subkeys) > 0: description += ', with key ' + sign_key.uids[0].uid lines.append(('GPG sign', description)) if self.envelope.encrypt: description = 'Yes' encrypt_keys = self.envelope.encrypt_keys.values() if len(encrypt_keys) == 1: description += ', with key ' elif len(encrypt_keys) > 1: description += ', with keys ' first_key = True for key in encrypt_keys: if key is not None: if first_key: first_key = False else: description += ', ' if len(key.subkeys) > 0: description += key.uids[0].uid lines.append(('GPG encrypt', description)) # add header list widget iff header values exists if lines: key_att = settings.get_theming_attribute('envelope', 'header_key') value_att = settings.get_theming_attribute('envelope', 'header_value') gaps_att = settings.get_theming_attribute('envelope', 'header') self.header_wgt = HeadersList(lines, key_att, value_att, gaps_att) displayed_widgets.append(self.header_wgt) # display attachments lines = [] for a in self.envelope.attachments: lines.append(AttachmentWidget(a, selectable=False)) if lines: self.attachment_wgt = urwid.Pile(lines) displayed_widgets.append(self.attachment_wgt) self.body_wgt = urwid.Text(self.envelope.body) displayed_widgets.append(self.body_wgt) self.body = urwid.ListBox(displayed_widgets) def toggle_all_headers(self): """toggles visibility of all envelope headers""" self.all_headers = not self.all_headers self.rebuild() class SearchBuffer(Buffer): """shows a result list of threads for a query""" modename = 'search' threads = [] def __init__(self, ui, initialquery='', sort_order=None): self.dbman = ui.dbman self.ui = ui self.querystring = initialquery default_order = settings.get('search_threads_sort_order') self.sort_order = sort_order or default_order self.result_count = 0 self.isinitialized = False self.proc = None # process that fills our pipe self.rebuild() Buffer.__init__(self, ui, self.body) def __str__(self): formatstring = '[search] for "%s" (%d message%s)' return formatstring % (self.querystring, self.result_count, 's' * (not (self.result_count == 1))) def get_info(self): info = {} info['querystring'] = self.querystring info['result_count'] = self.result_count info['result_count_positive'] = 's' * (not (self.result_count == 1)) return info def cleanup(self): self.kill_filler_process() def kill_filler_process(self): """ terminates the process that fills this buffers :class:`~alot.walker.PipeWalker`. """ if self.proc: if self.proc.is_alive(): self.proc.terminate() def rebuild(self): self.isinitialized = True self.kill_filler_process() self.result_count = self.dbman.count_messages(self.querystring) try: self.pipe, self.proc = self.dbman.get_threads(self.querystring, self.sort_order) except NotmuchError: self.ui.notify('malformed query string: %s' % self.querystring, 'error') self.listbox = urwid.ListBox([]) self.body = self.listbox return self.threadlist = PipeWalker(self.pipe, ThreadlineWidget, dbman=self.dbman) self.listbox = urwid.ListBox(self.threadlist) self.body = self.listbox def get_selected_threadline(self): """ returns curently focussed :class:`alot.widgets.ThreadlineWidget` from the result list. """ (threadlinewidget, size) = self.threadlist.get_focus() return threadlinewidget def get_selected_thread(self): """returns currently selected :class:`~alot.db.Thread`""" threadlinewidget = self.get_selected_threadline() thread = None if threadlinewidget: thread = threadlinewidget.get_thread() return thread def consume_pipe(self): while not self.threadlist.empty: self.threadlist._get_next_item() def focus_first(self): self.body.set_focus(0) def focus_last(self): self.consume_pipe() num_lines = len(self.threadlist.get_lines()) self.body.set_focus(num_lines - 1) class ThreadBuffer(Buffer): """displays a thread as a tree of messages""" modename = 'thread' def __init__(self, ui, thread): """ :param ui: main UI :type ui: :class:`~alot.ui.UI` :param thread: thread to display :type thread: :class:`~alot.db.Thread` """ self.thread = thread self.message_count = thread.get_total_messages() # two semaphores for auto-removal of unread tag self._auto_unread_dont_touch_mids = set([]) self._auto_unread_writing = False self.rebuild() Buffer.__init__(self, ui, self.body) def __str__(self): return '[thread] %s (%d message%s)' % (self.thread.get_subject(), self.message_count, 's' * (self.message_count > 1)) def get_info(self): info = {} info['subject'] = self.thread.get_subject() info['authors'] = self.thread.get_authors_string() info['tid'] = self.thread.get_thread_id() info['message_count'] = self.message_count return info def get_selected_thread(self): """returns the displayed :class:`~alot.db.Thread`""" return self.thread def rebuild(self): try: self.thread.refresh() except NonexistantObjectError: self.body = urwid.SolidFill() self.message_count = 0 return self._tree = ThreadTree(self.thread) bars_att = settings.get_theming_attribute('thread', 'arrow_bars') heads_att = settings.get_theming_attribute('thread', 'arrow_heads') A = ArrowTree(self._tree, indent=2, childbar_offset=0, arrow_tip_att=heads_att, arrow_att=bars_att, ) self._nested_tree = NestedTree(A, interpret_covered=True) self.body = TreeBox(self._nested_tree) self.message_count = self.thread.get_total_messages() def render(self, size, focus=False): if settings.get('auto_remove_unread'): logging.debug('Tbuffer: auto remove unread tag from msg?') msg = self.get_selected_message() mid = msg.get_message_id() focus_pos = self.body.get_focus()[1] summary_pos = (self.body.get_focus()[1][0], (0,)) cursor_on_non_summary = (focus_pos != summary_pos) if cursor_on_non_summary: if not mid in self._auto_unread_dont_touch_mids: if 'unread' in msg.get_tags(): logging.debug('Tbuffer: removing unread') def clear(): self._auto_unread_writing = False self._auto_unread_dont_touch_mids.add(mid) self._auto_unread_writing = True msg.remove_tags(['unread'], afterwards=clear) fcmd = commands.globals.FlushCommand(silent=True) self.ui.apply_command(fcmd) else: logging.debug('Tbuffer: No, msg not unread') else: logging.debug('Tbuffer: No, mid locked for autorm-unread') else: if not self._auto_unread_writing and \ mid in self._auto_unread_dont_touch_mids: self._auto_unread_dont_touch_mids.remove(mid) logging.debug('Tbuffer: No, cursor on summary') return self.body.render(size, focus) def get_selected_mid(self): """returns Message ID of focussed message""" return self.body.get_focus()[1][0] def get_selected_message_position(self): """returns position of focussed message in the thread tree""" return self._sanitize_position((self.get_selected_mid(),)) def get_selected_messagetree(self): """returns currently focussed :class:`MessageTree`""" return self._nested_tree[self.body.get_focus()[1][:1]] def get_selected_message(self): """returns focussed :class:`~alot.db.message.Message`""" return self.get_selected_messagetree()._message def get_messagetree_positions(self): """ returns a Generator to walk through all positions of :class:`MessageTree` in the :class:`ThreadTree` of this buffer. """ return [(pos,) for pos in self._tree.positions()] def messagetrees(self): """ returns a Generator of all :class:`MessageTree` in the :class:`ThreadTree` of this buffer. """ for pos in self._tree.positions(): yield self._tree[pos] def refresh(self): """refresh and flushe caches of Thread tree""" self.body.refresh() # needed for ui.get_deep_focus.. def get_focus(self): return self.body.get_focus() def set_focus(self, pos): logging.debug('setting focus to %s ' % str(pos)) self.body.set_focus(pos) def focus_first(self): """set focus to first message of thread""" self.body.set_focus(self._nested_tree.root) def focus_last(self): self.body.set_focus(next(self._nested_tree.positions(reverse=True))) def _sanitize_position(self, pos): return self._nested_tree._sanitize_position(pos, self._nested_tree._tree) def focus_selected_message(self): """focus the summary line of currently focussed message""" # move focus to summary (root of current MessageTree) self.set_focus(self.get_selected_message_position()) def focus_parent(self): """move focus to parent of currently focussed message""" mid = self.get_selected_mid() newpos = self._tree.parent_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) def focus_first_reply(self): """move focus to first reply to currently focussed message""" mid = self.get_selected_mid() newpos = self._tree.first_child_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) def focus_last_reply(self): """move focus to last reply to currently focussed message""" mid = self.get_selected_mid() newpos = self._tree.last_child_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) def focus_next_sibling(self): """focus next sibling of currently focussed message in thread tree""" mid = self.get_selected_mid() newpos = self._tree.next_sibling_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) def focus_prev_sibling(self): """ focus previous sibling of currently focussed message in thread tree """ mid = self.get_selected_mid() localroot = self._sanitize_position((mid,)) if localroot == self.get_focus()[1]: newpos = self._tree.prev_sibling_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) else: newpos = localroot if newpos is not None: self.body.set_focus(newpos) def focus_next(self): """focus next message in depth first order""" mid = self.get_selected_mid() newpos = self._tree.next_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) def focus_prev(self): """focus previous message in depth first order""" mid = self.get_selected_mid() localroot = self._sanitize_position((mid,)) if localroot == self.get_focus()[1]: newpos = self._tree.prev_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) else: newpos = localroot if newpos is not None: self.body.set_focus(newpos) def focus_property(self, prop, direction): """does a walk in the given direction and focuses the first message tree that matches the given property""" newpos = self.get_selected_mid() newpos = direction(newpos) while newpos is not None: MT = self._tree[newpos] if prop(MT): newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) break newpos = direction(newpos) def focus_next_matching(self, querystring): """focus next matching message in depth first order""" self.focus_property(lambda x: x._message.matches(querystring), self._tree.next_position) def focus_prev_matching(self, querystring): """focus previous matching message in depth first order""" self.focus_property(lambda x: x._message.matches(querystring), self._tree.prev_position) def focus_next_unfolded(self): """focus next unfolded message in depth first order""" self.focus_property(lambda x: not x.is_collapsed(x.root), self._tree.next_position) def focus_prev_unfolded(self): """focus previous unfolded message in depth first order""" self.focus_property(lambda x: not x.is_collapsed(x.root), self._tree.prev_position) def expand(self, msgpos): """expand message at given position""" MT = self._tree[msgpos] MT.expand(MT.root) def messagetree_at_position(self, pos): """get :class:`MessageTree` for given position""" return self._tree[pos[0]] def expand_all(self): """expand all messages in thread""" for MT in self.messagetrees(): MT.expand(MT.root) def collapse(self, msgpos): """collapse message at given position""" MT = self._tree[msgpos] MT.collapse(MT.root) self.focus_selected_message() def collapse_all(self): """collapse all messages in thread""" for MT in self.messagetrees(): MT.collapse(MT.root) self.focus_selected_message() def unfold_matching(self, querystring, focus_first=True): """ expand all messages that match a given querystring. :param querystring: query to match :type querystring: str :param focus_first: set the focus to the first matching message :type focus_first: bool """ first = None for MT in self.messagetrees(): msg = MT._message if msg.matches(querystring): MT.expand(MT.root) if first is None: first = (self._tree.position_of_messagetree(MT), MT.root) self.body.set_focus(first) else: MT.collapse(MT.root) self.body.refresh() class TagListBuffer(Buffer): """lists all tagstrings present in the notmuch database""" modename = 'taglist' def __init__(self, ui, alltags=[], filtfun=None): self.filtfun = filtfun self.ui = ui self.tags = alltags self.isinitialized = False self.rebuild() Buffer.__init__(self, ui, self.body) def rebuild(self): if self.isinitialized: focusposition = self.taglist.get_focus()[1] else: focusposition = 0 self.isinitialized = True lines = list() displayedtags = sorted(filter(self.filtfun, self.tags), key=unicode.lower) for (num, b) in enumerate(displayedtags): if (num % 2) == 0: attr = settings.get_theming_attribute('taglist', 'line_even') else: attr = settings.get_theming_attribute('taglist', 'line_odd') focus_att = settings.get_theming_attribute('taglist', 'line_focus') tw = TagWidget(b, attr, focus_att) rows = [('fixed', tw.width(), tw)] if tw.hidden: rows.append(urwid.Text(b + ' [hidden]')) elif tw.translated is not b: rows.append(urwid.Text('(%s)' % b)) line = urwid.Columns(rows, dividechars=1) line = urwid.AttrMap(line, attr, focus_att) lines.append(line) self.taglist = urwid.ListBox(urwid.SimpleListWalker(lines)) self.body = self.taglist self.taglist.set_focus(focusposition % len(displayedtags)) def focus_first(self): self.body.set_focus(0) def focus_last(self): allpos = self.taglist.body.positions(reverse=True) if allpos: lastpos = allpos[0] self.body.set_focus(lastpos) def get_selected_tag(self): """returns selected tagstring""" (cols, pos) = self.taglist.get_focus() tagwidget = cols.original_widget.get_focus() return tagwidget.get_tag() alot-0.3.5/alot/commands/000077500000000000000000000000001217172710600152225ustar00rootroot00000000000000alot-0.3.5/alot/commands/__init__.py000066400000000000000000000142041217172710600173340ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os import re import glob import logging import argparse from alot.settings import settings import alot.helper from alot.helper import split_commandstring class Command(object): """base class for commands""" repeatable = False def __init__(self): self.prehook = None self.posthook = None self.undoable = False self.help = self.__doc__ def apply(self, caller): """code that gets executed when this command is applied""" pass COMMANDS = { 'search': {}, 'envelope': {}, 'bufferlist': {}, 'taglist': {}, 'thread': {}, 'global': {}, } def lookup_command(cmdname, mode): """ returns commandclass, argparser and forced parameters used to construct a command for `cmdname` when called in `mode`. :param cmdname: name of the command to look up :type cmdname: str :param mode: mode identifier :type mode: str :rtype: (:class:`Command`, :class:`~argparse.ArgumentParser`, dict(str->dict)) >>> (cmd, parser, kwargs) = lookup_command('save', 'thread') >>> cmd """ if cmdname in COMMANDS[mode]: return COMMANDS[mode][cmdname] elif cmdname in COMMANDS['global']: return COMMANDS['global'][cmdname] else: return None, None, None def lookup_parser(cmdname, mode): """ returns the :class:`CommandArgumentParser` used to construct a command for `cmdname` when called in `mode`. """ return lookup_command(cmdname, mode)[1] class CommandParseError(Exception): """could not parse commandline string""" pass class CommandArgumentParser(argparse.ArgumentParser): """ :class:`~argparse.ArgumentParser` that raises :class:`CommandParseError` instead of printing to `sys.stderr`""" def exit(self, message): raise CommandParseError(message) def error(self, message): raise CommandParseError(message) class registerCommand(object): """ Decorator used to register a :class:`Command` as handler for command `name` in `mode` so that it can be looked up later using :func:`lookup_command`. Consider this example that shows how a :class:`Command` class definition is decorated to register it as handler for 'save' in mode 'thread' and add boolean and string arguments:: @registerCommand('thread', 'save', arguments=[ (['--all'], {'action': 'store_true', 'help':'save all'}), (['path'], {'nargs':'?', 'help':'path to save to'})], help='save attachment(s)') class SaveAttachmentCommand(Command): pass """ def __init__(self, mode, name, help=None, usage=None, forced={}, arguments=[]): """ :param mode: mode identifier :type mode: str :param name: command name to register as :type name: str :param help: help string summarizing what this command does :type help: str :param usage: overides the auto generated usage string :type usage: str :param forced: keyword parameter used for commands constructor :type forced: dict (str->str) :param arguments: list of arguments given as pairs (args, kwargs) accepted by :meth:`argparse.ArgumentParser.add_argument`. :type arguments: list of (list of str, dict (str->str) """ self.mode = mode self.name = name self.help = help self.usage = usage self.forced = forced self.arguments = arguments def __call__(self, klass): helpstring = self.help or klass.__doc__ argparser = CommandArgumentParser(description=helpstring, usage=self.usage, prog=self.name, add_help=False) for args, kwargs in self.arguments: argparser.add_argument(*args, **kwargs) COMMANDS[self.mode][self.name] = (klass, argparser, self.forced) return klass def commandfactory(cmdline, mode='global'): """ parses `cmdline` and constructs a :class:`Command`. :param cmdline: command line to interpret :type cmdline: str :param mode: mode identifier :type mode: str >>> cmd = alot.commands.commandfactory('save --all /foo', mode='thread') >>> cmd >> cmd.all True >>> cmd.path u'/foo' """ # split commandname and parameters if not cmdline: return None logging.debug('mode:%s got commandline "%s"' % (mode, cmdline)) # allow to shellescape without a space after '!' if cmdline.startswith('!'): cmdline = 'shellescape \'%s\'' % cmdline[1:] cmdline = re.sub(r'"(.*)"', r'"\\"\1\\""', cmdline) try: args = split_commandstring(cmdline) except ValueError as e: raise CommandParseError(e.message) args = map(lambda x: alot.helper.string_decode(x, 'utf-8'), args) logging.debug('ARGS: %s' % args) cmdname = args[0] args = args[1:] # unfold aliases # TODO: read from settingsmanager # get class, argparser and forced parameter (cmdclass, parser, forcedparms) = lookup_command(cmdname, mode) if cmdclass is None: msg = 'unknown command: %s' % cmdname logging.debug(msg) raise CommandParseError(msg) parms = vars(parser.parse_args(args)) parms.update(forcedparms) logging.debug('cmd parms %s' % parms) # create Command cmd = cmdclass(**parms) # set pre and post command hooks get_hook = settings.get_hook cmd.prehook = get_hook('pre_%s_%s' % (mode, cmdname)) or \ get_hook('pre_global_%s' % cmdname) cmd.posthook = get_hook('post_%s_%s' % (mode, cmdname)) or \ get_hook('post_global_%s' % cmdname) return cmd pyfiles = glob.glob1(os.path.dirname(__file__), '*.py') __all__ = list(filename[:-3] for filename in pyfiles) alot-0.3.5/alot/commands/bufferlist.py000066400000000000000000000015541217172710600177460ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file from alot.commands import Command, registerCommand from . import globals MODE = 'bufferlist' @registerCommand(MODE, 'open') class BufferFocusCommand(Command): """focus selected buffer""" def apply(self, ui): selected = ui.current_buffer.get_selected_buffer() ui.buffer_focus(selected) @registerCommand(MODE, 'close') class BufferCloseCommand(Command): """close focussed buffer""" def apply(self, ui): bufferlist = ui.current_buffer selected = bufferlist.get_selected_buffer() ui.apply_command(globals.BufferCloseCommand(buffer=selected)) if bufferlist is not selected: bufferlist.rebuild() ui.update() alot-0.3.5/alot/commands/envelope.py000066400000000000000000000521221217172710600174130ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import argparse import os import re import glob import logging import email import tempfile from twisted.internet.defer import inlineCallbacks import datetime from alot.account import SendingMailFailed, StoreMailError from alot.errors import GPGProblem, GPGCode from alot import buffers from alot import commands from alot import crypto from alot.commands import Command, registerCommand from alot.commands import globals from alot.helper import string_decode from alot.settings import settings from alot.utils.booleanaction import BooleanAction from alot.db.errors import DatabaseError MODE = 'envelope' @registerCommand(MODE, 'attach', arguments=[ (['path'], {'help': 'file(s) to attach (accepts wildcads)'})]) class AttachCommand(Command): """attach files to the mail""" repeatable = True def __init__(self, path=None, **kwargs): """ :param path: files to attach (globable string) :type path: str """ Command.__init__(self, **kwargs) self.path = path def apply(self, ui): envelope = ui.current_buffer.envelope if self.path: # TODO: not possible, otherwise argparse error before files = filter(os.path.isfile, glob.glob(os.path.expanduser(self.path))) if not files: ui.notify('no matches, abort') return else: ui.notify('no files specified, abort') return logging.info("attaching: %s" % files) for path in files: envelope.attach(path) ui.current_buffer.rebuild() @registerCommand(MODE, 'unattach', arguments=[ (['hint'], {'nargs': '?', 'help': 'which attached file to remove'}), ]) class UnattachCommand(Command): """remove attachments from current envelope""" repeatable = True def __init__(self, hint=None, **kwargs): """ :param hint: which attached file to remove :type hint: str """ Command.__init__(self, **kwargs) self.hint = hint def apply(self, ui): envelope = ui.current_buffer.envelope if self.hint is not None: for a in envelope.attachments: if self.hint in a.get_filename(): envelope.attachments.remove(a) else: envelope.attachments = [] ui.current_buffer.rebuild() @registerCommand(MODE, 'refine', arguments=[ (['key'], {'help': 'header to refine'})]) class RefineCommand(Command): """prompt to change the value of a header""" def __init__(self, key='', **kwargs): """ :param key: key of the header to change :type key: str """ Command.__init__(self, **kwargs) self.key = key def apply(self, ui): value = ui.current_buffer.envelope.get(self.key, '') cmdstring = 'set %s %s' % (self.key, value) ui.apply_command(globals.PromptCommand(cmdstring)) @registerCommand(MODE, 'save') class SaveCommand(Command): """save draft""" def apply(self, ui): envelope = ui.current_buffer.envelope # determine account to use sname, saddr = email.Utils.parseaddr(envelope.get('From')) account = settings.get_account_by_address(saddr) if account is None: if not settings.get_accounts(): ui.notify('no accounts set.', priority='error') return else: account = settings.get_accounts()[0] if account.draft_box is None: ui.notify('abort: account <%s> has no draft_box set.' % saddr, priority='error') return mail = envelope.construct_mail() # store mail locally # add Date header mail['Date'] = email.Utils.formatdate(localtime=True) path = account.store_draft_mail(crypto.email_as_string(mail)) msg = 'draft saved successfully' # add mail to index if maildir path available if path is not None: ui.notify(msg + ' to %s' % path) logging.debug('adding new mail to index') try: ui.dbman.add_message(path, account.draft_tags) ui.apply_command(globals.FlushCommand()) ui.apply_command(commands.globals.BufferCloseCommand()) except DatabaseError as e: logging.error(e.message) ui.notify('could not index message:\n%s' % e.message, priority='error', block=True) else: ui.apply_command(commands.globals.BufferCloseCommand()) @registerCommand(MODE, 'send') class SendCommand(Command): """send mail""" def __init__(self, mail=None, envelope=None, **kwargs): """ :param mail: email to send :type email: email.message.Message :param envelope: envelope to use to construct the outgoing mail. This will be ignored in case the mail parameter is set. :type envelope: alot.db.envelope.envelope """ Command.__init__(self, **kwargs) self.mail = mail self.envelope = envelope self.envelope_buffer = None @inlineCallbacks def apply(self, ui): if self.mail is None: if self.envelope is None: # needed to close later self.envelope_buffer = ui.current_buffer self.envelope = self.envelope_buffer.envelope # This is to warn the user before re-sending # an already sent message in case the envelope buffer # was not closed because it was the last remaining buffer. if self.envelope.sent_time: mod = self.envelope.modified_since_sent when = self.envelope.sent_time warning = 'A modified version of ' * mod warning += 'this message has been sent at %s.' % when warning += ' Do you want to resend?' if (yield ui.choice(warning, cancel='no', msg_position='left')) == 'no': return # don't do anything if another SendCommand is in the middle of # sending the message and we were triggered accidentally if self.envelope.sending: msg = 'sending this message already!' logging.debug(msg) return clearme = ui.notify(u'constructing mail (GPG, attachments)\u2026', timeout=-1) try: self.mail = self.envelope.construct_mail() self.mail['Date'] = email.Utils.formatdate(localtime=True) self.mail = crypto.email_as_string(self.mail) except GPGProblem, e: ui.clear_notify([clearme]) ui.notify(e.message, priority='error') return ui.clear_notify([clearme]) # determine account to use for sending msg = self.mail if not isinstance(msg, email.message.Message): msg = email.message_from_string(self.mail) sname, saddr = email.Utils.parseaddr(msg.get('From', '')) account = settings.get_account_by_address(saddr) if account is None: if not settings.get_accounts(): ui.notify('no accounts set', priority='error') return else: account = settings.get_accounts()[0] # make sure self.mail is a string logging.debug(self.mail.__class__) if isinstance(self.mail, email.message.Message): self.mail = str(self.mail) # define callback def afterwards(returnvalue): initial_tags = [] if self.envelope is not None: self.envelope.sending = False self.envelope.sent_time = datetime.datetime.now() initial_tags = self.envelope.tags logging.debug('mail sent successfully') ui.clear_notify([clearme]) if self.envelope_buffer is not None: cmd = commands.globals.BufferCloseCommand(self.envelope_buffer) ui.apply_command(cmd) ui.notify('mail sent successfully') # store mail locally # This can raise StoreMailError path = account.store_sent_mail(self.mail) # add mail to index if maildir path available if path is not None: logging.debug('adding new mail to index') ui.dbman.add_message(path, account.sent_tags + initial_tags) ui.apply_command(globals.FlushCommand()) # define errback def send_errb(failure): if self.envelope is not None: self.envelope.sending = False ui.clear_notify([clearme]) failure.trap(SendingMailFailed) logging.error(failure.getTraceback()) errmsg = 'failed to send: %s' % failure.value ui.notify(errmsg, priority='error', block=True) def store_errb(failure): failure.trap(StoreMailError) logging.error(failure.getTraceback()) errmsg = 'could not store mail: %s' % failure.value ui.notify(errmsg, priority='error', block=True) # send out clearme = ui.notify('sending..', timeout=-1) if self.envelope is not None: self.envelope.sending = True d = account.send_mail(self.mail) d.addCallback(afterwards) d.addErrback(send_errb) d.addErrback(store_errb) @registerCommand(MODE, 'edit', arguments=[ (['--spawn'], {'action': BooleanAction, 'default': None, 'help': 'spawn editor in new terminal'}), (['--refocus'], {'action': BooleanAction, 'default': True, 'help': 'refocus envelope after editing'})]) class EditCommand(Command): """edit mail""" def __init__(self, envelope=None, spawn=None, refocus=True, **kwargs): """ :param envelope: email to edit :type envelope: :class:`~alot.db.envelope.Envelope` :param spawn: force spawning of editor in a new terminal :type spawn: bool :param refocus: m """ self.envelope = envelope self.openNew = (envelope is not None) self.force_spawn = spawn self.refocus = refocus self.edit_only_body = False Command.__init__(self, **kwargs) def apply(self, ui): ebuffer = ui.current_buffer if not self.envelope: self.envelope = ui.current_buffer.envelope # determine editable headers edit_headers = set(settings.get('edit_headers_whitelist')) if '*' in edit_headers: edit_headers = set(self.envelope.headers.keys()) blacklist = set(settings.get('edit_headers_blacklist')) if '*' in blacklist: blacklist = set(self.envelope.headers.keys()) edit_headers = edit_headers - blacklist logging.info('editable headers: %s' % edit_headers) def openEnvelopeFromTmpfile(): # This parses the input from the tempfile. # we do this ourselves here because we want to be able to # just type utf-8 encoded stuff into the tempfile and let alot # worry about encodings. # get input # tempfile will be removed on buffer cleanup f = open(self.envelope.tmpfile.name) enc = settings.get('editor_writes_encoding') template = string_decode(f.read(), enc) f.close() # call post-edit translate hook translate = settings.get_hook('post_edit_translate') if translate: template = translate(template, ui=ui, dbm=ui.dbman) self.envelope.parse_template(template, only_body=self.edit_only_body) if self.openNew: ui.buffer_open(buffers.EnvelopeBuffer(ui, self.envelope)) else: ebuffer.envelope = self.envelope ebuffer.rebuild() # decode header headertext = u'' for key in edit_headers: vlist = self.envelope.get_all(key) if not vlist: # ensure editable headers are present in template vlist = [''] else: # remove to be edited lines from envelope del self.envelope[key] for value in vlist: # newlines (with surrounding spaces) by spaces in values value = value.strip() value = re.sub('[ \t\r\f\v]*\n[ \t\r\f\v]*', ' ', value) headertext += '%s: %s\n' % (key, value) # determine editable content bodytext = self.envelope.body if headertext: content = '%s\n%s' % (headertext, bodytext) self.edit_only_body = False else: content = bodytext self.edit_only_body = True # call pre-edit translate hook translate = settings.get_hook('pre_edit_translate') if translate: content = translate(content, ui=ui, dbm=ui.dbman) # write stuff to tempfile old_tmpfile = None if self.envelope.tmpfile: old_tmpfile = self.envelope.tmpfile self.envelope.tmpfile = tempfile.NamedTemporaryFile(delete=False, prefix='alot.') self.envelope.tmpfile.write(content.encode('utf-8')) self.envelope.tmpfile.flush() self.envelope.tmpfile.close() if old_tmpfile: os.unlink(old_tmpfile.name) cmd = globals.EditCommand(self.envelope.tmpfile.name, on_success=openEnvelopeFromTmpfile, spawn=self.force_spawn, thread=self.force_spawn, refocus=self.refocus) ui.apply_command(cmd) @registerCommand(MODE, 'set', arguments=[ (['--append'], {'action': 'store_true', 'help': 'keep previous values'}), (['key'], {'help': 'header to refine'}), (['value'], {'nargs': '+', 'help': 'value'})]) class SetCommand(Command): """set header value""" def __init__(self, key, value, append=False, **kwargs): """ :param key: key of the header to change :type key: str :param value: new value :type value: str """ self.key = key self.value = ' '.join(value) self.reset = not append Command.__init__(self, **kwargs) def apply(self, ui): envelope = ui.current_buffer.envelope if self.reset: if self.key in envelope: del(envelope[self.key]) envelope.add(self.key, self.value) ui.current_buffer.rebuild() @registerCommand(MODE, 'unset', arguments=[ (['key'], {'help': 'header to refine'})]) class UnsetCommand(Command): """remove header field""" def __init__(self, key, **kwargs): """ :param key: key of the header to remove :type key: str """ self.key = key Command.__init__(self, **kwargs) def apply(self, ui): del(ui.current_buffer.envelope[self.key]) ui.current_buffer.rebuild() @registerCommand(MODE, 'toggleheaders') class ToggleHeaderCommand(Command): """toggle display of all headers""" repeatable = True def apply(self, ui): ui.current_buffer.toggle_all_headers() @registerCommand(MODE, 'sign', forced={'action': 'sign'}, arguments=[ (['keyid'], {'nargs': argparse.REMAINDER, 'help': 'which key id to use'})], help='mark mail to be signed before sending') @registerCommand(MODE, 'unsign', forced={'action': 'unsign'}, help='mark mail not to be signed before sending') @registerCommand(MODE, 'togglesign', forced={'action': 'toggle'}, arguments=[ (['keyid'], {'nargs': argparse.REMAINDER, 'help': 'which key id to use'})], help='toggle sign status') class SignCommand(Command): """toggle signing this email""" repeatable = True def __init__(self, action=None, keyid=None, **kwargs): """ :param action: whether to sign/unsign/toggle :type action: str :param keyid: which key id to use :type keyid: str """ self.action = action self.keyid = keyid Command.__init__(self, **kwargs) def apply(self, ui): sign = None key = None envelope = ui.current_buffer.envelope # sign status if self.action == 'sign': sign = True elif self.action == 'unsign': sign = False elif self.action == 'toggle': sign = not envelope.sign envelope.sign = sign # try to find key if hint given as parameter if sign: if len(self.keyid) > 0: keyid = str(' '.join(self.keyid)) try: key = crypto.get_key(keyid, validate=True, sign=True) except GPGProblem, e: envelope.sign = False ui.notify(e.message, priority='error') return envelope.sign_key = key # reload buffer ui.current_buffer.rebuild() @registerCommand(MODE, 'encrypt', forced={'action': 'encrypt'}, arguments=[ (['keyids'], {'nargs': argparse.REMAINDER, 'help': 'keyid of the key to encrypt with'})], help='request encryption of message before sendout') @registerCommand(MODE, 'unencrypt', forced={'action': 'unencrypt'}, help='remove request to encrypt message before sending') @registerCommand(MODE, 'toggleencrypt', forced={'action': 'toggleencrypt'}, arguments=[ (['keyids'], {'nargs': argparse.REMAINDER, 'help': 'keyid of the key to encrypt with'})], help='toggle if message should be encrypted before sendout') @registerCommand(MODE, 'rmencrypt', forced={'action': 'rmencrypt'}, arguments=[ (['keyids'], {'nargs': argparse.REMAINDER, 'help': 'keyid of the key to encrypt with'})], help='do not encrypt to given recipient key') class EncryptCommand(Command): def __init__(self, action=None, keyids=None, **kwargs): """ :param action: wether to encrypt/unencrypt/toggleencrypt :type action: str :param keyid: the id of the key to encrypt :type keyid: str """ self.encrypt_keys = keyids self.action = action Command.__init__(self, **kwargs) @inlineCallbacks def apply(self, ui): envelope = ui.current_buffer.envelope if self.action == 'rmencrypt': try: for keyid in self.encrypt_keys: tmp_key = crypto.get_key(keyid) del envelope.encrypt_keys[crypto.hash_key(tmp_key)] except GPGProblem as e: ui.notify(e.message, priority='error') if not envelope.encrypt_keys: envelope.encrypt = False ui.current_buffer.rebuild() return elif self.action == 'encrypt': encrypt = True elif self.action == 'unencrypt': encrypt = False elif self.action == 'toggleencrypt': encrypt = not envelope.encrypt envelope.encrypt = encrypt if encrypt: if not self.encrypt_keys: for recipient in envelope.headers['To'][0].split(','): if not recipient: continue match = re.search("<(.*@.*)>", recipient) if match: recipient = match.group(0) self.encrypt_keys.append(recipient) logging.debug("encryption keys: " + str(self.encrypt_keys)) for keyid in self.encrypt_keys: try: key = crypto.get_key(keyid, validate=True, encrypt=True) except GPGProblem as e: if e.code == GPGCode.AMBIGUOUS_NAME: possible_keys = crypto.list_keys(hint=keyid) tmp_choices = [k.uids[0].uid for k in possible_keys] choices = {str(len(tmp_choices) - x): tmp_choices[x] for x in range(0, len(tmp_choices))} keyid = yield ui.choice("ambiguous keyid! Which" + "key do you want to use?", choices, cancel=None) if keyid: self.encrypt_keys.append(keyid) continue else: ui.notify(e.message, priority='error') continue envelope.encrypt_keys[crypto.hash_key(key)] = key if not envelope.encrypt_keys: envelope.encrypt = False # reload buffer ui.current_buffer.rebuild() alot-0.3.5/alot/commands/globals.py000066400000000000000000001001151217172710600172150ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os import code from twisted.internet import threads import subprocess import email import urwid from twisted.internet.defer import inlineCallbacks import logging import argparse import glob from StringIO import StringIO from alot.commands import Command, registerCommand from alot.completion import CommandLineCompleter from alot.commands import CommandParseError from alot.commands import commandfactory from alot import buffers from alot.widgets.utils import DialogBox from alot import helper from alot.db.errors import DatabaseLockedError from alot.completion import ContactsCompleter from alot.completion import AccountCompleter from alot.completion import TagsCompleter from alot.db.envelope import Envelope from alot import commands from alot.settings import settings from alot.helper import split_commandstring, split_commandline from alot.utils.booleanaction import BooleanAction MODE = 'global' @registerCommand(MODE, 'exit') class ExitCommand(Command): """shut down cleanly""" @inlineCallbacks def apply(self, ui): msg = 'index not fully synced. ' if ui.db_was_locked else '' if settings.get('bug_on_exit') or ui.db_was_locked: msg += 'really quit?' if (yield ui.choice(msg, select='yes', cancel='no', msg_position='left')) == 'no': return for b in ui.buffers: b.cleanup() ui.exit() @registerCommand(MODE, 'search', usage='search query', arguments=[ (['--sort'], {'help': 'sort order', 'choices': [ 'oldest_first', 'newest_first', 'message_id', 'unsorted']}), (['query'], {'nargs': argparse.REMAINDER, 'help': 'search string'})]) class SearchCommand(Command): """open a new search buffer""" repeatable = True def __init__(self, query, sort=None, **kwargs): """ :param query: notmuch querystring :type query: str :param sort: how to order results. Must be one of 'oldest_first', 'newest_first', 'message_id' or 'unsorted'. :type sort: str """ self.query = ' '.join(query) self.order = sort Command.__init__(self, **kwargs) def apply(self, ui): if self.query: open_searches = ui.get_buffers_of_type(buffers.SearchBuffer) to_be_focused = None for sb in open_searches: if sb.querystring == self.query: to_be_focused = sb if to_be_focused: if ui.current_buffer != to_be_focused: ui.buffer_focus(to_be_focused) else: # refresh an already displayed search ui.current_buffer.rebuild() ui.update() else: ui.buffer_open(buffers.SearchBuffer(ui, self.query, sort_order=self.order)) else: ui.notify('empty query string') @registerCommand(MODE, 'prompt', arguments=[ (['startwith'], {'nargs': '?', 'default': '', 'help': 'initial content'})]) class PromptCommand(Command): """prompts for commandline and interprets it upon select""" def __init__(self, startwith='', **kwargs): """ :param startwith: initial content of the prompt widget :type startwith: str """ self.startwith = startwith Command.__init__(self, **kwargs) @inlineCallbacks def apply(self, ui): logging.info('open command shell') mode = ui.mode or 'global' cmpl = CommandLineCompleter(ui.dbman, mode, ui.current_buffer) cmdline = yield ui.prompt('', text=self.startwith, completer=cmpl, history=ui.commandprompthistory, ) logging.debug('CMDLINE: %s' % cmdline) # interpret and apply commandline if cmdline: # save into prompt history ui.commandprompthistory.append(cmdline) ui.apply_commandline(cmdline) @registerCommand(MODE, 'refresh') class RefreshCommand(Command): """refresh the current buffer""" repeatable = True def apply(self, ui): ui.current_buffer.rebuild() ui.update() @registerCommand(MODE, 'shellescape', arguments=[ (['--spawn'], {'action': BooleanAction, 'default': None, 'help': 'run in terminal window'}), (['--thread'], {'action': BooleanAction, 'default': None, 'help': 'run in separate thread'}), (['--refocus'], {'action': BooleanAction, 'help': 'refocus current buffer \ after command has finished'}), (['cmd'], {'help': 'command line to execute'})], forced={'shell': True}, ) class ExternalCommand(Command): """run external command""" repeatable = True def __init__(self, cmd, stdin=None, shell=False, spawn=False, refocus=True, thread=False, on_success=None, **kwargs): """ :param cmd: the command to call :type cmd: list or str :param stdin: input to pipe to the process :type stdin: file or str :param spawn: run command in a new terminal :type spawn: bool :param shell: let shell interpret command string :type shell: bool :param thread: run asynchronously, don't block alot :type thread: bool :param refocus: refocus calling buffer after cmd termination :type refocus: bool :param on_success: code to execute after command successfully exited :type on_success: callable """ logging.debug({'spawn': spawn}) # make sure cmd is a list of str if isinstance(cmd, unicode): # convert cmdstring to list: in case shell==True, # Popen passes only the first item in the list to $SHELL cmd = [cmd] if shell else split_commandstring(cmd) # determine complete command list to pass touchhook = settings.get_hook('touch_external_cmdlist') # filter cmd, shell and thread through hook if defined if touchhook is not None: logging.debug('calling hook: touch_external_cmdlist') res = touchhook(cmd, shell=shell, spawn=spawn, thread=thread) logging.debug('got: %s' % res) cmd, shell, self.in_thread = res # otherwise if spawn requested and X11 is running elif spawn: if 'DISPLAY' in os.environ: term_cmd = settings.get('terminal_cmd', '') logging.info('spawn in terminal: %s' % term_cmd) termcmdlist = split_commandstring(term_cmd) cmd = termcmdlist + cmd else: thread = False self.cmdlist = cmd self.stdin = stdin self.shell = shell self.refocus = refocus self.in_thread = thread self.on_success = on_success Command.__init__(self, **kwargs) def apply(self, ui): logging.debug('cmdlist: %s' % self.cmdlist) callerbuffer = ui.current_buffer # set standard input for subcommand stdin = None if self.stdin is not None: # wrap strings in StrinIO so that they behaves like a file if isinstance(self.stdin, unicode): stdin = StringIO(self.stdin) else: stdin = self.stdin def afterwards(data): if data == 'success': if callable(self.on_success): self.on_success() else: ui.notify(data, priority='error') if self.refocus and callerbuffer in ui.buffers: logging.info('refocussing') ui.buffer_focus(callerbuffer) logging.info('calling external command: %s' % self.cmdlist) def thread_code(*args): try: if stdin is None: proc = subprocess.Popen(self.cmdlist, shell=self.shell, stderr=subprocess.PIPE) ret = proc.wait() err = proc.stderr.read() else: proc = subprocess.Popen(self.cmdlist, shell=self.shell, stdin=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate(stdin.read()) ret = proc.wait() if ret == 0: return 'success' else: return err.strip() except OSError as e: return str(e) if self.in_thread: d = threads.deferToThread(thread_code) d.addCallback(afterwards) else: ui.mainloop.screen.stop() ret = thread_code() ui.mainloop.screen.start() # make sure urwid renders its canvas at the correct size ui.mainloop.screen_size = None ui.mainloop.draw_screen() afterwards(ret) #@registerCommand(MODE, 'edit', arguments=[ # (['--nospawn'], {'action': 'store_true', 'help':'spawn '}), #todo # (['path'], {'help':'file to edit'})] #] #) class EditCommand(ExternalCommand): """edit a file""" def __init__(self, path, spawn=None, thread=None, **kwargs): """ :param path: path to the file to be edited :type path: str :param spawn: force running edtor in a new terminal :type spawn: bool :param thread: run asynchronously, don't block alot :type thread: bool """ self.spawn = spawn if spawn is None: self.spawn = settings.get('editor_spawn') self.thread = thread if thread is None: self.thread = settings.get('editor_in_thread') editor_cmdstring = None if os.path.isfile('/usr/bin/editor'): editor_cmdstring = '/usr/bin/editor' editor_cmdstring = os.environ.get('EDITOR', editor_cmdstring) editor_cmdstring = settings.get('editor_cmd') or editor_cmdstring logging.debug('using editor_cmd: %s' % editor_cmdstring) self.cmdlist = None if '%s' in editor_cmdstring: cmdstring = editor_cmdstring.replace('%s', helper.shell_quote(path)) self.cmdlist = split_commandstring(cmdstring) else: self.cmdlist = split_commandstring(editor_cmdstring) + [path] logging.debug({'spawn: ': self.spawn, 'in_thread': self.thread}) ExternalCommand.__init__(self, self.cmdlist, spawn=self.spawn, thread=self.thread, **kwargs) def apply(self, ui): if self.cmdlist is None: ui.notify('no editor set', priority='error') else: return ExternalCommand.apply(self, ui) @registerCommand(MODE, 'pyshell') class PythonShellCommand(Command): """open an interactive python shell for introspection""" repeatable = True def apply(self, ui): ui.mainloop.screen.stop() code.interact(local=locals()) ui.mainloop.screen.start() @registerCommand(MODE, 'repeat') class RepeatCommand(Command): """Repeats the command executed last time""" def __init__(self, **kwargs): Command.__init__(self, **kwargs) def apply(self, ui): if ui.last_commandline is not None: ui.apply_commandline(ui.last_commandline) else: ui.notify('no last command') @registerCommand(MODE, 'call', arguments=[ (['command'], {'help': 'python command string to call'})]) class CallCommand(Command): """ Executes python code """ repeatable = True def __init__(self, command, **kwargs): """ :param command: python command string to call :type command: str """ Command.__init__(self, **kwargs) self.command = command def apply(self, ui): try: hooks = settings.hooks env = {'ui': ui, 'settings': settings} for k, v in env.items(): if k not in hooks.__dict__: hooks.__dict__[k] = v exec(self.command) except Exception as e: logging.exception(e) msg = 'an error occurred during execution of "%s":\n%s' ui.notify(msg % (self.command, e), priority='error') @registerCommand(MODE, 'bclose', arguments=[ (['--redraw'], {'action': BooleanAction, 'help': 'redraw current buffer \ after command has finished'}), (['--force'], {'action': 'store_true', 'help': 'never ask for confirmation'})]) class BufferCloseCommand(Command): """close a buffer""" repeatable = True def __init__(self, buffer=None, force=False, redraw=True, **kwargs): """ :param buffer: the buffer to close or None for current :type buffer: `alot.buffers.Buffer` :param force: force buffer close :type force: bool """ self.buffer = buffer self.force = force self.redraw = redraw Command.__init__(self, **kwargs) @inlineCallbacks def apply(self, ui): if self.buffer is None: self.buffer = ui.current_buffer if (isinstance(self.buffer, buffers.EnvelopeBuffer) and not self.buffer.envelope.sent_time): if (not self.force and (yield ui.choice('close without sending?', select='yes', cancel='no', msg_position='left')) == 'no'): return if len(ui.buffers) == 1: if settings.get('quit_on_last_bclose'): logging.info('closing the last buffer, exiting') ui.apply_command(ExitCommand()) else: logging.info('not closing last remaining buffer as ' 'global.quit_on_last_bclose is set to False') else: ui.buffer_close(self.buffer, self.redraw) @registerCommand(MODE, 'bprevious', forced={'offset': -1}, help='focus previous buffer') @registerCommand(MODE, 'bnext', forced={'offset': +1}, help='focus next buffer') @registerCommand(MODE, 'buffer', arguments=[ (['index'], {'type': int, 'help': 'buffer index to focus'}), ], help='focus buffer with given index') class BufferFocusCommand(Command): """focus a :class:`~alot.buffers.Buffer`""" repeatable = True def __init__(self, buffer=None, index=None, offset=0, **kwargs): """ :param buffer: the buffer to focus or None :type buffer: `alot.buffers.Buffer` :param index: index (in bufferlist) of the buffer to focus. :type index: int :param offset: position of the buffer to focus relative to the currently focussed one. This is used only if `buffer` is set to `None` :type offset: int """ self.buffer = buffer self.index = index self.offset = offset Command.__init__(self, **kwargs) def apply(self, ui): if self.buffer is None: if self.index is not None: try: self.buffer = ui.buffers[self.index] except IndexError: ui.notify('no buffer exists at index %d' % self.index) return else: self.index = ui.buffers.index(ui.current_buffer) num = len(ui.buffers) self.buffer = ui.buffers[(self.index + self.offset) % num] ui.buffer_focus(self.buffer) @registerCommand(MODE, 'bufferlist') class OpenBufferlistCommand(Command): """open a list of active buffers""" def __init__(self, filtfun=None, **kwargs): """ :param filtfun: filter to apply to displayed list :type filtfun: callable (str->bool) """ self.filtfun = filtfun Command.__init__(self, **kwargs) def apply(self, ui): blists = ui.get_buffers_of_type(buffers.BufferlistBuffer) if blists: ui.buffer_focus(blists[0]) else: bl = buffers.BufferlistBuffer(ui, self.filtfun) ui.buffer_open(bl) @registerCommand(MODE, 'taglist') class TagListCommand(Command): """opens taglist buffer""" def __init__(self, filtfun=None, **kwargs): """ :param filtfun: filter to apply to displayed list :type filtfun: callable (str->bool) """ self.filtfun = filtfun Command.__init__(self, **kwargs) def apply(self, ui): tags = ui.dbman.get_all_tags() blists = ui.get_buffers_of_type(buffers.TagListBuffer) if blists: buf = blists[0] buf.tags = tags buf.rebuild() ui.buffer_focus(buf) else: ui.buffer_open(buffers.TagListBuffer(ui, tags, self.filtfun)) @registerCommand(MODE, 'flush') class FlushCommand(Command): """flush write operations or retry until committed""" repeatable = True def __init__(self, callback=None, silent=False, **kwargs): """ :param callback: function to call after successful writeout :type callback: callable """ Command.__init__(self, **kwargs) self.callback = callback self.silent = silent def apply(self, ui): try: ui.dbman.flush() if callable(self.callback): self.callback() logging.debug('flush complete') if ui.db_was_locked: if not self.silent: ui.notify('changes flushed') ui.db_was_locked = False ui.update() except DatabaseLockedError: timeout = settings.get('flush_retry_timeout') def f(*args): self.apply(ui) ui.mainloop.set_alarm_in(timeout, f) if not ui.db_was_locked: if not self.silent: ui.notify( 'index locked, will try again in %d secs' % timeout) ui.db_was_locked = True ui.update() return # TODO: choices @registerCommand(MODE, 'help', arguments=[ (['commandname'], {'help': 'command or \'bindings\''})]) class HelpCommand(Command): """ display help for a command. Use \'bindings\' to display all keybings interpreted in current mode.' """ def __init__(self, commandname='', **kwargs): """ :param commandname: command to document :type commandname: str """ Command.__init__(self, **kwargs) self.commandname = commandname def apply(self, ui): logging.debug('HELP') if self.commandname == 'bindings': text_att = settings.get_theming_attribute('help', 'text') title_att = settings.get_theming_attribute('help', 'title') section_att = settings.get_theming_attribute('help', 'section') # get mappings if ui.mode in settings._bindings: modemaps = dict(settings._bindings[ui.mode].items()) else: modemaps = {} is_scalar = lambda k_v: k_v[0] in settings._bindings.scalars globalmaps = dict(filter(is_scalar, settings._bindings.items())) # build table maxkeylength = len(max((modemaps).keys() + globalmaps.keys(), key=len)) keycolumnwidth = maxkeylength + 2 linewidgets = [] # mode specific maps if modemaps: txt = (section_att, '\n%s-mode specific maps' % ui.mode) linewidgets.append(urwid.Text(txt)) for (k, v) in modemaps.items(): line = urwid.Columns([('fixed', keycolumnwidth, urwid.Text((text_att, k))), urwid.Text((text_att, v))]) linewidgets.append(line) # global maps linewidgets.append(urwid.Text((section_att, '\nglobal maps'))) for (k, v) in globalmaps.items(): if k not in modemaps: line = urwid.Columns( [('fixed', keycolumnwidth, urwid.Text((text_att, k))), urwid.Text((text_att, v))]) linewidgets.append(line) body = urwid.ListBox(linewidgets) titletext = 'Bindings Help (escape cancels)' box = DialogBox(body, titletext, bodyattr=text_att, titleattr=title_att) # put promptwidget as overlay on main widget overlay = urwid.Overlay(box, ui.root_widget, 'center', ('relative', 70), 'middle', ('relative', 70)) ui.show_as_root_until_keypress(overlay, 'esc') else: logging.debug('HELP %s' % self.commandname) parser = commands.lookup_parser(self.commandname, ui.mode) if parser: ui.notify(parser.format_help(), block=True) else: ui.notify('command not known: %s' % self.commandname, priority='error') @registerCommand(MODE, 'compose', arguments=[ (['--sender'], {'nargs': '?', 'help': 'sender'}), (['--template'], {'nargs': '?', 'help': 'path to a template message file'}), (['--subject'], {'nargs': '?', 'help': 'subject line'}), (['--to'], {'nargs': '+', 'help': 'recipients'}), (['--cc'], {'nargs': '+', 'help': 'copy to'}), (['--bcc'], {'nargs': '+', 'help': 'blind copy to'}), (['--attach'], {'nargs': '+', 'help': 'attach files'}), (['--omit_signature'], {'action': 'store_true', 'help': 'do not add signature'}), (['--spawn'], {'action': BooleanAction, 'default': None, 'help': 'spawn editor in new terminal'}), ]) class ComposeCommand(Command): """compose a new email""" def __init__(self, envelope=None, headers={}, template=None, sender=u'', subject=u'', to=[], cc=[], bcc=[], attach=None, omit_signature=False, spawn=None, **kwargs): """ :param envelope: use existing envelope :type envelope: :class:`~alot.db.envelope.Envelope` :param headers: forced header values :type header: doct (str->str) :param template: name of template to parse into the envelope after creation. This should be the name of a file in your template_dir :type template: str :param sender: From-header value :type sender: str :param subject: Subject-header value :type subject: str :param to: To-header value :type to: str :param cc: Cc-header value :type cc: str :param bcc: Bcc-header value :type bcc: str :param attach: Path to files to be attached (globable) :type attach: str :param omit_signature: do not attach/append signature :type omit_signature: bool :param spawn: force spawning of editor in a new terminal :type spawn: bool """ Command.__init__(self, **kwargs) self.envelope = envelope self.template = template self.headers = headers self.sender = sender self.subject = subject self.to = to self.cc = cc self.bcc = bcc self.attach = attach self.omit_signature = omit_signature self.force_spawn = spawn @inlineCallbacks def apply(self, ui): if self.envelope is None: self.envelope = Envelope() if self.template is not None: # get location of tempsdir, containing msg templates tempdir = settings.get('template_dir') tempdir = os.path.expanduser(tempdir) if not tempdir: xdgdir = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) tempdir = os.path.join(xdgdir, 'alot', 'templates') path = os.path.expanduser(self.template) if not os.path.dirname(path): # use tempsdir if not os.path.isdir(tempdir): ui.notify('no templates directory: %s' % tempdir, priority='error') return path = os.path.join(tempdir, path) if not os.path.isfile(path): ui.notify('could not find template: %s' % path, priority='error') return try: self.envelope.parse_template(open(path).read()) except Exception as e: ui.notify(str(e), priority='error') return # set forced headers for key, value in self.headers.items(): self.envelope.add(key, value) # set forced headers for separate parameters if self.sender: self.envelope.add('From', self.sender) if self.subject: self.envelope.add('Subject', self.subject) if self.to: self.envelope.add('To', ','.join(self.to)) if self.cc: self.envelope.add('Cc', ','.join(self.cc)) if self.bcc: self.envelope.add('Bcc', ','.join(self.bcc)) # get missing From header if not 'From' in self.envelope.headers: accounts = settings.get_accounts() if len(accounts) == 1: a = accounts[0] fromstring = "%s <%s>" % (a.realname, a.address) self.envelope.add('From', fromstring) else: cmpl = AccountCompleter() fromaddress = yield ui.prompt('From', completer=cmpl, tab=1) if fromaddress is None: ui.notify('canceled') return self.envelope.add('From', fromaddress) # add signature if not self.omit_signature: name, addr = email.Utils.parseaddr(self.envelope['From']) account = settings.get_account_by_address(addr) if account is not None: if account.signature: logging.debug('has signature') sig = os.path.expanduser(account.signature) if os.path.isfile(sig): logging.debug('is file') if account.signature_as_attachment: name = account.signature_filename or None self.envelope.attach(sig, filename=name) logging.debug('attached') else: sigcontent = open(sig).read() enc = helper.guess_encoding(sigcontent) mimetype = helper.guess_mimetype(sigcontent) if mimetype.startswith('text'): sigcontent = helper.string_decode(sigcontent, enc) self.envelope.body += '\n' + sigcontent else: ui.notify('could not locate signature: %s' % sig, priority='error') if (yield ui.choice('send without signature?', 'yes', 'no')) == 'no': return # Figure out whether we should GPG sign messages by default # and look up key if so sender = self.envelope.get('From') name, addr = email.Utils.parseaddr(sender) account = settings.get_account_by_address(addr) if account: self.envelope.sign = account.sign_by_default self.envelope.sign_key = account.gpg_key # get missing To header if 'To' not in self.envelope.headers: allbooks = not settings.get('complete_matching_abook_only') logging.debug(allbooks) if account is not None: abooks = settings.get_addressbooks(order=[account], append_remaining=allbooks) logging.debug(abooks) completer = ContactsCompleter(abooks) else: completer = None to = yield ui.prompt('To', completer=completer) if to is None: ui.notify('canceled') return self.envelope.add('To', to.strip(' \t\n,')) if settings.get('ask_subject') and \ not 'Subject' in self.envelope.headers: subject = yield ui.prompt('Subject') logging.debug('SUBJECT: "%s"' % subject) if subject is None: ui.notify('canceled') return self.envelope.add('Subject', subject) if settings.get('compose_ask_tags'): comp = TagsCompleter(ui.dbman) tagsstring = yield ui.prompt('Tags', completer=comp) tags = filter(lambda x: x, tagsstring.split(',')) if tags is None: ui.notify('canceled') return self.envelope.tags = tags if self.attach: for gpath in self.attach: for a in glob.glob(gpath): self.envelope.attach(a) logging.debug('attaching: ' + a) cmd = commands.envelope.EditCommand(envelope=self.envelope, spawn=self.force_spawn, refocus=False) ui.apply_command(cmd) @registerCommand(MODE, 'move', help='move focus in current buffer', arguments=[(['movement'], { 'nargs': argparse.REMAINDER, 'help': 'up, down, [half]page up, ' '[half]page down, first'})]) class MoveCommand(Command): """move in widget""" def __init__(self, movement=None, **kwargs): if movement is None: self.movement = '' else: self.movement = ' '.join(movement) Command.__init__(self, **kwargs) def apply(self, ui): if self.movement in ['up', 'down', 'page up', 'page down']: ui.mainloop.process_input([self.movement]) elif self.movement in ['halfpage down', 'halfpage up']: ui.mainloop.process_input( ui.mainloop.screen_size[1] / 2 * [self.movement.split()[-1]]) elif self.movement == 'first': if hasattr(ui.current_buffer, "focus_first"): ui.current_buffer.focus_first() elif self.movement == 'last': if hasattr(ui.current_buffer, "focus_last"): ui.current_buffer.focus_last() else: ui.notify('unknown movement: ' + self.movement, priority='error') class CommandSequenceCommand(Command): """Meta-Command that just applies a sequence of given Commands in order""" def __init__(self, cmdline='', **kwargs): Command.__init__(self, **kwargs) self.cmdline = cmdline @inlineCallbacks def apply(self, ui): # split commandline if necessary for cmdstring in split_commandline(self.cmdline): logging.debug('CMDSEQ: apply %s' % str(cmdstring)) # translate cmdstring into :class:`Command` try: cmd = commandfactory(cmdstring, ui.mode) # store cmdline for use with 'repeat' command if cmd.repeatable: ui.last_commandline = self.cmdline.lstrip() except CommandParseError as e: ui.notify(e.message, priority='error') return yield ui.apply_command(cmd) alot-0.3.5/alot/commands/search.py000066400000000000000000000225721217172710600170510ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import argparse import logging from alot.commands import Command, registerCommand from alot.commands.globals import PromptCommand from alot.commands.globals import MoveCommand from alot.db.errors import DatabaseROError from alot import commands from alot import buffers MODE = 'search' @registerCommand(MODE, 'select') class OpenThreadCommand(Command): """open thread in a new buffer""" def __init__(self, thread=None, **kwargs): """ :param thread: thread to open (Uses focussed thread if unset) :type thread: :class:`~alot.db.Thread` """ self.thread = thread Command.__init__(self, **kwargs) def apply(self, ui): if not self.thread: self.thread = ui.current_buffer.get_selected_thread() if self.thread: query = ui.current_buffer.querystring logging.info('open thread view for %s' % self.thread) sb = buffers.ThreadBuffer(ui, self.thread) ui.buffer_open(sb) sb.unfold_matching(query) @registerCommand(MODE, 'refine', help='refine query', arguments=[ (['--sort'], {'help': 'sort order', 'choices': [ 'oldest_first', 'newest_first', 'message_id', 'unsorted']}), (['query'], {'nargs': argparse.REMAINDER, 'help': 'search string'})]) @registerCommand(MODE, 'sort', help='set sort order', arguments=[ (['sort'], {'help': 'sort order', 'choices': [ 'oldest_first', 'newest_first', 'message_id', 'unsorted']}), ]) class RefineCommand(Command): """refine the querystring of this buffer""" def __init__(self, query=None, sort=None, **kwargs): """ :param query: new querystring given as list of strings as returned by argparse :type query: list of str """ if query is None: self.querystring = None else: self.querystring = ' '.join(query) self.sort_order = sort Command.__init__(self, **kwargs) def apply(self, ui): if self.querystring or self.sort_order: sbuffer = ui.current_buffer oldquery = sbuffer.querystring if self.querystring not in [None, oldquery]: sbuffer.querystring = self.querystring sbuffer = ui.current_buffer if self.sort_order: sbuffer.sort_order = self.sort_order sbuffer.rebuild() ui.update() else: ui.notify('empty query string') @registerCommand(MODE, 'refineprompt') class RefinePromptCommand(Command): """prompt to change this buffers querystring""" repeatable = True def apply(self, ui): sbuffer = ui.current_buffer oldquery = sbuffer.querystring ui.apply_command(PromptCommand('refine ' + oldquery)) @registerCommand(MODE, 'retagprompt') class RetagPromptCommand(Command): """prompt to retag selected threads\' tags""" def apply(self, ui): thread = ui.current_buffer.get_selected_thread() if not thread: return tags = [] for tag in thread.get_tags(): if ' ' in tag: tags.append('"%s"' % tag) else: tags.append(tag) initial_tagstring = ','.join(tags) ui.apply_command(PromptCommand('retag ' + initial_tagstring)) @registerCommand(MODE, 'tag', forced={'action': 'add'}, arguments=[ (['--no-flush'], {'action': 'store_false', 'dest': 'flush', 'default': 'True', 'help': 'postpone a writeout to the index'}), (['--all'], {'action': 'store_true', 'dest': 'allmessages', 'default': False, 'help': 'retag all messages in search result'}), (['tags'], {'help': 'comma separated list of tags'})], help='add tags to all messages in the thread', ) @registerCommand(MODE, 'retag', forced={'action': 'set'}, arguments=[ (['--no-flush'], {'action': 'store_false', 'dest': 'flush', 'default': 'True', 'help': 'postpone a writeout to the index'}), (['--all'], {'action': 'store_true', 'dest': 'allmessages', 'default': False, 'help': 'retag all messages in search result'}), (['tags'], {'help': 'comma separated list of tags'})], help='set tags of all messages in the thread', ) @registerCommand(MODE, 'untag', forced={'action': 'remove'}, arguments=[ (['--no-flush'], {'action': 'store_false', 'dest': 'flush', 'default': 'True', 'help': 'postpone a writeout to the index'}), (['--all'], {'action': 'store_true', 'dest': 'allmessages', 'default': False, 'help': 'retag all messages in search result'}), (['tags'], {'help': 'comma separated list of tags'})], help='remove tags from all messages in the thread', ) @registerCommand(MODE, 'toggletags', forced={'action': 'toggle'}, arguments=[ (['--no-flush'], {'action': 'store_false', 'dest': 'flush', 'default': 'True', 'help': 'postpone a writeout to the index'}), (['tags'], {'help': 'comma separated list of tags'})], help="""flip presence of tags on this thread. A tag is considered present if at least one message contained in this thread is tagged with it. In that case this command will remove the tag from every message in the thread. """) class TagCommand(Command): """manipulate message tags""" repeatable = True def __init__(self, tags=u'', action='add', allmessages=False, flush=True, **kwargs): """ :param tags: comma separated list of tagstrings to set :type tags: str :param action: adds tags if 'add', removes them if 'remove', adds tags and removes all other if 'set' or toggle individually if 'toggle' :type action: str :param all: tag all messages in search result :type all: bool :param flush: imediately write out to the index :type flush: bool """ self.tagsstring = tags self.action = action self.allm = allmessages self.flush = flush Command.__init__(self, **kwargs) def apply(self, ui): searchbuffer = ui.current_buffer threadline_widget = searchbuffer.get_selected_threadline() # pass if the current buffer has no selected threadline # (displays an empty search result) if threadline_widget is None: return testquery = searchbuffer.querystring thread = threadline_widget.get_thread() if not self.allm: testquery = "(%s) AND thread:%s" % (testquery, thread.get_thread_id()) logging.debug('all? %s' % self.allm) logging.debug('q: %s' % testquery) hitcount_before = ui.dbman.count_messages(testquery) def remove_thread(): logging.debug('remove thread from result list: %s' % thread) if threadline_widget in searchbuffer.threadlist: # remove this thread from result list searchbuffer.threadlist.remove(threadline_widget) def refresh(): # remove thread from resultset if it doesn't match the search query # any more and refresh selected threadline otherwise hitcount_after = ui.dbman.count_messages(testquery) # update total result count if not self.allm: if hitcount_after == 0: remove_thread() else: threadline_widget.rebuild() else: searchbuffer.rebuild() searchbuffer.result_count += (hitcount_after - hitcount_before) ui.update() tags = filter(lambda x: x, self.tagsstring.split(',')) try: if self.action == 'add': ui.dbman.tag(testquery, tags, remove_rest=False) if self.action == 'set': ui.dbman.tag(testquery, tags, remove_rest=True) elif self.action == 'remove': ui.dbman.untag(testquery, tags) elif self.action == 'toggle': if not self.allm: to_remove = [] to_add = [] for t in tags: if t in thread.get_tags(): to_remove.append(t) else: to_add.append(t) thread.remove_tags(to_remove) thread.add_tags(to_add, afterwards=refresh) except DatabaseROError: ui.notify('index in read-only mode', priority='error') return # flush index if self.flush: ui.apply_command(commands.globals.FlushCommand(callback=refresh)) @registerCommand(MODE, 'move', help='move focus in search buffer', arguments=[(['movement'], { 'nargs': argparse.REMAINDER, 'help': 'last'})]) class MoveFocusCommand(MoveCommand): def apply(self, ui): logging.debug(self.movement) if self.movement == 'last': ui.current_buffer.focus_last() else: MoveCommand.apply(self, ui) alot-0.3.5/alot/commands/taglist.py000066400000000000000000000011231217172710600172400ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file from alot.commands import Command, registerCommand from alot.commands.globals import SearchCommand MODE = 'taglist' @registerCommand(MODE, 'select') class TaglistSelectCommand(Command): """search for messages with selected tag""" def apply(self, ui): tagstring = ui.current_buffer.get_selected_tag() cmd = SearchCommand(query=['tag:"%s"' % tagstring]) ui.apply_command(cmd) alot-0.3.5/alot/commands/thread.py000066400000000000000000001212361217172710600170500ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os import re import logging import tempfile import argparse from twisted.internet.defer import inlineCallbacks import subprocess from email.Utils import getaddresses, parseaddr import mailcap from cStringIO import StringIO from alot.commands import Command, registerCommand from alot.commands.globals import ExternalCommand from alot.commands.globals import FlushCommand from alot.commands.globals import ComposeCommand from alot.commands.globals import MoveCommand from alot.commands.envelope import SendCommand from alot import completion from alot.db.utils import decode_header from alot.db.utils import encode_header from alot.db.utils import extract_headers from alot.db.utils import extract_body from alot.db.envelope import Envelope from alot.db.attachment import Attachment from alot.db.errors import DatabaseROError from alot.settings import settings from alot.helper import parse_mailcap_nametemplate from alot.helper import split_commandstring from alot.utils.booleanaction import BooleanAction from alot.completion import ContactsCompleter from alot.widgets.globals import AttachmentWidget MODE = 'thread' def determine_sender(mail, action='reply'): """ Inspect a given mail to reply/forward/bounce and find the most appropriate account to act from and construct a suitable From-Header to use. :param mail: the email to inspect :type mail: `email.message.Message` :param action: intended use case: one of "reply", "forward" or "bounce" :type action: str """ assert action in ['reply', 'forward', 'bounce'] realname = None address = None # get accounts my_accounts = settings.get_accounts() assert my_accounts, 'no accounts set!' # extract list of addresses to check for my address candidate_addresses = getaddresses(mail.get_all('To', []) + mail.get_all('Cc', []) + mail.get_all('Delivered-To', []) + mail.get_all('From', [])) logging.debug('candidate addresses: %s' % candidate_addresses) # pick the most important account that has an address in candidates # and use that accounts realname and the address found here for account in my_accounts: acc_addresses = account.get_addresses() for alias in acc_addresses: if realname is not None: break regex = re.compile(re.escape(alias), flags=re.IGNORECASE) for seen_name, seen_address in candidate_addresses: if regex.match(seen_address): logging.debug("match!: '%s' '%s'" % (seen_address, alias)) if settings.get(action + '_force_realname'): realname = account.realname else: realname = seen_name if settings.get(action + '_force_address'): address = account.address else: address = seen_address # revert to default account if nothing found if realname is None: account = my_accounts[0] realname = account.realname address = account.address logging.debug('using realname: "%s"' % realname) logging.debug('using address: %s' % address) from_value = address if realname == '' else '%s <%s>' % (realname, address) return from_value, account @registerCommand(MODE, 'reply', arguments=[ (['--all'], {'action': 'store_true', 'help': 'reply to all'}), (['--spawn'], {'action': BooleanAction, 'default': None, 'help': 'open editor in new window'})]) class ReplyCommand(Command): """reply to message""" repeatable = True def __init__(self, message=None, all=False, spawn=None, **kwargs): """ :param message: message to reply to (defaults to selected message) :type message: `alot.db.message.Message` :param all: group reply; copies recipients from Bcc/Cc/To to the reply :type all: bool :param spawn: force spawning of editor in a new terminal :type spawn: bool """ self.message = message self.groupreply = all self.force_spawn = spawn Command.__init__(self, **kwargs) def apply(self, ui): # get message to forward if not given in constructor if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() # set body text name, address = self.message.get_author() timestamp = self.message.get_date() qf = settings.get_hook('reply_prefix') if qf: quotestring = qf(name, address, timestamp, ui=ui, dbm=ui.dbman) else: quotestring = 'Quoting %s (%s)\n' % (name or address, timestamp) mailcontent = quotestring quotehook = settings.get_hook('text_quote') if quotehook: mailcontent += quotehook(self.message.accumulate_body()) else: quote_prefix = settings.get('quote_prefix') for line in self.message.accumulate_body().splitlines(): mailcontent += quote_prefix + line + '\n' envelope = Envelope(bodytext=mailcontent) # copy subject subject = decode_header(mail.get('Subject', '')) reply_subject_hook = settings.get_hook('reply_subject') if reply_subject_hook: subject = reply_subject_hook(subject) else: rsp = settings.get('reply_subject_prefix') if not subject.startswith(('Re:', rsp)): subject = rsp + subject envelope.add('Subject', subject) # set From-header and sending account try: from_header, account = determine_sender(mail, 'reply') except AssertionError as e: ui.notify(e.message, priority='error') return envelope.add('From', from_header) # set To sender = mail['Reply-To'] or mail['From'] my_addresses = settings.get_addresses() sender_address = parseaddr(sender)[1] cc = '' # check if reply is to self sent message if sender_address in my_addresses: recipients = [mail['To']] emsg = 'Replying to own message, set recipients to: %s' \ % recipients logging.debug(emsg) else: recipients = [sender] if self.groupreply: # make sure that our own address is not included # if the message was self-sent, then our address is not included MFT = mail.get_all('Mail-Followup-To', []) followupto = self.clear_my_address(my_addresses, MFT) if followupto and settings.get('honor_followup_to'): logging.debug('honor followup to: %s', followupto) recipients = [followupto] # since Mail-Followup-To was set, ignore the Cc header else: if sender != mail['From']: recipients.append(mail['From']) # append To addresses if not replying to self sent message if sender_address not in my_addresses: cleared = self.clear_my_address( my_addresses, mail.get_all('To', [])) recipients.append(cleared) # copy cc for group-replies if 'Cc' in mail: cc = self.clear_my_address( my_addresses, mail.get_all('Cc', [])) envelope.add('Cc', decode_header(cc)) to = ', '.join(recipients) logging.debug('reply to: %s' % to) envelope.add('To', decode_header(to)) # if any of the recipients is a mailinglist that we are subscribed to, # set Mail-Followup-To header so that duplicates are avoided if settings.get('followup_to'): # to and cc are already cleared of our own address allrecipients = [to] + [cc] lists = settings.get('mailinglists') # check if any recipient address matches a known mailing list if any([addr in lists for n, addr in getaddresses(allrecipients)]): followupto = ', '.join(allrecipients) logging.debug('mail followup to: %s' % followupto) envelope.add('Mail-Followup-To', decode_header(followupto)) # set In-Reply-To header envelope.add('In-Reply-To', '<%s>' % self.message.get_message_id()) # set References header old_references = mail.get('References', '') if old_references: old_references = old_references.split() references = old_references[-8:] if len(old_references) > 8: references = old_references[:1] + references references.append('<%s>' % self.message.get_message_id()) envelope.add('References', ' '.join(references)) else: envelope.add('References', '<%s>' % self.message.get_message_id()) # continue to compose ui.apply_command(ComposeCommand(envelope=envelope, spawn=self.force_spawn)) def clear_my_address(self, my_addresses, value): """return recipient header without the addresses in my_addresses""" new_value = [] for name, address in getaddresses(value): if address not in my_addresses: if name != '': new_value.append('"%s" <%s>' % (name, address)) else: new_value.append(address) return ', '.join(new_value) @registerCommand(MODE, 'forward', arguments=[ (['--attach'], {'action': 'store_true', 'help': 'attach original mail'}), (['--spawn'], {'action': BooleanAction, 'default': None, 'help': 'open editor in new window'})]) class ForwardCommand(Command): """forward message""" repeatable = True def __init__(self, message=None, attach=True, spawn=None, **kwargs): """ :param message: message to forward (defaults to selected message) :type message: `alot.db.message.Message` :param attach: attach original mail instead of inline quoting its body :type attach: bool :param spawn: force spawning of editor in a new terminal :type spawn: bool """ self.message = message self.inline = not attach self.force_spawn = spawn Command.__init__(self, **kwargs) def apply(self, ui): # get message to forward if not given in constructor if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() envelope = Envelope() if self.inline: # inline mode # set body text name, address = self.message.get_author() timestamp = self.message.get_date() qf = settings.get_hook('forward_prefix') if qf: quote = qf(name, address, timestamp, ui=ui, dbm=ui.dbman) else: quote = 'Forwarded message from %s (%s):\n' % ( name or address, timestamp) mailcontent = quote quotehook = settings.get_hook('text_quote') if quotehook: mailcontent += quotehook(self.message.accumulate_body()) else: quote_prefix = settings.get('quote_prefix') for line in self.message.accumulate_body().splitlines(): mailcontent += quote_prefix + line + '\n' envelope.body = mailcontent else: # attach original mode # attach original msg mail.set_type('message/rfc822') mail['Content-Disposition'] = 'attachment' envelope.attach(Attachment(mail)) # copy subject subject = decode_header(mail.get('Subject', '')) subject = 'Fwd: ' + subject forward_subject_hook = settings.get_hook('forward_subject') if forward_subject_hook: subject = forward_subject_hook(subject) else: fsp = settings.get('forward_subject_prefix') if not subject.startswith(('Fwd:', fsp)): subject = fsp + subject envelope.add('Subject', subject) # set From-header and sending account try: from_header, account = determine_sender(mail, 'reply') except AssertionError as e: ui.notify(e.message, priority='error') return envelope.add('From', from_header) # continue to compose ui.apply_command(ComposeCommand(envelope=envelope, spawn=self.force_spawn)) @registerCommand(MODE, 'bounce') class BounceMailCommand(Command): """directly re-send selected message""" repeatable = True def __init__(self, message=None, **kwargs): """ :param message: message to bounce (defaults to selected message) :type message: `alot.db.message.Message` """ self.message = message Command.__init__(self, **kwargs) @inlineCallbacks def apply(self, ui): # get mail to bounce if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() # look if this makes sense: do we have any accounts set up? my_accounts = settings.get_accounts() if not my_accounts: ui.notify('no accounts set', priority='error') return # remove "Resent-*" headers if already present del mail['Resent-From'] del mail['Resent-To'] del mail['Resent-Cc'] del mail['Resent-Date'] del mail['Resent-Message-ID'] # set Resent-From-header and sending account try: resent_from_header, account = determine_sender(mail, 'bounce') except AssertionError as e: ui.notify(e.message, priority='error') return mail['Resent-From'] = resent_from_header # set Reset-To allbooks = not settings.get('complete_matching_abook_only') logging.debug('allbooks: %s', allbooks) if account is not None: abooks = settings.get_addressbooks(order=[account], append_remaining=allbooks) logging.debug(abooks) completer = ContactsCompleter(abooks) else: completer = None to = yield ui.prompt('To', completer=completer) if to is None: ui.notify('canceled') return mail['Resent-To'] = to.strip(' \t\n,') logging.debug("bouncing mail") logging.debug(mail.__class__) ui.apply_command(SendCommand(mail=mail)) @registerCommand(MODE, 'editnew', arguments=[ (['--spawn'], {'action': BooleanAction, 'default': None, 'help': 'open editor in new window'})]) class EditNewCommand(Command): """edit message in as new""" def __init__(self, message=None, spawn=None, **kwargs): """ :param message: message to reply to (defaults to selected message) :type message: `alot.db.message.Message` :param spawn: force spawning of editor in a new terminal :type spawn: bool """ self.message = message self.force_spawn = spawn Command.__init__(self, **kwargs) def apply(self, ui): if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() # set body text name, address = self.message.get_author() mailcontent = self.message.accumulate_body() envelope = Envelope(bodytext=mailcontent) # copy selected headers to_copy = ['Subject', 'From', 'To', 'Cc', 'Bcc', 'In-Reply-To', 'References'] for key in to_copy: value = decode_header(mail.get(key, '')) if value: envelope.add(key, value) # copy attachments for b in self.message.get_attachments(): envelope.attach(b) ui.apply_command(ComposeCommand(envelope=envelope, spawn=self.force_spawn, omit_signature=True)) @registerCommand(MODE, 'fold', forced={'visible': False}, arguments=[ ( ['query'], {'help': 'query used to filter messages to affect', 'nargs': '*'}), ], help='fold message(s)') @registerCommand(MODE, 'unfold', forced={'visible': True}, arguments=[ (['query'], {'help': 'query used to filter messages to affect', 'nargs': '*'}), ], help='unfold message(s)') @registerCommand(MODE, 'togglesource', forced={'raw': 'toggle'}, arguments=[ (['query'], {'help': 'query used to filter messages to affect', 'nargs': '*'}), ], help='display message source') @registerCommand(MODE, 'toggleheaders', forced={'all_headers': 'toggle'}, arguments=[ (['query'], { 'help': 'query used to filter messages to affect', 'nargs': '*'}), ], help='display all headers') class ChangeDisplaymodeCommand(Command): """fold or unfold messages""" repeatable = True def __init__(self, query=None, visible=None, raw=None, all_headers=None, **kwargs): """ :param query: notmuch query string used to filter messages to affect :type query: str :param visible: unfold if `True`, fold if `False`, ignore if `None` :type visible: True, False, 'toggle' or None :param raw: display raw message text. :type raw: True, False, 'toggle' or None :param all_headers: show all headers (only visible if not in raw mode) :type all_headers: True, False, 'toggle' or None """ self.query = None if query: self.query = ' '.join(query) self.visible = visible self.raw = raw self.all_headers = all_headers Command.__init__(self, **kwargs) def apply(self, ui): tbuffer = ui.current_buffer logging.debug('matching lines %s...' % (self.query)) if self.query is None: messagetrees = [tbuffer.get_selected_messagetree()] else: messagetrees = tbuffer.messagetrees() if self.query != '*': def matches(msgt): msg = msgt.get_message() return msg.matches(self.query) messagetrees = filter(matches, messagetrees) for mt in messagetrees: # determine new display values for this message if self.visible == 'toggle': visible = mt.is_collapsed(mt.root) else: visible = self.visible if self.raw == 'toggle': tbuffer.focus_selected_message() raw = not mt.display_source if self.raw == 'toggle' else self.raw all_headers = not mt.display_all_headers \ if self.all_headers == 'toggle' else self.all_headers # collapse/expand depending on new 'visible' value if visible is False: mt.collapse(mt.root) elif visible is True: # could be None mt.expand(mt.root) tbuffer.focus_selected_message() # set new values in messagetree obj if raw is not None: mt.display_source = raw if all_headers is not None: mt.display_all_headers = all_headers mt.debug() # let the messagetree reassemble itself mt.reassemble() # refresh the buffer (clears Tree caches etc) tbuffer.refresh() @registerCommand(MODE, 'pipeto', arguments=[ (['cmd'], {'help': 'shellcommand to pipe to', 'nargs': '+'}), (['--all'], {'action': 'store_true', 'help': 'pass all messages'}), (['--format'], {'help': 'output format', 'default': 'raw', 'choices': ['raw', 'decoded', 'id', 'filepath']}), (['--separately'], {'action': 'store_true', 'help': 'call command once for each message'}), (['--background'], {'action': 'store_true', 'help': 'don\'t stop the interface'}), (['--add_tags'], {'action': 'store_true', 'help': 'add \'Tags\' header to the message'}), (['--shell'], {'action': 'store_true', 'help': 'let the shell interpret the command'}), (['--notify_stdout'], {'action': 'store_true', 'help': 'display cmd\'s stdout as notification'}), ], ) class PipeCommand(Command): """pipe message(s) to stdin of a shellcommand""" repeatable = True def __init__(self, cmd, all=False, separately=False, background=False, shell=False, notify_stdout=False, format='raw', add_tags=False, noop_msg='no command specified', confirm_msg='', done_msg=None, **kwargs): """ :param cmd: shellcommand to open :type cmd: str or list of str :param all: pipe all, not only selected message :type all: bool :param separately: call command once per message :type separately: bool :param background: do not suspend the interface :type background: bool :param notify_stdout: display command\'s stdout as notification message :type notify_stdout: bool :param shell: let the shell interpret the command :type shell: bool 'raw': message content as is, 'decoded': message content, decoded quoted printable, 'id': message ids, separated by newlines, 'filepath': paths to message files on disk :type format: str :param add_tags: add 'Tags' header to the message :type add_tags: bool :param noop_msg: error notification to show if `cmd` is empty :type noop_msg: str :param confirm_msg: confirmation question to ask (continues directly if unset) :type confirm_msg: str :param done_msg: notification message to show upon success :type done_msg: str """ Command.__init__(self, **kwargs) if isinstance(cmd, unicode): cmd = split_commandstring(cmd) self.cmd = cmd self.whole_thread = all self.separately = separately self.background = background self.shell = shell self.notify_stdout = notify_stdout self.output_format = format self.add_tags = add_tags self.noop_msg = noop_msg self.confirm_msg = confirm_msg self.done_msg = done_msg @inlineCallbacks def apply(self, ui): # abort if command unset if not self.cmd: ui.notify(self.noop_msg, priority='error') return # get messages to pipe if self.whole_thread: thread = ui.current_buffer.get_selected_thread() if not thread: return to_print = thread.get_messages().keys() else: to_print = [ui.current_buffer.get_selected_message()] # ask for confirmation if needed if self.confirm_msg: if (yield ui.choice(self.confirm_msg, select='yes', cancel='no')) == 'no': return # prepare message sources pipestrings = [] separator = '\n\n' logging.debug('PIPETO format') logging.debug(self.output_format) if self.output_format == 'id': pipestrings = [e.get_message_id() for e in to_print] separator = '\n' elif self.output_format == 'filepath': pipestrings = [e.get_filename() for e in to_print] separator = '\n' else: for msg in to_print: mail = msg.get_email() if self.add_tags: mail['Tags'] = encode_header('Tags', ', '.join(msg.get_tags())) if self.output_format == 'raw': pipestrings.append(mail.as_string()) elif self.output_format == 'decoded': headertext = extract_headers(mail) bodytext = extract_body(mail) msgtext = '%s\n\n%s' % (headertext, bodytext) pipestrings.append(msgtext.encode('utf-8')) if not self.separately: pipestrings = [separator.join(pipestrings)] if self.shell: self.cmd = [' '.join(self.cmd)] # do teh monkey for mail in pipestrings: if self.background: logging.debug('call in background: %s' % str(self.cmd)) proc = subprocess.Popen(self.cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate(mail) if self.notify_stdout: ui.notify(out) else: logging.debug('stop urwid screen') ui.mainloop.screen.stop() logging.debug('call: %s' % str(self.cmd)) # if proc.stdout is defined later calls to communicate # seem to be non-blocking! proc = subprocess.Popen(self.cmd, shell=True, stdin=subprocess.PIPE, # stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate(mail) logging.debug('start urwid screen') ui.mainloop.screen.start() if err: ui.notify(err, priority='error') return # display 'done' message if self.done_msg: ui.notify(self.done_msg) @registerCommand(MODE, 'remove', arguments=[ (['--all'], {'action': 'store_true', 'help': 'remove whole thread'})]) class RemoveCommand(Command): """remove message(s) from the index""" repeatable = True def __init__(self, all=False, **kwargs): """ :param all: remove all messages from thread, not just selected one :type all: bool """ Command.__init__(self, **kwargs) self.all = all @inlineCallbacks def apply(self, ui): threadbuffer = ui.current_buffer # get messages and notification strings if self.all: thread = threadbuffer.get_selected_thread() tid = thread.get_thread_id() messages = thread.get_messages().keys() confirm_msg = 'remove all messages in thread?' ok_msg = 'removed all messages in thread: %s' % tid else: msg = threadbuffer.get_selected_message() messages = [msg] confirm_msg = 'remove selected message?' ok_msg = 'removed message: %s' % msg.get_message_id() # ask for confirmation if (yield ui.choice(confirm_msg, select='yes', cancel='no')) == 'no': return # notify callback def callback(): threadbuffer.rebuild() ui.notify(ok_msg) # remove messages for m in messages: ui.dbman.remove_message(m, afterwards=callback) ui.apply_command(FlushCommand()) @registerCommand(MODE, 'print', arguments=[ (['--all'], {'action': 'store_true', 'help': 'print all messages'}), (['--raw'], {'action': 'store_true', 'help': 'pass raw mail string'}), (['--separately'], {'action': 'store_true', 'help': 'call print command once for each message'}), (['--add_tags'], {'action': 'store_true', 'help': 'add \'Tags\' header to the message'}), ], ) class PrintCommand(PipeCommand): """print message(s)""" repeatable = True def __init__(self, all=False, separately=False, raw=False, add_tags=False, **kwargs): """ :param all: print all, not only selected messages :type all: bool :param separately: call print command once per message :type separately: bool :param raw: pipe raw message string to print command :type raw: bool :param add_tags: add 'Tags' header to the message :type add_tags: bool """ # get print command cmd = settings.get('print_cmd') or '' # set up notification strings if all: confirm_msg = 'print all messages in thread?' ok_msg = 'printed thread using %s' % cmd else: confirm_msg = 'print selected message?' ok_msg = 'printed message using %s' % cmd # no print cmd set noop_msg = 'no print command specified. Set "print_cmd" in the '\ 'global section.' PipeCommand.__init__(self, [cmd], all=all, separately=separately, background=True, shell=False, format='raw' if raw else 'decoded', add_tags=add_tags, noop_msg=noop_msg, confirm_msg=confirm_msg, done_msg=ok_msg, **kwargs) @registerCommand(MODE, 'save', arguments=[ (['--all'], {'action': 'store_true', 'help': 'save all attachments'}), (['path'], {'nargs': '?', 'help': 'path to save to'})]) class SaveAttachmentCommand(Command): """save attachment(s)""" def __init__(self, all=False, path=None, **kwargs): """ :param all: save all, not only selected attachment :type all: bool :param path: path to write to. if `all` is set, this must be a directory. :type path: str """ Command.__init__(self, **kwargs) self.all = all self.path = path @inlineCallbacks def apply(self, ui): pcomplete = completion.PathCompleter() savedir = settings.get('attachment_prefix', '~') if self.all: msg = ui.current_buffer.get_selected_message() if not self.path: self.path = yield ui.prompt('save attachments to', text=os.path.join(savedir, ''), completer=pcomplete) if self.path: if os.path.isdir(os.path.expanduser(self.path)): for a in msg.get_attachments(): dest = a.save(self.path) name = a.get_filename() if name: ui.notify('saved %s as: %s' % (name, dest)) else: ui.notify('saved attachment as: %s' % dest) else: ui.notify('not a directory: %s' % self.path, priority='error') else: ui.notify('canceled') else: # save focussed attachment focus = ui.get_deep_focus() if isinstance(focus, AttachmentWidget): attachment = focus.get_attachment() filename = attachment.get_filename() if not self.path: msg = 'save attachment (%s) to ' % filename initialtext = os.path.join(savedir, filename) self.path = yield ui.prompt(msg, completer=pcomplete, text=initialtext) if self.path: try: dest = attachment.save(self.path) ui.notify('saved attachment as: %s' % dest) except (IOError, OSError) as e: ui.notify(str(e), priority='error') else: ui.notify('canceled') class OpenAttachmentCommand(Command): """displays an attachment according to mailcap""" def __init__(self, attachment, **kwargs): """ :param attachment: attachment to open :type attachment: :class:`~alot.db.attachment.Attachment` """ Command.__init__(self, **kwargs) self.attachment = attachment def apply(self, ui): logging.info('open attachment') mimetype = self.attachment.get_content_type() # returns pair of preliminary command string and entry dict containing # more info. We only use the dict and construct the command ourselves _, entry = settings.mailcap_find_match(mimetype) if entry: afterwards = None # callback, will rm tempfile if used handler_stdin = None tempfile_name = None handler_raw_commandstring = entry['view'] # read parameter part = self.attachment.get_mime_representation() parms = tuple(map('='.join, part.get_params())) # in case the mailcap defined command contains no '%s', # we pipe the files content to the handling command via stdin if '%s' in handler_raw_commandstring: nametemplate = entry.get('nametemplate', '%s') prefix, suffix = parse_mailcap_nametemplate(nametemplate) tmpfile = tempfile.NamedTemporaryFile(delete=False, prefix=prefix, suffix=suffix) tempfile_name = tmpfile.name self.attachment.write(tmpfile) tmpfile.close() def afterwards(): os.unlink(tempfile_name) else: handler_stdin = StringIO() self.attachment.write(handler_stdin) # create handler command list handler_cmd = mailcap.subst(handler_raw_commandstring, mimetype, filename=tempfile_name, plist=parms) handler_cmdlist = split_commandstring(handler_cmd) # 'needsterminal' makes handler overtake the terminal nt = entry.get('needsterminal', None) overtakes = (nt is None) ui.apply_command(ExternalCommand(handler_cmdlist, stdin=handler_stdin, on_success=afterwards, thread=overtakes)) else: ui.notify('unknown mime type') @registerCommand(MODE, 'move', help='move focus in current buffer', arguments=[(['movement'], { 'nargs': argparse.REMAINDER, 'help': 'up, down, page up, ' 'page down, first, last'})]) class MoveFocusCommand(MoveCommand): def apply(self, ui): logging.debug(self.movement) tbuffer = ui.current_buffer if self.movement == 'parent': tbuffer.focus_parent() elif self.movement == 'first reply': tbuffer.focus_first_reply() elif self.movement == 'last reply': tbuffer.focus_last_reply() elif self.movement == 'next sibling': tbuffer.focus_next_sibling() elif self.movement == 'previous sibling': tbuffer.focus_prev_sibling() elif self.movement == 'next': tbuffer.focus_next() elif self.movement == 'previous': tbuffer.focus_prev() elif self.movement == 'next unfolded': tbuffer.focus_next_unfolded() elif self.movement == 'previous unfolded': tbuffer.focus_prev_unfolded() else: MoveCommand.apply(self, ui) # TODO add 'next matching' if threadbuffer stores the original query # TODO: add next by date.. tbuffer.body.refresh() @registerCommand(MODE, 'select') class ThreadSelectCommand(Command): """select focussed element. The fired action depends on the focus: - if message summary, this toggles visibility of the message, - if attachment line, this opens the attachment""" def apply(self, ui): focus = ui.get_deep_focus() if isinstance(focus, AttachmentWidget): logging.info('open attachment') ui.apply_command(OpenAttachmentCommand(focus.get_attachment())) else: ui.apply_command(ChangeDisplaymodeCommand(visible='toggle')) @registerCommand(MODE, 'tag', forced={'action': 'add'}, arguments=[ (['--all'], {'action': 'store_true', 'help': 'tag all messages in thread'}), (['--no-flush'], {'action': 'store_false', 'dest': 'flush', 'help': 'postpone a writeout to the index'}), (['tags'], {'help': 'comma separated list of tags'})], help='add tags to message(s)', ) @registerCommand(MODE, 'retag', forced={'action': 'set'}, arguments=[ (['--all'], {'action': 'store_true', 'help': 'tag all messages in thread'}), (['--no-flush'], {'action': 'store_false', 'dest': 'flush', 'help': 'postpone a writeout to the index'}), (['tags'], {'help': 'comma separated list of tags'})], help='set message(s) tags.', ) @registerCommand(MODE, 'untag', forced={'action': 'remove'}, arguments=[ (['--all'], {'action': 'store_true', 'help': 'tag all messages in thread'}), (['--no-flush'], {'action': 'store_false', 'dest': 'flush', 'help': 'postpone a writeout to the index'}), (['tags'], {'help': 'comma separated list of tags'})], help='remove tags from message(s)', ) @registerCommand(MODE, 'toggletags', forced={'action': 'toggle'}, arguments=[ (['--all'], {'action': 'store_true', 'help': 'tag all messages in thread'}), (['--no-flush'], {'action': 'store_false', 'dest': 'flush', 'help': 'postpone a writeout to the index'}), (['tags'], {'help': 'comma separated list of tags'})], help='flip presence of tags on message(s)', ) class TagCommand(Command): """manipulate message tags""" repeatable = True def __init__(self, tags=u'', action='add', all=False, flush=True, **kwargs): """ :param tags: comma separated list of tagstrings to set :type tags: str :param action: adds tags if 'add', removes them if 'remove', adds tags and removes all other if 'set' or toggle individually if 'toggle' :type action: str :param all: tag all messages in thread :type all: bool :param flush: imediately write out to the index :type flush: bool """ self.tagsstring = tags self.all = all self.action = action self.flush = flush Command.__init__(self, **kwargs) def apply(self, ui): tbuffer = ui.current_buffer if self.all: messagetrees = tbuffer.messagetrees() else: messagetrees = [tbuffer.get_selected_messagetree()] def refresh_widgets(): for mt in messagetrees: mt.refresh() # put currently selected message id on a block list for the # auto-remove-unread feature. This makes sure that explicit # tag-unread commands for the current message are not undone on the # next keypress (triggering the autorm again)... mid = tbuffer.get_selected_mid() tbuffer._auto_unread_dont_touch_mids.add(mid) tbuffer.refresh() tags = filter(lambda x: x, self.tagsstring.split(',')) try: for mt in messagetrees: m = mt.get_message() if self.action == 'add': m.add_tags(tags, afterwards=refresh_widgets) if self.action == 'set': m.add_tags(tags, afterwards=refresh_widgets, remove_rest=True) elif self.action == 'remove': m.remove_tags(tags, afterwards=refresh_widgets) elif self.action == 'toggle': to_remove = [] to_add = [] for t in tags: if t in m.get_tags(): to_remove.append(t) else: to_add.append(t) m.remove_tags(to_remove) m.add_tags(to_add, afterwards=refresh_widgets) except DatabaseROError: ui.notify('index in read-only mode', priority='error') return # flush index if self.flush: ui.apply_command(FlushCommand()) alot-0.3.5/alot/completion.py000066400000000000000000000541131217172710600161500ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import re import os import glob import logging import argparse import alot.crypto as crypto import alot.commands as commands from alot.buffers import EnvelopeBuffer from alot.settings import settings from alot.utils.booleanaction import BooleanAction from alot.helper import split_commandline from alot.addressbooks import AddressbookError from errors import CompletionError class Completer(object): """base class for completers""" def complete(self, original, pos): """returns a list of completions and cursor positions for the string original from position pos on. :param original: the string to complete :type original: str :param pos: starting position to complete from :type pos: int :returns: pairs of completed string and cursor position in the new string :rtype: list of (str, int) :raises: :exc:`CompletionError` """ return list() def relevant_part(self, original, pos, sep=' '): """ calculates the subword in a `sep`-splitted list of substrings of `original` that `pos` is ia.n """ start = original.rfind(sep, 0, pos) + 1 end = original.find(sep, pos - 1) if end == -1: end = len(original) return original[start:end], start, end, pos - start class StringlistCompleter(Completer): """completer for a fixed list of strings""" def __init__(self, resultlist, ignorecase=True, match_anywhere=False): """ :param resultlist: strings used for completion :type resultlist: list of str :param liberal: match case insensitive and not prefix-only :type liberal: bool """ self.resultlist = resultlist self.flags = re.IGNORECASE if ignorecase else 0 self.match_anywhere = match_anywhere def complete(self, original, pos): pref = original[:pos] re_prefix = '.*' if self.match_anywhere else '' def match(s, m): r = re_prefix + m + '.*' return re.match(r, s, flags=self.flags) is not None return [(a, len(a)) for a in self.resultlist if match(a, pref)] class MultipleSelectionCompleter(Completer): """ Meta-Completer that turns any Completer into one that deals with a list of completion strings using the wrapped Completer. This allows for example to easily construct a completer for comma separated recipient-lists using a :class:`ContactsCompleter`. """ def __init__(self, completer, separator=', '): """ :param completer: completer to use for individual substrings :type completer: Completer :param separator: separator used to split the completion string into substrings to be fed to `completer`. :type separator: str """ self._completer = completer self._separator = separator def relevant_part(self, original, pos): """ calculates the subword of `original` that `pos` is in """ start = original.rfind(self._separator, 0, pos) if start == -1: start = 0 else: start = start + len(self._separator) end = original.find(self._separator, pos - 1) if end == -1: end = len(original) return original[start:end], start, end, pos - start def complete(self, original, pos): mypart, start, end, mypos = self.relevant_part(original, pos) res = [] for c, p in self._completer.complete(mypart, mypos): newprefix = original[:start] + c if not original[end:].startswith(self._separator): newprefix += self._separator res.append((newprefix + original[end:], len(newprefix))) return res class QueryCompleter(Completer): """completion for a notmuch query string""" def __init__(self, dbman): """ :param dbman: used to look up avaliable tagstrings :type dbman: :class:`~alot.db.DBManager` """ self.dbman = dbman abooks = settings.get_addressbooks() self._abookscompleter = AbooksCompleter(abooks, addressesonly=True) self._tagcompleter = TagCompleter(dbman) self.keywords = ['tag', 'from', 'to', 'subject', 'attachment', 'is', 'id', 'thread', 'folder'] def complete(self, original, pos): mypart, start, end, mypos = self.relevant_part(original, pos) myprefix = mypart[:mypos] m = re.search('(tag|is|to|from):(\w*)', myprefix) if m: cmd, params = m.groups() cmdlen = len(cmd) + 1 # length of the keyword part incld colon if cmd in ['to', 'from']: localres = self._abookscompleter.complete(mypart[cmdlen:], mypos - cmdlen) else: localres = self._tagcompleter.complete(mypart[cmdlen:], mypos - cmdlen) resultlist = [] for ltxt, lpos in localres: newtext = original[:start] + cmd + ':' + ltxt + original[end:] newpos = start + len(cmd) + 1 + lpos resultlist.append((newtext, newpos)) return resultlist else: matched = filter(lambda t: t.startswith(myprefix), self.keywords) resultlist = [] for keyword in matched: newprefix = original[:start] + keyword + ':' resultlist.append((newprefix + original[end:], len(newprefix))) return resultlist class TagCompleter(StringlistCompleter): """complete a tagstring""" def __init__(self, dbman): """ :param dbman: used to look up avaliable tagstrings :type dbman: :class:`~alot.db.DBManager` """ resultlist = dbman.get_all_tags() StringlistCompleter.__init__(self, resultlist) class TagsCompleter(MultipleSelectionCompleter): """completion for a comma separated list of tagstrings""" def __init__(self, dbman): """ :param dbman: used to look up avaliable tagstrings :type dbman: :class:`~alot.db.DBManager` """ self._completer = TagCompleter(dbman) self._separator = ',' class ContactsCompleter(MultipleSelectionCompleter): """completes contacts from given address books""" def __init__(self, abooks, addressesonly=False): """ :param abooks: used to look up email addresses :type abooks: list of :class:`~alot.account.AddresBook` :param addressesonly: only insert address, not the realname of the contact :type addressesonly: bool """ self._completer = AbooksCompleter(abooks, addressesonly=addressesonly) self._separator = ', ' class AbooksCompleter(Completer): """completes a contact from given address books""" def __init__(self, abooks, addressesonly=False): """ :param abooks: used to look up email addresses :type abooks: list of :class:`~alot.account.AddresBook` :param addressesonly: only insert address, not the realname of the contact :type addressesonly: bool """ self.abooks = abooks self.addressesonly = addressesonly def complete(self, original, pos): if not self.abooks: return [] prefix = original[:pos] res = [] for abook in self.abooks: try: res = res + abook.lookup(prefix) except AddressbookError as e: raise CompletionError(e) if self.addressesonly: returnlist = [(email, len(email)) for (name, email) in res] else: returnlist = [] for name, email in res: if name: newtext = "%s <%s>" % (name, email) else: newtext = email returnlist.append((newtext, len(newtext))) return returnlist class ArgparseOptionCompleter(Completer): """completes option parameters for a given argparse.Parser""" def __init__(self, parser): """ :param parser: the option parser we look up parameter and choices from :type parser: `argparse.ArgumentParser` """ self.parser = parser self.actions = parser._optionals._actions def complete(self, original, pos): pref = original[:pos] res = [] for act in self.actions: if '=' in pref: optionstring = pref[:pref.rfind('=') + 1] # get choices if 'choices' in act.__dict__: # TODO: respect prefix choices = act.choices or [] res = res + [optionstring + a for a in choices] else: for optionstring in act.option_strings: if optionstring.startswith(pref): # append '=' for options that await a string value if isinstance(act, argparse._StoreAction) or\ isinstance(act, BooleanAction): optionstring += '=' res.append(optionstring) return [(a, len(a)) for a in res] class AccountCompleter(StringlistCompleter): """completes users' own mailaddresses""" def __init__(self, **kwargs): accounts = settings.get_accounts() resultlist = ["%s <%s>" % (a.realname, a.address) for a in accounts] StringlistCompleter.__init__(self, resultlist, match_anywhere=True, **kwargs) class CommandNameCompleter(Completer): """completes command names""" def __init__(self, mode): """ :param mode: mode identifier :type mode: str """ self.mode = mode def complete(self, original, pos): # TODO refine should get current querystring commandprefix = original[:pos] logging.debug('original="%s" prefix="%s"' % (original, commandprefix)) cmdlist = commands.COMMANDS['global'].copy() cmdlist.update(commands.COMMANDS[self.mode]) matching = [t for t in cmdlist if t.startswith(commandprefix)] return [(t, len(t)) for t in matching] class CommandCompleter(Completer): """completes one command consisting of command name and parameters""" def __init__(self, dbman, mode, currentbuffer=None): """ :param dbman: used to look up avaliable tagstrings :type dbman: :class:`~alot.db.DBManager` :param mode: mode identifier :type mode: str :param currentbuffer: currently active buffer. If defined, this will be used to dynamically extract possible completion strings :type currentbuffer: :class:`~alot.buffers.Buffer` """ self.dbman = dbman self.mode = mode self.currentbuffer = currentbuffer self._commandnamecompleter = CommandNameCompleter(mode) self._querycompleter = QueryCompleter(dbman) self._tagcompleter = TagCompleter(dbman) abooks = settings.get_addressbooks() self._contactscompleter = ContactsCompleter(abooks) self._pathcompleter = PathCompleter() self._accountscompleter = AccountCompleter() self._secretkeyscompleter = CryptoKeyCompleter(private=True) self._publickeyscompleter = CryptoKeyCompleter(private=False) def complete(self, line, pos): # remember how many preceding space characters we see until the command # string starts. We'll continue to complete from there on and will add # these whitespaces again at the very end whitespaceoffset = len(line) - len(line.lstrip()) line = line[whitespaceoffset:] pos = pos - whitespaceoffset words = line.split(' ', 1) res = [] if pos <= len(words[0]): # we complete commands for cmd, cpos in self._commandnamecompleter.complete(line, pos): newtext = ('%s %s' % (cmd, ' '.join(words[1:]))) res.append((newtext, cpos + 1)) else: cmd, params = words localpos = pos - (len(cmd) + 1) parser = commands.lookup_parser(cmd, self.mode) if parser is not None: # set 'res' - the result set of matching completionstrings # depending on the current mode and command # detect if we are completing optional parameter arguments_until_now = params[:localpos].split(' ') all_optionals = True logging.debug(str(arguments_until_now)) for a in arguments_until_now: logging.debug(a) if a and not a.startswith('-'): all_optionals = False # complete optional parameter if # 1. all arguments prior to current position are optional # 2. the parameter starts with '-' or we are at its beginning if all_optionals: myarg = arguments_until_now[-1] start_myarg = params.rindex(myarg) beforeme = params[:start_myarg] # set up local stringlist completer # and let it complete for given list of options localcompleter = ArgparseOptionCompleter(parser) localres = localcompleter.complete(myarg, len(myarg)) res = [( beforeme + c, p + start_myarg) for (c, p) in localres] # global elif cmd == 'search': res = self._querycompleter.complete(params, localpos) elif cmd == 'help': res = self._commandnamecompleter.complete(params, localpos) elif cmd in ['compose']: res = self._contactscompleter.complete(params, localpos) # search elif self.mode == 'search' and cmd == 'refine': res = self._querycompleter.complete(params, localpos) elif self.mode == 'search' and cmd in ['tag', 'retag', 'untag', 'toggletags']: localcomp = MultipleSelectionCompleter(self._tagcompleter, separator=',') res = localcomp.complete(params, localpos) elif self.mode == 'search' and cmd == 'toggletag': localcomp = MultipleSelectionCompleter(self._tagcompleter, separator=' ') res = localcomp.complete(params, localpos) # envelope elif self.mode == 'envelope' and cmd == 'set': plist = params.split(' ', 1) if len(plist) == 1: # complete from header keys localprefix = params headers = ['Subject', 'To', 'Cc', 'Bcc', 'In-Reply-To', 'From'] localcompleter = StringlistCompleter(headers) localres = localcompleter.complete( localprefix, localpos) res = [(c, p + 6) for (c, p) in localres] else: # must have 2 elements header, params = plist localpos = localpos - (len(header) + 1) if header.lower() in ['to', 'cc', 'bcc']: res = self._contactscompleter.complete(params, localpos) elif header.lower() == 'from': res = self._accountscompleter.complete(params, localpos) # prepend 'set ' + header and correct position def f((completed, pos)): return ('%s %s' % (header, completed), pos + len(header) + 1) res = map(f, res) logging.debug(res) elif self.mode == 'envelope' and cmd == 'unset': plist = params.split(' ', 1) if len(plist) == 1: # complete from header keys localprefix = params buf = self.currentbuffer if buf: if isinstance(buf, EnvelopeBuffer): available = buf.envelope.headers.keys() localcompleter = StringlistCompleter(available) localres = localcompleter.complete(localprefix, localpos) res = [(c, p + 6) for (c, p) in localres] elif self.mode == 'envelope' and cmd == 'attach': res = self._pathcompleter.complete(params, localpos) elif self.mode == 'envelope' and cmd in ['sign', 'togglesign']: res = self._secretkeyscompleter.complete(params, localpos) elif self.mode == 'envelope' and cmd in ['encrypt', 'rmencrypt', 'toggleencrypt']: res = self._publickeyscompleter.complete(params, localpos) # thread elif self.mode == 'thread' and cmd == 'save': res = self._pathcompleter.complete(params, localpos) elif self.mode == 'thread' and cmd in ['fold', 'unfold', 'togglesource', 'toggleheaders']: res = self._querycompleter.complete(params, localpos) elif self.mode == 'thread' and cmd in ['tag', 'retag', 'untag', 'toggletags']: localcomp = MultipleSelectionCompleter(self._tagcompleter, separator=',') res = localcomp.complete(params, localpos) elif cmd == 'move': directions = ['up', 'down', 'page up', 'page down'] if self.mode == 'thread': directions += ['first', 'last', 'next', 'previous', 'last reply', 'first reply', 'parent', 'next unfolded', 'previous unfolded', 'next sibling', 'previous sibling'] localcompleter = StringlistCompleter(directions) res = localcompleter.complete(params, localpos) # prepend cmd and correct position res = [('%s %s' % (cmd, t), p + len(cmd) + 1) for (t, p) in res] # re-insert whitespaces and correct position wso = whitespaceoffset res = [(' ' * wso + cmdstr, p + wso) for cmdstr, p in res] return res class CommandLineCompleter(Completer): """completes command lines: semicolon separated command strings""" def __init__(self, dbman, mode, currentbuffer=None): """ :param dbman: used to look up avaliable tagstrings :type dbman: :class:`~alot.db.DBManager` :param mode: mode identifier :type mode: str :param currentbuffer: currently active buffer. If defined, this will be used to dynamically extract possible completion strings :type currentbuffer: :class:`~alot.buffers.Buffer` """ self._commandcompleter = CommandCompleter(dbman, mode, currentbuffer) def get_context(self, line, pos): """ computes start and end position of substring of line that is the command string under given position """ commands = split_commandline(line) + [''] i = 0 start = 0 end = len(commands[i]) while pos > end: i += 1 start = end + 1 end += 1 + len(commands[i]) return start, end def complete(self, line, pos): cstart, cend = self.get_context(line, pos) before = line[:cstart] after = line[cend:] cmdstring = line[cstart:cend] cpos = pos - cstart res = [] for ccmd, ccpos in self._commandcompleter.complete(cmdstring, cpos): newtext = before + ccmd + after newpos = pos + (ccpos - cpos) res.append((newtext, newpos)) return res class PathCompleter(Completer): """completion for paths""" def complete(self, original, pos): if not original: return [('~/', 2)] prefix = os.path.expanduser(original[:pos]) def escape(path): return path.replace('\\', '\\\\').replace(' ', '\ ') def deescape(escaped_path): return escaped_path.replace('\\ ', ' ').replace('\\\\', '\\') def prep(path): escaped_path = escape(path) return escaped_path, len(escaped_path) return map(prep, glob.glob(deescape(prefix) + '*')) class CryptoKeyCompleter(StringlistCompleter): """completion for gpg keys""" def __init__(self, private=False): """ :param private: return private keys :type private: bool """ keys = crypto.list_keys(private=private) resultlist = [] for k in keys: for s in k.subkeys: resultlist.append(s.keyid) for u in k.uids: resultlist.append(u.email) StringlistCompleter.__init__(self, resultlist, match_anywhere=True) alot-0.3.5/alot/crypto.py000066400000000000000000000213301217172710600153120ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import re import os from email.generator import Generator from cStringIO import StringIO from alot.errors import GPGProblem, GPGCode from email.mime.multipart import MIMEMultipart import gpgme def email_as_string(mail): """ Converts the given message to a string, without mangling "From" lines (like as_string() does). :param mail: email to convert to string :rtype: str """ fp = StringIO() g = Generator(fp, mangle_from_=False, maxheaderlen=78) g.flatten(mail) as_string = RFC3156_canonicalize(fp.getvalue()) if isinstance(mail, MIMEMultipart): # Get the boundary for later boundary = mail.get_boundary() # Workaround for http://bugs.python.org/issue14983: # Insert a newline before the outer mail boundary so that other mail # clients can verify the signature when sending an email which contains # attachments. as_string = re.sub(r'--(\r\n)--' + boundary, '--\g<1>\g<1>--' + boundary, as_string, flags=re.MULTILINE) return as_string def _hash_algo_name(hash_algo): """ Re-implements GPGME's hash_algo_name as long as pygpgme doesn't wrap that function. :param hash_algo: GPGME hash_algo :rtype: str """ mapping = { gpgme.MD_MD5: "MD5", gpgme.MD_SHA1: "SHA1", gpgme.MD_RMD160: "RIPEMD160", gpgme.MD_MD2: "MD2", gpgme.MD_TIGER: "TIGER192", gpgme.MD_HAVAL: "HAVAL", gpgme.MD_SHA256: "SHA256", gpgme.MD_SHA384: "SHA384", gpgme.MD_SHA512: "SHA512", gpgme.MD_MD4: "MD4", gpgme.MD_CRC32: "CRC32", gpgme.MD_CRC32_RFC1510: "CRC32RFC1510", gpgme.MD_CRC24_RFC2440: "CRC24RFC2440", } if hash_algo in mapping: return mapping[hash_algo] else: raise GPGProblem(("Invalid hash_algo passed to hash_algo_name." " Please report this as a bug in alot."), code=GPGCode.INVALID_HASH) def RFC3156_micalg_from_algo(hash_algo): """ Converts a GPGME hash algorithm name to one conforming to RFC3156. GPGME returns hash algorithm names such as "SHA256", but RFC3156 says that programs need to use names such as "pgp-sha256" instead. :param hash_algo: GPGME hash_algo :rtype: str """ # hash_algo will be something like SHA256, but we need pgp-sha256. hash_algo = _hash_algo_name(hash_algo) return 'pgp-' + hash_algo.lower() def RFC3156_canonicalize(text): """ Canonicalizes plain text (MIME-encoded usually) according to RFC3156. This function works as follows (in that order): 1. Convert all line endings to \\\\r\\\\n (DOS line endings). 2. Ensure the text ends with a newline (\\\\r\\\\n). 3. Encode all occurences of "From " at the beginning of a line to "From=20" in order to prevent other mail programs to replace this with "> From" (to avoid MBox conflicts) and thus invalidate the signature. :param text: text to canonicalize (already encoded as quoted-printable) :rtype: str """ text = re.sub("\r?\n", "\r\n", text) if not text.endswith("\r\n"): text += "\r\n" text = re.sub("^From ", "From=20", text, flags=re.MULTILINE) return text def get_key(keyid, validate=False, encrypt=False, sign=False): """ Gets a key from the keyring by filtering for the specified keyid, but only if the given keyid is specific enough (if it matches multiple keys, an exception will be thrown). :param keyid: filter term for the keyring (usually a key ID) :rtype: gpgme.Key """ ctx = gpgme.Context() try: key = ctx.get_key(keyid) if validate: validate_key(key, encrypt=encrypt, sign=sign) except gpgme.GpgmeError as e: if e.code == gpgme.ERR_AMBIGUOUS_NAME: raise GPGProblem(("More than one key found matching this filter." + " Please be more specific (use a key ID like " + "4AC8EE1D)."), code=GPGCode.AMBIGUOUS_NAME) elif e.code == gpgme.ERR_INV_VALUE or e.code == gpgme.ERR_EOF: raise GPGProblem("Can not find key for \'" + keyid + "\'.", code=GPGCode.NOT_FOUND) else: raise e return key def list_keys(hint=None, private=False): """ Returns a list of all keys containing keyid. :param keyid: The part we search for :param private: Whether secret keys are listed :rtype: list """ ctx = gpgme.Context() return ctx.keylist(hint, private) def detached_signature_for(plaintext_str, key=None): """ Signs the given plaintext string and returns the detached signature. A detached signature in GPG speak is a separate blob of data containing a signature for the specified plaintext. :param plaintext_str: text to sign :param key: gpgme_key_t object representing the key to use :rtype: tuple of gpgme.NewSignature array and str """ ctx = gpgme.Context() ctx.armor = True if key is not None: ctx.signers = [key] plaintext_data = StringIO(plaintext_str) signature_data = StringIO() sigs = ctx.sign(plaintext_data, signature_data, gpgme.SIG_MODE_DETACH) signature_data.seek(0, os.SEEK_SET) signature = signature_data.read() return sigs, signature def encrypt(plaintext_str, keys=None): """ Encrypts the given plaintext string and returns a PGP/MIME compatible string :param plaintext_str: the mail to encrypt :param key: gpgme_key_t object representing the key to use :rtype: a string holding the encrypted mail """ plaintext_data = StringIO(plaintext_str) encrypted_data = StringIO() ctx = gpgme.Context() ctx.armor = True ctx.encrypt(keys, gpgme.ENCRYPT_ALWAYS_TRUST, plaintext_data, encrypted_data) encrypted_data.seek(0, os.SEEK_SET) encrypted = encrypted_data.read() return encrypted def verify_detached(message, signature): '''Verifies whether the message is authentic by checking the signature. :param message: the message as `str` :param signature: a `str` containing an OpenPGP signature :returns: a list of :class:`gpgme.Signature` :raises: :class:`~alot.errors.GPGProblem` if the verification fails ''' message_data = StringIO(message) signature_data = StringIO(signature) ctx = gpgme.Context() try: return ctx.verify(signature_data, message_data, None) except gpgme.GpgmeError as e: raise GPGProblem(e.message, code=e.code) def decrypt_verify(encrypted): '''Decrypts the given ciphertext string and returns both the signatures (if any) and the plaintext. :param encrypted: the mail to decrypt :returns: a tuple (sigs, plaintext) with sigs being a list of a :class:`gpgme.Signature` and plaintext is a `str` holding the decrypted mail :raises: :class:`~alot.errors.GPGProblem` if the decryption fails ''' encrypted_data = StringIO(encrypted) plaintext_data = StringIO() ctx = gpgme.Context() try: sigs = ctx.decrypt_verify(encrypted_data, plaintext_data) except gpgme.GpgmeError as e: raise GPGProblem(e.message, code=e.code) plaintext_data.seek(0, os.SEEK_SET) return sigs, plaintext_data.read() def hash_key(key): """ Returns a hash of the given key. This is a workaround for https://bugs.launchpad.net/pygpgme/+bug/1089865 and can be removed if the missing feature is added to pygpgme :param key: the key we want a hash of :rtype: a has of the key as string """ hash_str = "" for tmp_key in key.subkeys: hash_str += tmp_key.keyid return hash_str def validate_key(key, sign=False, encrypt=False): if key.revoked: raise GPGProblem("The key \"" + key.uids[0].uid + "\" is revoked.", code=GPGCode.KEY_REVOKED) elif key.expired: raise GPGProblem("The key \"" + key.uids[0].uid + "\" is expired.", code=GPGCode.KEY_EXPIRED) elif key.invalid: raise GPGProblem("The key \"" + key.uids[0].uid + "\" is invalid.", code=GPGCode.KEY_INVALID) if encrypt and not key.can_encrypt: raise GPGProblem("The key \"" + key.uids[0].uid + "\" can not " + "encrypt.", code=GPGCode.KEY_CANNOT_ENCRYPT) if sign and not key.can_sign: raise GPGProblem("The key \"" + key.uids[0].uid + "\" can not sign.", code=GPGCode.KEY_CANNOT_SIGN) alot-0.3.5/alot/db/000077500000000000000000000000001217172710600140065ustar00rootroot00000000000000alot-0.3.5/alot/db/__init__.py000066400000000000000000000004011217172710600161120ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file from thread import Thread from message import Message DB_ENC = 'UTF-8' alot-0.3.5/alot/db/attachment.py000066400000000000000000000061061217172710600165130ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os import tempfile import email.charset as charset from email.header import Header from copy import deepcopy charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8') import alot.helper as helper from alot.helper import string_decode from alot.db.utils import decode_header class Attachment(object): """represents a mail attachment""" def __init__(self, emailpart): """ :param emailpart: a non-multipart email that is the attachment :type emailpart: :class:`email.message.Message` """ self.part = emailpart def __str__(self): desc = '%s:%s (%s)' % (self.get_content_type(), self.get_filename(), helper.humanize_size(self.get_size())) return string_decode(desc) def get_filename(self): """ return name of attached file. If the content-disposition header contains no file name, this returns `None` """ fname = self.part.get_filename() if fname: extracted_name = decode_header(fname) if extracted_name: return os.path.basename(extracted_name) return None def get_content_type(self): """mime type of the attachment part""" ctype = self.part.get_content_type() # replace underspecified mime description by a better guess if ctype in ['octet/stream', 'application/octet-stream']: ctype = helper.guess_mimetype(self.get_data()) return ctype def get_size(self): """returns attachments size in bytes""" return len(self.part.get_payload()) def save(self, path): """ save the attachment to disk. Uses :meth:`~get_filename` in case path is a directory """ filename = self.get_filename() path = os.path.expanduser(path) if os.path.isdir(path): if filename: basename = os.path.basename(filename) FILE = open(os.path.join(path, basename), "w") else: FILE = tempfile.NamedTemporaryFile(delete=False, dir=path) else: FILE = open(path, "w") # this throws IOErrors for invalid path self.write(FILE) FILE.close() return FILE.name def write(self, fhandle): """writes content to a given filehandle""" fhandle.write(self.get_data()) def get_data(self): """return data blob from wrapped file""" return self.part.get_payload(decode=True) def get_mime_representation(self): """returns mime part that constitutes this attachment""" part = deepcopy(self.part) cd = self.part['Content-Disposition'] del part['Content-Disposition'] part['Content-Disposition'] = Header(cd, maxlinelen=78, header_name='Content-Disposition') return part alot-0.3.5/alot/db/envelope.py000066400000000000000000000313351217172710600162020ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os import email import re import glob import email.charset as charset charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8') from email.encoders import encode_7or8bit from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication from alot import __version__ import logging import alot.helper as helper import alot.crypto as crypto import gpgme from alot.settings import settings from alot.errors import GPGProblem, GPGCode from .attachment import Attachment from .utils import encode_header class Envelope(object): """a message that is not yet sent and still editable. It holds references to unencoded! body text and mail headers among other things. Envelope implements the python container API for easy access of header values. So `e['To']`, `e['To'] = 'foo@bar.baz'` and 'e.get_all('To')' would work for an envelope `e`.. """ headers = None """ dict containing the mail headers (a list of strings for each header key) """ body = None """mail body as unicode string""" tmpfile = None """template text for initial content""" attachments = None """list of :class:`Attachments `""" tags = [] """tags to add after successful sendout""" def __init__( self, template=None, bodytext=u'', headers=None, attachments=[], sign=False, sign_key=None, encrypt=False, tags=[]): """ :param template: if not None, the envelope will be initialised by :meth:`parsing ` this string before setting any other values given to this constructor. :type template: str :param bodytext: text used as body part :type bodytext: str :param headers: unencoded header values :type headers: dict (str -> [unicode]) :param attachments: file attachments to include :type attachments: list of :class:`~alot.db.attachment.Attachment` :param tags: tags to add after successful sendout and saving this msg :type tags: list of str """ assert isinstance(bodytext, unicode) logging.debug('TEMPLATE: %s' % template) if template: self.parse_template(template) logging.debug('PARSED TEMPLATE: %s' % template) logging.debug('BODY: %s' % self.body) if self.body is None: self.body = bodytext self.headers = headers or {} self.attachments = list(attachments) self.sign = sign self.sign_key = sign_key self.encrypt = encrypt self.encrypt_keys = {} self.tags = tags # tags to add after successful sendout self.sent_time = None self.modified_since_sent = False self.sending = False # semaphore to avoid accidental double sendout def __str__(self): return "Envelope (%s)\n%s" % (self.headers, self.body) def __setitem__(self, name, val): """setter for header values. this allows adding header like so: >>> envelope['Subject'] = u'sm\xf8rebr\xf8d' """ if name not in self.headers: self.headers[name] = [] self.headers[name].append(val) if self.sent_time: self.modified_since_sent = True def __getitem__(self, name): """getter for header values. :raises: KeyError if undefined """ return self.headers[name][0] def __delitem__(self, name): del(self.headers[name]) if self.sent_time: self.modified_since_sent = True def __contains__(self, name): return self.headers.__contains__(name) def get(self, key, fallback=None): """secure getter for header values that allows specifying a `fallback` return string (defaults to None). This returns the first matching value and doesn't raise KeyErrors""" if key in self.headers: value = self.headers[key][0] else: value = fallback return value def get_all(self, key, fallback=[]): """returns all header values for given key""" if key in self.headers: value = self.headers[key] else: value = fallback return value def add(self, key, value): """add header value""" if key not in self.headers: self.headers[key] = [] self.headers[key].append(value) if self.sent_time: self.modified_since_sent = True def attach(self, attachment, filename=None, ctype=None): """ attach a file :param attachment: File to attach, given as :class:`~alot.db.attachment.Attachment` object or path to a file. :type attachment: :class:`~alot.db.attachment.Attachment` or str :param filename: filename to use in content-disposition. Will be ignored if `path` matches multiple files :param ctype: force content-type to be used for this attachment :type ctype: str """ if isinstance(attachment, Attachment): self.attachments.append(attachment) elif isinstance(attachment, basestring): path = os.path.expanduser(attachment) part = helper.mimewrap(path, filename, ctype) self.attachments.append(Attachment(part)) else: raise TypeError('attach accepts an Attachment or str') if self.sent_time: self.modified_since_sent = True def construct_mail(self): """ compiles the information contained in this envelope into a :class:`email.Message`. """ # Build body text part. To properly sign/encrypt messages later on, we # convert the text to its canonical format (as per RFC 2015). canonical_format = self.body.encode('utf-8') canonical_format = canonical_format.replace('\\t', ' ' * 4) textpart = MIMEText(canonical_format, 'plain', 'utf-8') # wrap it in a multipart container if necessary if self.attachments: inner_msg = MIMEMultipart() inner_msg.attach(textpart) # add attachments for a in self.attachments: inner_msg.attach(a.get_mime_representation()) else: inner_msg = textpart if self.sign: plaintext = crypto.email_as_string(inner_msg) logging.debug('signing plaintext: ' + plaintext) try: signatures, signature_str = crypto.detached_signature_for( plaintext, self.sign_key) if len(signatures) != 1: raise GPGProblem("Could not sign message (GPGME " "did not return a signature)", code=GPGCode.KEY_CANNOT_SIGN) except gpgme.GpgmeError as e: if e.code == gpgme.ERR_BAD_PASSPHRASE: # If GPG_AGENT_INFO is unset or empty, the user just does # not have gpg-agent running (properly). if os.environ.get('GPG_AGENT_INFO', '').strip() == '': msg = "Got invalid passphrase and GPG_AGENT_INFO\ not set. Please set up gpg-agent." raise GPGProblem(msg, code=GPGCode.BAD_PASSPHRASE) else: raise GPGProblem("Bad passphrase. Is gpg-agent " "running?", code=GPGCode.BAD_PASSPHRASE) raise GPGProblem(str(e), code=GPGCode.KEY_CANNOT_SIGN) micalg = crypto.RFC3156_micalg_from_algo(signatures[0].hash_algo) unencrypted_msg = MIMEMultipart('signed', micalg=micalg, protocol= 'application/pgp-signature') # wrap signature in MIMEcontainter stype = 'pgp-signature; name="signature.asc"' signature_mime = MIMEApplication(_data=signature_str, _subtype=stype, _encoder=encode_7or8bit) signature_mime['Content-Description'] = 'signature' signature_mime.set_charset('us-ascii') # add signed message and signature to outer message unencrypted_msg.attach(inner_msg) unencrypted_msg.attach(signature_mime) unencrypted_msg['Content-Disposition'] = 'inline' else: unencrypted_msg = inner_msg if self.encrypt: plaintext = crypto.email_as_string(unencrypted_msg) logging.debug('encrypting plaintext: ' + plaintext) try: encrypted_str = crypto.encrypt(plaintext, self.encrypt_keys.values()) except gpgme.GpgmeError as e: raise GPGProblem(str(e), code=GPGCode.KEY_CANNOT_ENCRYPT) outer_msg = MIMEMultipart('encrypted', protocol='application/pgp-encrypted') version_str = 'Version: 1' encryption_mime = MIMEApplication(_data=version_str, _subtype='pgp-encrypted', _encoder=encode_7or8bit) encryption_mime.set_charset('us-ascii') encrypted_mime = MIMEApplication(_data=encrypted_str, _subtype='octet-stream', _encoder=encode_7or8bit) encrypted_mime.set_charset('us-ascii') outer_msg.attach(encryption_mime) outer_msg.attach(encrypted_mime) else: outer_msg = unencrypted_msg headers = self.headers.copy() # add Message-ID if 'Message-ID' not in headers: headers['Message-ID'] = [email.Utils.make_msgid()] if 'User-Agent' in headers: uastring_format = headers['User-Agent'][0] else: uastring_format = settings.get('user_agent').strip() uastring = uastring_format.format(version=__version__) if uastring: headers['User-Agent'] = [uastring] # copy headers from envelope to mail for k, vlist in headers.items(): for v in vlist: outer_msg[k] = encode_header(k, v) return outer_msg def parse_template(self, tmp, reset=False, only_body=False): """parses a template or user edited string to fills this envelope. :param tmp: the string to parse. :type tmp: str :param reset: remove previous envelope content :type reset: bool """ logging.debug('GoT: """\n%s\n"""' % tmp) if self.sent_time: self.modified_since_sent = True if only_body: self.body = tmp else: m = re.match('(?P([a-zA-Z0-9_-]+:.+\n)*)\n?(?P(\s*.*)*)', tmp) assert m d = m.groupdict() headertext = d['h'] self.body = d['b'] # remove existing content if reset: self.headers = {} # go through multiline, utf-8 encoded headers # we decode the edited text ourselves here as # email.message_from_file can't deal with raw utf8 header values key = value = None for line in headertext.splitlines(): if re.match('[a-zA-Z0-9_-]+:', line): # new k/v pair if key and value: # save old one from stack self.add(key, value) # save key, value = line.strip().split(':', 1) # parse new pair # strip spaces, otherwise we end up having " foo" as value # of "Subject: foo" value = value.strip() elif key and value: # append new line without key prefix value += line if key and value: # save last one if present self.add(key, value) # interpret 'Attach' pseudo header if 'Attach' in self: to_attach = [] for line in self.get_all('Attach'): gpath = os.path.expanduser(line.strip()) to_attach += filter(os.path.isfile, glob.glob(gpath)) logging.debug('Attaching: %s' % to_attach) for path in to_attach: self.attach(path) del(self['Attach']) alot-0.3.5/alot/db/errors.py000066400000000000000000000010331217172710600156710ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file class DatabaseError(Exception): pass class DatabaseROError(DatabaseError): """cannot write to read-only database""" pass class DatabaseLockedError(DatabaseError): """cannot write to locked index""" pass class NonexistantObjectError(DatabaseError): """requested thread or message does not exist in the index""" pass alot-0.3.5/alot/db/manager.py000066400000000000000000000407211217172710600157760ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file from notmuch import Database, NotmuchError, XapianError import notmuch import multiprocessing import logging import sys import os import errno import signal from twisted.internet import reactor from collections import deque from message import Message from alot.settings import settings from thread import Thread from errors import DatabaseError from errors import DatabaseLockedError from errors import DatabaseROError from errors import NonexistantObjectError from alot.db import DB_ENC from alot.db.utils import is_subdir_of class FillPipeProcess(multiprocessing.Process): def __init__(self, it, stdout, stderr, pipe, fun=(lambda x: x)): multiprocessing.Process.__init__(self) self.it = it self.pipe = pipe[1] self.fun = fun self.keep_going = True self.stdout = stdout self.stderr = stderr def handle_sigterm(self, signo, frame): # this is used to suppress any EINTR errors at interpreter # shutdown self.keep_going = False # raises SystemExit to shut down the interpreter from the # signal handler sys.exit() def run(self): # replace filedescriptors 1 and 2 (stdout and stderr) with # pipes to the parent process os.dup2(self.stdout, 1) os.dup2(self.stderr, 2) # register a signal handler for SIGTERM signal.signal(signal.SIGTERM, self.handle_sigterm) for a in self.it: try: self.pipe.send(self.fun(a)) except IOError as e: # suppress spurious EINTR errors at interpreter # shutdown if e.errno != errno.EINTR or self.keep_going: raise self.pipe.close() class DBManager(object): """ Keeps track of your index parameters, maintains a write-queue and lets you look up threads and messages directly to the persistent wrapper classes. """ _sort_orders = { 'oldest_first': notmuch.database.Query.SORT.OLDEST_FIRST, 'newest_first': notmuch.database.Query.SORT.NEWEST_FIRST, 'unsorted': notmuch.database.Query.SORT.UNSORTED, 'message_id': notmuch.database.Query.SORT.MESSAGE_ID, } """constants representing sort orders""" def __init__(self, path=None, ro=False): """ :param path: absolute path to the notmuch index :type path: str :param ro: open the index in read-only mode :type ro: bool """ self.ro = ro self.path = path self.writequeue = deque([]) self.processes = [] def flush(self): """ write out all queued write-commands in order, each one in a separate :meth:`atomic ` transaction. If this fails the current action is rolled back, stays in the write queue and an exception is raised. You are responsible to retry flushing at a later time if you want to ensure that the cached changes are applied to the database. :exception: :exc:`~errors.DatabaseROError` if db is opened read-only :exception: :exc:`~errors.DatabaseLockedError` if db is locked """ if self.ro: raise DatabaseROError() if self.writequeue: # read notmuch's config regarding imap flag synchronization sync = settings.get_notmuch_setting('maildir', 'synchronize_flags') # go through writequeue entries while self.writequeue: current_item = self.writequeue.popleft() logging.debug('write-out item: %s' % str(current_item)) # watch out for notmuch errors to re-insert current_item # to the queue on errors try: # the first two coordinants are cnmdname and post-callback cmd, afterwards = current_item[:2] logging.debug('cmd created') # aquire a writeable db handler try: mode = Database.MODE.READ_WRITE db = Database(path=self.path, mode=mode) except NotmuchError: raise DatabaseLockedError() logging.debug('got write lock') # make this a transaction db.begin_atomic() logging.debug('got atomic') if cmd == 'add': logging.debug('add') path, tags = current_item[2:] msg, status = db.add_message(path, sync_maildir_flags=sync) logging.debug('added msg') msg.freeze() logging.debug('freeze') for tag in tags: msg.add_tag(tag.encode(DB_ENC), sync_maildir_flags=sync) logging.debug('added tags ') msg.thaw() logging.debug('thaw') elif cmd == 'remove': path = current_item[2] db.remove_message(path) else: # tag/set/untag querystring, tags = current_item[2:] query = db.create_query(querystring) for msg in query.search_messages(): msg.freeze() if cmd == 'tag': for tag in tags: msg.add_tag(tag.encode(DB_ENC), sync_maildir_flags=sync) if cmd == 'set': msg.remove_all_tags() for tag in tags: msg.add_tag(tag.encode(DB_ENC), sync_maildir_flags=sync) elif cmd == 'untag': for tag in tags: msg.remove_tag(tag.encode(DB_ENC), sync_maildir_flags=sync) msg.thaw() logging.debug('ended atomic') # end transaction and reinsert queue item on error if db.end_atomic() != notmuch.STATUS.SUCCESS: raise DatabaseError('end_atomic failed') logging.debug('ended atomic') # close db db.close() logging.debug('closed db') # call post-callback if callable(afterwards): logging.debug(str(afterwards)) afterwards() logging.debug('called callback') # re-insert item to the queue upon Xapian/NotmuchErrors except (XapianError, NotmuchError) as e: logging.exception(e) self.writequeue.appendleft(current_item) raise DatabaseError(unicode(e)) except DatabaseLockedError as e: logging.debug('index temporarily locked') self.writequeue.appendleft(current_item) raise e logging.debug('flush finished') def kill_search_processes(self): """ terminate all search processes that originate from this managers :meth:`get_threads`. """ for p in self.processes: p.terminate() self.processes = [] def tag(self, querystring, tags, afterwards=None, remove_rest=False): """ add tags to messages matching `querystring`. This appends a tag operation to the write queue and raises :exc:`~errors.DatabaseROError` if in read only mode. :param querystring: notmuch search string :type querystring: str :param tags: a list of tags to be added :type tags: list of str :param afterwards: callback that gets called after successful application of this tagging operation :type afterwards: callable :param remove_rest: remove tags from matching messages before tagging :type remove_rest: bool :exception: :exc:`~errors.DatabaseROError` .. note:: This only adds the requested operation to the write queue. You need to call :meth:`DBManager.flush` to actually write out. """ if self.ro: raise DatabaseROError() if remove_rest: self.writequeue.append(('set', afterwards, querystring, tags)) else: self.writequeue.append(('tag', afterwards, querystring, tags)) def untag(self, querystring, tags, afterwards=None): """ removes tags from messages that match `querystring`. This appends an untag operation to the write queue and raises :exc:`~errors.DatabaseROError` if in read only mode. :param querystring: notmuch search string :type querystring: str :param tags: a list of tags to be added :type tags: list of str :param afterwards: callback that gets called after successful application of this tagging operation :type afterwards: callable :exception: :exc:`~errors.DatabaseROError` .. note:: This only adds the requested operation to the write queue. You need to call :meth:`DBManager.flush` to actually write out. """ if self.ro: raise DatabaseROError() self.writequeue.append(('untag', afterwards, querystring, tags)) def count_messages(self, querystring): """returns number of messages that match `querystring`""" return self.query(querystring).count_messages() def count_threads(self, querystring): """returns number of threads that match `querystring`""" return self.query(querystring).count_threads() def search_thread_ids(self, querystring): """ returns the ids of all threads that match the `querystring` This copies! all integer thread ids into an new list. :returns: list of str """ return self.query_threaded(querystring) def _get_notmuch_thread(self, tid): """returns :class:`notmuch.database.Thread` with given id""" query = self.query('thread:' + tid) try: return query.search_threads().next() except StopIteration: errmsg = 'no thread with id %s exists!' % tid raise NonexistantObjectError(errmsg) def get_thread(self, tid): """returns :class:`Thread` with given thread id (str)""" return Thread(self, self._get_notmuch_thread(tid)) def _get_notmuch_message(self, mid): """returns :class:`notmuch.database.Message` with given id""" mode = Database.MODE.READ_ONLY db = Database(path=self.path, mode=mode) try: return db.find_message(mid) except: errmsg = 'no message with id %s exists!' % mid raise NonexistantObjectError(errmsg) def get_message(self, mid): """returns :class:`Message` with given message id (str)""" return Message(self, self._get_notmuch_message(mid)) def get_all_tags(self): """ returns all tagsstrings used in the database :rtype: list of str """ db = Database(path=self.path) return [t for t in db.get_all_tags()] def async(self, cbl, fun): """ return a pair (pipe, process) so that the process writes `fun(a)` to the pipe for each element `a` in the iterable returned by the callable `cbl`. :param cbl: a function returning something iterable :type cbl: callable :param fun: an unary translation function :type fun: callable :rtype: (:class:`multiprocessing.Pipe`, :class:`multiprocessing.Process`) """ # create two unix pipes to redirect the workers stdout and # stderr stdout = os.pipe() stderr = os.pipe() # create a multiprocessing pipe for the results pipe = multiprocessing.Pipe(False) receiver, sender = pipe process = FillPipeProcess(cbl(), stdout[1], stderr[1], pipe, fun) process.start() self.processes.append(process) logging.debug('Worker process {0} spawned'.format(process.pid)) def threaded_wait(): # wait(2) for the process to die process.join() if process.exitcode < 0: msg = 'received signal {0}'.format(-process.exitcode) elif process.exitcode > 0: msg = 'returned error code {0}'.format(process.exitcode) else: msg = 'exited successfully' logging.debug('Worker process {0} {1}'.format(process.pid, msg)) self.processes.remove(process) # spawn a thread to collect the worker process once it dies # preventing it from hanging around as zombie reactor.callInThread(threaded_wait) def threaded_reader(prefix, fd): with os.fdopen(fd) as handle: for line in handle: logging.debug('Worker process {0} said on {1}: {2}'.format( process.pid, prefix, line.rstrip())) # spawn two threads that read from the stdout and stderr pipes # and write anything that appears there to the log reactor.callInThread(threaded_reader, 'stdout', stdout[0]) os.close(stdout[1]) reactor.callInThread(threaded_reader, 'stderr', stderr[0]) os.close(stderr[1]) # closing the sending end in this (receiving) process guarantees # that here the apropriate EOFError is raised upon .recv in the walker sender.close() return receiver, process def get_threads(self, querystring, sort='newest_first'): """ asynchronously look up thread ids matching `querystring`. :param querystring: The query string to use for the lookup :type querystring: str. :param sort: Sort order. one of ['oldest_first', 'newest_first', 'message_id', 'unsorted'] :type query: str :returns: a pipe together with the process that asynchronously writes to it. :rtype: (:class:`multiprocessing.Pipe`, :class:`multiprocessing.Process`) """ assert sort in self._sort_orders.keys() q = self.query(querystring) q.set_sort(self._sort_orders[sort]) return self.async(q.search_threads, (lambda a: a.get_thread_id())) def query(self, querystring): """ creates :class:`notmuch.Query` objects on demand :param querystring: The query string to use for the lookup :type query: str. :returns: :class:`notmuch.Query` -- the query object. """ mode = Database.MODE.READ_ONLY db = Database(path=self.path, mode=mode) return db.create_query(querystring) def add_message(self, path, tags=[], afterwards=None): """ Adds a file to the notmuch index. :param path: path to the file :type path: str :param tags: tagstrings to add :type tags: list of str :param afterwards: callback to trigger after adding :type afterwards: callable or None """ if self.ro: raise DatabaseROError() if not is_subdir_of(path,self.path): msg = 'message path %s ' % path msg += ' is not below notmuchs ' msg += 'root path (%s)' % self.path raise DatabaseError(msg) else: self.writequeue.append(('add', afterwards, path, tags)) def remove_message(self, message, afterwards=None): """ Remove a message from the notmuch index :param message: message to remove :type message: :class:`Message` :param afterwards: callback to trigger after removing :type afterwards: callable or None """ if self.ro: raise DatabaseROError() path = message.get_filename() self.writequeue.append(('remove', afterwards, path)) alot-0.3.5/alot/db/message.py000066400000000000000000000217431217172710600160130ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import email from datetime import datetime import email.charset as charset charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8') from notmuch import NullPointerError import alot.helper as helper from alot.settings import settings from .utils import extract_headers, extract_body, message_from_file from alot.db.utils import decode_header from .attachment import Attachment class Message(object): """ a persistent notmuch message object. It it uses a :class:`~alot.db.DBManager` for cached manipulation and lazy lookups. """ def __init__(self, dbman, msg, thread=None): """ :param dbman: db manager that is used for further lookups :type dbman: alot.db.DBManager :param msg: the wrapped message :type msg: notmuch.database.Message :param thread: this messages thread (will be looked up later if `None`) :type thread: :class:`~alot.db.Thread` or `None` """ self._dbman = dbman self._id = msg.get_message_id() self._thread_id = msg.get_thread_id() self._thread = thread casts_date = lambda: datetime.fromtimestamp(msg.get_date()) self._datetime = helper.safely_get(casts_date, ValueError, None) self._filename = msg.get_filename() author = helper.safely_get(lambda: msg.get_header('From'), NullPointerError) self._from = decode_header(author) self._email = None # will be read upon first use self._attachments = None # will be read upon first use self._tags = set(msg.get_tags()) def __str__(self): """prettyprint the message""" aname, aaddress = self.get_author() if not aname: aname = aaddress return "%s (%s)" % (aname, self.get_datestring()) def __hash__(self): """needed for sets of Messages""" return hash(self._id) def __cmp__(self, other): """needed for Message comparison""" res = cmp(self.get_message_id(), other.get_message_id()) return res def get_email(self): """returns :class:`email.Message` for this message""" path = self.get_filename() warning = "Subject: Caution!\n"\ "Message file is no longer accessible:\n%s" % path if not self._email: try: f_mail = open(path) self._email = message_from_file(f_mail) f_mail.close() except IOError: self._email = email.message_from_string(warning) return self._email def get_date(self): """returns Date header value as :class:`~datetime.datetime`""" return self._datetime def get_filename(self): """returns absolute path of message files location""" return self._filename def get_message_id(self): """returns messages id (str)""" return self._id def get_thread_id(self): """returns id (str) of the thread this message belongs to""" return self._thread_id def get_message_parts(self): """returns a list of all body parts of this message""" # TODO really needed? email iterators can do this out = [] for msg in self.get_email().walk(): if not msg.is_multipart(): out.append(msg) return out def get_tags(self): """returns tags attached to this message as list of strings""" l = sorted(self._tags) return l def get_thread(self): """returns the :class:`~alot.db.Thread` this msg belongs to""" if not self._thread: self._thread = self._dbman.get_thread(self._thread_id) return self._thread def has_replies(self): """returns true if this message has at least one reply""" return (len(self.get_replies()) > 0) def get_replies(self): """returns replies to this message as list of :class:`Message`""" t = self.get_thread() return t.get_replies_to(self) def get_datestring(self): """ returns reformated datestring for this message. It uses :meth:`SettingsManager.represent_datetime` to represent this messages `Date` header :rtype: str """ if self._datetime is None: res = None else: res = settings.represent_datetime(self._datetime) return res def get_author(self): """ returns realname and address of this messages author :rtype: (str,str) """ return email.Utils.parseaddr(self._from) def get_headers_string(self, headers): """ returns subset of this messages headers as human-readable format: all header values are decoded, the resulting string has one line "KEY: VALUE" for each requested header present in the mail. :param headers: headers to extract :type headers: list of str """ return extract_headers(self.get_email(), headers) def add_tags(self, tags, afterwards=None, remove_rest=False): """ adds tags to message .. note:: This only adds the requested operation to this objects :class:`DBManager's ` write queue. You need to call :meth:`~alot.db.DBManager.flush` to write out. :param tags: a list of tags to be added :type tags: list of str :param afterwards: callback that gets called after successful application of this tagging operation :type afterwards: callable :param remove_rest: remove all other tags :type remove_rest: bool """ def myafterwards(): if remove_rest: self._tags = set(tags) else: self._tags = self._tags.union(tags) if callable(afterwards): afterwards() self._dbman.tag('id:' + self._id, tags, afterwards=myafterwards, remove_rest=remove_rest) self._tags = self._tags.union(tags) def remove_tags(self, tags, afterwards=None): """remove tags from message .. note:: This only adds the requested operation to this objects :class:`DBManager's ` write queue. You need to call :meth:`~alot.db.DBManager.flush` to actually out. :param tags: a list of tags to be added :type tags: list of str :param afterwards: callback that gets called after successful application of this tagging operation :type afterwards: callable """ def myafterwards(): self._tags = self._tags.difference(tags) if callable(afterwards): afterwards() self._dbman.untag('id:' + self._id, tags, myafterwards) def get_attachments(self): """ returns messages attachments Derived from the leaves of the email mime tree that and are not part of :rfc:`2015` syntax for encrypted/signed mails and either have :mailheader:`Content-Disposition` `attachment` or have :mailheader:`Content-Disposition` `inline` but specify a filename (as parameter to `Content-Disposition`). :rtype: list of :class:`Attachment` """ if not self._attachments: self._attachments = [] for part in self.get_message_parts(): cd = part.get('Content-Disposition', '') filename = part.get_filename() ct = part.get_content_type() # replace underspecified mime description by a better guess if ct in ['octet/stream', 'application/octet-stream']: content = part.get_payload(decode=True) ct = helper.guess_mimetype(content) if cd.startswith('attachment'): if ct not in ['application/pgp-encrypted', 'application/pgp-signature']: self._attachments.append(Attachment(part)) elif cd.startswith('inline'): if filename is not None and ct != 'application/pgp': self._attachments.append(Attachment(part)) return self._attachments def accumulate_body(self): """ returns bodystring extracted from this mail """ # TODO: allow toggle commands to decide which part is considered body return extract_body(self.get_email()) def get_text_content(self): return extract_body(self.get_email(), types=['text/plain']) def matches(self, querystring): """tests if this messages is in the resultset for `querystring`""" searchfor = querystring + ' AND id:' + self._id return self._dbman.count_messages(searchfor) > 0 alot-0.3.5/alot/db/thread.py000066400000000000000000000231461217172710600156350ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file from datetime import datetime from message import Message from alot.settings import settings class Thread(object): """ A wrapper around a notmuch mailthread (:class:`notmuch.database.Thread`) that ensures persistence of the thread: It can be safely read multiple times, its manipulation is done via a :class:`alot.db.DBManager` and it can directly provide contained messages as :class:`~alot.db.message.Message`. """ def __init__(self, dbman, thread): """ :param dbman: db manager that is used for further lookups :type dbman: :class:`~alot.db.DBManager` :param thread: the wrapped thread :type thread: :class:`notmuch.database.Thread` """ self._dbman = dbman self._id = thread.get_thread_id() self.refresh(thread) def refresh(self, thread=None): """refresh thread metadata from the index""" if not thread: thread = self._dbman._get_notmuch_thread(self._id) self._total_messages = thread.get_total_messages() self._notmuch_authors_string = thread.get_authors() self._subject = thread.get_subject() self._authors = None ts = thread.get_oldest_date() try: self._oldest_date = datetime.fromtimestamp(ts) except ValueError: # year is out of range self._oldest_date = None try: timestamp = thread.get_newest_date() self._newest_date = datetime.fromtimestamp(timestamp) except ValueError: # year is out of range self._newest_date = None self._tags = set([t for t in thread.get_tags()]) self._messages = {} # this maps messages to its children self._toplevel_messages = [] def __str__(self): return "thread:%s: %s" % (self._id, self.get_subject()) def get_thread_id(self): """returns id of this thread""" return self._id def get_tags(self, intersection=False): """ returns tagsstrings attached to this thread :param intersection: return tags present in all contained messages instead of in at least one (union) :type intersection: bool :rtype: set of str """ tags = set(list(self._tags)) if intersection: for m in self.get_messages().keys(): tags = tags.intersection(set(m.get_tags())) return tags def add_tags(self, tags, afterwards=None, remove_rest=False): """ add `tags` to all messages in this thread .. note:: This only adds the requested operation to this objects :class:`DBManager's ` write queue. You need to call :meth:`DBManager.flush ` to actually write out. :param tags: a list of tags to be added :type tags: list of str :param afterwards: callback that gets called after successful application of this tagging operation :type afterwards: callable :param remove_rest: remove all other tags :type remove_rest: bool """ def myafterwards(): if remove_rest: self._tags = set(tags) else: self._tags = self._tags.union(tags) if callable(afterwards): afterwards() self._dbman.tag('thread:' + self._id, tags, afterwards=myafterwards, remove_rest=remove_rest) def remove_tags(self, tags, afterwards=None): """ remove `tags` (list of str) from all messages in this thread .. note:: This only adds the requested operation to this objects :class:`DBManager's ` write queue. You need to call :meth:`DBManager.flush ` to actually write out. :param tags: a list of tags to be added :type tags: list of str :param afterwards: callback that gets called after successful application of this tagging operation :type afterwards: callable """ rmtags = set(tags).intersection(self._tags) if rmtags: def myafterwards(): self._tags = self._tags.difference(tags) if callable(afterwards): afterwards() self._dbman.untag('thread:' + self._id, tags, myafterwards) self._tags = self._tags.difference(rmtags) def get_authors(self): """ returns a list of authors (name, addr) of the messages. The authors are ordered by msg date and unique (by addr). :rtype: list of (str, str) """ if self._authors is None: self._authors = [] seen = {} msgs = self.get_messages().keys() msgs_with_date = filter(lambda m: m.get_date() is not None, msgs) msgs_without_date = filter(lambda m: m.get_date() is None, msgs) # sort messages with date and append the others msgs_with_date.sort(None, lambda m: m.get_date()) msgs = msgs_with_date + msgs_without_date for m in msgs: pair = m.get_author() if not pair[1] in seen: seen[pair[1]] = True self._authors.append(pair) return self._authors def get_authors_string(self, own_addrs=None, replace_own=None): """ returns a string of comma-separated authors Depending on settings, it will substitute "me" for author name if address is user's own. :param own_addrs: list of own email addresses to replace :type own_addrs: list of str :param replace_own: whether or not to actually do replacement :type replace_own: bool :rtype: str """ if replace_own is None: replace_own = settings.get('thread_authors_replace_me') if replace_own: if own_addrs is None: own_addrs = settings.get_addresses() authorslist = [] for aname, aaddress in self.get_authors(): if aaddress in own_addrs: aname = settings.get('thread_authors_me') if not aname: aname = aaddress if not aname in authorslist: authorslist.append(aname) return ', '.join(authorslist) else: return self._notmuch_authors_string def get_subject(self): """returns subject string""" return self._subject def get_toplevel_messages(self): """ returns all toplevel messages contained in this thread. This are all the messages without a parent message (identified by 'in-reply-to' or 'references' header. :rtype: list of :class:`~alot.db.message.Message` """ if not self._messages: self.get_messages() return self._toplevel_messages def get_messages(self): """ returns all messages in this thread as dict mapping all contained messages to their direct responses. :rtype: dict mapping :class:`~alot.db.message.Message` to a list of :class:`~alot.db.message.Message`. """ if not self._messages: # if not already cached query = self._dbman.query('thread:' + self._id) thread = query.search_threads().next() def accumulate(acc, msg): M = Message(self._dbman, msg, thread=self) acc[M] = [] r = msg.get_replies() if r is not None: for m in r: acc[M].append(accumulate(acc, m)) return M self._messages = {} for m in thread.get_toplevel_messages(): self._toplevel_messages.append(accumulate(self._messages, m)) return self._messages def get_replies_to(self, msg): """ returns all replies to the given message contained in this thread. :param msg: parent message to look up :type msg: :class:`~alot.db.message.Message` :returns: list of :class:`~alot.db.message.Message` or `None` """ mid = msg.get_message_id() msg_hash = self.get_messages() for m in msg_hash.keys(): if m.get_message_id() == mid: return msg_hash[m] return None def get_newest_date(self): """ returns date header of newest message in this thread as :class:`~datetime.datetime` """ return self._newest_date def get_oldest_date(self): """ returns date header of oldest message in this thread as :class:`~datetime.datetime` """ return self._oldest_date def get_total_messages(self): """returns number of contained messages""" return self._total_messages def matches(self, query): """ Check if this thread matches the given notmuch query. :param query: The query to check against :type query: string :returns: True if this thread matches the given query, False otherwise :rtype: bool """ thread_query = 'thread:{tid} AND {subquery}'.format(tid=self._id, subquery=query) num_matches = self._dbman.count_messages(thread_query) return num_matches > 0 alot-0.3.5/alot/db/utils.py000066400000000000000000000354001217172710600155220ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os import email import tempfile import re from email.header import Header import email.charset as charset charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8') from email.iterators import typed_subpart_iterator import logging import mailcap from cStringIO import StringIO import alot.crypto as crypto import alot.helper as helper from alot.errors import GPGProblem from alot.settings import settings from alot.helper import string_sanitize from alot.helper import string_decode from alot.helper import parse_mailcap_nametemplate from alot.helper import split_commandstring X_SIGNATURE_VALID_HEADER = 'X-Alot-OpenPGP-Signature-Valid' X_SIGNATURE_MESSAGE_HEADER = 'X-Alot-OpenPGP-Signature-Message' def add_signature_headers(mail, sigs, error_msg): '''Add pseudo headers to the mail indicating whether the signature verification was successful. :param mail: :class:`email.message.Message` the message to entitle :param sigs: list of :class:`gpgme.Signature` :param error_msg: `str` containing an error message, the empty string indicating no error ''' sig_from = '' if len(sigs) == 0: error_msg = error_msg or 'no signature found' else: try: sig_from = crypto.get_key(sigs[0].fpr).uids[0].uid except: sig_from = sigs[0].fpr mail.add_header( X_SIGNATURE_VALID_HEADER, 'False' if error_msg else 'True', ) mail.add_header( X_SIGNATURE_MESSAGE_HEADER, 'Invalid: {0}'.format(error_msg) if error_msg else 'Valid: {0}'.format(sig_from), ) def get_params(mail, failobj=list(), header='content-type', unquote=True): '''Get Content-Type parameters as dict. RFC 2045 specifies that parameter names are case-insensitive, so we normalize them here. :param mail: :class:`email.message.Message` :param failobj: object to return if no such header is found :param header: the header to search for parameters, default :param unquote: unquote the values :returns: a `dict` containing the parameters ''' return {k.lower(): v for k, v in mail.get_params(failobj, header, unquote)} def message_from_file(handle): '''Reads a mail from the given file-like object and returns an email object, very much like email.message_from_file. In addition to that OpenPGP encrypted data is detected and decrypted. If this succeeds, any mime messages found in the recovered plaintext message are added to the returned message object. :param handle: a file-like object :returns: :class:`email.message.Message` possibly augmented with decrypted data ''' m = email.message_from_file(handle) # make sure noone smuggles a token in (data from m is untrusted) del m[X_SIGNATURE_VALID_HEADER] del m[X_SIGNATURE_MESSAGE_HEADER] p = get_params(m) app_pgp_sig = 'application/pgp-signature' app_pgp_enc = 'application/pgp-encrypted' # handle OpenPGP signed data if (m.is_multipart() and m.get_content_subtype() == 'signed' and p.get('protocol', None) == app_pgp_sig): # RFC 3156 is quite strict: # * exactly two messages # * the second is of type 'application/pgp-signature' # * the second contains the detached signature malformed = False if len(m.get_payload()) != 2: malformed = 'expected exactly two messages, got {0}'.format( len(m.get_payload())) ct = m.get_payload(1).get_content_type() if ct != app_pgp_sig: malformed = 'expected Content-Type: {0}, got: {1}'.format( app_pgp_sig, ct) # TODO: RFC 3156 says the alg has to be lower case, but I've # seen a message with 'PGP-'. maybe we should be more # permissive here, or maybe not, this is crypto stuff... if not p.get('micalg', 'nothing').startswith('pgp-'): malformed = 'expected micalg=pgp-..., got: {0}'.format( p.get('micalg', 'nothing')) sigs = [] if not malformed: try: sigs = crypto.verify_detached(m.get_payload(0).as_string(), m.get_payload(1).get_payload()) except GPGProblem as e: malformed = str(e) add_signature_headers(m, sigs, malformed) # handle OpenPGP encrypted data elif (m.is_multipart() and m.get_content_subtype() == 'encrypted' and p.get('protocol', None) == app_pgp_enc and 'Version: 1' in m.get_payload(0).get_payload()): # RFC 3156 is quite strict: # * exactly two messages # * the first is of type 'application/pgp-encrypted' # * the first contains 'Version: 1' # * the second is of type 'application/octet-stream' # * the second contains the encrypted and possibly signed data malformed = False ct = m.get_payload(0).get_content_type() if ct != app_pgp_enc: malformed = 'expected Content-Type: {0}, got: {1}'.format( app_pgp_enc, ct) want = 'application/octet-stream' ct = m.get_payload(1).get_content_type() if ct != want: malformed = 'expected Content-Type: {0}, got: {1}'.format(want, ct) if not malformed: try: sigs, d = crypto.decrypt_verify(m.get_payload(1).get_payload()) except GPGProblem as e: # signature verification failures end up here too if # the combined method is used, currently this prevents # the interpretation of the recovered plain text # mail. maybe that's a feature. malformed = str(e) else: # parse decrypted message n = message_from_string(d) # add the decrypted message to m. note that n contains # all the attachments, no need to walk over n here. m.attach(n) # add any defects found m.defects.extend(n.defects) # there are two methods for both signed and encrypted # data, one is called 'RFC 1847 Encapsulation' by # RFC 3156, and one is the 'Combined method'. if len(sigs) == 0: # 'RFC 1847 Encapsulation', the signature is a # detached signature found in the recovered mime # message of type multipart/signed. if X_SIGNATURE_VALID_HEADER in n: for k in (X_SIGNATURE_VALID_HEADER, X_SIGNATURE_MESSAGE_HEADER): m[k] = n[k] else: # an encrypted message without signatures # should arouse some suspicion, better warn # the user add_signature_headers(m, [], 'no signature found') else: # 'Combined method', the signatures are returned # by the decrypt_verify function. # note that if we reached this point, we know the # signatures are valid. if they were not valid, # the else block of the current try would not have # been executed add_signature_headers(m, sigs, '') if malformed: msg = 'Malformed OpenPGP message: {0}'.format(malformed) m.attach(email.message_from_string(msg)) return m def message_from_string(s): '''Reads a mail from the given string. This is the equivalent of :func:`email.message_from_string` which does nothing but to wrap the given string in a StringIO object and to call :func:`email.message_from_file`. Please refer to the documentation of :func:`message_from_file` for details. ''' return message_from_file(StringIO(s)) def extract_headers(mail, headers=None): """ returns subset of this messages headers as human-readable format: all header values are decoded, the resulting string has one line "KEY: VALUE" for each requested header present in the mail. :param mail: the mail to use :type mail: :class:`email.Message` :param headers: headers to extract :type headers: list of str """ headertext = u'' if headers is None: headers = mail.keys() for key in headers: value = u'' if key in mail: value = decode_header(mail.get(key, '')) headertext += '%s: %s\n' % (key, value) return headertext def extract_body(mail, types=None): """ returns a body text string for given mail. If types is `None`, `text/*` is used: The exact preferred type is specified by the prefer_plaintext config option which defaults to text/html. :param mail: the mail to use :type mail: :class:`email.Message` :param types: mime content types to use for body string :type types: list of str """ preferred = 'text/plain' if settings.get( 'prefer_plaintext') else 'text/html' has_preferred = False # see if the mail has our preferred type if types is None: has_preferred = list(typed_subpart_iterator( mail, *preferred.split('/'))) body_parts = [] for part in mail.walk(): ctype = part.get_content_type() if types is not None: if ctype not in types: continue cd = part.get('Content-Disposition', '') if cd.startswith('attachment'): continue # if the mail has our preferred type, we only keep this type # note that if types != None, has_preferred always stays False if has_preferred and ctype != preferred: continue enc = part.get_content_charset() or 'ascii' raw_payload = part.get_payload(decode=True) if ctype == 'text/plain': raw_payload = string_decode(raw_payload, enc) body_parts.append(string_sanitize(raw_payload)) else: # get mime handler key = 'copiousoutput' handler, entry = settings.mailcap_find_match(ctype, key=key) tempfile_name = None stdin = None if entry: handler_raw_commandstring = entry['view'] # in case the mailcap defined command contains no '%s', # we pipe the files content to the handling command via stdin if '%s' in handler_raw_commandstring: # open tempfile, respect mailcaps nametemplate nametemplate = entry.get('nametemplate', '%s') prefix, suffix = parse_mailcap_nametemplate(nametemplate) tmpfile = tempfile.NamedTemporaryFile(delete=False, prefix=prefix, suffix=suffix) # write payload to tmpfile tmpfile.write(raw_payload) tmpfile.close() tempfile_name = tmpfile.name else: stdin = raw_payload # read parameter, create handler command parms = tuple(map('='.join, part.get_params())) # create and call external command cmd = mailcap.subst(entry['view'], ctype, filename=tempfile_name, plist=parms) logging.debug('command: %s' % cmd) logging.debug('parms: %s' % str(parms)) cmdlist = split_commandstring(cmd) # call handler rendered_payload, errmsg, retval = helper.call_cmd( cmdlist, stdin=stdin) # remove tempfile if tempfile_name: os.unlink(tempfile_name) if rendered_payload: # handler had output body_parts.append(string_sanitize(rendered_payload)) return u'\n\n'.join(body_parts) def decode_header(header, normalize=False): """ decode a header value to a unicode string values are usually a mixture of different substrings encoded in quoted printable using diffetrent encodings. This turns it into a single unicode string :param header: the header value :type header: str :param normalize: replace trailing spaces after newlines :type normalize: bool :rtype: unicode """ # If the value isn't ascii as RFC2822 prescribes, # we just return the unicode bytestring as is value = string_decode(header) # convert to unicode try: value = value.encode('ascii') except UnicodeEncodeError: return value # some mailers send out incorrectly escaped headers # and double quote the escaped realname part again. remove those value = re.sub(r'\"(.*?=\?.*?.*?)\"', r'\1', value) # otherwise we interpret RFC2822 encoding escape sequences valuelist = email.header.decode_header(value) decoded_list = [] for v, enc in valuelist: v = string_decode(v, enc) decoded_list.append(string_sanitize(v)) value = u' '.join(decoded_list) if normalize: value = re.sub(r'\n\s+', r' ', value) return value def encode_header(key, value): """ encodes a unicode string as a valid header value :param key: the header field this value will be stored in :type key: str :param value: the value to be encoded :type value: unicode """ # handle list of "realname " entries separately if key.lower() in ['from', 'to', 'cc', 'bcc']: rawentries = value.split(',') encodedentries = [] for entry in rawentries: m = re.search('\s*(.*)\s+<(.*\@.*\.\w*)>\s*$', entry) if m: # If a realname part is contained name, address = m.groups() # try to encode as ascii, if that fails, revert to utf-8 # name must be a unicode string here namepart = Header(name) # append address part encoded as ascii entry = '%s <%s>' % (namepart.encode(), address) encodedentries.append(entry) value = Header(', '.join(encodedentries)) else: value = Header(value) return value def is_subdir_of(subpath, superpath): #make both absolute superpath = os.path.realpath(superpath) subpath = os.path.realpath(subpath) #return true, if the common prefix of both is equal to directory #e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b return os.path.commonprefix([subpath, superpath]) == superpath alot-0.3.5/alot/defaults/000077500000000000000000000000001217172710600152305ustar00rootroot00000000000000alot-0.3.5/alot/defaults/abook_contacts.spec000066400000000000000000000002041217172710600210710ustar00rootroot00000000000000[format] program = string version = string [__many__] name = string(default=None) email = force_list(default=list()) alot-0.3.5/alot/defaults/alot.rc.spec000066400000000000000000000314201217172710600174460ustar00rootroot00000000000000 ask_subject = boolean(default=True) # ask for subject when compose # automatically remove 'unread' tag when focussing messages in thread mode auto_remove_unread = boolean(default=True) # prompt for initial tags when compose compose_ask_tags = boolean(default=False) # directory prefix for downloading attachments attachment_prefix = string(default='~') # timeout in (floating point) seconds until partial input is cleared input_timeout = float(default=1.0) # confirm exit bug_on_exit = boolean(default=False) # offset of next focused buffer if the current one gets closed bufferclose_focus_offset = integer(default=-1) # number of colours to use colourmode = option(1, 16, 256, default=256) # number of spaces used to replace tab characters tabwidth = integer(default=8) # templates directory that contains your message templates. # It will be used if you give `compose --template` a filename without a path prefix. template_dir = string(default='$XDG_CONFIG_HOME/alot/templates') # directory containing theme files themes_dir = string(default=None) # name of the theme to use theme = string(default=None) # headers that get displayed by default displayed_headers = force_list(default=list(From,To,Cc,Bcc,Subject)) # headers that are hidden in envelope buffers by default envelope_headers_blacklist = force_list(default=list(In-Reply-To,References)) # Replace own email addresses with "me" in author lists # Uses own addresses and aliases in all configured accounts. thread_authors_replace_me = boolean(default=True) # Word to replace own addresses with. Works in combination with # :ref:`thread_authors_replace_me ` thread_authors_me = string(default='Me') # set terminal command used for spawning shell commands terminal_cmd = string(default='x-terminal-emulator -e') # editor command # if unset, alot will first try the :envvar:`EDITOR` env variable, then :file:`/usr/bin/editor` editor_cmd = string(default=None) # file encoding used by your editor editor_writes_encoding = string(default='UTF-8') # use terminal_command to spawn a new terminal for the editor? # equivalent to always providing the `--spawn=yes` parameter to compose/edit commands editor_spawn = boolean(default=False) # call editor in separate thread. # In case your editor doesn't run in the same window as alot, setting true here # will make alot non-blocking during edits editor_in_thread = boolean(default=False) # Which header fields should be editable in your editor # used are those that match the whitelist and don't match the blacklist. # in both cases '*' may be used to indicate all fields. edit_headers_whitelist = force_list(default=list(*,)) # see :ref:`edit_headers_whitelist ` edit_headers_blacklist = force_list(default=list(Content-Type,MIME-Version,References,In-Reply-To)) # timeout in seconds after a failed attempt to writeout the database is repeated flush_retry_timeout = integer(default=5) # where to look up hooks hooksfile = string(default='~/.config/alot/hooks.py') # time in secs to display status messages notify_timeout = integer(default=2) # display status-bar at the bottom of the screen? show_statusbar = boolean(default=True) # Format of the status-bar in bufferlist mode. # This is a pair of strings to be left and right aligned in the status-bar that may contain variables: # # * `{buffer_no}`: index of this buffer in the global buffer list # * `{total_messages}`: total numer of messages indexed by notmuch # * `{pending_writes}`: number of pending write operations to the index bufferlist_statusbar = mixed_list(string, string, default=list('[{buffer_no}: bufferlist]','{input_queue} total messages: {total_messages}')) # Format of the status-bar in search mode. # This is a pair of strings to be left and right aligned in the status-bar. # Apart from the global variables listed at :ref:`bufferlist_statusbar ` # these strings may contain variables: # # * `{querystring}`: search string # * `{result_count}`: number of matching messages # * `{result_count_positive}`: 's' if result count is greater than 0. search_statusbar = mixed_list(string, string, default=list('[{buffer_no}: search] for "{querystring}"','{input_queue} {result_count} of {total_messages} messages')) # Format of the status-bar in thread mode. # This is a pair of strings to be left and right aligned in the status-bar. # Apart from the global variables listed at :ref:`bufferlist_statusbar ` # these strings may contain variables: # # * `{tid}`: thread id # * `{subject}`: subject line of the thread # * `{authors}`: abbreviated authors string for this thread # * `{message_count}`: number of contained messages thread_statusbar = mixed_list(string, string, default=list('[{buffer_no}: thread] {subject}','{input_queue} total messages: {total_messages}')) # Format of the status-bar in taglist mode. # This is a pair of strings to be left and right aligned in the status-bar. # These strings may contain variables listed at :ref:`bufferlist_statusbar ` # that will be substituted accordingly. taglist_statusbar = mixed_list(string, string, default=list('[{buffer_no}: taglist]','{input_queue} total messages: {total_messages}')) # Format of the status-bar in envelope mode. # This is a pair of strings to be left and right aligned in the status-bar. # Apart from the global variables listed at :ref:`bufferlist_statusbar ` # these strings may contain variables: # # * `{to}`: To-header of the envelope envelope_statusbar = mixed_list(string, string, default=list('[{buffer_no}: envelope]','{input_queue} total messages: {total_messages}')) # timestamp format in `strftime format syntax `_ timestamp_format = string(default=None) # how to print messages: # this specifies a shell command used for printing. # threads/messages are piped to this command as plain text. # muttprint/a2ps works nicely print_cmd = string(default=None) # initial command when none is given as argument: initial_command = string(default='search tag:inbox AND NOT tag:killed') # default sort order of results in a search search_threads_sort_order = option('oldest_first', 'newest_first', 'message_id', 'unsorted', default='newest_first') # in case more than one account has an address book: # Set this to True to make tab completion for recipients during compose only # look in the abook of the account matching the sender address complete_matching_abook_only = boolean(default=False) # shut down when the last buffer gets closed quit_on_last_bclose = boolean(default=False) # value of the User-Agent header used for outgoing mails. # setting this to the empty string will cause alot to omit the header all together. # The string '{version}' will be replaced by the version string of the running instance. user_agent = string(default='alot/{version}') # Suffix of the prompt used when waiting for user input prompt_suffix = string(default=':') # String prepended to line when quoting quote_prefix = string(default='> ') # String prepended to subject header on reply # only if original subject doesn't start with 'Re:' or this prefix reply_subject_prefix = string(default='Re: ') # String prepended to subject header on forward # only if original subject doesn't start with 'Fwd:' or this prefix forward_subject_prefix = string(default='Fwd: ') # Always use the proper realname when constructing "From" headers for replies. # Set this to False to use the realname string as received in the original message. reply_force_realname = boolean(default=True) # Always use the accounts main address when constructing "From" headers for replies. # Set this to False to use the address string as received in the original message. reply_force_address = boolean(default=False) # Always use the proper realname when constructing "From" headers for forwards. # Set this to False to use the realname string as received in the original message. forward_force_realname = boolean(default=True) # Always use the accounts main address when constructing "From" headers for forwards. # Set this to False to use the address string as received in the original message. forward_force_address = boolean(default=False) # Always use the proper realname when constructing "Resent-From" headers for bounces. # Set this to False to use the realname string as received in the original message. bounce_force_realname = boolean(default=True) # Always use the accounts main address when constructing "Resent-From" headers for bounces. # Set this to False to use the address string as received in the original message. bounce_force_address = boolean(default=False) # When group-reply-ing to an email that has the "Mail-Followup-To" header set, # use the content of this header as the new "To" header and leave the "Cc" # header empty honor_followup_to = boolean(default=False) # When one of the recipients of an email is a subscribed mailing list, set the # "Mail-Followup-To" header to the list of recipients without yourself followup_to = boolean(default=False) # The list of addresses associated to the mailinglists you are subscribed to mailinglists = force_list(default=list()) # prefer plaintext alternatives over html content in multipart/alternative prefer_plaintext = boolean(default=False) # Key bindings [bindings] __many__ = string(default=None) [[___many___]] __many__ = string(default=None) [tags] # for each tag [[__many__]] # unfocussed normal = attrtriple(default=None) # focussed focus = attrtriple(default=None) # don't display at all? hidden = boolean(default=False) # alternative string representation translated = string(default=None) # substitution to generate translated from section name translation = mixed_list(string, string, default=None) [accounts] [[__many__]] # your main email address address = string # used to format the (proposed) From-header in outgoing mails realname = string # used to clear your addresses/ match account when formatting replies aliases = force_list(default=list()) # sendmail command. This is the shell command used to send out mails via the sendmail protocol sendmail_command = string(default='sendmail -t') # where to store outgoing mails, e.g. `maildir:///home/you/mail/Sent`. # You can use mbox, maildir, mh, babyl and mmdf in the protocol part of the URL. # # .. note:: If you want to add outgoing mails automatically to the notmuch index # you must use maildir in a path within your notmuch database path. sent_box = mail_container(default=None) # where to store draft mails, e.g. `maildir:///home/you/mail/Drafts`. # You can use mbox, maildir, mh, babyl and mmdf in the protocol part of the URL. # # .. note:: You will most likely want drafts indexed by notmuch to be able to # later access them within alot. This currently only works for # maildir containers in a path below your notmuch database path. draft_box = mail_container(default=None) # list of tags to automatically add to outgoing messages sent_tags = force_list(default=list('sent')) # path to signature file that gets attached to all outgoing mails from this account, optionally # renamed to :ref:`signature_filename `. signature = string(default=None) # attach signature file if set to True, append its content (mimetype text) # to the body text if set to False. signature_as_attachment = boolean(default=False) # signature file's name as it appears in outgoing mails if # :ref:`signature_as_attachment ` is set to True signature_filename = string(default=None) # Outgoing messages will be GPG signed by default if this is set to True. sign_by_default = boolean(default=False) # The GPG key ID you want to use with this account. If unset, alot will # use your default key. gpg_key = gpg_key_hint(default=None) # address book for this account [[[abook]]] # type identifier for address book type = option('shellcommand', 'abook', default=None) # make case-insensitive lookups ignorecase = boolean(default=True) # command to lookup contacts in shellcommand abooks # it will be called with the lookup prefix as only argument command = string(default=None) # regular expression used to match name/address pairs in the output of `command` # for shellcommand abooks regexp = string(default=None) # contacts file used for type 'abook' address book abook_contacts_file = string(default='~/.abook/addressbook') alot-0.3.5/alot/defaults/default.bindings000066400000000000000000000027341217172710600204010ustar00rootroot00000000000000up = move up down = move down page up = move page up page down = move page down j = move down k = move up 'g g' = move first G = move last ' ' = move page down 'ctrl d' = move halfpage down 'ctrl u' = move halfpage up @ = refresh ? = help bindings I = search tag:inbox AND NOT tag:killed '#' = taglist shift tab = bprevious U = search tag:unread tab = bnext \ = prompt 'search ' d = bclose $ = flush m = compose o = prompt 'search ' q = exit ';' = bufferlist ':' = prompt . = repeat [bufferlist] x = close enter = open [search] enter = select a = toggletags inbox & = toggletags killed ! = toggletags flagged s = toggletags unread l = retagprompt O = refineprompt | = refineprompt [envelope] a = prompt 'attach ~/' y = send P = save s = 'refine Subject' f = prompt 'set From ' t = 'refine To' b = 'refine Bcc' c = 'refine Cc' S = togglesign enter = edit 'g f' = togglesource [taglist] enter = select [thread] enter = select C = fold * E = unfold * c = fold e = unfold < = fold > = unfold 'g f' = togglesource H = toggleheaders P = print --all --separately --add_tags S = save --all g = reply --all f = forward p = print --add_tags n = editnew b= bounce s = save r = reply | = prompt 'pipeto ' 'g j' = move next sibling 'g k' = move previous sibling 'g h' = move parent 'g l' = move first reply ' ' = move next alot-0.3.5/alot/defaults/default.theme000066400000000000000000000114611217172710600177030ustar00rootroot00000000000000############################################################################ # Default Theme # # # # for alot. © 2012 Patrick Totzke, GNU GPL3+, https://github.com/pazz/alot # ############################################################################ [global] footer = 'standout','','white,bold','dark blue','white,bold','#006' body = 'default','','dark gray','default','g58','default' notify_error = 'standout','','white','dark red','white','dark red' notify_normal = 'default','','light gray','dark gray','light gray','#68a' prompt = 'default','','light gray','black','light gray','g11' tag = 'default','','light gray','black','light gray','default' tag_focus = 'standout, bold','','white','dark gray','#ffa','#68a' [help] text = 'default','','default','dark gray','default','g35' section = 'underline','','bold,underline','dark gray','bold,underline','g35' title = 'standout','','white','dark blue','white,bold,underline','g35' [bufferlist] line_focus = 'standout','','yellow','light gray','#ff8','g58' line_even = 'default','','light gray','black','default','g3' line_odd = 'default','','light gray','black','default','default' [taglist] line_focus = 'standout','','yellow','light gray','#ff8','g58' line_even = 'default','','light gray','black','default','g3' line_odd = 'default','','light gray','black','default','default' [thread] arrow_heads = '','','dark red','','#a00','' arrow_bars = '','','dark red','','#800','' attachment = 'default','','light gray','dark gray','light gray','dark gray' attachment_focus = 'underline','','light gray','light green','light gray','light green' body = 'default','','light gray','default','light gray','default' body_focus = 'default','','light gray','default','white','default' header = 'default','','white','dark gray','white','dark gray' header_key = 'default','','white','dark gray','white','dark gray' header_value = 'default','','light gray','dark gray','light gray','dark gray' [[summary]] even = 'default','','white','light blue','white','#006' odd = 'default','','white','dark blue','white','#068' focus = 'standout','','white','light gray','#ff8','g58' [envelope] body = 'default','','light gray','default','light gray','default' header = 'default','','white','dark gray','white','dark gray' header_key = 'default','','white','dark gray','white','dark gray' header_value = 'default','','light gray','dark gray','light gray','dark gray' [search] [[threadline]] normal = 'default','','default','default','#6d6','default' focus = 'standout','','light gray','light gray','g85','g58' parts = date,mailcount,tags,authors,subject [[[date]]] normal = 'default','','light gray','default','g74','default' focus = 'standout','','yellow','light gray','yellow','g58' width = 'fit',10,10 alignment = right [[[mailcount]]] normal = 'default','','light gray','default','g66','default' focus = 'standout','','yellow','light gray','yellow','g58' width = 'fit', 5,5 [[[tags]]] normal = 'bold','','dark cyan','','dark cyan','' focus = 'standout','','yellow','light gray','yellow','g58' [[[authors]]] normal = 'default,underline','','light blue','default','#068','default' focus = 'standout','','yellow','light gray','yellow','g58' width = 'fit',0,30 [[[subject]]] normal = 'default','','light gray','default','g66','default' focus = 'standout','','yellow','light gray','yellow','g58' width = 'weight', 1 [[[content]]] normal = 'default','','light gray','default','dark gray','default' focus = 'standout','','yellow','light gray','yellow','g58' width = 'weight', 1 # highlight threads containing unread messages [[threadline-unread]] tagged_with = 'unread' normal = 'default','','default,bold','default','#6d6,bold','default' parts = date,mailcount,tags,authors,subject [[[date]]] normal = 'default','','light gray,bold','default','white','default' [[[mailcount]]] normal = 'default','','light gray,bold','default','g93','default' [[[tags]]] normal = 'bold','','dark cyan,bold','','#6dd','' [[[authors]]] normal = 'default,underline','','light blue,bold','default','#68f','default' [[[subject]]] normal = 'default','','light gray,bold','default','g93','default' [[[content]]] normal = 'default','','light gray,bold','default','dark gray,bold','default' alot-0.3.5/alot/defaults/notmuch.rc000066400000000000000000000000451217172710600172320ustar00rootroot00000000000000[maildir] synchronize_flags = False alot-0.3.5/alot/defaults/notmuch.rc.spec000066400000000000000000000000661217172710600201660ustar00rootroot00000000000000[maildir] synchronize_flags = boolean(default=False) alot-0.3.5/alot/defaults/theme.spec000066400000000000000000000036201217172710600172070ustar00rootroot00000000000000[global] # attributes used in all modi footer = attrtriple body = attrtriple notify_error = attrtriple notify_normal = attrtriple prompt = attrtriple tag = attrtriple tag_focus = attrtriple [help] # formatting of the `help bindings` overlay text = attrtriple section = attrtriple title = attrtriple # mode specific attributes [bufferlist] line_focus = attrtriple line_even = attrtriple line_odd = attrtriple [taglist] line_focus = attrtriple line_even = attrtriple line_odd = attrtriple [search] [[threadline]] normal = attrtriple focus = attrtriple # order subwidgets are displayed. subset of {date,mailcount,tags,authors,subject,count} # every element listed must have its own subsection below parts = string_list(default=None) [[[__many__]]] normal = attrtriple focus = attrtriple width = widthtuple(default=None) alignment = align(default='left') [[__many__]] normal = attrtriple(default=None) focus = attrtriple(default=None) parts = string_list(default=None) query = string(default=None) tagged_with = force_list(default=None) [[[__many__]]] normal = attrtriple(default=None) focus = attrtriple(default=None) width = widthtuple(default=None) alignment = align(default=None) [thread] arrow_heads = attrtriple arrow_bars = attrtriple attachment = attrtriple attachment_focus = attrtriple body = attrtriple body_focus = attrtriple(default=None) header = attrtriple header_key = attrtriple header_value = attrtriple [[summary]] even = attrtriple odd = attrtriple focus = attrtriple [envelope] body = attrtriple header = attrtriple header_key = attrtriple header_value = attrtriple alot-0.3.5/alot/errors.py000066400000000000000000000011401217172710600153030ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file class GPGCode: AMBIGUOUS_NAME = 1 NOT_FOUND = 2 BAD_PASSPHRASE = 3 KEY_REVOKED = 4 KEY_EXPIRED = 5 KEY_INVALID = 6 KEY_CANNOT_ENCRYPT = 7 KEY_CANNOT_SIGN = 8 INVALID_HASH = 9 class GPGProblem(Exception): """GPG Error""" def __init__(self, message, code): self.code = code super(GPGProblem, self).__init__(message) class CompletionError(Exception): pass alot-0.3.5/alot/foreign/000077500000000000000000000000001217172710600150525ustar00rootroot00000000000000alot-0.3.5/alot/foreign/__init__.py000066400000000000000000000001531217172710600171620ustar00rootroot00000000000000try: import functools.lru_cache as lru_cache except: from lru_cache import lru_cache as lru_cache alot-0.3.5/alot/foreign/lru_cache.py000066400000000000000000000136561217172710600173640ustar00rootroot00000000000000# This is a backport of functools.lru_cache, which is part of the stdlib =>v3.3. # http://code.activestate.com/recipes/578078-py26-and-py30-backport-of-python-33s-lru-cache/ from collections import namedtuple from functools import update_wrapper from threading import Lock _CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) def lru_cache(maxsize=100, typed=False): """Least-recently-used cache decorator. If *maxsize* is set to None, the LRU features are disabled and the cache can grow without bound. If *typed* is True, arguments of different types will be cached separately. For example, f(3.0) and f(3) will be treated as distinct calls with distinct results. Arguments to the cached function must be hashable. View the cache statistics named tuple (hits, misses, maxsize, currsize) with f.cache_info(). Clear the cache and statistics with f.cache_clear(). Access the underlying function with f.__wrapped__. See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used """ # Users should only access the lru_cache through its public API: # cache_info, cache_clear, and f.__wrapped__ # The internals of the lru_cache are encapsulated for thread safety and # to allow the implementation to change (including a possible C version). def decorating_function(user_function): cache = dict() stats = [0, 0] # make statistics updateable non-locally HITS, MISSES = 0, 1 # names for the stats fields kwd_mark = (object(),) # separate positional and keyword args cache_get = cache.get # bound method to lookup key or return None _len = len # localize the global len() function lock = Lock() # because linkedlist updates aren't threadsafe root = [] # root of the circular doubly linked list nonlocal_root = [root] # make updateable non-locally root[:] = [root, root, None, None] # initialize by pointing to self PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields def make_key(args, kwds, typed, tuple=tuple, sorted=sorted, type=type): # helper function to build a cache key from positional and keyword args key = args if kwds: sorted_items = tuple(sorted(kwds.items())) key += kwd_mark + sorted_items if typed: key += tuple(type(v) for v in args) if kwds: key += tuple(type(v) for k, v in sorted_items) return key if maxsize == 0: def wrapper(*args, **kwds): # no caching, just do a statistics update after a successful call result = user_function(*args, **kwds) stats[MISSES] += 1 return result elif maxsize is None: def wrapper(*args, **kwds): # simple caching without ordering or size limit key = make_key(args, kwds, typed) if kwds or typed else args result = cache_get(key, root) # root used here as a unique not-found sentinel if result is not root: stats[HITS] += 1 return result result = user_function(*args, **kwds) cache[key] = result stats[MISSES] += 1 return result else: def wrapper(*args, **kwds): # size limited caching that tracks accesses by recency key = make_key(args, kwds, typed) if kwds or typed else args with lock: link = cache_get(key) if link is not None: # record recent use of the key by moving it to the front of the list root, = nonlocal_root link_prev, link_next, key, result = link link_prev[NEXT] = link_next link_next[PREV] = link_prev last = root[PREV] last[NEXT] = root[PREV] = link link[PREV] = last link[NEXT] = root stats[HITS] += 1 return result result = user_function(*args, **kwds) with lock: root = nonlocal_root[0] if _len(cache) < maxsize: # put result in a new link at the front of the list last = root[PREV] link = [last, root, key, result] cache[key] = last[NEXT] = root[PREV] = link else: # use root to store the new key and result root[KEY] = key root[RESULT] = result cache[key] = root # empty the oldest link and make it the new root root = nonlocal_root[0] = root[NEXT] del cache[root[KEY]] root[KEY] = None root[RESULT] = None stats[MISSES] += 1 return result def cache_info(): """Report cache statistics""" with lock: return _CacheInfo(stats[HITS], stats[MISSES], maxsize, len(cache)) def cache_clear(): """Clear the cache and cache statistics""" with lock: cache.clear() root = nonlocal_root[0] root[:] = [root, root, None, None] stats[:] = [0, 0] wrapper.__wrapped__ = user_function wrapper.cache_info = cache_info wrapper.cache_clear = cache_clear return update_wrapper(wrapper, user_function) return decorating_function alot-0.3.5/alot/foreign/urwidtrees/000077500000000000000000000000001217172710600172475ustar00rootroot00000000000000alot-0.3.5/alot/foreign/urwidtrees/README.md000066400000000000000000000075611217172710600205370ustar00rootroot00000000000000Urwid Tree Container API ======================== This is a POC implementation of a new Widget Container API for the [urwid][urwid] toolkit. Its design goals are * clear separation classes that define, decorate and display trees of widgets * representation of trees by local operations on node positions * easy to use default implementation for simple trees * Collapses are considered decoration We propose a `urwid.ListBox`-based widget that display trees where siblings grow vertically and children horizontally. This `TreeBox` widget handles key presses to move in the tree and collapse/expand subtrees if possible. The choice to define trees by overwriting local position movements allows to easily define potentially infinite tree structures. See `example4` for how to walk local file systems. The overall structure of the API contains three parts: Structure --------- `tree.Tree` objects define a tree structure by implementing the local movement methods parent_position first_child_position last_child_position next_sibling_position prev_sibling_position Each of which takes and returns a `position` object of arbitrary type (fixed for the Tree) as done in urwids ListWalker API. Apart from this, a `Tree` is assumed to define a dedicated position `tree.root` that is used as fallback initially focussed element, and define the `__getitem__` method to return its content (usually a Widget) for a given position. Note that `Tree` only defines a tree structure, it does not necessarily have any decoration around its contained Widgets. There is a ready made subclass called `SimpleTree` that offers the tree API for a given nested tuple structure. If you write your own classes its a good idea to subclass `Tree` and just overwrite the above mentioned methods as the base class already offers a number of derivative methods. Decoration ---------- Is done by using (subclasses of ) `decoration.DecoratedTree`. Objects of this type wrap around a given `Tree` and themselves behave like a (possibly altered) tree. Per default, `DecoratedTree` just passes every method on to its underlying tree. Decoration is done *not* by overwriting `__getitem__`, but by offering two additional methods get_decorated() decorate(). `get_decorated(pos)` returns the (decorated) content of the original tree at the given position. `decorate(pos, widget,..)` decorates the given widget assuming its placed at a given position. The former is trivially based on the latter, Containers that display `Tree`s use `get_decorated` instead of `__getitem__` when working on `DecoratedTree`s. The reason for this slightly odd design choice is that first it makes it easy to read the original content of a decorated tree: You simply use `dtree[pos]`. Secondly, this makes it possible to recursively add line decoration when nesting (decorated) Trees. The module `decoration` offers a few readily usable `DecoratedTree` subclasses that implement decoration by indentation, arrow shapes and subtree collapsing: `CollapsibleTree`, `IndentedTree`, `CollapsibleIndentedTree`, `ArrowTree` and `CollapsibleArrowTree`. Each can be further customized by constructor parameters. Containers ---------- `widgets.TreeBox` is essentially a `urwid.ListBox` that displays a given `Tree`. Per default no decoration is used and the widgets of the tree are simply displayed line by line in depth first order. `TreeBox`'s constructor accepts a `focus` parameter to specify the initially focussed position. Internally, it uses a `TreeListWalker` to linearize the tree to a list. `widgets.TreeListWalker` serve as adapter between `Tree` and ListWalker APIs: They implement the ListWalker API using the data from a given `Tree` in depth-first order. As such, one can directly pass on a `TreeListWalker` to an `urwid.ListBox` if one doesn't want to use tree-based focus movement or key bindings for collapsing subtrees. [urwid]: http://excess.org/urwid/ alot-0.3.5/alot/foreign/urwidtrees/__init__.py000066400000000000000000000007041217172710600213610ustar00rootroot00000000000000try: # lru_cache is part of the stdlib from v3.2 onwards import functools.lru_cache as lru_cache except: # on older versions we use a backport import lru_cache as lru_cache from tree import Tree, SimpleTree from decoration import DecoratedTree, CollapsibleTree from decoration import IndentedTree, CollapsibleIndentedTree from decoration import ArrowTree, CollapsibleArrowTree from nested import NestedTree from widgets import TreeBox alot-0.3.5/alot/foreign/urwidtrees/decoration.py000066400000000000000000000470321217172710600217560ustar00rootroot00000000000000# Copyright (C) 2013 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. from tree import Tree, SimpleTree import urwid import logging NO_SPACE_MSG = 'too little space for requested decoration' class TreeDecorationError(Exception): pass class DecoratedTree(Tree): """ :class:`Tree` that wraps around another :class:`Tree` and allows to read original content as well as decorated versions thereof. """ def __init__(self, content): if not isinstance(content, Tree): # do we need this? content = SimpleTree(content) self._tree = content self.root = self._tree.root def get_decorated(self, pos): """ return widget that consists of the content of original tree at given position plus its decoration. """ return self.decorate(pos, self[pos]) def decorate(self, pos, widget, is_first=True): """ decorate `widget` according to a position `pos` in the original tree. setting `is_first` to False indicates that we are decorating a line that is *part* of the (multi-line) content at this position, but not the first part. This allows to omit incoming arrow heads for example. """ return widget # pass on everything else to the original tree. def parent_position(self, pos): return self._tree.parent_position(pos) def first_child_position(self, pos): return self._tree.first_child_position(pos) def last_child_position(self, pos): return self._tree.last_child_position(pos) def next_sibling_position(self, pos): return self._tree.next_sibling_position(pos) def prev_sibling_position(self, pos): return self._tree.prev_sibling_position(pos) def __getitem__(self, pos): return self._tree[pos] class CollapseMixin(object): """ Mixin for :class:`Tree` that allows to collapse subtrees. This works by overwriting :meth:`[first|last]_child_position `, forcing them to return `None` if the given position is considered collapsed. We use a (given) callable `is_collapsed` that accepts positions and returns a boolean to determine which node is considered collapsed. """ def __init__(self, is_collapsed=lambda pos: False, **kwargs): self._initially_collapsed = is_collapsed self._divergent_positions = [] def is_collapsed(self, pos): """checks if given position is currently collapsed""" collapsed = self._initially_collapsed(pos) if pos in self._divergent_positions: collapsed = not collapsed return collapsed # implement functionality by overwriting local position transformations # TODO: ATM this assumes we are in a wrapper: it uses self._tree. # This is not necessarily true, for example for subclasses of SimpleTree! # maybe define this whole class as a wrapper? def last_child_position(self, pos): if self.is_collapsed(pos): return None return self._tree.last_child_position(pos) def first_child_position(self, pos): if self.is_collapsed(pos): return None return self._tree.first_child_position(pos) def collapsible(self, pos): return not self._tree.is_leaf(pos) def set_position_collapsed(self, pos, is_collapsed): if self.collapsible(pos): if self._initially_collapsed(pos) == is_collapsed: if pos in self._divergent_positions: self._divergent_positions.remove(pos) else: if pos not in self._divergent_positions: self._divergent_positions.append(pos) def toggle_collapsed(self, pos): self.set_position_collapsed(pos, not self.is_collapsed(pos)) def collapse(self, pos): self.set_position_collapsed(pos, True) def collapse_all(self): self.set_collapsed_all(True) def expand_all(self): self.set_collapsed_all(False) def set_collapsed_all(self, is_collapsed): self._initially_collapsed = lambda x: is_collapsed self._divergent_positions = [] def expand(self, pos): self.set_position_collapsed(pos, False) class CollapseIconMixin(CollapseMixin): """ Mixin for :classs:`Tree` that allows to allows to collapse subtrees and use an indicator icon in line decorations. This Mixin adds the ability to construct collapse-icon for a position, indicating its collapse status to :class:`CollapseMixin`. """ def __init__(self, is_collapsed=lambda pos: False, icon_collapsed_char='+', icon_expanded_char='-', icon_collapsed_att=None, icon_expanded_att=None, icon_frame_left_char='[', icon_frame_right_char=']', icon_frame_att=None, icon_focussed_att=None, **kwargs): """TODO: docstrings""" CollapseMixin.__init__(self, is_collapsed, **kwargs) self._icon_collapsed_char = icon_collapsed_char self._icon_expanded_char = icon_expanded_char self._icon_collapsed_att = icon_collapsed_att self._icon_expanded_att = icon_expanded_att self._icon_frame_left_char = icon_frame_left_char self._icon_frame_right_char = icon_frame_right_char self._icon_frame_att = icon_frame_att self._icon_focussed_att = icon_focussed_att def _construct_collapse_icon(self, pos): width = 0 widget = None char = self._icon_expanded_char charatt = self._icon_expanded_att if self.is_collapsed(pos): char = self._icon_collapsed_char charatt = self._icon_collapsed_att if char is not None: columns = [] if self._icon_frame_left_char is not None: lchar = self._icon_frame_left_char charlen = len(lchar) leftframe = urwid.Text((self._icon_frame_att, lchar)) columns.append((charlen, leftframe)) width += charlen # next we build out icon widget: we feed all markups to a Text, # make it selectable (to toggle collapse) if requested markup = (charatt, char) widget = urwid.Text(markup) charlen = len(char) columns.append((charlen, widget)) width += charlen if self._icon_frame_right_char is not None: rchar = self._icon_frame_right_char charlen = len(rchar) rightframe = urwid.Text((self._icon_frame_att, rchar)) columns.append((charlen, rightframe)) width += charlen widget = urwid.Columns(columns) return width, widget class CollapsibleTree(CollapseMixin, DecoratedTree): """Undecorated Tree that allows to collapse subtrees""" def __init__(self, tree, **kwargs): DecoratedTree.__init__(self, tree) CollapseMixin.__init__(self, **kwargs) class IndentedTree(DecoratedTree): """Indent tree nodes according to their depth in the tree""" def __init__(self, tree, indent=2): """ :param tree: tree of widgets to be displayed :type tree: Tree :param indent: indentation width :type indent: int """ self._indent = indent DecoratedTree.__init__(self, tree) def decorate(self, pos, widget, is_first=True): line = None indent = self._tree.depth(pos) * self._indent cols = [(indent, urwid.SolidFill(' ')), widget] # construct a Columns, defining all spacer as Box widgets line = urwid.Columns(cols, box_columns=range(len(cols))[:-1]) return line class CollapsibleIndentedTree(CollapseIconMixin, IndentedTree): """ Indent collapsible tree nodes according to their depth in the tree and display icons indicating collapse-status in the gaps. """ def __init__(self, walker, icon_offset=1, indent=4, **kwargs): """ :param walker: tree of widgets to be displayed :type walker: Tree :param indent: indentation width :type indent: int :param icon_offset: distance from icon to the eginning of the tree node. :type icon_offset: int """ self._icon_offset = icon_offset IndentedTree.__init__(self, walker, indent=indent) CollapseIconMixin.__init__(self, **kwargs) def decorate(self, pos, widget, is_first=True): """ builds a list element for given position in the tree. It consists of the original widget taken from the Tree and some decoration columns depending on the existence of parent and sibling positions. The result is a urwid.Culumns widget. """ void = urwid.SolidFill(' ') line = None cols = [] depth = self._tree.depth(pos) # add spacer filling all but the last indent if depth > 0: cols.append((depth * self._indent, void)), # spacer # construct last indent # TODO iwidth, icon = self._construct_collapse_icon(pos) available_space = self._indent firstindent_width = self._icon_offset + iwidth # stop if indent is too small for this decoration if firstindent_width > available_space: raise TreeDecorationError(NO_SPACE_MSG) # add icon only for non-leafs is_leaf = self._tree.is_leaf(pos) if not is_leaf: if icon is not None: # space to the left cols.append((available_space - firstindent_width, urwid.SolidFill(' '))) # icon icon_pile = urwid.Pile([('pack', icon), void]) cols.append((iwidth, icon_pile)) # spacer until original widget available_space = self._icon_offset cols.append((available_space, urwid.SolidFill(' '))) else: # otherwise just add another spacer cols.append((self._indent, urwid.SolidFill(' '))) cols.append(widget) # original widget ] # construct a Columns, defining all spacer as Box widgets line = urwid.Columns(cols, box_columns=range(len(cols))[:-1]) return line class ArrowTree(IndentedTree): """ Decorates the tree by indenting nodes according to their depth and using the gaps to draw arrows indicate the tree structure. """ def __init__(self, walker, indent=3, childbar_offset=0, arrow_hbar_char=u'\u2500', arrow_hbar_att=None, arrow_vbar_char=u'\u2502', arrow_vbar_att=None, arrow_tip_char=u'\u27a4', arrow_tip_att=None, arrow_att=None, arrow_connector_tchar=u'\u251c', arrow_connector_lchar=u'\u2514', arrow_connector_att=None, **kwargs): """ :param walker: tree of widgets to be displayed :type walker: Tree :param indent: indentation width :type indent: int """ IndentedTree.__init__(self, walker, indent) self._childbar_offset = childbar_offset self._arrow_hbar_char = arrow_hbar_char self._arrow_hbar_att = arrow_hbar_att self._arrow_vbar_char = arrow_vbar_char self._arrow_vbar_att = arrow_vbar_att self._arrow_connector_lchar = arrow_connector_lchar self._arrow_connector_tchar = arrow_connector_tchar self._arrow_connector_att = arrow_connector_att self._arrow_tip_char = arrow_tip_char self._arrow_tip_att = arrow_tip_att self._arrow_att = arrow_att def _construct_spacer(self, pos, acc): """ build a spacer that occupies the horizontally indented space between pos's parent and the root node. It will return a list of tuples to be fed into a Columns widget. """ parent = self._tree.parent_position(pos) if parent is not None: grandparent = self._tree.parent_position(parent) if self._indent > 0 and grandparent is not None: parent_sib = self._tree.next_sibling_position(parent) draw_vbar = parent_sib is not None and \ self._arrow_vbar_char is not None space_width = self._indent - 1 * (draw_vbar) - self._childbar_offset if space_width > 0: void = urwid.AttrMap(urwid.SolidFill(' '), self._arrow_att) acc.insert(0, ((space_width, void))) if draw_vbar: barw = urwid.SolidFill(self._arrow_vbar_char) bar = urwid.AttrMap(barw, self._arrow_vbar_att or self._arrow_att) acc.insert(0, ((1, bar))) return self._construct_spacer(parent, acc) else: return acc def _construct_connector(self, pos): """ build widget to be used as "connector" bit between the vertical bar between siblings and their respective horizontab bars leading to the arrow tip """ # connector symbol, either L or |- shaped. connectorw = None connector = None if self._tree.next_sibling_position(pos) is not None: # |- shaped if self._arrow_connector_tchar is not None: connectorw = urwid.Text(self._arrow_connector_tchar) else: # L shaped if self._arrow_connector_lchar is not None: connectorw = urwid.Text(self._arrow_connector_lchar) if connectorw is not None: att = self._arrow_connector_att or self._arrow_att connector = urwid.AttrMap(connectorw, att) return connector def _construct_arrow_tip(self, pos): """returns arrow tip as (width, widget)""" arrow_tip = None width = 0 if self._arrow_tip_char: txt = urwid.Text(self._arrow_tip_char) arrow_tip = urwid.AttrMap( txt, self._arrow_tip_att or self._arrow_att) width = len(self._arrow_tip_char) return width, arrow_tip def _construct_first_indent(self, pos): """ build spacer to occupy the first indentation level from pos to the left. This is separate as it adds arrowtip and sibling connector. """ cols = [] void = urwid.AttrMap(urwid.SolidFill(' '), self._arrow_att) available_width = self._indent if self._tree.depth(pos) > 0: connector = self._construct_connector(pos) if connector is not None: width = connector.pack()[0] if width > available_width: raise TreeDecorationError(NO_SPACE_MSG) available_width -= width if self._tree.next_sibling_position(pos) is not None: barw = urwid.SolidFill(self._arrow_vbar_char) below = urwid.AttrMap(barw, self._arrow_vbar_att or self._arrow_att) else: below = void # pile up connector and bar spacer = urwid.Pile([('pack', connector), below]) cols.append((width, spacer)) #arrow tip awidth, at = self._construct_arrow_tip(pos) if at is not None: if awidth > available_width: raise TreeDecorationError(NO_SPACE_MSG) available_width -= awidth at_spacer = urwid.Pile([('pack', at), void]) cols.append((awidth, at_spacer)) # bar between connector and arrow tip if available_width > 0: barw = urwid.SolidFill(self._arrow_hbar_char) bar = urwid.AttrMap( barw, self._arrow_hbar_att or self._arrow_att) hb_spacer = urwid.Pile([(1, bar), void]) cols.insert(1, (available_width, hb_spacer)) return cols def decorate(self, pos, widget, is_first=True): """ builds a list element for given position in the tree. It consists of the original widget taken from the Tree and some decoration columns depending on the existence of parent and sibling positions. The result is a urwid.Culumns widget. """ line = None if pos is not None: original_widget = widget cols = self._construct_spacer(pos, []) # Construct arrow leading from parent here, # if we have a parent and indentation is turned on if self._indent > 0: if is_first: indent = self._construct_first_indent(pos) if indent is not None: cols = cols + indent else: parent = self._tree.parent_position(pos) if self._indent > 0 and parent is not None: parent_sib = self._tree.next_sibling_position(pos) draw_vbar = parent_sib is not None void = urwid.AttrMap(urwid.SolidFill(' '), self._arrow_att) if self._childbar_offset > 0: cols.append((self._childbar_offset, void)) if draw_vbar: barw = urwid.SolidFill(self._arrow_vbar_char) bar = urwid.AttrMap( barw, self._arrow_vbar_att or self._arrow_att) rspace_width = self._indent - \ 1 - self._childbar_offset cols.append((1, bar)) cols.append((rspace_width, void)) else: cols.append((self._indent, void)) # add the original widget for this line cols.append(original_widget) # construct a Columns, defining all spacer as Box widgets line = urwid.Columns(cols, box_columns=range(len(cols))[:-1]) return line class CollapsibleArrowTree(CollapseIconMixin, ArrowTree): """Arrow-decoration that allows collapsing subtrees""" def __init__(self, treelistwalker, icon_offset=0, indent=5, **kwargs): self._icon_offset = icon_offset ArrowTree.__init__(self, treelistwalker, indent, **kwargs) CollapseIconMixin.__init__(self, **kwargs) def _construct_arrow_tip(self, pos): cols = [] overall_width = self._icon_offset if self._icon_offset > 0: # how often we repeat the hbar_char until width icon_offset is # reached hbar_char_count = len(self._arrow_hbar_char) / self._icon_offset barw = urwid.Text(self._arrow_hbar_char * hbar_char_count) bar = urwid.AttrMap(barw, self._arrow_hbar_att or self._arrow_att) cols.insert(1, (self._icon_offset, bar)) # add icon only for non-leafs if self.collapsible(pos): iwidth, icon = self._construct_collapse_icon(pos) if icon is not None: cols.insert(0, (iwidth, icon)) overall_width += iwidth # get arrow tip awidth, tip = ArrowTree._construct_arrow_tip(self, pos) if tip is not None: cols.append((awidth, tip)) overall_width += awidth return overall_width, urwid.Columns(cols) alot-0.3.5/alot/foreign/urwidtrees/example1.py000077500000000000000000000047141217172710600213460ustar00rootroot00000000000000#!/usr/bin/python # Copyright (C) 2013 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. import urwid from tree import SimpleTree from widgets import TreeBox # define some colours palette = [ ('body', 'black', 'light gray'), ('focus', 'light gray', 'dark blue', 'standout'), ('bars', 'dark blue', 'light gray', ''), ('arrowtip', 'light blue', 'light gray', ''), ('connectors', 'light red', 'light gray', ''), ] # We use selectable Text widgets for our example.. class FocusableText(urwid.WidgetWrap): """Selectable Text used for nodes in our example""" def __init__(self, txt): t = urwid.Text(txt) w = urwid.AttrMap(t, 'body', 'focus') urwid.WidgetWrap.__init__(self, w) def selectable(self): return True def keypress(self, size, key): return key # define a test tree in the format accepted by SimpleTree. Essentially, a # tree is given as (nodewidget, [list, of, subtrees]). SimpleTree accepts # lists of such trees. def construct_example_simpletree_structure(selectable_nodes=True, children=3): Text = FocusableText if selectable_nodes else urwid.Text # define root node tree = (Text('ROOT'), []) # define some children c = g = gg = 0 # counter for i in range(children): subtree = (Text('Child %d' % c), []) # and grandchildren.. for j in range(children): subsubtree = (Text('Grandchild %d' % g), []) for k in range(children): leaf = (Text('Grand Grandchild %d' % gg), None) subsubtree[1].append(leaf) gg += 1 # inc grand-grandchild counter subtree[1].append(subsubtree) g += 1 # inc grandchild counter tree[1].append(subtree) c += 1 return tree def construct_example_tree(selectable_nodes=True, children=2): # define a list of tree structures to be passed on to SimpleTree forrest = [construct_example_simpletree_structure(selectable_nodes, children)] # stick out test tree into a SimpleTree and return return SimpleTree(forrest) if __name__ == "__main__": # get example tree stree = construct_example_tree() # put the tree into a treebox treebox = TreeBox(stree) # add some decoration rootwidget = urwid.AttrMap(treebox, 'body') urwid.MainLoop(rootwidget, palette).run() # go alot-0.3.5/alot/foreign/urwidtrees/example2.arrows.py000077500000000000000000000017511217172710600226610ustar00rootroot00000000000000#!/usr/bin/python # Copyright (C) 2013 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. from example1 import construct_example_tree, palette # example data from decoration import ArrowTree # for Decoration from widgets import TreeBox import urwid if __name__ == "__main__": # get example tree stree = construct_example_tree() # Here, we add some decoration by wrapping the tree using ArrowTree. atree = ArrowTree(stree, # customize at will.. # arrow_hbar_char=u'\u2550', # arrow_vbar_char=u'\u2551', # arrow_tip_char=u'\u25B7', # arrow_connector_tchar=u'\u2560', # arrow_connector_lchar=u'\u255A', ) # put the into a treebox treebox = TreeBox(atree) rootwidget = urwid.AttrMap(treebox, 'body') urwid.MainLoop(rootwidget, palette).run() # go alot-0.3.5/alot/foreign/urwidtrees/example3.collapse.py000077500000000000000000000033441217172710600231470ustar00rootroot00000000000000#!/usr/bin/python # Copyright (C) 2013 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. from example1 import construct_example_tree, palette # example data from decoration import CollapsibleIndentedTree # for Decoration from widgets import TreeBox import urwid if __name__ == "__main__": # get some SimpleTree stree = construct_example_tree() # Use (subclasses of) the wrapper decoration.CollapsibleTree to construct a # tree where collapsible subtrees. Apart from the original tree, these take # a callable `is_collapsed` that defines initial collapsed-status if a # given position. # We want all grandchildren collapsed initially if_grandchild = lambda pos: stree.depth(pos) > 1 # We use CollapsibleIndentedTree around the original example tree. # This uses Indentation to indicate the tree structure and squeezes in # text-icons to indicate the collapsed status. # Also try CollapsibleTree or CollapsibleArrowTree.. tree = CollapsibleIndentedTree(stree, is_collapsed=if_grandchild, icon_focussed_att='focus', # indent=6, # childbar_offset=1, # icon_frame_left_char=None, # icon_frame_right_char=None, # icon_expanded_char='-', # icon_collapsed_char='+', ) # put the tree into a treebox treebox = TreeBox(tree) rootwidget = urwid.AttrMap(treebox, 'body') urwid.MainLoop(rootwidget, palette).run() # go alot-0.3.5/alot/foreign/urwidtrees/example4.filesystem.py000077500000000000000000000101451217172710600235270ustar00rootroot00000000000000#!/usr/bin/python # Copyright (C) 2013 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. import urwid import os from example1 import palette # example data from widgets import TreeBox from tree import Tree from decoration import CollapsibleArrowTree # define selectable urwid.Text widgets to display paths class FocusableText(urwid.WidgetWrap): """Widget to display paths lines""" def __init__(self, txt): t = urwid.Text(txt) w = urwid.AttrMap(t, 'body', 'focus') urwid.WidgetWrap.__init__(self, w) def selectable(self): return True def keypress(self, size, key): return key # define Tree that can walk your filesystem class DirectoryTree(Tree): """ A custom Tree representing our filesystem structure. This implementation is rather inefficient: basically every position-lookup will call `os.listdir`.. This makes navigation in the tree quite slow. In real life you'd want to do some caching. As positions we use absolute path strings. """ # determine dir separator and form of root node pathsep = os.path.sep drive, _ = os.path.splitdrive(pathsep) # define root node This is part of the Tree API! root = drive + pathsep def __getitem__(self, pos): return FocusableText(pos) # generic helper def _list_dir(self, path): """returns absolute paths for all entries in a directory""" try: elements = [os.path.join( path, x) for x in os.listdir(path) if os.path.isdir(path)] elements.sort() except OSError: elements = None return elements def _get_siblings(self, pos): """lists the parent directory of pos """ parent = self.parent_position(pos) siblings = [pos] if parent is not None: siblings = self._list_dir(parent) return siblings # Tree API def parent_position(self, pos): parent = None if pos != '/': parent = os.path.split(pos)[0] return parent def first_child_position(self, pos): candidate = None if os.path.isdir(pos): children = self._list_dir(pos) if children: candidate = children[0] return candidate def last_child_position(self, pos): candidate = None if os.path.isdir(pos): children = self._list_dir(pos) if children: candidate = children[-1] return candidate def next_sibling_position(self, pos): candidate = None siblings = self._get_siblings(pos) myindex = siblings.index(pos) if myindex + 1 < len(siblings): # pos is not the last entry candidate = siblings[myindex + 1] return candidate def prev_sibling_position(self, pos): candidate = None siblings = self._get_siblings(pos) myindex = siblings.index(pos) if myindex > 0: # pos is not the first entry candidate = siblings[myindex - 1] return candidate if __name__ == "__main__": cwd = os.getcwd() # get current working directory dtree = DirectoryTree() # get a directory walker # Use CollapsibleArrowTree for decoration. # define initial collapse: as_deep_as_cwd = lambda pos: dtree.depth(pos) >= dtree.depth(cwd) # We hide the usual arrow tip and use a customized collapse-icon. decorated_tree = CollapsibleArrowTree(dtree, is_collapsed=as_deep_as_cwd, arrow_tip_char=None, icon_frame_left_char=None, icon_frame_right_char=None, icon_collapsed_char=u'\u25B6', icon_expanded_char=u'\u25B7',) # stick it into a TreeBox and use 'body' color attribute for gaps tb = TreeBox(decorated_tree, focus=cwd) root_widget = urwid.AttrMap(tb, 'body') urwid.MainLoop(root_widget, palette).run() # go alot-0.3.5/alot/foreign/urwidtrees/example5.nested.py000077500000000000000000000060611217172710600226300ustar00rootroot00000000000000#!/usr/bin/python # Copyright (C) 2013 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. from example1 import palette, construct_example_tree # example data from example1 import FocusableText # Selectable Text used for nodes from widgets import TreeBox from tree import SimpleTree from nested import NestedTree from decoration import ArrowTree, CollapsibleArrowTree # decoration import urwid import logging if __name__ == "__main__": #logging.basicConfig(filename='example.log',level=logging.DEBUG) # Take some Arrow decorated Tree that we later stick inside another tree. innertree = ArrowTree(construct_example_tree()) # Some collapsible, arrow decorated tree with extra indent anotherinnertree = CollapsibleArrowTree(construct_example_tree(), indent=10) # A SimpleTree, that contains the two above middletree = SimpleTree( [ (FocusableText('Middle ROOT'), [ (FocusableText('Mid Child One'), None), (FocusableText('Mid Child Two'), None), (innertree, None), (FocusableText('Mid Child Three'), [ (FocusableText('Mid Grandchild One'), None), (FocusableText('Mid Grandchild Two'), None), ] ), (anotherinnertree, # middletree defines a childnode here. This is usually # covered by the tree 'anotherinnertree', unless the # interepreting NestedTree's constructor gets parameter # interpret_covered=True.. [ (FocusableText('XXX I\'m invisible!'), None), ]), ] ) ] ) # end SimpleTree constructor for middletree # use customized arrow decoration for middle tree middletree = ArrowTree(middletree, arrow_hbar_char=u'\u2550', arrow_vbar_char=u'\u2551', arrow_tip_char=u'\u25B7', arrow_connector_tchar=u'\u2560', arrow_connector_lchar=u'\u255A') # define outmost tree outertree = SimpleTree( [ (FocusableText('Outer ROOT'), [ (FocusableText('Child One'), None), (middletree, None), (FocusableText('last outer child'), None), ] ) ] ) # end SimpleTree constructor # add some Arrow decoration outertree = ArrowTree(outertree) # wrap the whole thing into a Nested Tree outertree = NestedTree(outertree, # show covered nodes like XXX interpret_covered=False ) # put it into a treebox and run treebox = TreeBox(outertree) rootwidget = urwid.AttrMap(treebox, 'body') urwid.MainLoop(rootwidget, palette).run() # go alot-0.3.5/alot/foreign/urwidtrees/lru_cache.py000066400000000000000000000136561217172710600215610ustar00rootroot00000000000000# This is a backport of functools.lru_cache, which is part of the stdlib =>v3.3. # http://code.activestate.com/recipes/578078-py26-and-py30-backport-of-python-33s-lru-cache/ from collections import namedtuple from functools import update_wrapper from threading import Lock _CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) def lru_cache(maxsize=100, typed=False): """Least-recently-used cache decorator. If *maxsize* is set to None, the LRU features are disabled and the cache can grow without bound. If *typed* is True, arguments of different types will be cached separately. For example, f(3.0) and f(3) will be treated as distinct calls with distinct results. Arguments to the cached function must be hashable. View the cache statistics named tuple (hits, misses, maxsize, currsize) with f.cache_info(). Clear the cache and statistics with f.cache_clear(). Access the underlying function with f.__wrapped__. See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used """ # Users should only access the lru_cache through its public API: # cache_info, cache_clear, and f.__wrapped__ # The internals of the lru_cache are encapsulated for thread safety and # to allow the implementation to change (including a possible C version). def decorating_function(user_function): cache = dict() stats = [0, 0] # make statistics updateable non-locally HITS, MISSES = 0, 1 # names for the stats fields kwd_mark = (object(),) # separate positional and keyword args cache_get = cache.get # bound method to lookup key or return None _len = len # localize the global len() function lock = Lock() # because linkedlist updates aren't threadsafe root = [] # root of the circular doubly linked list nonlocal_root = [root] # make updateable non-locally root[:] = [root, root, None, None] # initialize by pointing to self PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields def make_key(args, kwds, typed, tuple=tuple, sorted=sorted, type=type): # helper function to build a cache key from positional and keyword args key = args if kwds: sorted_items = tuple(sorted(kwds.items())) key += kwd_mark + sorted_items if typed: key += tuple(type(v) for v in args) if kwds: key += tuple(type(v) for k, v in sorted_items) return key if maxsize == 0: def wrapper(*args, **kwds): # no caching, just do a statistics update after a successful call result = user_function(*args, **kwds) stats[MISSES] += 1 return result elif maxsize is None: def wrapper(*args, **kwds): # simple caching without ordering or size limit key = make_key(args, kwds, typed) if kwds or typed else args result = cache_get(key, root) # root used here as a unique not-found sentinel if result is not root: stats[HITS] += 1 return result result = user_function(*args, **kwds) cache[key] = result stats[MISSES] += 1 return result else: def wrapper(*args, **kwds): # size limited caching that tracks accesses by recency key = make_key(args, kwds, typed) if kwds or typed else args with lock: link = cache_get(key) if link is not None: # record recent use of the key by moving it to the front of the list root, = nonlocal_root link_prev, link_next, key, result = link link_prev[NEXT] = link_next link_next[PREV] = link_prev last = root[PREV] last[NEXT] = root[PREV] = link link[PREV] = last link[NEXT] = root stats[HITS] += 1 return result result = user_function(*args, **kwds) with lock: root = nonlocal_root[0] if _len(cache) < maxsize: # put result in a new link at the front of the list last = root[PREV] link = [last, root, key, result] cache[key] = last[NEXT] = root[PREV] = link else: # use root to store the new key and result root[KEY] = key root[RESULT] = result cache[key] = root # empty the oldest link and make it the new root root = nonlocal_root[0] = root[NEXT] del cache[root[KEY]] root[KEY] = None root[RESULT] = None stats[MISSES] += 1 return result def cache_info(): """Report cache statistics""" with lock: return _CacheInfo(stats[HITS], stats[MISSES], maxsize, len(cache)) def cache_clear(): """Clear the cache and cache statistics""" with lock: cache.clear() root = nonlocal_root[0] root[:] = [root, root, None, None] stats[:] = [0, 0] wrapper.__wrapped__ = user_function wrapper.cache_info = cache_info wrapper.cache_clear = cache_clear return update_wrapper(wrapper, user_function) return decorating_function alot-0.3.5/alot/foreign/urwidtrees/nested.py000066400000000000000000000367731217172710600211230ustar00rootroot00000000000000# Copyright (C) 2013 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. from tree import Tree from decoration import DecoratedTree, CollapseMixin class NestedTree(Tree): """ A Tree that wraps around Trees that may contain list walkers or other trees. The wrapped tree may contain normal widgets as well. List walkers and subtree contents will be expanded into the tree presented by this wrapper. This wrapper's positions are tuples of positions of the original and subtrees: For example, `(X,Y,Z)` points at position Z in tree/list at position Y in tree/list at position X in the original tree. NestedTree transparently behaves like a collapsible DecoratedTree. """ @property def root(self): root = (self._tree.root,) rcontent = self._tree[self._tree.root] if isinstance(rcontent, Tree): root = root + (rcontent.root,) return root def _sanitize_position(self, pos, tree=None): """ Ensure a position tuple until the result does not point to a :class:`Tree` any more. """ if pos is not None: tree = tree or self._tree entry = self._lookup_entry(tree, pos) if isinstance(entry, Tree): pos = pos + self._sanitize_position((entry.root,), tree=entry) return pos def __init__(self, tree, interpret_covered=False): self._tree = tree self._interpret_covered = interpret_covered def _lookup_entry(self, tree, pos): if len(pos) == 0: entry = tree[tree.root] else: entry = tree[pos[0]] if len(pos) > 1 and isinstance(entry, Tree): subtree = entry entry = self._lookup_entry(subtree, pos[1:]) return entry def _depth(self, tree, pos, outmost_only=True): depth = self._tree.depth(pos[1:]) if not outmost_only: entry = self._tree[pos[0]] if isinstance(entry, Tree) and len(pos) > 1: depth += self._depth(entry, pos[1:], outmost_only=False) return depth def depth(self, pos, outmost=True): return self._depth(self._tree, pos) def __getitem__(self, pos): return self._lookup_entry(self._tree, pos) # DecoratedTree API def _get_decorated_entry(self, tree, pos, widget=None, is_first=True): entry = tree[pos[0]] if len(pos) > 1 and isinstance(entry, Tree): subtree = entry entry = self._get_decorated_entry( subtree, pos[1:], widget, is_first) else: entry = widget or entry if isinstance(tree, (DecoratedTree, NestedTree)): # has decorate-API isf = len(pos) < 2 if not isf and isinstance(tree[pos[0]], Tree): isf = (tree[pos[0]].parent_position(pos[1]) is None) or not is_first entry = tree.decorate(pos[0], entry, is_first=isf) return entry def get_decorated(self, pos): return self._get_decorated_entry(self._tree, pos) def decorate(self, pos, widget, is_first=True): return self._get_decorated_entry(self._tree, pos, widget, is_first) # Collapse API def _get_subtree_for(self, pos): """returns Tree that manages pos[-1]""" res = self._tree candidate = self._lookup_entry(self._tree, pos[:-1]) if isinstance(candidate, Tree): res = candidate return res def collapsible(self, pos): res = False subtree = self._get_subtree_for(pos) if isinstance(subtree, (CollapseMixin, NestedTree)): res = subtree.collapsible(pos[-1]) return res def is_collapsed(self, pos): res = False subtree = self._get_subtree_for(pos) if isinstance(subtree, (CollapseMixin, NestedTree)): res = subtree.is_collapsed(pos[-1]) return res def toggle_collapsed(self, pos): subtree = self._get_subtree_for(pos) if isinstance(subtree, (CollapseMixin, NestedTree)): subtree.toggle_collapsed(pos) def collapse(self, pos): subtree = self._get_subtree_for(pos) if isinstance(subtree, (CollapseMixin, NestedTree)): subtree.collapse(pos[-1]) def collapse_all(self): self._collapse_all(self._tree, self.root) def _collapse_all(self, tree, pos=None): if pos is not None: if isinstance(tree, (CollapseMixin, NestedTree)): tree.expand_all() if len(pos) > 1: self._collapse_all(tree[pos[0]], pos[1:]) nextpos = tree.next_position(pos[0]) if nextpos is not None: nentry = tree[nextpos] if isinstance(nentry, Tree): self._collapse_all(nentry, (nentry.root,)) self._collapse_all(tree, (nextpos,)) if isinstance(tree, (CollapseMixin, NestedTree)): tree.collapse_all() def expand(self, pos): subtree = self._get_subtree_for(pos) if isinstance(subtree, (CollapseMixin, NestedTree)): subtree.expand(pos[-1]) def expand_all(self): self._expand_all(self._tree, self.root) def _expand_all(self, tree, pos=None): if pos is not None: if isinstance(tree, (CollapseMixin, NestedTree)): tree.expand_all() if len(pos) > 1: self._expand_all(tree[pos[0]], pos[1:]) nextpos = tree.next_position(pos[0]) if nextpos is not None: nentry = tree[nextpos] if isinstance(nentry, Tree): self._expand_all(nentry, (nentry.root,)) self._expand_all(tree, (nextpos,)) if isinstance(tree, (CollapseMixin, NestedTree)): tree.expand_all() def is_leaf(self, pos, outmost_only=False): return self.first_child_position(pos, outmost_only) is None ################################################ # Tree API ################################################ def parent_position(self, pos): candidate_pos = self._parent_position(self._tree, pos) # return sanitized path (ensure it points to content, not a subtree) return self._sanitize_position(candidate_pos) def _parent_position(self, tree, pos): candidate_pos = None if len(pos) > 1: # get the deepest subtree subtree_pos = pos[:-1] subtree = self._lookup_entry(tree, subtree_pos) # get parent for our position in this subtree least_pos = pos[-1] subparent_pos = subtree.parent_position(least_pos) if subparent_pos is not None: # in case there is one, we are done, the position we look for # is the path up to the subtree plus the local parent position. candidate_pos = subtree_pos + (subparent_pos,) else: # otherwise we recur and look for subtree's parent in the next # outer tree candidate_pos = self._parent_position(self._tree, subtree_pos) else: # there is only one position in the path, we return its parent in # the outmost tree outer_parent = self._tree.parent_position(pos[0]) if outer_parent is not None: # result needs to be valid position (tuple of local positions) candidate_pos = outer_parent, return candidate_pos def first_child_position(self, pos, outmost_only=False): childpos = self._first_child_position(self._tree, pos, outmost_only) return self._sanitize_position(childpos, self._tree) def _first_child_position(self, tree, pos, outmost_only=False): childpos = None # get content at first path element in outmost tree entry = tree[pos[0]] if isinstance(entry, Tree) and not outmost_only and len(pos) > 1: # this points to a tree and we don't check the outmost tree only # recur: get first child in the subtree for remaining path subchild = self._first_child_position(entry, pos[1:]) if subchild is not None: # found a childposition, re-append the path up to this subtree childpos = (pos[0],) + subchild return childpos else: # continue in the next outer tree only if we do not drop # "covered" parts and the position path points to a parent-less # position in the subtree. if (entry.parent_position(pos[1]) is not None or not self._interpret_covered): return None # return the first child of the outmost tree outerchild = tree.first_child_position(pos[0]) if outerchild is not None: childpos = outerchild, return childpos def last_child_position(self, pos, outmost_only=False): childpos = self._last_child_position(self._tree, pos, outmost_only) return self._sanitize_position(childpos, self._tree) def _last_child_position(self, tree, pos, outmost_only=False): childpos = None # get content at first path element in outmost tree entry = tree[pos[0]] if isinstance(entry, Tree) and not outmost_only and len(pos) > 1: # this points to a tree and we don't check the outmost tree only # get last child in the outmost tree if we do not drop "covered" # parts and the position path points to a root of the subtree. if self._interpret_covered: if entry.parent_position(pos[1]) is None: # return the last child of the outmost tree outerchild = tree.last_child_position(pos[0]) if outerchild is not None: childpos = outerchild, # continue as if we have not found anything yet if childpos is None: # recur: get last child in the subtree for remaining path subchild = self._last_child_position(entry, pos[1:]) if subchild is not None: # found a childposition, re-prepend path up to this subtree childpos = (pos[0],) + subchild else: # outmost position element does not point to a tree: # return the last child of the outmost tree outerchild = tree.last_child_position(pos[0]) if outerchild is not None: childpos = outerchild, return childpos def _next_sibling_position(self, tree, pos): candidate = None if len(pos) > 1: # if position path does not point to position in outmost tree, # first get the subtree as pointed out by first dimension, recur # and check if some inner tree already returns a sibling subtree = tree[pos[0]] subsibling_pos = self._next_sibling_position(subtree, pos[1:]) if subsibling_pos is not None: # we found our sibling, prepend the path up to the subtree candidate = pos[:1] + subsibling_pos else: # no deeper tree has sibling. If inner position is root node # the sibling in the outer tree is a valid candidate subparent = subtree.parent_position(pos[1]) if subparent is None: # check if outer tree defines sibling next_sib = tree.next_sibling_position(pos[0]) if next_sib is not None: # it has, we found our candidate candidate = next_sib, # if the inner position has depth 1, then the first child # of its parent in the outer tree can be seen as candidate for # this position next sibling. Those live in the shadow of the # inner tree and are hidden unless requested otherwise elif subtree.parent_position(subparent) is None and \ self._interpret_covered: # we respect "covered" stuff and inner position has depth 1 # get (possibly nested) first child in outer tree candidate = self._first_child_position(tree, pos[:1]) else: # the position path points to the outmost tree # just return its next sibling in the outmost tree next_sib = tree.next_sibling_position(pos[0]) if next_sib is not None: candidate = next_sib, return candidate def next_sibling_position(self, pos): candidate = self._next_sibling_position(self._tree, pos) return self._sanitize_position(candidate, self._tree) def _prev_sibling_position(self, tree, pos): candidate = None if len(pos) > 1: # if position path does not point to position in outmost tree, # first get the subtree as pointed out by first dimension, recur # and check if some inner tree already returns a sibling subtree = tree[pos[0]] subsibling_pos = self._prev_sibling_position(subtree, pos[1:]) if subsibling_pos is not None: # we found our sibling, prepend the path up to the subtree candidate = pos[:1] + subsibling_pos else: # no deeper tree has sibling. If inner position is root node # the sibling in the outer tree is a valid candidate subparent = subtree.parent_position(pos[1]) if subparent is None: prev_sib = tree.prev_sibling_position(pos[0]) if prev_sib is not None: candidate = prev_sib, return candidate # my position could be "hidden" by being child of a # position pointing to a Tree object (which is then unfolded). if self._interpret_covered: # we respect "covered" stuff: # if parent is Tree, return last child of its (last) root parent_pos = self._parent_position(tree, pos) if parent_pos is not None: parent = self._lookup_entry(self._tree, parent_pos) if isinstance(parent, Tree): sib = parent.last_sibling_position(parent.root) candidate = parent.last_child_position(sib) if candidate is not None: candidate = parent_pos + (candidate,) else: # pos points to position in outmost tree prev_sib = tree.prev_sibling_position(pos[0]) if prev_sib is not None: candidate = prev_sib, # In case our new candidate points to a Tree, pick its last root node if candidate is not None: entry = self._lookup_entry(tree, candidate) if isinstance(entry, Tree): candidate = (candidate) + (entry.last_sibling_position(entry.root),) return candidate def prev_sibling_position(self, pos): candidate = self._prev_sibling_position(self._tree, pos) return self._sanitize_position(candidate, self._tree) def last_decendant(self, pos): def lastd(pos): c = self.last_child_position(pos) if c is not None: c = self.last_sibling_position(c) return c return self._last_in_direction(pos, lastd) alot-0.3.5/alot/foreign/urwidtrees/tree.py000066400000000000000000000210751217172710600205650ustar00rootroot00000000000000# Copyright (C) 2013 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. import logging class Tree(object): """ Base class for a tree strucures that can be displayed by :class:`TreeBox` widgets. An instance defines a structure by defining local transformations on positions. That is, by overwriting * `next_sibling_position` * `prev_sibling_position` * `parent_position` * `first_child_position` * `last_child_position` that compute the next position in the respective direction. Also, they need to implement method `__getitem__` that returns a :class:`Widget` for a given position. The type of objects used as positions may vary in subclasses and is deliberately unspecified for the base class. This base class already implements methods based on the local transformations above. These include :meth:`depth`, :meth:`last_decendant` and :meth:`[next|prev]_position ` that computes next/previous positions in depth-first order. """ root = None # local helper def _get(self, pos): """loads widget at given position; handling invalid arguments""" res = None, None if pos is not None: try: res = self[pos], pos except (IndexError, KeyError): pass return res def _next_of_kin(self, pos): """ looks up the next sibling of the closest ancestor with not-None next siblings. """ candidate = None parent = self.parent_position(pos) if parent is not None: candidate = self.next_sibling_position(parent) if candidate is None: candidate = self._next_of_kin(parent) return candidate def _last_in_direction(self, starting_pos, direction): """ recursively move in the tree in given direction and return the last position. :param starting_pos: position to start at :param direction: callable that transforms a position into a position. """ next_pos = direction(starting_pos) if next_pos is None: return starting_pos else: return self._last_in_direction(next_pos, direction) def depth(self, pos): """determine depth of node at pos""" parent = self.parent_position(pos) if parent is None: return 0 else: return self.depth(parent) + 1 def is_leaf(self, pos): """checks if given position has no children""" return self.first_child_position(pos) is None def first_ancestor(self, pos): """ position of pos's ancestor with depth 0. Usually, this should return the root node, but a :class:`Tree` might represent a forrest - have multiple nodes without parent. """ return self._last_in_direction(pos, self.parent_position) def last_decendant(self, pos): """position of last (in DFO) decendant of pos""" return self._last_in_direction(pos, self.last_child_position) def last_sibling_position(self, pos): """position of last sibling of pos""" return self._last_in_direction(pos, self.next_sibling_position) def first_sibling_position(self, pos): """position of first sibling of pos""" return self._last_in_direction(pos, self.prev_sibling_position) def next_position(self, pos): """returns the next position in depth-first order""" candidate = None if pos is not None: candidate = self.first_child_position(pos) if candidate is None: candidate = self.next_sibling_position(pos) if candidate is None: candidate = self._next_of_kin(pos) return candidate def prev_position(self, pos): """returns the previous position in depth-first order""" candidate = None if pos is not None: prevsib = self.prev_sibling_position(pos) # is None if first if prevsib is not None: candidate = self.last_decendant(prevsib) else: parent = self.parent_position(pos) if parent is not None: candidate = parent return candidate def positions(self, reverse=False): """returns a generator that walks the positions of this tree in DFO""" def Posgen(reverse): if reverse: lastrootsib = self.last_sibling_position(self.root) current = self.last_decendant(lastrootsib) while current is not None: yield current current = self.prev_position(current) else: current = self.root while current is not None: yield current current = self.next_position(current) return Posgen(reverse) #################################################################### # End of high-level helper implementation. The following need to be # overwritten by subclasses #################################################################### def parent_position(self, pos): """returns the position of the parent node of the node at `pos` or `None` if none exists.""" return None def first_child_position(self, pos): """returns the position of the first child of the node at `pos`, or `None` if none exists.""" return None def last_child_position(self, pos): """returns the position of the last child of the node at `pos`, or `None` if none exists.""" return None def next_sibling_position(self, pos): """returns the position of the next sibling of the node at `pos`, or `None` if none exists.""" return None def prev_sibling_position(self, pos): """returns the position of the previous sibling of the node at `pos`, or `None` if none exists.""" return None class SimpleTree(Tree): """ Walks on a given fixed acyclic structure given as a list of nodes; every node is a tuple `(content, children)`, where `content` is a `urwid.Widget` to be displayed at that position and `children` is either `None` or a list of nodes. Positions are lists of integers determining a path from the root node with position `(0,)`. """ def __init__(self, treelist): self._treelist = treelist self.root = (0,) if treelist else None Tree.__init__(self) # a few local helper methods def _get_substructure(self, treelist, pos): """recursive helper to look up node-tuple for `pos` in `treelist`""" subtree = None if len(pos) > 1: subtree = self._get_substructure(treelist[pos[0]][1], pos[1:]) else: try: subtree = treelist[pos[0]] except (IndexError, TypeError): pass return subtree def _get_node(self, treelist, pos): """ look up widget at `pos` of `treelist`; default to None if nonexistent. """ node = None if pos is not None: subtree = self._get_substructure(treelist, pos) if subtree is not None: node = subtree[0] return node def _confirm_pos(self, pos): """look up widget for pos and default to None""" candidate = None if self._get_node(self._treelist, pos) is not None: candidate = pos return candidate # Tree API def __getitem__(self, pos): return self._get_node(self._treelist, pos) def parent_position(self, pos): parent = None if pos is not None: if len(pos) > 1: parent = pos[:-1] return parent def first_child_position(self, pos): return self._confirm_pos(pos + (0,)) def last_child_position(self, pos): candidate = None subtree = self._get_substructure(self._treelist, pos) if subtree is not None: children = subtree[1] if children is not None: candidate = pos + (len(children) - 1,) return candidate def next_sibling_position(self, pos): return self._confirm_pos(pos[:-1] + (pos[-1] + 1,)) def prev_sibling_position(self, pos): return pos[:-1] + (pos[-1] - 1,) if (pos[-1] > 0) else None # optimizations def depth(self, pos): """more performant implementation due to specific structure of pos""" return len(pos) - 1 alot-0.3.5/alot/foreign/urwidtrees/widgets.py000066400000000000000000000175651217172710600213050ustar00rootroot00000000000000# Copyright (C) 2013 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. import urwid import logging from urwid import WidgetWrap, ListBox from urwid import signals from decoration import DecoratedTree, CollapseMixin from nested import NestedTree from lru_cache import lru_cache # The following are used to check dynamically if a tree offers sub-APIs def implementsDecorateAPI(tree): """determines if given tree offers line decoration""" return isinstance(tree, (DecoratedTree, NestedTree)) def implementsCollapseAPI(tree): """determines if given tree can collapse positions""" res = False if isinstance(tree, (CollapseMixin, NestedTree)): res = True return res class TreeListWalker(urwid.ListWalker): """ ListWalker to walk through a class:`Tree`. This translates a :class:`Tree` into a :class:`urwid.ListWalker` that is digestible by :class:`urwid.ListBox`. It uses :meth:`Tree.[next|prev]_position ` to determine the next/previous position in depth first order. """ def __init__(self, tree, focus=None): """ :param tree: the tree to be displayed :type tree: Tree :param focus: position of node to be focussed initially. This has to be a valid position in the Tree. It defaults to the value of `Tree.root`. """ self._tree = tree self._focus = focus or tree.root self.root = tree.root @lru_cache() def __getitem__(self, pos): """gets (possibly decorated) line widget at given position""" if implementsDecorateAPI(self._tree): entry = self._tree.get_decorated(pos) else: entry = self._tree[pos] return entry def clear_cache(self): """removes all cached lines""" self.__getitem__.cache_clear() def _get(self, pos): """looks up widget for given position; handling invalid arguments""" res = None, None if pos is not None: try: res = self[pos], pos except (IndexError, KeyError): pass return res # List Walker API. def get_focus(self): return self._get(self._focus) def set_focus(self, pos): self._focus = pos def get_next(self, pos): return self._get(self._tree.next_position(pos)) def get_prev(self, pos): return self._get(self._tree.prev_position(pos)) def positions(self, reverse=False): """returns a generator that walks the tree's positions""" return self._tree.positions(reverse) # end of List Walker API class TreeBox(WidgetWrap): """ A widget that displays a given :class:`Tree`. This is essentially a :class:`ListBox` with the ability to move the focus based on directions in the Tree and to collapse/expand subtrees if possible. TreeBox interprets `left/right` as well as `page up/`page down` to move the focus to parent/first child and next/previous sibling respectively. All other keys are passed to the underlying ListBox. """ def __init__(self, tree, focus=None): """ :param tree: tree of widgets to be displayed. :type tree: Tree :param focus: initially focussed position """ self._tree = tree self._walker = TreeListWalker(tree) self._outer_list = ListBox(self._walker) if focus is not None: self._outer_list.set_focus(focus) self.__super.__init__(self._outer_list) # Widget API def get_focus(self): return self._outer_list.get_focus() def set_focus(self, pos): return self._outer_list.set_focus(pos) def refresh(self): self._walker.clear_cache() signals.emit_signal(self._walker, "modified") def keypress(self, size, key): key = self._outer_list.keypress(size, key) if key in ['left', 'right', '[', ']', '-', '+', 'C', 'E', ]: if key == 'left': self.focus_parent() elif key == 'right': self.focus_first_child() elif key == '[': self.focus_prev_sibling() elif key == ']': self.focus_next_sibling() elif key == '-': self.collapse_focussed() elif key == '+': self.expand_focussed() elif key == 'C': self.collapse_all() elif key == 'E': self.expand_all() # This is a hack around ListBox misbehaving: # it seems impossible to set the focus without calling keypress as # otherwise the change becomes visible only after the next render() return self._outer_list.keypress(size, None) else: return self._outer_list.keypress(size, key) # Collapse operations def collapse_focussed(self): """ Collapse currently focussed position; works only if the underlying tree allows it. """ if implementsCollapseAPI(self._tree): w, focuspos = self.get_focus() self._tree.collapse(focuspos) self._walker.clear_cache() self.refresh() def expand_focussed(self): """ Expand currently focussed position; works only if the underlying tree allows it. """ if implementsCollapseAPI(self._tree): w, focuspos = self.get_focus() self._tree.expand(focuspos) self._walker.clear_cache() self.refresh() def collapse_all(self): """ Collapse all positions; works only if the underlying tree allows it. """ if implementsCollapseAPI(self._tree): self._tree.collapse_all() self.set_focus(self._tree.root) self._walker.clear_cache() self.refresh() def expand_all(self): """ Expand all positions; works only if the underlying tree allows it. """ if implementsCollapseAPI(self._tree): self._tree.expand_all() self._walker.clear_cache() self.refresh() # Tree based focus movement def focus_parent(self): """move focus to parent node of currently focussed one""" w, focuspos = self.get_focus() parent = self._tree.parent_position(focuspos) if parent is not None: self.set_focus(parent) def focus_first_child(self): """move focus to first child of currently focussed one""" w, focuspos = self.get_focus() child = self._tree.first_child_position(focuspos) if child is not None: self.set_focus(child) def focus_last_child(self): """move focus to last child of currently focussed one""" w, focuspos = self.get_focus() child = self._tree.last_child_position(focuspos) if child is not None: self.set_focus(child) def focus_next_sibling(self): """move focus to next sibling of currently focussed one""" w, focuspos = self.get_focus() sib = self._tree.next_sibling_position(focuspos) if sib is not None: self.set_focus(sib) def focus_prev_sibling(self): """move focus to previous sibling of currently focussed one""" w, focuspos = self.get_focus() sib = self._tree.prev_sibling_position(focuspos) if sib is not None: self.set_focus(sib) def focus_next(self): """move focus to next position (DFO)""" w, focuspos = self.get_focus() next = self._tree.next_position(focuspos) if next is not None: self.set_focus(next) def focus_prev(self): """move focus to previous position (DFO)""" w, focuspos = self.get_focus() prev = self._tree.prev_position(focuspos) if prev is not None: self.set_focus(prev) alot-0.3.5/alot/helper.py000066400000000000000000000433611217172710600152610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file from datetime import timedelta from datetime import datetime from collections import deque import subprocess import shlex import email import mimetypes import os import re from email.mime.audio import MIMEAudio from email.mime.base import MIMEBase from email.mime.image import MIMEImage from email.mime.text import MIMEText import urwid import magic from twisted.internet import reactor from twisted.internet.protocol import ProcessProtocol from twisted.internet.defer import Deferred import StringIO import logging def split_commandline(s, comments=False, posix=True): """ splits semi-colon separated commandlines """ # shlex seems to remove unescaped quotes and backslashes s = s.replace('\\', '\\\\') s = s.replace('\'', '\\\'') s = s.replace('\"', '\\\"') # encode s to utf-8 for shlex if isinstance(s, unicode): s = s.encode('utf-8') lex = shlex.shlex(s, posix=posix) lex.whitespace_split = True lex.whitespace = ';' if not comments: lex.commenters = '' return list(lex) def split_commandstring(cmdstring): """ split command string into a list of strings to pass on to subprocess.Popen and the like. This simply calls shlex.split but works also with unicode bytestrings. """ if isinstance(cmdstring, unicode): cmdstring = cmdstring.encode('utf-8', errors='ignore') return shlex.split(cmdstring) def safely_get(clb, E, on_error=''): """ returns result of :func:`clb` and falls back to `on_error` in case exception `E` is raised. :param clb: function to evaluate :type clb: callable :param E: exception to catch :type E: Exception :param on_error: default string returned when exception is caught :type on_error: str """ try: return clb() except E: return on_error def string_sanitize(string, tab_width=8): r""" strips, and replaces non-printable characters :param tab_width: number of spaces to replace tabs with. Read from `globals.tabwidth` setting if `None` :type tab_width: int or `None` >>> string_sanitize(' foo\rbar ', 8) 'foobar' >>> string_sanitize('foo\tbar', 8) 'foo bar' >>> string_sanitize('foo\t\tbar', 8) 'foo bar' """ string = string.strip() string = string.replace('\r', '') lines = list() for line in string.split('\n'): tab_count = line.count('\t') if tab_count > 0: line_length = 0 new_line = list() for i, chunk in enumerate(line.split('\t')): line_length += len(chunk) new_line.append(chunk) if i < tab_count: next_tab_stop_in = tab_width - (line_length % tab_width) new_line.append(' ' * next_tab_stop_in) line_length += next_tab_stop_in lines.append(''.join(new_line)) else: lines.append(line) return '\n'.join(lines) def string_decode(string, enc='ascii'): """ safely decodes string to unicode bytestring, respecting `enc` as a hint. """ if enc is None: enc = 'ascii' try: string = unicode(string, enc, errors='replace') except LookupError: # malformed enc string string = string.decode('ascii', errors='replace') except TypeError: # already unicode pass return string def shorten(string, maxlen): """shortens string if longer than maxlen, appending ellipsis""" if maxlen > 1 and len(string) > maxlen: string = string[:maxlen - 1] + u'\u2026' return string[:maxlen] def shorten_author_string(authors_string, maxlength): """ Parse a list of authors concatenated as a text string (comma separated) and smartly adjust them to maxlength. 1) If the complete list of sender names does not fit in maxlength, it tries to shorten names by using only the first part of each. 2) If the list is still too long, hide authors according to the following priority: - First author is always shown (if too long is shorten with ellipsis) - If possible, last author is also shown (if too long, uses ellipsis) - If there are more than 2 authors in the thread, show the maximum of them. More recent senders have higher priority. - If it is finally necessary to hide any author, an ellipsis between first and next authors is added. >>> authors = u'King Kong, Mucho Muchacho, Jaime Huerta, Flash Gordon' >>> print shorten_author_string(authors, 60) King Kong, Mucho Muchacho, Jaime Huerta, Flash Gordon >>> print shorten_author_string(authors, 40) King, Mucho, Jaime, Flash >>> print shorten_author_string(authors, 20) King, …, Jai…, Flash >>> print shorten_author_string(authors, 10) King, … >>> print shorten_author_string(authors, 2) K… >>> print shorten_author_string(authors, 1) K """ # I will create a list of authors by parsing author_string. I use # deque to do popleft without performance penalties authors = deque() # If author list is too long, it uses only the first part of each # name (gmail style) short_names = len(authors_string) > maxlength for au in authors_string.split(", "): if short_names: author_as_list = au.split() if len(author_as_list) > 0: authors.append(author_as_list[0]) else: authors.append(au) # Author chain will contain the list of author strings to be # concatenated using commas for the final formatted author_string. authors_chain = deque() if len(authors) == 0: return u'' # reserve space for first author first_au = shorten(authors.popleft(), maxlength) remaining_length = maxlength - len(first_au) # Tries to add an ellipsis if no space to show more than 1 author if authors and maxlength > 3 and remaining_length < 3: first_au = shorten(first_au, maxlength - 3) remaining_length += 3 # Tries to add as more authors as possible. It takes into account # that if any author will be hidden, and ellipsis should be added while authors and remaining_length >= 3: au = authors.pop() if len(au) > 1 and (remaining_length == 3 or (authors and remaining_length < 7)): authors_chain.appendleft(u'\u2026') break else: if authors: # 5= ellipsis + 2 x comma and space used as separators au_string = shorten(au, remaining_length - 5) else: # 2 = comma and space used as separator au_string = shorten(au, remaining_length - 2) remaining_length -= len(au_string) + 2 authors_chain.appendleft(au_string) # Add the first author to the list and concatenate list authors_chain.appendleft(first_au) authorsstring = ', '.join(authors_chain) return authorsstring def pretty_datetime(d): """ translates :class:`datetime` `d` to a "sup-style" human readable string. >>> now = datetime.now() >>> now.strftime('%c') 'Sat 31 Mar 2012 14:47:26 ' >>> pretty_datetime(now) u'just now' >>> pretty_datetime(now - timedelta(minutes=1)) u'1min ago' >>> pretty_datetime(now - timedelta(hours=5)) u'5h ago' >>> pretty_datetime(now - timedelta(hours=12)) u'02:54am' >>> pretty_datetime(now - timedelta(days=1)) u'yest 02pm' >>> pretty_datetime(now - timedelta(days=2)) u'Thu 02pm' >>> pretty_datetime(now - timedelta(days=7)) u'Mar 24' >>> pretty_datetime(now - timedelta(days=356)) u'Apr 2011' """ ampm = d.strftime('%P') if len(ampm): hourfmt = '%I' + ampm hourminfmt = '%I:%M' + ampm else: hourfmt = '%Hh' hourminfmt = '%H:%M' now = datetime.now() today = now.date() if d.date() == today or d > now - timedelta(hours=6): delta = datetime.now() - d if delta.seconds < 60: string = 'just now' elif delta.seconds < 3600: string = '%dmin ago' % (delta.seconds / 60) elif delta.seconds < 6 * 3600: string = '%dh ago' % (delta.seconds / 3600) else: string = d.strftime(hourminfmt) elif d.date() == today - timedelta(1): string = d.strftime('yest ' + hourfmt) elif d.date() > today - timedelta(7): string = d.strftime('%a ' + hourfmt) elif d.year != today.year: string = d.strftime('%b %Y') else: string = d.strftime('%b %d') return string_decode(string, 'UTF-8') def call_cmd(cmdlist, stdin=None): """ get a shell commands output, error message and return value and immediately return. .. warning:: This returns with the first screen content for interactive commands. :param cmdlist: shellcommand to call, already splitted into a list accepted by :meth:`subprocess.Popen` :type cmdlist: list of str :param stdin: string to pipe to the process :type stdin: str :return: triple of stdout, stderr, return value of the shell command :rtype: str, str, int """ out, err, ret = '', '', 0 try: if stdin: proc = subprocess.Popen(cmdlist, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate(stdin) ret = proc.poll() else: try: out = subprocess.check_output(cmdlist) except subprocess.CalledProcessError as e: err = e.output ret = e.returncode except OSError as e: err = e.strerror ret = e.errno out = string_decode(out, urwid.util.detected_encoding) err = string_decode(err, urwid.util.detected_encoding) return out, err, ret def call_cmd_async(cmdlist, stdin=None, env=None): """ get a shell commands output, error message and return value as a deferred. :type cmdlist: list of str :param stdin: string to pipe to the process :type stdin: str :return: deferred that calls back with triple of stdout, stderr and return value of the shell command :rtype: `twisted.internet.defer.Deferred` """ class _EverythingGetter(ProcessProtocol): def __init__(self, deferred): self.deferred = deferred self.outBuf = StringIO.StringIO() self.errBuf = StringIO.StringIO() self.outReceived = self.outBuf.write self.errReceived = self.errBuf.write def processEnded(self, status): termenc = urwid.util.detected_encoding out = string_decode(self.outBuf.getvalue(), termenc) err = string_decode(self.errBuf.getvalue(), termenc) if status.value.exitCode == 0: self.deferred.callback(out) else: terminated_obj = status.value terminated_obj.stderr = err self.deferred.errback(terminated_obj) d = Deferred() environment = os.environ if env is not None: environment.update(env) logging.debug('ENV = %s' % environment) logging.debug('CMD = %s' % cmdlist) proc = reactor.spawnProcess(_EverythingGetter(d), executable=cmdlist[0], env=environment, args=cmdlist) if stdin: logging.debug('writing to stdin') proc.write(stdin) proc.closeStdin() return d def guess_mimetype(blob): """ uses file magic to determine the mime-type of the given data blob. :param blob: file content as read by file.read() :type blob: data :returns: mime-type, falls back to 'application/octet-stream' :rtype: str """ mimetype = 'application/octet-stream' # this is a bit of a hack to support different versions of python magic. # Hopefully at some point this will no longer be necessary # # the version with open() is the bindings shipped with the file source from # http://darwinsys.com/file/ - this is what is used by the python-magic # package on Debian/Ubuntu. However, it is not available on pypi/via pip. # # the version with from_buffer() is available at # https://github.com/ahupp/python-magic and directly installable via pip. # # for more detail see https://github.com/pazz/alot/pull/588 if hasattr(magic, 'open'): m = magic.open(magic.MAGIC_MIME_TYPE) m.load() magictype = m.buffer(blob) elif hasattr(magic, 'from_buffer'): magictype = magic.from_buffer(blob, mime=True) else: raise Exception('Unknown magic API') # libmagic does not always return proper mimetype strings, cf. issue #459 if re.match(r'\w+\/\w+', magictype): mimetype = magictype return mimetype def guess_encoding(blob): """ uses file magic to determine the encoding of the given data blob. :param blob: file content as read by file.read() :type blob: data :returns: encoding :rtype: str """ # this is a bit of a hack to support different versions of python magic. # Hopefully at some point this will no longer be necessary # # the version with open() is the bindings shipped with the file source from # http://darwinsys.com/file/ - this is what is used by the python-magic # package on Debian/Ubuntu. However it is not available on pypi/via pip. # # the version with from_buffer() is available at # https://github.com/ahupp/python-magic and directly installable via pip. # # for more detail see https://github.com/pazz/alot/pull/588 if hasattr(magic, 'open'): m = magic.open(magic.MAGIC_MIME_ENCODING) m.load() return m.buffer(blob) elif hasattr(magic, 'from_buffer'): m = magic.Magic(mime_encoding=True) return m.from_buffer(blob) else: raise Exception('Unknown magic API') def libmagic_version_at_least(version): """ checks if the libmagic library installed is more recent than a given version. :param version: minimum version expected in the form XYY (i.e. 5.14 -> 514) with XYY >= 513 """ if hasattr(magic, 'open'): magic_wrapper = magic._libraries['magic'] elif hasattr(magic, 'from_buffer'): magic_wrapper = magic.libmagic else: raise Exception('Unknown magic API') if not hasattr(magic_wrapper, 'magic_version'): # The magic_version function has been introduced in libmagic 5.13, # if it's not present, we can't guess right, so let's assume False return False return (magic_wrapper.magic_version >= version) # TODO: make this work on blobs, not paths def mimewrap(path, filename=None, ctype=None): content = open(path, 'rb').read() if not ctype: ctype = guess_mimetype(content) # libmagic < 5.12 incorrectly detects excel/powerpoint files as # 'application/msword' (see #179 and #186 in libmagic bugtracker) # This is a workaround, based on file extension, useful as long # as distributions still ship libmagic 5.11. if (ctype == 'application/msword' and not libmagic_version_at_least(513)): mimetype, encoding = mimetypes.guess_type(path) if mimetype: ctype = mimetype maintype, subtype = ctype.split('/', 1) if maintype == 'text': part = MIMEText(content.decode(guess_encoding(content), 'replace'), _subtype=subtype, _charset='utf-8') elif maintype == 'image': part = MIMEImage(content, _subtype=subtype) elif maintype == 'audio': part = MIMEAudio(content, _subtype=subtype) else: part = MIMEBase(maintype, subtype) part.set_payload(content) # Encode the payload using Base64 email.encoders.encode_base64(part) # Set the filename parameter if not filename: filename = os.path.basename(path) part.add_header('Content-Disposition', 'attachment', filename=filename) return part def shell_quote(text): r''' >>> print(shell_quote("hello")) 'hello' >>> print(shell_quote("hello'there")) 'hello'"'"'there' ''' return "'%s'" % text.replace("'", """'"'"'""") def tag_cmp(a, b): r''' Sorting tags using this function puts all tags of length 1 at the beginning. This groups all tags mapped to unicode characters. ''' if min(len(a), len(b)) == 1 and max(len(a), len(b)) > 1: return cmp(len(a), len(b)) else: return cmp(a.lower(), b.lower()) def humanize_size(size): r''' >>> humanize_size(1) '1' >>> humanize_size(123) '123' >>> humanize_size(1234) '1K' >>> humanize_size(1234 * 1024) '1.2M' >>> humanize_size(1234 * 1024 * 1024) '1234.0M' ''' for factor, format_string in ((1, '%i'), (1024, '%iK'), (1024 * 1024, '%.1fM')): if size / factor < 1024: return format_string % (float(size) / factor) return format_string % (size / factor) def parse_mailcap_nametemplate(tmplate='%s'): """this returns a prefix and suffix to be used in the tempfile module for a given mailcap nametemplate string""" nt_list = tmplate.split('%s') template_prefix = '' template_suffix = '' if len(nt_list) == 2: template_suffix = nt_list[1] template_prefix = nt_list[0] else: template_suffix = tmplate return (template_prefix, template_suffix) alot-0.3.5/alot/init.py000077500000000000000000000143041217172710600147430ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import sys import logging import os import alot from alot.settings import settings from alot.settings.errors import ConfigError from alot.db.manager import DBManager from alot.ui import UI import alot.commands as commands from alot.commands import * from alot.commands import CommandParseError from twisted.python import usage class SubcommandOptions(usage.Options): optFlags = [] def parseArgs(self, *args): self.args = args def as_argparse_opts(self): optstr = '' for k, v in self.items(): # flags translate int value 0 or 1.. if k in [a[0] for a in self.optFlags]: # if flag optstr += ('--%s ' % k) * v else: if v is not None: optstr += '--%s \'%s\' ' % (k, v) return optstr def opt_version(self): print alot.__version__ sys.exit(0) class ComposeOptions(SubcommandOptions): optParameters = [ ['sender', '', None, 'From line'], ['subject', '', None, 'subject line'], ['cc', '', None, 'copy to'], ['bcc', '', None, 'blind copy to'], ['template', '', None, 'path to template file'], ['attach', '', None, 'files to attach'], ] optFlags = [ ['omit_signature', '', 'do not add signature'], ] def parseArgs(self, *args): SubcommandOptions.parseArgs(self, *args) self['to'] = ' '.join(args) or None class SearchOptions(SubcommandOptions): accepted = ['oldest_first', 'newest_first', 'message_id', 'unsorted'] def colourint(val): if val not in accepted: raise ValueError("Unknown sort order") return val colourint.coerceDoc = "Must be one of " + str(accepted) optParameters = [ ['sort', 'newest_first', None, 'Sort order'], ] class Options(usage.Options): optFlags = [["read-only", "r", 'open db in read only mode'], ] def colourint(val): val = int(val) if val not in [1, 16, 256]: raise ValueError("Not in range") return val colourint.coerceDoc = "Must be 1, 16 or 256" def debuglogstring(val): if val not in ['error', 'debug', 'info', 'warning']: raise ValueError("Not in range") return val debuglogstring.coerceDoc = "Must be one of debug,info,warning or error" optParameters = [ ['config', 'c', None, 'config file'], ['notmuch-config', 'n', None, 'notmuch config'], ['colour-mode', 'C', None, 'terminal colour mode', colourint], ['mailindex-path', 'p', None, 'path to notmuch index'], ['debug-level', 'd', 'info', 'debug log', debuglogstring], ['logfile', 'l', '/dev/null', 'logfile'], ] search_help = "start in a search buffer using the querystring provided "\ "as parameter. See the SEARCH SYNTAX section of notmuch(1)." subCommands = [['search', None, SearchOptions, search_help], ['compose', None, ComposeOptions, "compose a new message"]] def opt_version(self): print alot.__version__ sys.exit(0) def main(): # interpret cml arguments args = Options() try: args.parseOptions() # When given no argument, parses sys.argv[1:] except usage.UsageError, errortext: print '%s' % errortext print 'Try --help for usage details.' sys.exit(1) # logging root_logger = logging.getLogger() for log_handler in root_logger.handlers: root_logger.removeHandler(log_handler) root_logger = None numeric_loglevel = getattr(logging, args['debug-level'].upper(), None) logfilename = os.path.expanduser(args['logfile']) logformat = '%(levelname)s:%(module)s:%(message)s' logging.basicConfig(level=numeric_loglevel, filename=logfilename, filemode='w', format=logformat) # locate alot config files configfiles = [ os.path.join(os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), 'alot', 'config'), ] if args['config']: expanded_path = os.path.expanduser(args['config']) if not os.path.exists(expanded_path): msg = 'Config file "%s" does not exist. Goodbye for now.' sys.exit(msg % expanded_path) configfiles.insert(0, expanded_path) # locate notmuch config notmuchpath = os.environ.get('NOTMUCH_CONFIG', '~/.notmuch-config') if args['notmuch-config']: notmuchpath = args['notmuch-config'] notmuchconfig = os.path.expanduser(notmuchpath) alotconfig = None # read the first alot config file we find for configfilename in configfiles: if os.path.exists(configfilename): alotconfig = configfilename break # use only the first try: settings.read_config(alotconfig) settings.read_notmuch_config(notmuchconfig) except (ConfigError, OSError, IOError), e: sys.exit(e) # store options given by config swiches to the settingsManager: if args['colour-mode']: settings.set('colourmode', args['colour-mode']) # get ourselves a database manager indexpath = settings.get_notmuch_setting('database', 'path') indexpath = args['mailindex-path'] or indexpath dbman = DBManager(path=indexpath, ro=args['read-only']) # determine what to do try: if args.subCommand == 'search': query = ' '.join(args.subOptions.args) cmdstring = 'search %s %s' % (args.subOptions.as_argparse_opts(), query) cmd = commands.commandfactory(cmdstring, 'global') elif args.subCommand == 'compose': cmdstring = 'compose %s' % args.subOptions.as_argparse_opts() cmd = commands.commandfactory(cmdstring, 'global') else: default_commandline = settings.get('initial_command') cmd = commands.commandfactory(default_commandline, 'global') except CommandParseError, e: sys.exit(e) # set up and start interface UI(dbman, cmd) alot-0.3.5/alot/settings/000077500000000000000000000000001217172710600152615ustar00rootroot00000000000000alot-0.3.5/alot/settings/__init__.py000066400000000000000000000004111217172710600173660ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file from alot.settings.manager import SettingsManager settings = SettingsManager() alot-0.3.5/alot/settings/checks.py000066400000000000000000000112411217172710600170720ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import mailbox import re from urwid import AttrSpec, AttrSpecError from urlparse import urlparse from validate import VdtTypeError from validate import is_list from validate import ValidateError, VdtValueTooLongError, VdtValueError from alot import crypto from alot.errors import GPGProblem def attr_triple(value): """ Check that interprets the value as `urwid.AttrSpec` triple for the colour modes 1,16 and 256. It assumes a <6 tuple of attribute strings for mono foreground, mono background, 16c fg, 16c bg, 256 fg and 256 bg respectively. If any of these are missing, we downgrade to the next lower available pair, defaulting to 'default'. :raises: VdtValueTooLongError, VdtTypeError :rtype: triple of `urwid.AttrSpec` """ keys = ['dfg', 'dbg', '1fg', '1bg', '16fg', '16bg', '256fg', '256bg'] acc = {} if not isinstance(value, (list, tuple)): value = value, if len(value) > 6: raise VdtValueTooLongError(value) # ensure we have exactly 6 attribute strings attrstrings = (value + (6 - len(value)) * [None])[:6] # add fallbacks for the empty list attrstrings = (2 * ['default']) + attrstrings for i, value in enumerate(attrstrings): if value: acc[keys[i]] = value else: acc[keys[i]] = acc[keys[i - 2]] try: mono = AttrSpec(acc['1fg'], acc['1bg'], 1) normal = AttrSpec(acc['16fg'], acc['16bg'], 16) high = AttrSpec(acc['256fg'], acc['256bg'], 256) except AttrSpecError, e: raise ValidateError(e.message) return mono, normal, high def align_mode(value): """ test if value is one of 'left', 'right' or 'center' """ if value not in ['left', 'right', 'center']: raise VdtValueError return value def width_tuple(value): """ test if value is a valid width indicator (for a sub-widget in a column). This can either be ('fit', min, max): use the length actually needed for the content, padded to use at least width min, and cut of at width max. Here, min and max are positive integers or 0 to disable the boundary. ('weight',n): have it relative weight of n compared to other columns. Here, n is an int. """ if value is None: res = 'fit', 0, 0 elif not isinstance(value, (list, tuple)): raise VdtTypeError(value) elif value[0] not in ['fit', 'weight']: raise VdtTypeError(value) if value[0] == 'fit': if not isinstance(value[1], int) or not isinstance(value[2], int): VdtTypeError(value) res = 'fit', int(value[1]), int(value[2]) else: if not isinstance(value[1], int): VdtTypeError(value) res = 'weight', int(value[1]) return res def mail_container(value): """ Check that the value points to a valid mail container, in URI-style, e.g.: `mbox:///home/username/mail/mail.box`. The value is cast to a :class:`mailbox.Mailbox` object. """ if not re.match(r'.*://.*', value): raise VdtTypeError(value) mburl = urlparse(value) if mburl.scheme == 'mbox': box = mailbox.mbox(mburl.path) elif mburl.scheme == 'maildir': box = mailbox.Maildir(mburl.path) elif mburl.scheme == 'mh': box = mailbox.MH(mburl.path) elif mburl.scheme == 'babyl': box = mailbox.Babyl(mburl.path) elif mburl.scheme == 'mmdf': box = mailbox.MMDF(mburl.path) else: raise VdtTypeError(value) return box def force_list(value, min=None, max=None): """ Check that a value is a list, coercing strings into a list with one member. You can optionally specify the minimum and maximum number of members. A minumum of greater than one will fail if the user only supplies a string. The difference to :func:`validate.force_list` is that this test will return an empty list instead of `['']` if the config value matches `r'\s*,?\s*'`. >>> vtor.check('force_list', 'hello') ['hello'] >>> vtor.check('force_list', '') [] """ if not isinstance(value, (list, tuple)): value = [value] rlist = is_list(value, min, max) if rlist == ['']: rlist = [] return rlist def gpg_key(value): """ test if value points to a known gpg key and return that key as :class:`pyme.pygpgme._gpgme_key`. """ try: return crypto.get_key(value) except GPGProblem, e: raise ValidateError(e.message) alot-0.3.5/alot/settings/errors.py000066400000000000000000000004101217172710600171420ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file class ConfigError(Exception): """could not parse user config""" pass alot-0.3.5/alot/settings/manager.py000066400000000000000000000361271217172710600172560ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import imp import os import re import mailcap import logging from configobj import ConfigObj, Section from alot.account import SendmailAccount from alot.addressbooks import MatchSdtoutAddressbook, AbookAddressBook from alot.helper import pretty_datetime, string_decode from errors import ConfigError from utils import read_config from utils import resolve_att from checks import force_list from checks import mail_container from checks import gpg_key from checks import attr_triple from checks import align_mode from theme import Theme DEFAULTSPATH = os.path.join(os.path.dirname(__file__), '..', 'defaults') class SettingsManager(object): """Organizes user settings""" def __init__(self, alot_rc=None, notmuch_rc=None): """ :param alot_rc: path to alot's config file :type alot_rc: str :param notmuch_rc: path to notmuch's config file :type notmuch_rc: str """ self.hooks = None self._mailcaps = mailcap.getcaps() self._config = ConfigObj() self._notmuchconfig = None self._theme = None self._accounts = None self._accountmap = None bindings_path = os.path.join(DEFAULTSPATH, 'default.bindings') self._bindings = ConfigObj(bindings_path) if alot_rc is not None: self.read_config(alot_rc) if notmuch_rc is not None: self.read_notmuch_config(notmuch_rc) def read_notmuch_config(self, path): """parse notmuch's config file from path""" spec = os.path.join(DEFAULTSPATH, 'notmuch.rc.spec') self._notmuchconfig = read_config(path, spec) def read_config(self, path): """parse alot's config file from path""" spec = os.path.join(DEFAULTSPATH, 'alot.rc.spec') newconfig = read_config(path, spec, checks={'mail_container': mail_container, 'force_list': force_list, 'align': align_mode, 'attrtriple': attr_triple, 'gpg_key_hint': gpg_key}) self._config.merge(newconfig) hooks_path = os.path.expanduser(self._config.get('hooksfile')) try: self.hooks = imp.load_source('hooks', hooks_path) except: logging.debug('unable to load hooks file:%s' % hooks_path) if 'bindings' in newconfig: newbindings = newconfig['bindings'] if isinstance(newbindings, Section): self._bindings.merge(newbindings) # themes themestring = newconfig['theme'] themes_dir = self._config.get('themes_dir') if themes_dir: themes_dir = os.path.expanduser(themes_dir) else: configdir = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) themes_dir = os.path.join(configdir, 'alot', 'themes') logging.debug(themes_dir) # if config contains theme string use that if themestring: if not os.path.isdir(themes_dir): err_msg = 'cannot find theme %s: themes_dir %s is missing' raise ConfigError(err_msg % (themestring, themes_dir)) else: theme_path = os.path.join(themes_dir, themestring) try: self._theme = Theme(theme_path) except ConfigError as e: err_msg = 'Theme file %s failed validation:\n' raise ConfigError((err_msg % themestring) + str(e.message)) # if still no theme is set, resort to default if self._theme is None: theme_path = os.path.join(DEFAULTSPATH, 'default.theme') self._theme = Theme(theme_path) self._accounts = self._parse_accounts(self._config) self._accountmap = self._account_table(self._accounts) def _parse_accounts(self, config): """ read accounts information from config :param config: valit alot config :type config: `configobj.ConfigObj` :returns: list of accounts """ accounts = [] if 'accounts' in config: for acc in config['accounts'].sections: accsec = config['accounts'][acc] args = dict(config['accounts'][acc]) # create abook for this account abook = accsec['abook'] logging.debug('abook defined: %s' % abook) if abook['type'] == 'shellcommand': cmd = abook['command'] regexp = abook['regexp'] if cmd is not None and regexp is not None: args['abook'] = MatchSdtoutAddressbook(cmd, match=regexp) else: msg = 'underspecified abook of type \'shellcommand\':' msg += '\ncommand: %s\nregexp:%s' % (cmd, regexp) raise ConfigError(msg) elif abook['type'] == 'abook': contacts_path = abook['abook_contacts_file'] args['abook'] = AbookAddressBook( contacts_path, ignorecase=abook['ignorecase']) else: del(args['abook']) cmd = args['sendmail_command'] del(args['sendmail_command']) newacc = SendmailAccount(cmd, **args) accounts.append(newacc) return accounts def _account_table(self, accounts): """ creates a lookup table (emailaddress -> account) for a given list of accounts :param accounts: list of accounts :type accounts: list of `alot.account.Account` :returns: hashtable :rvalue: dict (str -> `alot.account.Account`) """ accountmap = {} for acc in accounts: accountmap[acc.address] = acc for alias in acc.aliases: accountmap[alias] = acc return accountmap def get(self, key, fallback=None): """ look up global config values from alot's config :param key: key to look up :type key: str :param fallback: fallback returned if key is not present :type fallback: str :returns: config value with type as specified in the spec-file """ value = None if key in self._config: value = self._config[key] if isinstance(value, Section): value = None if value is None: value = fallback return value def set(self, key, value): """ setter for global config values :param key: config option identifise :type key: str :param value: option to set :type value: depends on the specfile :file:`alot.rc.spec` """ self._config[key] = value def get_notmuch_setting(self, section, key, fallback=None): """ look up config values from notmuch's config :param section: key is in :type section: str :param key: key to look up :type key: str :param fallback: fallback returned if key is not present :type fallback: str :returns: config value with type as specified in the spec-file """ value = None if section in self._notmuchconfig: if key in self._notmuchconfig[section]: value = self._notmuchconfig[section][key] if value is None: value = fallback return value def get_theming_attribute(self, mode, name, part=None): """ looks up theming attribute :param mode: ui-mode (e.g. `search`,`thread`...) :type mode: str :param name: identifier of the atttribute :type name: str :rtype: urwid.AttrSpec """ colours = int(self._config.get('colourmode')) return self._theme.get_attribute(colours, mode, name, part) def get_threadline_theming(self, thread): """ looks up theming info a threadline displaying a given thread. This wraps around :meth:`~alot.settings.theme.Theme.get_threadline_theming`, filling in the current colour mode. :param thread: thread to theme :type thread: alot.db.thread.Thread """ colours = int(self._config.get('colourmode')) return self._theme.get_threadline_theming(thread, colours) def get_tagstring_representation(self, tag, onebelow_normal=None, onebelow_focus=None): """ looks up user's preferred way to represent a given tagstring. :param tag: tagstring :type tag: str :param onebelow_normal: attribute that shines through if unfocussed :type onebelow_normal: urwid.AttrSpec :param onebelow_focus: attribute that shines through if focussed :type onebelow_focus: urwid.AttrSpec If `onebelow_normal` or `onebelow_focus` is given these attributes will be used as fallbacks for fg/bg values '' and 'default'. This returns a dictionary mapping :normal: to :class:`urwid.AttrSpec` used if unfocussed :focussed: to :class:`urwid.AttrSpec` used if focussed :translated: to an alternative string representation """ colourmode = int(self._config.get('colourmode')) theme = self._theme cfg = self._config colours = [1, 16, 256] def colourpick(triple): """ pick attribute from triple (mono,16c,256c) according to current colourmode""" if triple is None: return None return triple[colours.index(colourmode)] # global default attributes for tagstrings. # These could contain values '' and 'default' which we interpret as # "use the values from the widget below" default_normal = theme.get_attribute(colourmode, 'global', 'tag') default_focus = theme.get_attribute(colourmode, 'global', 'tag_focus') # local defaults for tagstring attributes. depend on next lower widget fallback_normal = resolve_att(onebelow_normal, default_normal) fallback_focus = resolve_att(onebelow_focus, default_focus) for sec in cfg['tags'].sections: if re.match('^' + sec + '$', tag): normal = resolve_att(colourpick(cfg['tags'][sec]['normal']), fallback_normal) focus = resolve_att(colourpick(cfg['tags'][sec]['focus']), fallback_focus) translated = cfg['tags'][sec]['translated'] if translated is None: translated = tag translation = cfg['tags'][sec]['translation'] if translation: translated = re.sub(translation[0], translation[1], tag) break else: normal = fallback_normal focus = fallback_focus translated = tag return {'normal': normal, 'focussed': focus, 'translated': translated} def get_hook(self, key): """return hook (`callable`) identified by `key`""" if self.hooks: if key in self.hooks.__dict__: return self.hooks.__dict__[key] return None def get_mapped_input_keysequences(self, mode='global', prefix=u''): candidates = self._bindings.scalars if mode != 'global': candidates = candidates + self._bindings[mode].scalars if prefix is not None: prefixs = prefix + ' ' cand = filter(lambda x: x.startswith(prefixs), candidates) if prefix in candidates: candidates = cand + [prefix] else: candidates = cand return candidates def get_keybinding(self, mode, key): """look up keybinding from `MODE-maps` sections :param mode: mode identifier :type mode: str :param key: urwid-style key identifier :type key: str :returns: a command line to be applied upon keypress :rtype: str """ cmdline = None bindings = self._bindings if key in bindings.scalars: cmdline = bindings[key] if mode in bindings.sections: if key in bindings[mode].scalars: value = bindings[mode][key] if value: cmdline = value # Workaround for ConfigObj misbehaviour. cf issue #500 # this ensures that we get at least strings only as commandlines if isinstance(cmdline, list): cmdline = ','.join(cmdline) return cmdline def get_accounts(self): """ returns known accounts :rtype: list of :class:`Account` """ return self._accounts def get_account_by_address(self, address): """ returns :class:`Account` for a given email address (str) :param address: address to look up :type address: string :rtype: :class:`Account` or None """ for myad in self.get_addresses(): if myad in address: return self._accountmap[myad] return None def get_main_addresses(self): """returns addresses of known accounts without its aliases""" return [a.address for a in self._accounts] def get_addresses(self): """returns addresses of known accounts including all their aliases""" return self._accountmap.keys() def get_addressbooks(self, order=[], append_remaining=True): """returns list of all defined :class:`AddressBook` objects""" abooks = [] for a in order: if a: if a.abook: abooks.append(a.abook) if append_remaining: for a in self._accounts: if a.abook and a.abook not in abooks: abooks.append(a.abook) return abooks def mailcap_find_match(self, *args, **kwargs): """ Propagates :func:`mailcap.find_match` but caches the mailcap (first argument) """ return mailcap.findmatch(self._mailcaps, *args, **kwargs) def represent_datetime(self, d): """ turns a given datetime obj into a unicode string representation. This will: 1) look if a fixed 'timestamp_format' is given in the config 2) check if a 'timestamp_format' hook is defined 3) use :func:`~alot.helper.pretty_datetime` as fallback """ fixed_format = self.get('timestamp_format') if fixed_format: rep = string_decode(d.strftime(fixed_format), 'UTF-8') else: format_hook = self.get_hook('timestamp_format') if format_hook: rep = string_decode(format_hook(d), 'UTF-8') else: rep = pretty_datetime(d) return rep alot-0.3.5/alot/settings/theme.py000066400000000000000000000124311217172710600167360ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os from utils import read_config from checks import align_mode from checks import attr_triple from checks import width_tuple from checks import force_list from errors import ConfigError DEFAULTSPATH = os.path.join(os.path.dirname(__file__), '..', 'defaults') DUMMYDEFAULT = ('default',) * 6 class Theme(object): """Colour theme""" def __init__(self, path): """ :param path: path to theme file :type path: str :raises: :class:`~alot.settings.errors.ConfigError` """ self._spec = os.path.join(DEFAULTSPATH, 'theme.spec') self._config = read_config(path, self._spec, checks={'align': align_mode, 'widthtuple': width_tuple, 'force_list': force_list, 'attrtriple': attr_triple}) self._colours = [1, 16, 256] # make sure every entry in 'order' lists have their own subsections threadline = self._config['search']['threadline'] for sec in self._config['search']: if sec.startswith('threadline'): tline = self._config['search'][sec] if tline['parts'] is not None: listed = set(tline['parts']) here = set(tline.sections) indefault = set(threadline.sections) diff = listed.difference(here.union(indefault)) if diff: msg = 'missing threadline parts: %s' % ', '.join(diff) raise ConfigError(msg) def get_attribute(self, colourmode, mode, name, part=None): """ returns requested attribute :param mode: ui-mode (e.g. `search`,`thread`...) :type mode: str :param name: of the atttribute :type name: str :param colourmode: colour mode; in [1, 16, 256] :type colourmode: int :rtype: urwid.AttrSpec """ thmble = self._config[mode][name] if part is not None: thmble = thmble[part] thmble = thmble or DUMMYDEFAULT return thmble[self._colours.index(colourmode)] def get_threadline_theming(self, thread, colourmode): """ look up how to display a Threadline wiidget in search mode for a given thread. :param thread: Thread to theme Threadline for :type thread: alot.db.thread.Thread :param colourmode: colourmode to use, one of 1,16,256. :type colourmode: int This will return a dict mapping :normal: to `urwid.AttrSpec`, :focus: to `urwid.AttrSpec`, :parts: to a list of strings indentifying subwidgets to be displayed in this order. Moreover, for every part listed this will map 'part' to a dict mapping :normal: to `urwid.AttrSpec`, :focus: to `urwid.AttrSpec`, :width: to a tuple indicating the width of the subpart. This is either `('fit', min, max)` to force the widget to be at least `min` and at most `max` characters wide, or `('weight', n)` which makes it share remaining space with other 'weight' parts. :alignment: where to place the content if shorter than the widget. This is either 'right', 'left' or 'center'. """ def pickcolour(triple): return triple[self._colours.index(colourmode)] def matches(sec, thread): if sec.get('tagged_with') is not None: if not set(sec['tagged_with']).issubset(thread.get_tags()): return False if sec.get('query') is not None: if not thread.matches(sec['query']): return False return True default = self._config['search']['threadline'] match = default candidates = self._config['search'].sections for candidatename in candidates: candidate = self._config['search'][candidatename] if candidatename.startswith('threadline') and\ (not candidatename == 'threadline') and\ matches(candidate, thread): match = candidate break # fill in values res = {} res['normal'] = pickcolour(match.get('normal') or default['normal']) res['focus'] = pickcolour(match.get('focus') or default['focus']) res['parts'] = match.get('parts') or default['parts'] for part in res['parts']: defaultsec = default.get(part) partsec = match.get(part) or {} def fill(key, fallback=None): pvalue = partsec.get(key) or defaultsec.get(key) return pvalue or fallback res[part] = {} res[part]['width'] = fill('width', ('fit', 0, 0)) res[part]['alignment'] = fill('alignment', 'right') res[part]['normal'] = pickcolour(fill('normal')) res[part]['focus'] = pickcolour(fill('focus')) return res alot-0.3.5/alot/settings/utils.py000066400000000000000000000052101217172710600167710ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file from configobj import ConfigObj, ConfigObjError, flatten_errors from validate import Validator from errors import ConfigError from urwid import AttrSpec def read_config(configpath=None, specpath=None, checks={}): """ get a (validated) config object for given config file path. :param configpath: path to config-file :type configpath: str :param specpath: path to spec-file :type specpath: str :param checks: custom checks to use for validator. see `validate docs `_ :type checks: dict str->callable, :raises: :class:`~alot.settings.errors.ConfigError` :rtype: `configobj.ConfigObj` """ try: config = ConfigObj(infile=configpath, configspec=specpath, file_error=True, encoding='UTF8') except ConfigObjError as e: raise ConfigError(e) except IOError: raise ConfigError('Could not read %s' % configpath) except UnboundLocalError: # this works around a bug in configobj msg = '%s is malformed. Check for sections without parents..' raise ConfigError(msg % configpath) if specpath: validator = Validator() validator.functions.update(checks) try: results = config.validate(validator, preserve_errors=True) except ConfigObjError as e: raise ConfigError(e.message) if results is not True: error_msg = '' for (section_list, key, res) in flatten_errors(config, results): if key is not None: if res is False: msg = 'key "%s" in section "%s" is missing.' msg = msg % (key, ', '.join(section_list)) else: msg = 'key "%s" in section "%s" failed validation: %s' msg = msg % (key, ', '.join(section_list), res) else: msg = 'section "%s" is missing' % '.'.join(section_list) error_msg += msg + '\n' raise ConfigError(error_msg) return config def resolve_att(a, fallback): """ replace '' and 'default' by fallback values """ if a is None: return fallback if a.background in ['default', '']: bg = fallback.background else: bg = a.background if a.foreground in ['default', '']: fg = fallback.foreground else: fg = a.foreground return AttrSpec(fg, bg) alot-0.3.5/alot/ui.py000066400000000000000000000557351217172710600144270ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import urwid import logging from twisted.internet import reactor, defer from settings import settings from buffers import BufferlistBuffer from alot.commands import CommandParseError from alot.commands.globals import CommandSequenceCommand from alot.helper import string_decode from alot.widgets.globals import CompleteEdit from alot.widgets.globals import ChoiceWidget class UI(object): """ This class integrates all components of alot and offers methods for user interaction like :meth:`prompt`, :meth:`notify` etc. It handles the urwid widget tree and mainloop (we use twisted) and is responsible for opening, closing and focussing buffers. """ def __init__(self, dbman, initialcmd): """ :param dbman: :class:`~alot.db.DBManager` :param initialcmd: commandline applied after setting up interface :type initialcmd: str :param colourmode: determines which theme to chose :type colourmode: int in [1,16,256] """ self.dbman = dbman """Database Manager (:class:`~alot.db.manager.DBManager`)""" self.buffers = [] """list of active buffers""" self.current_buffer = None """points to currently active :class:`~alot.buffers.Buffer`""" self.db_was_locked = False """flag used to prevent multiple 'index locked' notifications""" self.mode = 'global' """interface mode identifier - type of current buffer""" self.commandprompthistory = [] """history of the command line prompt""" self.input_queue = [] """stores partial keyboard input""" self.last_commandline = None """saves the last executed commandline""" # define empty notification pile self._notificationbar = None # should we show a status bar? self._show_statusbar = settings.get('show_statusbar') # pass keypresses to the root widget and never interpret bindings self._passall = False # indicates "input lock": only focus move commands are interpreted self._locked = False self._unlock_callback = None # will be called after input lock ended self._unlock_key = None # key that ends input lock # alarm handle for callback that clears input queue (to cancel alarm) self._alarm = None # create root widget global_att = settings.get_theming_attribute('global', 'body') mainframe = urwid.Frame(urwid.SolidFill()) self.root_widget = urwid.AttrMap(mainframe, global_att) # set up main loop self.mainloop = urwid.MainLoop(self.root_widget, handle_mouse=False, event_loop=urwid.TwistedEventLoop(), unhandled_input=self._unhandeled_input, input_filter=self._input_filter) # set up colours colourmode = int(settings.get('colourmode')) logging.info('setup gui in %d colours' % colourmode) self.mainloop.screen.set_terminal_properties(colors=colourmode) logging.debug('fire first command') self.apply_command(initialcmd) # start urwids mainloop self.mainloop.run() def _input_filter(self, keys, raw): """ handles keypresses. This function gets triggered directly by class:`urwid.MainLoop` upon user input and is supposed to pass on its `keys` parameter to let the root widget handle keys. We intercept the input here to trigger custom commands as defined in our keybindings. """ logging.debug("Got key (%s, %s)" % (keys, raw)) # work around: escape triggers this twice, with keys = raw = [] # the first time.. if not keys: return # let widgets handle input if key is virtual window resize keypress # or we are in "passall" mode elif 'window resize' in keys or self._passall: return keys # end "lockdown" mode if the right key was pressed elif self._locked and keys[0] == self._unlock_key: self._locked = False self.mainloop.widget = self.root_widget if callable(self._unlock_callback): self._unlock_callback() # otherwise interpret keybinding else: # define callback that resets input queue def clear(*args): if self._alarm is not None: self.mainloop.remove_alarm(self._alarm) self.input_queue = [] def fire(ignored, cmdline): clear() logging.debug("cmdline: '%s'" % cmdline) if not self._locked: try: self.apply_commandline(cmdline) except CommandParseError, e: self.notify(e.message, priority='error') # move keys are always passed elif cmdline in ['move up', 'move down', 'move page up', 'move page down']: return [cmdline[5:]] key = keys[0] self.input_queue.append(key) keyseq = ' '.join(self.input_queue) candidates = settings.get_mapped_input_keysequences(self.mode, prefix=keyseq) if keyseq in candidates: # case: current input queue is a mapped keysequence # get binding and interpret it if non-null cmdline = settings.get_keybinding(self.mode, keyseq) if cmdline: if len(candidates) > 1: timeout = float(settings.get('input_timeout')) if self._alarm is not None: self.mainloop.remove_alarm(self._alarm) self._alarm = self.mainloop.set_alarm_in( timeout, fire, cmdline) else: return fire(self.mainloop, cmdline) elif not candidates: # case: no sequence with prefix keyseq is mapped # just clear the input queue clear() else: # case: some sequences with proper prefix keyseq is mapped timeout = float(settings.get('input_timeout')) if self._alarm is not None: self.mainloop.remove_alarm(self._alarm) self._alarm = self.mainloop.set_alarm_in(timeout, clear) # update statusbar self.update() def apply_commandline(self, cmdline): """ Dispatches the interpretation of the command line string to :class:`CommandSequenceCommand `. :param cmdline: command line to interpret :type cmdline: str """ cmd = CommandSequenceCommand(cmdline) self.apply_command(cmd) def _unhandeled_input(self, key): """ Called by :class:`urwid.MainLoop` if a keypress was passed to the root widget by `self._input_filter` but is not handled in any widget. We keep it for debuging purposes. """ logging.debug('unhandled input: %s' % key) def show_as_root_until_keypress(self, w, key, afterwards=None): """ Replaces root widget by given :class:`urwid.Widget` and makes the UI ignore all further commands apart from cursor movement. If later on `key` is pressed, the old root widget is reset, callable `afterwards` is called and normal behaviour is resumed. """ self.mainloop.widget = w self._unlock_key = key self._unlock_callback = afterwards self._locked = True def prompt(self, prefix, text=u'', completer=None, tab=0, history=[]): """ prompt for text input. This returns a :class:`~twisted.defer.Deferred` that calls back with the input string. :param prefix: text to print before the input field :type prefix: str :param text: initial content of the input field :type text: str :param completer: completion object to use :type completer: :meth:`alot.completion.Completer` :param tab: number of tabs to press initially (to select completion results) :type tab: int :param history: history to be used for up/down keys :type history: list of str :rtype: :class:`twisted.defer.Deferred` """ d = defer.Deferred() # create return deferred oldroot = self.mainloop.widget def select_or_cancel(text): # restore main screen and invoke callback # (delayed return) with given text self.mainloop.widget = oldroot self._passall = False d.callback(text) def cerror(e): logging.error(e) self.notify('completion error: %s' % e.message, priority='error') self.update() prefix = prefix + settings.get('prompt_suffix') # set up widgets leftpart = urwid.Text(prefix, align='left') editpart = CompleteEdit(completer, on_exit=select_or_cancel, edit_text=text, history=history, on_error=cerror) for i in range(tab): # hit some tabs editpart.keypress((0,), 'tab') # build promptwidget both = urwid.Columns( [ ('fixed', len(prefix), leftpart), ('weight', 1, editpart), ]) att = settings.get_theming_attribute('global', 'prompt') both = urwid.AttrMap(both, att) # put promptwidget as overlay on main widget overlay = urwid.Overlay(both, oldroot, ('fixed left', 0), ('fixed right', 0), ('fixed bottom', 1), None) self.mainloop.widget = overlay self._passall = True return d # return deferred def exit(self): """ shuts down user interface without cleaning up. Use a :class:`alot.commands.globals.ExitCommand` for a clean shutdown. """ exit_msg = None try: reactor.stop() except Exception as e: exit_msg = 'Could not stop reactor: {}.'.format(e) logging.error(exit_msg + '\nShutting down anyway..') def buffer_open(self, buf): """register and focus new :class:`~alot.buffers.Buffer`.""" # call pre_buffer_open hook prehook = settings.get_hook('pre_buffer_open') if prehook is not None: prehook(ui=self, dbm=self.dbman, buf=buf) if self.current_buffer is not None: offset = settings.get('bufferclose_focus_offset') * -1 currentindex = self.buffers.index(self.current_buffer) self.buffers.insert(currentindex + offset, buf) else: self.buffers.append(buf) self.buffer_focus(buf) # call post_buffer_open hook posthook = settings.get_hook('post_buffer_open') if posthook is not None: posthook(ui=self, dbm=self.dbman, buf=buf) def buffer_close(self, buf, redraw=True): """ closes given :class:`~alot.buffers.Buffer`. This it removes it from the bufferlist and calls its cleanup() method. """ # call pre_buffer_close hook prehook = settings.get_hook('pre_buffer_close') if prehook is not None: prehook(ui=self, dbm=self.dbman, buf=buf) buffers = self.buffers success = False if buf not in buffers: string = 'tried to close unknown buffer: %s. \n\ni have:%s' logging.error(string % (buf, self.buffers)) elif self.current_buffer == buf: logging.info('closing current buffer %s' % buf) index = buffers.index(buf) buffers.remove(buf) offset = settings.get('bufferclose_focus_offset') nextbuffer = buffers[(index + offset) % len(buffers)] self.buffer_focus(nextbuffer, redraw) buf.cleanup() success = True else: string = 'closing buffer %d:%s' buffers.remove(buf) buf.cleanup() success = True # call post_buffer_closed hook posthook = settings.get_hook('post_buffer_closed') if posthook is not None: posthook(ui=self, dbm=self.dbman, buf=buf, success=success) def buffer_focus(self, buf, redraw=True): """focus given :class:`~alot.buffers.Buffer`.""" # call pre_buffer_focus hook prehook = settings.get_hook('pre_buffer_focus') if prehook is not None: prehook(ui=self, dbm=self.dbman, buf=buf) success = False if buf not in self.buffers: logging.error('tried to focus unknown buffer') else: if self.current_buffer != buf: self.current_buffer = buf self.mode = buf.modename if isinstance(self.current_buffer, BufferlistBuffer): self.current_buffer.rebuild() self.update() success = True # call post_buffer_focus hook posthook = settings.get_hook('post_buffer_focus') if posthook is not None: posthook(ui=self, dbm=self.dbman, buf=buf, success=success) def get_deep_focus(self, startfrom=None): """return the bottom most focussed widget of the widget tree""" if not startfrom: startfrom = self.current_buffer if 'get_focus' in dir(startfrom): focus = startfrom.get_focus() if isinstance(focus, tuple): focus = focus[0] if isinstance(focus, urwid.Widget): return self.get_deep_focus(startfrom=focus) return startfrom def get_buffers_of_type(self, t): """ returns currently open buffers for a given subclass of :class:`~alot.buffers.Buffer`. :param t: Buffer class :type t: alot.buffers.Buffer :rtype: list """ return filter(lambda x: isinstance(x, t), self.buffers) def clear_notify(self, messages): """ Clears notification popups. Call this to ged rid of messages that don't time out. :param messages: The popups to remove. This should be exactly what :meth:`notify` returned when creating the popup """ newpile = self._notificationbar.widget_list for l in messages: if l in newpile: newpile.remove(l) if newpile: self._notificationbar = urwid.Pile(newpile) else: self._notificationbar = None self.update() def choice(self, message, choices={'y': 'yes', 'n': 'no'}, select=None, cancel=None, msg_position='above'): """ prompt user to make a choice. :param message: string to display before list of choices :type message: unicode :param choices: dict of possible choices :type choices: dict: keymap->choice (both str) :param select: choice to return if enter/return is hit. Ignored if set to `None`. :type select: str :param cancel: choice to return if escape is hit. Ignored if set to `None`. :type cancel: str :param msg_position: determines if `message` is above or left of the prompt. Must be `above` or `left`. :type msg_position: str :rtype: :class:`twisted.defer.Deferred` """ assert select in choices.values() + [None] assert cancel in choices.values() + [None] assert msg_position in ['left', 'above'] d = defer.Deferred() # create return deferred oldroot = self.mainloop.widget def select_or_cancel(text): self.mainloop.widget = oldroot self._passall = False d.callback(text) # set up widgets msgpart = urwid.Text(message) choicespart = ChoiceWidget(choices, callback=select_or_cancel, select=select, cancel=cancel) # build widget if msg_position == 'left': both = urwid.Columns( [ ('fixed', len(message), msgpart), ('weight', 1, choicespart), ], dividechars=1) else: # above both = urwid.Pile([msgpart, choicespart]) att = settings.get_theming_attribute('global', 'prompt') both = urwid.AttrMap(both, att, att) # put promptwidget as overlay on main widget overlay = urwid.Overlay(both, oldroot, ('fixed left', 0), ('fixed right', 0), ('fixed bottom', 1), None) self.mainloop.widget = overlay self._passall = True return d # return deferred def notify(self, message, priority='normal', timeout=0, block=False): """ opens notification popup. :param message: message to print :type message: str :param priority: priority string, used to format the popup: currently, 'normal' and 'error' are defined. If you use 'X' here, the attribute 'global_notify_X' is used to format the popup. :type priority: str :param timeout: seconds until message disappears. Defaults to the value of 'notify_timeout' in the general config section. A negative value means never time out. :type timeout: int :param block: this notification blocks until a keypress is made :type block: bool :returns: an urwid widget (this notification) that can be handed to :meth:`clear_notify` for removal """ def build_line(msg, prio): cols = urwid.Columns([urwid.Text(msg)]) att = settings.get_theming_attribute('global', 'notify_' + prio) return urwid.AttrMap(cols, att) msgs = [build_line(message, priority)] if not self._notificationbar: self._notificationbar = urwid.Pile(msgs) else: newpile = self._notificationbar.widget_list + msgs self._notificationbar = urwid.Pile(newpile) self.update() def clear(*args): self.clear_notify(msgs) if block: # put "cancel to continue" widget as overlay on main widget txt = build_line('(escape continues)', priority) overlay = urwid.Overlay(txt, self.root_widget, ('fixed left', 0), ('fixed right', 0), ('fixed bottom', 0), None) self.show_as_root_until_keypress(overlay, 'esc', afterwards=clear) else: if timeout >= 0: if timeout == 0: timeout = settings.get('notify_timeout') self.mainloop.set_alarm_in(timeout, clear) return msgs[0] def update(self, redraw=True): """redraw interface""" # get the main urwid.Frame widget mainframe = self.root_widget.original_widget # body if self.current_buffer: mainframe.set_body(self.current_buffer) # footer lines = [] if self._notificationbar: # .get_text()[0] != ' ': lines.append(self._notificationbar) if self._show_statusbar: lines.append(self.build_statusbar()) if lines: mainframe.set_footer(urwid.Pile(lines)) else: mainframe.set_footer(None) # force a screen redraw if self.mainloop.screen.started and redraw: self.mainloop.draw_screen() def build_statusbar(self): """construct and return statusbar widget""" info = {} cb = self.current_buffer btype = None if cb is not None: info = cb.get_info() btype = cb.modename info['buffer_no'] = self.buffers.index(cb) info['buffer_type'] = btype info['total_messages'] = self.dbman.count_messages('*') info['pending_writes'] = len(self.dbman.writequeue) info['input_queue'] = ' '.join(self.input_queue) lefttxt = righttxt = u'' if cb is not None: lefttxt, righttxt = settings.get(btype + '_statusbar', (u'', u'')) lefttxt = string_decode(lefttxt, 'UTF-8') lefttxt = lefttxt.format(**info) righttxt = string_decode(righttxt, 'UTF-8') righttxt = righttxt.format(**info) footerleft = urwid.Text(lefttxt, align='left') pending_writes = len(self.dbman.writequeue) if pending_writes > 0: righttxt = ('|' * pending_writes) + ' ' + righttxt footerright = urwid.Text(righttxt, align='right') columns = urwid.Columns([ footerleft, ('fixed', len(righttxt), footerright)]) footer_att = settings.get_theming_attribute('global', 'footer') return urwid.AttrMap(columns, footer_att) def apply_command(self, cmd): """ applies a command This calls the pre and post hooks attached to the command, as well as :meth:`cmd.apply`. :param cmd: an applicable command :type cmd: :class:`~alot.commands.Command` """ if cmd: # define (callback) function that invokes post-hook def call_posthook(retval_from_apply): if cmd.posthook: logging.info('calling post-hook') return defer.maybeDeferred(cmd.posthook, ui=self, dbm=self.dbman) # define error handler for Failures/Exceptions # raised in cmd.apply() def errorHandler(failure): logging.error(failure.getTraceback()) errmsg = failure.getErrorMessage() if errmsg: msg = "%s\n(check the log for details)" self.notify( msg % failure.getErrorMessage(), priority='error') # call cmd.apply def call_apply(ignored): return defer.maybeDeferred(cmd.apply, self) prehook = cmd.prehook or (lambda **kwargs: None) d = defer.maybeDeferred(prehook, ui=self, dbm=self.dbman) d.addCallback(call_apply) d.addCallback(call_posthook) d.addErrback(errorHandler) return d alot-0.3.5/alot/utils/000077500000000000000000000000001217172710600145615ustar00rootroot00000000000000alot-0.3.5/alot/utils/__init__.py000066400000000000000000000000001217172710600166600ustar00rootroot00000000000000alot-0.3.5/alot/utils/booleanaction.py000066400000000000000000000015611217172710600177530ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import argparse TRUEISH = ['true', 'yes', 'on', '1', 't', 'y'] FALSISH = ['false', 'no', 'off', '0', 'f', 'n'] def boolean(string): string = string.lower() if string in FALSISH: return False elif string in TRUEISH: return True else: raise ValueError() class BooleanAction(argparse.Action): """ argparse action that can be used to store boolean values """ def __init__(self, *args, **kwargs): kwargs['type'] = boolean kwargs['metavar'] = 'BOOL' argparse.Action.__init__(self, *args, **kwargs) def __call__(self, parser, namespace, values, option_string=None): setattr(namespace, self.dest, values) alot-0.3.5/alot/walker.py000066400000000000000000000047201217172710600152630ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import urwid import logging class PipeWalker(urwid.ListWalker): """urwid.ListWalker that reads next items from a pipe and wraps them in `containerclass` widgets for displaying """ def __init__(self, pipe, containerclass, **kwargs): self.pipe = pipe self.kwargs = kwargs self.containerclass = containerclass self.lines = [] self.focus = 0 self.empty = False def __contains__(self, name): return self.lines.__contains__(name) def get_focus(self): return self._get_at_pos(self.focus) def set_focus(self, focus): self.focus = focus self._modified() def get_next(self, start_from): return self._get_at_pos(start_from + 1) def get_prev(self, start_from): return self._get_at_pos(start_from - 1) def remove(self, obj): next_focus = self.focus % len(self.lines) if self.focus == len(self.lines) - 1 and self.empty: next_focus = self.focus - 1 self.lines.remove(obj) if self.lines: self.set_focus(next_focus) self._modified() def _get_at_pos(self, pos): if pos < 0: # pos too low return (None, None) elif pos > len(self.lines): # pos too high return (None, None) elif len(self.lines) > pos: # pos already cached return (self.lines[pos], pos) else: # pos not cached yet, look at next item from iterator if self.empty: # iterator is empty return (None, None) else: widget = self._get_next_item() if widget: return (widget, pos) else: return (None, None) def _get_next_item(self): if self.empty: return None try: # the next line blocks until it can read from the pipe or # EOFError is raised. No races here. next_obj = self.pipe.recv() next_widget = self.containerclass(next_obj, **self.kwargs) self.lines.append(next_widget) except EOFError: logging.debug('EMPTY PIPE') next_widget = None self.empty = True return next_widget def get_lines(self): return self.lines alot-0.3.5/alot/widgets/000077500000000000000000000000001217172710600150675ustar00rootroot00000000000000alot-0.3.5/alot/widgets/__init__.py000066400000000000000000000000001217172710600171660ustar00rootroot00000000000000alot-0.3.5/alot/widgets/bufferlist.py000066400000000000000000000013271217172710600176110ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file """ Widgets specific to Bufferlist mode """ import urwid class BufferlineWidget(urwid.Text): """ selectable text widget that represents a :class:`~alot.buffers.Buffer` in the :class:`~alot.buffers.BufferlistBuffer`. """ def __init__(self, buffer): self.buffer = buffer line = buffer.__str__() urwid.Text.__init__(self, line, wrap='clip') def selectable(self): return True def keypress(self, size, key): return key def get_buffer(self): return self.buffer alot-0.3.5/alot/widgets/globals.py000066400000000000000000000223021217172710600170630ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file """ This contains alot-specific :class:`urwid.Widget` used in more than one mode. """ import urwid from alot.helper import string_decode from alot.settings import settings from alot.db.attachment import Attachment from alot.errors import CompletionError class AttachmentWidget(urwid.WidgetWrap): """ one-line summary of an :class:`~alot.db.attachment.Attachment`. """ def __init__(self, attachment, selectable=True): self._selectable = selectable self.attachment = attachment if not isinstance(attachment, Attachment): self.attachment = Attachment(self.attachment) att = settings.get_theming_attribute('thread', 'attachment') focus_att = settings.get_theming_attribute('thread', 'attachment_focus') widget = urwid.AttrMap(urwid.Text(self.attachment.__str__()), att, focus_att) urwid.WidgetWrap.__init__(self, widget) def get_attachment(self): return self.attachment def selectable(self): return self._selectable def keypress(self, size, key): return key class ChoiceWidget(urwid.Text): def __init__(self, choices, callback, cancel=None, select=None, separator=' '): self.choices = choices self.callback = callback self.cancel = cancel self.select = select self.separator = separator items = [] for k, v in choices.items(): if v == select and select is not None: items += ['[', k, ']:', v] else: items += ['(', k, '):', v] items += [self.separator] urwid.Text.__init__(self, items) def selectable(self): return True def keypress(self, size, key): if key == 'enter' and self.select is not None: self.callback(self.select) elif key == 'esc' and self.cancel is not None: self.callback(self.cancel) elif key in self.choices: self.callback(self.choices[key]) else: return key class CompleteEdit(urwid.Edit): """ This is a vamped-up :class:`urwid.Edit` widget that allows for tab-completion using :class:`~alot.completion.Completer` objects These widgets are meant to be used as user input prompts and hence react to 'return' key presses by calling a 'on_exit' callback that processes the current text value. The interpretation of some keypresses is hard-wired: :enter: calls 'on_exit' callback with current value :esc: calls 'on_exit' with value `None`, which can be interpreted as cancelation :tab: calls the completer and tabs forward in the result list :shift tab: tabs backward in the result list :up/down: move in the local input history :ctrl a/e: moves curser to the beginning/end of the input """ def __init__(self, completer, on_exit, on_error=None, edit_text=u'', history=None, **kwargs): """ :param completer: completer to use :type completer: alot.completion.Completer :param on_exit: "enter"-callback that interprets the input (str) :type on_exit: callable :param on_error: callback that handles :class:`completion errors ` :type on_error: callback :param edit_text: initial text :type edit_text: str :param history: initial command history :type history: list or str """ self.completer = completer self.on_exit = on_exit self.on_error = on_error self.history = list(history) # we temporarily add stuff here self.historypos = None if not isinstance(edit_text, unicode): edit_text = string_decode(edit_text) self.start_completion_pos = len(edit_text) self.completions = None urwid.Edit.__init__(self, edit_text=edit_text, **kwargs) def keypress(self, size, key): # if we tabcomplete if key in ['tab', 'shift tab'] and self.completer: # if not already in completion mode if self.completions is None: self.completions = [(self.edit_text, self.edit_pos)] try: self.completions += self.completer.complete(self.edit_text, self.edit_pos) self.focus_in_clist = 1 except CompletionError, e: if self.on_error is not None: self.on_error(e) else: # otherwise tab through results if key == 'tab': self.focus_in_clist += 1 else: self.focus_in_clist -= 1 if len(self.completions) > 1: ctext, cpos = self.completions[self.focus_in_clist % len(self.completions)] self.set_edit_text(ctext) self.set_edit_pos(cpos) else: self.completions = None elif key in ['up', 'down']: if self.history: if self.historypos is None: self.history.append(self.edit_text) self.historypos = len(self.history) - 1 if key == 'cursor up': self.historypos = (self.historypos + 1) % len(self.history) else: self.historypos = (self.historypos - 1) % len(self.history) self.set_edit_text(self.history[self.historypos]) elif key == 'enter': self.on_exit(self.edit_text) elif key == 'esc': self.on_exit(None) elif key == 'ctrl a': self.set_edit_pos(0) elif key == 'ctrl e': self.set_edit_pos(len(self.edit_text)) else: result = urwid.Edit.keypress(self, size, key) self.completions = None return result class HeadersList(urwid.WidgetWrap): """ renders a pile of header values as key/value list """ def __init__(self, headerslist, key_attr, value_attr, gaps_attr=None): """ :param headerslist: list of key/value pairs to display :type headerslist: list of (str, str) :param key_attr: theming attribute to use for keys :type key_attr: urwid.AttrSpec :param value_attr: theming attribute to use for values :type value_attr: urwid.AttrSpec :param gaps_attr: theming attribute to wrap lines in :type gaps_attr: urwid.AttrSpec """ self.headers = headerslist self.key_attr = key_attr self.value_attr = value_attr pile = urwid.Pile(self._build_lines(headerslist)) if gaps_attr is None: gaps_attr = key_attr pile = urwid.AttrMap(pile, gaps_attr) urwid.WidgetWrap.__init__(self, pile) def __str__(self): return str(self.headers) def _build_lines(self, lines): max_key_len = 1 headerlines = [] #calc max length of key-string for key, value in lines: if len(key) > max_key_len: max_key_len = len(key) for key, value in lines: ##todo : even/odd keyw = ('fixed', max_key_len + 1, urwid.Text((self.key_attr, key))) valuew = urwid.Text((self.value_attr, value)) line = urwid.Columns([keyw, valuew]) headerlines.append(line) return headerlines class TagWidget(urwid.AttrMap): """ text widget that renders a tagstring. It looks up the string it displays in the `tags` section of the config as well as custom theme settings for its tag. """ def __init__(self, tag, fallback_normal=None, fallback_focus=None): self.tag = tag representation = settings.get_tagstring_representation(tag, fallback_normal, fallback_focus) self.translated = representation['translated'] self.hidden = self.translated == '' self.txt = urwid.Text(self.translated, wrap='clip') normal_att = representation['normal'] focus_att = representation['focussed'] self.attmaps = {'normal': normal_att, 'focus': focus_att} urwid.AttrMap.__init__(self, self.txt, normal_att, focus_att) def set_map(self, attrstring): self.set_attr_map({None: self.attmaps[attrstring]}) def width(self): # evil voodoo hotfix for double width chars that may # lead e.g. to strings with length 1 that need width 2 return self.txt.pack()[0] def selectable(self): return True def keypress(self, size, key): return key def get_tag(self): return self.tag def set_focussed(self): self.set_attr_map(self.attmap['focus']) def set_unfocussed(self): self.set_attr_map(self.attmap['normal']) alot-0.3.5/alot/widgets/search.py000066400000000000000000000156561217172710600167230ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file """ Widgets specific to search mode """ import urwid from alot.settings import settings from alot.helper import shorten_author_string from alot.helper import tag_cmp from alot.widgets.utils import AttrFlipWidget from alot.widgets.globals import TagWidget class ThreadlineWidget(urwid.AttrMap): """ selectable line widget that represents a :class:`~alot.db.Thread` in the :class:`~alot.buffers.SearchBuffer`. """ def __init__(self, tid, dbman): self.dbman = dbman self.tid = tid self.thread = None # will be set by refresh() self.tag_widgets = [] self.structure = None self.rebuild() normal = self.structure['normal'] focussed = self.structure['focus'] urwid.AttrMap.__init__(self, self.columns, normal, focussed) def _build_part(self, name, struct, minw, maxw, align): def pad(string, shorten=None): if maxw: if len(string) > maxw: if shorten: string = shorten(string, maxw) else: string = string[:maxw] if minw: if len(string) < minw: if align == 'left': string = string.ljust(minw) elif align == 'center': string = string.center(minw) else: string = string.rjust(minw) return string part = None width = None if name == 'date': newest = None datestring = '' if self.thread: newest = self.thread.get_newest_date() datestring = settings.represent_datetime(newest) datestring = pad(datestring) width = len(datestring) part = AttrFlipWidget(urwid.Text(datestring), struct['date']) elif name == 'mailcount': if self.thread: mailcountstring = "(%d)" % self.thread.get_total_messages() else: mailcountstring = "(?)" mailcountstring = pad(mailcountstring) width = len(mailcountstring) mailcount_w = AttrFlipWidget(urwid.Text(mailcountstring), struct['mailcount']) part = mailcount_w elif name == 'authors': if self.thread: authors = self.thread.get_authors_string() or '(None)' else: authors = '(None)' authorsstring = pad(authors, shorten_author_string) authors_w = AttrFlipWidget(urwid.Text(authorsstring), struct['authors']) width = len(authorsstring) part = authors_w elif name == 'subject': if self.thread: subjectstring = self.thread.get_subject() or ' ' else: subjectstring = ' ' # sanitize subject string: subjectstring = subjectstring.replace('\n', ' ') subjectstring = subjectstring.replace('\r', '') subjectstring = pad(subjectstring) subject_w = AttrFlipWidget(urwid.Text(subjectstring, wrap='clip'), struct['subject']) if subjectstring: width = len(subjectstring) part = subject_w elif name == 'content': if self.thread: msgs = self.thread.get_messages().keys() else: msgs = [] # sort the most recent messages first msgs.sort(key=lambda msg: msg.get_date(), reverse=True) lastcontent = ' '.join([m.get_text_content() for m in msgs]) contentstring = pad(lastcontent.replace('\n', ' ').strip()) content_w = AttrFlipWidget(urwid.Text(contentstring, wrap='clip'), struct['content']) width = len(contentstring) part = content_w elif name == 'tags': if self.thread: fallback_normal = struct[name]['normal'] fallback_focus = struct[name]['focus'] tag_widgets = [TagWidget(t, fallback_normal, fallback_focus) for t in self.thread.get_tags()] tag_widgets.sort(tag_cmp, lambda tag_widget: tag_widget.translated) else: tag_widgets = [] cols = [] length = -1 for tag_widget in tag_widgets: if not tag_widget.hidden: wrapped_tagwidget = tag_widget tag_width = tag_widget.width() cols.append(('fixed', tag_width, wrapped_tagwidget)) length += tag_width + 1 if cols: part = urwid.Columns(cols, dividechars=1) width = length return width, part def rebuild(self): self.thread = self.dbman.get_thread(self.tid) self.widgets = [] columns = [] self.structure = settings.get_threadline_theming(self.thread) for partname in self.structure['parts']: minw = maxw = None width_tuple = self.structure[partname]['width'] if width_tuple is not None: if width_tuple[0] == 'fit': minw, maxw = width_tuple[1:] align_mode = self.structure[partname]['alignment'] width, part = self._build_part(partname, self.structure, minw, maxw, align_mode) if part is not None: if isinstance(part, urwid.Columns): for w in part.widget_list: self.widgets.append(w) else: self.widgets.append(part) # compute width and align if width_tuple[0] == 'weight': columnentry = width_tuple + (part,) else: columnentry = ('fixed', width, part) columns.append(columnentry) self.columns = urwid.Columns(columns, dividechars=1) self.original_widget = self.columns def render(self, size, focus=False): for w in self.widgets: w.set_map('focus' if focus else 'normal') return urwid.AttrMap.render(self, size, focus) def selectable(self): return True def keypress(self, size, key): return key def get_thread(self): return self.thread def _get_theme(self, component, focus=False): path = ['search', 'threadline', component] if focus: path.append('focus') else: path.append('normal') return settings.get_theming_attribute(path) alot-0.3.5/alot/widgets/thread.py000066400000000000000000000333711217172710600167170ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file """ Widgets specific to thread mode """ import urwid import logging from alot.settings import settings from alot.db.utils import decode_header, X_SIGNATURE_MESSAGE_HEADER from alot.helper import tag_cmp from alot.widgets.globals import TagWidget from alot.widgets.globals import AttachmentWidget from alot.foreign.urwidtrees import Tree, SimpleTree, CollapsibleTree from alot.db.utils import extract_body class MessageSummaryWidget(urwid.WidgetWrap): """ one line summary of a :class:`~alot.db.message.Message`. """ def __init__(self, message, even=True): """ :param message: a message :type message: alot.db.Message :param even: even entry in a pile of messages? Used for theming. :type even: bool """ self.message = message self.even = even if even: attr = settings.get_theming_attribute('thread', 'summary', 'even') else: attr = settings.get_theming_attribute('thread', 'summary', 'odd') focus_att = settings.get_theming_attribute('thread', 'summary', 'focus') cols = [] sumstr = self.__str__() txt = urwid.Text(sumstr) cols.append(txt) thread_tags = message.get_thread().get_tags(intersection=True) outstanding_tags = set(message.get_tags()).difference(thread_tags) tag_widgets = [TagWidget(t, attr, focus_att) for t in outstanding_tags] tag_widgets.sort(tag_cmp, lambda tag_widget: tag_widget.translated) for tag_widget in tag_widgets: if not tag_widget.hidden: cols.append(('fixed', tag_widget.width(), tag_widget)) line = urwid.AttrMap(urwid.Columns(cols, dividechars=1), attr, focus_att) urwid.WidgetWrap.__init__(self, line) def __str__(self): author, address = self.message.get_author() date = self.message.get_datestring() rep = author if author != '' else address if date is not None: rep += " (%s)" % date return rep def selectable(self): return True def keypress(self, size, key): return key class MessageBodyWidget(urwid.AttrMap): """ displays printable parts of an email """ def __init__(self, message): self._message = message bodytxt = extract_body(message.get_email()) att = settings.get_theming_attribute('thread', 'body') urwid.AttrMap.__init__(self, urwid.Text(bodytxt), att) class FocusableText(urwid.WidgetWrap): """Selectable Text used for nodes in our example""" def __init__(self, txt, att, att_focus): t = urwid.Text(txt) w = urwid.AttrMap(t, att, att_focus) urwid.WidgetWrap.__init__(self, w) def selectable(self): return True def keypress(self, size, key): return key class TextlinesList(SimpleTree): def __init__(self, content, attr=None, attr_focus=None): """ :class:`SimpleTree` that contains a list of all-level-0 Text widgets for each line in content. """ structure = [] for line in content.splitlines(): structure.append((FocusableText(line, attr, attr_focus), None)) SimpleTree.__init__(self, structure) class DictList(SimpleTree): """ :class:`SimpleTree` that displays key-value pairs. The structure will obey the Tree API but will not actually be a tree but a flat list: It contains one top-level node (displaying the k/v pair in Columns) per pair. That is, the root will be the first pair, its sibblings will be the other pairs and first|last_child will always be None. """ def __init__(self, content, key_attr, value_attr, gaps_attr=None): """ :param headerslist: list of key/value pairs to display :type headerslist: list of (str, str) :param key_attr: theming attribute to use for keys :type key_attr: urwid.AttrSpec :param value_attr: theming attribute to use for values :type value_attr: urwid.AttrSpec :param gaps_attr: theming attribute to wrap lines in :type gaps_attr: urwid.AttrSpec """ max_key_len = 1 structure = [] # calc max length of key-string for key, value in content: if len(key) > max_key_len: max_key_len = len(key) for key, value in content: # todo : even/odd keyw = ('fixed', max_key_len + 1, urwid.Text((key_attr, key))) valuew = urwid.Text((value_attr, value)) line = urwid.Columns([keyw, valuew]) if gaps_attr is not None: line = urwid.AttrMap(line, gaps_attr) structure.append((line, None)) SimpleTree.__init__(self, structure) class MessageTree(CollapsibleTree): """ :class:`Tree` that displays contents of a single :class:`alot.db.Message`. Its root node is a :class:`MessageSummaryWidget`, and its child nodes reflect the messages content (parts for headers/attachments etc). Collapsing this message corresponds to showing the summary only. """ def __init__(self, message, odd=True): """ :param message: Messag to display :type message: alot.db.Message :param odd: theme summary widget as if this is an odd line (in the message-pile) :type odd: bool """ self._message = message self._odd = odd self.display_source = False self._summaryw = None self._bodytree = None self._sourcetree = None self.display_all_headers = False self._all_headers_tree = None self._default_headers_tree = None self.display_attachments = True self._attachments = None self._maintree = SimpleTree(self._assemble_structure()) CollapsibleTree.__init__(self, self._maintree) def get_message(self): return self._message def reassemble(self): self._maintree._treelist = self._assemble_structure() def refresh(self): self._summaryw = None self.reassemble() def debug(self): logging.debug('collapsed %s' % self.is_collapsed(self.root)) logging.debug('display_source %s' % self.display_source) logging.debug('display_all_headers %s' % self.display_all_headers) logging.debug('display_attachements %s' % self.display_attachments) logging.debug('AHT %s' % str(self._all_headers_tree)) logging.debug('DHT %s' % str(self._default_headers_tree)) logging.debug('MAINTREE %s' % str(self._maintree._treelist)) def _assemble_structure(self): mainstruct = [] if self.display_source: mainstruct.append((self._get_source(), None)) else: mainstruct.append((self._get_headers(), None)) attachmenttree = self._get_attachments() if attachmenttree is not None: mainstruct.append((attachmenttree, None)) bodytree = self._get_body() if bodytree is not None: mainstruct.append((self._get_body(), None)) structure = [ (self._get_summary(), mainstruct) ] return structure def collapse_if_matches(self, querystring): """ collapse (and show summary only) if the :class:`alot.db.Message` matches given `querystring` """ self.set_position_collapsed( self.root, self._message.matches(querystring)) def _get_summary(self): if self._summaryw is None: self._summaryw = MessageSummaryWidget( self._message, even=(not self._odd)) return self._summaryw def _get_source(self): if self._sourcetree is None: sourcetxt = self._message.get_email().as_string() att = settings.get_theming_attribute('thread', 'body') att_focus = settings.get_theming_attribute('thread', 'body_focus') self._sourcetree = TextlinesList(sourcetxt, att, att_focus) return self._sourcetree def _get_body(self): if self._bodytree is None: bodytxt = extract_body(self._message.get_email()) if bodytxt: att = settings.get_theming_attribute('thread', 'body') att_focus = settings.get_theming_attribute( 'thread', 'body_focus') self._bodytree = TextlinesList(bodytxt, att, att_focus) return self._bodytree def _get_headers(self): if self.display_all_headers is True: if self._all_headers_tree is None: self._all_headers_tree = self.construct_header_pile() ret = self._all_headers_tree else: if self._default_headers_tree is None: headers = settings.get('displayed_headers') self._default_headers_tree = self.construct_header_pile( headers) ret = self._default_headers_tree return ret def _get_attachments(self): if self._attachments is None: alist = [] for a in self._message.get_attachments(): alist.append((AttachmentWidget(a), None)) if alist: self._attachments = SimpleTree(alist) return self._attachments def construct_header_pile(self, headers=None, normalize=True): mail = self._message.get_email() lines = [] if headers is None: # collect all header/value pairs in the order they appear headers = mail.keys() for key, value in mail.items(): dvalue = decode_header(value, normalize=normalize) lines.append((key, dvalue)) else: # only a selection of headers should be displayed. # use order of the `headers` parameter for key in headers: if key in mail: if key.lower() in ['cc', 'bcc', 'to']: values = mail.get_all(key) values = [decode_header( v, normalize=normalize) for v in values] lines.append((key, ', '.join(values))) else: for value in mail.get_all(key): dvalue = decode_header(value, normalize=normalize) lines.append((key, dvalue)) elif key.lower() == 'tags': logging.debug('want tags header') values = [] for t in self._message.get_tags(): tagrep = settings.get_tagstring_representation(t) if t is not tagrep['translated']: t = '%s (%s)' % (tagrep['translated'], t) values.append(t) lines.append((key, ', '.join(values))) # OpenPGP pseudo headers if mail[X_SIGNATURE_MESSAGE_HEADER]: lines.append(('PGP-Signature', mail[X_SIGNATURE_MESSAGE_HEADER])) key_att = settings.get_theming_attribute('thread', 'header_key') value_att = settings.get_theming_attribute('thread', 'header_value') gaps_att = settings.get_theming_attribute('thread', 'header') return DictList(lines, key_att, value_att, gaps_att) class ThreadTree(Tree): """ :class:`Tree` that parses a given :class:`alot.db.Thread` into a tree of :class:`MessageTrees ` that display this threads individual messages. As MessageTreess are *not* urwid widgets themself this is to be used in combination with :class:`NestedTree` only. """ def __init__(self, thread): self._thread = thread self.root = thread.get_toplevel_messages()[0].get_message_id() self._parent_of = {} self._first_child_of = {} self._last_child_of = {} self._next_sibling_of = {} self._prev_sibling_of = {} self._message = {} def accumulate(msg, odd=True): """recursively read msg and its replies""" mid = msg.get_message_id() self._message[mid] = MessageTree(msg, odd) odd = not odd last = None self._first_child_of[mid] = None for reply in thread.get_replies_to(msg): rid = reply.get_message_id() if self._first_child_of[mid] is None: self._first_child_of[mid] = rid self._parent_of[rid] = mid self._prev_sibling_of[rid] = last self._next_sibling_of[last] = rid last = rid odd = accumulate(reply, odd) self._last_child_of[mid] = last return odd last = None for msg in thread.get_toplevel_messages(): mid = msg.get_message_id() self._prev_sibling_of[mid] = last self._next_sibling_of[last] = mid accumulate(msg) last = mid self._next_sibling_of[last] = None # Tree API def __getitem__(self, pos): return self._message.get(pos, None) def parent_position(self, pos): return self._parent_of.get(pos, None) def first_child_position(self, pos): return self._first_child_of.get(pos, None) def last_child_position(self, pos): return self._last_child_of.get(pos, None) def next_sibling_position(self, pos): return self._next_sibling_of.get(pos, None) def prev_sibling_position(self, pos): return self._prev_sibling_of.get(pos, None) def position_of_messagetree(self, mt): return mt._message.get_message_id() alot-0.3.5/alot/widgets/utils.py000066400000000000000000000025621217172710600166060ustar00rootroot00000000000000# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file """ Utility Widgets not specific to alot """ import urwid class AttrFlipWidget(urwid.AttrMap): """ An AttrMap that can remember attributes to set """ def __init__(self, w, maps, init_map='normal'): self.maps = maps urwid.AttrMap.__init__(self, w, maps[init_map]) def set_map(self, attrstring): self.set_attr_map({None: self.maps[attrstring]}) class DialogBox(urwid.WidgetWrap): def __init__(self, body, title, bodyattr=None, titleattr=None): self.body = urwid.LineBox(body) self.title = urwid.Text(title) if titleattr is not None: self.title = urwid.AttrMap(self.title, titleattr) if bodyattr is not None: self.body = urwid.AttrMap(self.body, bodyattr) box = urwid.Overlay(self.title, self.body, align='center', valign='top', width=len(title), height=None, ) urwid.WidgetWrap.__init__(self, box) def selectable(self): return self.body.selectable() def keypress(self, size, key): return self.body.keypress(size, key) alot-0.3.5/bin/000077500000000000000000000000001217172710600132325ustar00rootroot00000000000000alot-0.3.5/bin/alot000077500000000000000000000015041217172710600141170ustar00rootroot00000000000000#!/usr/bin/env python # This file is part of alot, a terminal UI to notmuch mail (notmuchmail.org). # Copyright (C) 2011 Patrick Totzke # # 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 . from alot.init import main main() alot-0.3.5/docs/000077500000000000000000000000001217172710600134125ustar00rootroot00000000000000alot-0.3.5/docs/Makefile000066400000000000000000000124331217172710600150550ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source CONFIG_OPTION_TABLES = source/configuration/alotrc_table source/configuration/accounts_table COMMAND_OPTION_TABLES = source/usage/modes/bufferlist.rst \ source/usage/modes/envelope.rst \ source/usage/modes/global.rst \ source/usage/modes/search.rst \ source/usage/modes/taglist.rst \ source/usage/modes/thread.rst .PHONY: html help clean dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" $(COMMAND_OPTION_TABLES): python source/generate_commands.py $(CONFIG_OPTION_TABLES): python source/generate_configs.py clean: -rm -rf $(BUILDDIR)/* html: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." devhelp: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/alot" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/alot" @echo "# devhelp" epub: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(CONFIG_OPTION_TABLES) $(COMMAND_OPTION_TABLES) $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." alot-0.3.5/docs/source/000077500000000000000000000000001217172710600147125ustar00rootroot00000000000000alot-0.3.5/docs/source/api/000077500000000000000000000000001217172710600154635ustar00rootroot00000000000000alot-0.3.5/docs/source/api/commands.rst000066400000000000000000000040641217172710600200220ustar00rootroot00000000000000Commands ========= .. module:: alot.commands User actions are represented by :class:`Command` objects that can then be triggered by :meth:`alot.ui.UI.apply_command`. Command-line strings given by the user via the prompt or key bindings can be translated to :class:`Command` objects using :func:`alot.commands.commandfactory`. Specific actions are defined as subclasses of :class:`Command` and can be registered to a global command pool using the :class:`registerCommand` decorator. .. Note:: that the return value of :func:`commandfactory` depends on the current *mode* the user interface is in. The mode identifier is a string that is uniquely defined by the currently focuses :class:`~alot.buffers.Buffer`. .. note:: The names of the commands available to the user in any given mode do not correspond one-to-one to these subclasses. You can register a Command multiple times under different names, with different forced constructor parameters and so on. See for instance the definition of BufferFocusCommand in 'commands/globals.py':: @registerCommand(MODE, 'bprevious', forced={'offset': -1}, help='focus previous buffer') @registerCommand(MODE, 'bnext', forced={'offset': +1}, help='focus next buffer') class BufferFocusCommand(Command): def __init__(self, buffer=None, offset=0, **kwargs): ... .. autoclass:: Command :members: .. autoclass:: CommandParseError .. autoclass:: CommandArgumentParser .. autofunction:: commandfactory .. autofunction:: lookup_command .. autofunction:: lookup_parser .. autoclass:: registerCommand Globals -------- .. automodule:: alot.commands.globals :members: Envelope -------- .. automodule:: alot.commands.envelope :members: Bufferlist ---------- .. automodule:: alot.commands.bufferlist :members: Search -------- .. automodule:: alot.commands.search :members: Taglist -------- .. automodule:: alot.commands.taglist :members: Thread -------- .. automodule:: alot.commands.thread :members: alot-0.3.5/docs/source/api/conf.py000066400000000000000000000201061217172710600167610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # alot documentation build configuration file, created by # sphinx-quickstart on Tue Aug 9 15:00:51 2011. # # 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. import sys, os ############################### # readthedocs.org hack, # needed to use autodocs on their build-servers: # http://readthedocs.org/docs/read-the-docs/en/latest/faq.html?highlight=autodocs#where-do-i-need-to-put-my-docs-for-rtd-to-find-it class Mock(object): def __init__(self, *args, **kwargs): pass def __call__(self, *args, **kwargs): return Mock() @classmethod def __getattr__(self, name): return Mock() if name not in ('__file__', '__path__') else '/dev/null' MOCK_MODULES = ['notmuch', 'notmuch.globals', 'twisted', 'twisted.internet', 'twisted.internet.defer', 'twisted.python', 'twisted.python.failure', 'urwid', 'magic', 'argparse'] for mod_name in MOCK_MODULES: sys.modules[mod_name] = Mock() # end of readthedocs.org hack ############################## # 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. sys.path.insert(0, os.path.abspath('..')) from alot import __version__,__author__ # -- General configuration ----------------------------------------------------- # 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.intersphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'alot' copyright = u'2011 ' + __author__ # 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 = __version__ # The full version, including alpha/beta/rc tags. release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- 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 = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # 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'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'alotdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'alot.tex', u'alot Documentation', u'Patrick Totzke', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'alot', u'alot Documentation', [u'Patrick Totzke'], 1) ] autodoc_member_order = 'bysource' autoclass_content = 'both' intersphinx_mapping = { 'python': ('http://docs.python.org/3.2', None), 'notmuch': ('http://packages.python.org/notmuch', None), 'urwid': ('http://urwid.readthedocs.org/en/latest', None), } alot-0.3.5/docs/source/api/contributing.rst000066400000000000000000000023101217172710600207200ustar00rootroot00000000000000Contributing ============ Development is coordinated entirely via the projects `github page `_ especially the `issue tracker `_. You can send patches to notmuch's mailing list but pull requests on github are preferred. Here are a few more things you should know and check before you send pull requests: * Follow :pep:`8`. This means in particular a maximum linewidth of *79* and no trailing white spaces. If in doubt, use an Automatic tool (`[0] `_, `[1] `_, `[2] `_) to verify your code. * Document! Needless to say, we want readable and well documented code. Moreover, * use `sphinx directives `_ to document the parameters and return values of your methods so that we maintain up-to-date API docs. * Make sure your patch doesn't break the API docs. The build service at `readthedocs.org `_ is fragile when it comes to new import statements in our code. * If you implemented a new feature, update the user manual in :file:`/docs/user` accordingly. alot-0.3.5/docs/source/api/crypto.rst000066400000000000000000000000711217172710600175330ustar00rootroot00000000000000Crypto ====== .. automodule:: alot.crypto :members: alot-0.3.5/docs/source/api/database.rst000066400000000000000000000031421217172710600177610ustar00rootroot00000000000000Email Database ============== .. module:: alot.db The python bindings to libnotmuch define :class:`notmuch.Thread` and :class:`notmuch.Message`, which unfortunately are very fragile. Alot defines the wrapper classes :class:`alot.db.Thread` and :class:`alot.db.Message` that use an :class:`manager.DBManager` instance to transparently provide persistent objects. :class:`alot.db.Message` moreover contains convenience methods to extract information about the message like reformated header values, a summary, decoded and interpreted body text and a list of :class:`Attachments `. The central :class:`~alot.ui.UI` instance carries around a :class:`~manager.DBManager` object that is used for any lookups or modifications of the email base. :class:`~manager.DBManager` can directly look up :class:`Thread` and :class:`~alot.db.Message` objects and is able to postpone/cache/retry writing operations in case the Xapian index is locked by another process. Database Manager ----------------- .. autoclass:: alot.db.manager.DBManager :members: Errors ---------- .. module:: alot.db.errors .. autoclass:: DatabaseError :members: .. autoclass:: DatabaseROError :members: .. autoclass:: DatabaseLockedError :members: .. autoclass:: NonexistantObjectError :members: Wrapper ------- .. autoclass:: alot.db.Thread :members: .. autoclass:: alot.db.Message :members: Other Structures ---------------- .. autoclass:: alot.db.attachment.Attachment :members: .. autoclass:: alot.db.envelope.Envelope :members: Utilities --------- .. automodule:: alot.db.utils :members: alot-0.3.5/docs/source/api/index.rst000066400000000000000000000003011217172710600173160ustar00rootroot00000000000000API and Development ******************* .. module:: alot .. toctree:: :maxdepth: 1 overview contributing database interface settings utils commands crypto alot-0.3.5/docs/source/api/interface.rst000066400000000000000000000101711217172710600201550ustar00rootroot00000000000000User Interface ================== Alot sets up a widget tree and a :class:`mainloop ` in the constructor of :class:`alot.ui.UI`. The visible area is a :class:`urwid.Frame`, where the footer is used as a status line and the body part displays the currently active :class:`alot.buffers.Buffer`. To be able to bind keystrokes and translate them to :class:`Commands `, keypresses are *not* propagated down the widget tree as is customary in urwid. Instead, the root widget given to urwids mainloop is a custom wrapper (:class:`alot.ui.Inputwrap`) that interprets key presses. A dedicated :class:`~alot.commands.globals.SendKeypressCommand` can be used to trigger key presses to the wrapped root widget and thereby accessing standard urwid behaviour. In order to keep the interface non-blocking and react to events like terminal size changes, alot makes use of twisted's deferred_ - a framework that makes it easy to deal with callbacks. Many commands in alot make use of `inline callbacks`_, which allow you to treat deferred-returning functions almost like syncronous functions. Consider the following example of a function that prompts for some input and acts on it: .. _deferred: http://twistedmatrix.com/documents/current/core/howto/defer.html .. _`inline callbacks`: http://twistedmatrix.com/documents/8.1.0/api/twisted.internet.defer.html#inlineCallbacks .. code-block:: python from twisted.internet import defer @defer.inlineCallbacks def greet(ui): # ui is instance of alot.ui.UI name = yield ui.prompt('pls enter your name') ui.notify('your name is: ' + name) :class:`UI` - the main component ----------------------------------- .. module:: alot.ui .. autoclass:: UI :members: Buffers ---------- A buffer defines a view to your data. It knows how to render itself, to interpret keypresses and is visible in the "body" part of the widget frame. Different modes are defined by subclasses of the following base class. .. autoclass:: alot.buffers.Buffer :members: Available modes are: ========== ======================================== Mode Buffer Subclass ========== ======================================== search :class:`~alot.buffers.SearchBuffer` thread :class:`~alot.buffers.ThreadBuffer` bufferlist :class:`~alot.buffers.BufferlistBuffer` taglist :class:`~alot.buffers.TagListBuffer` envelope :class:`~alot.buffers.EnvelopeBuffer` ========== ======================================== .. automodule:: alot.buffers :members: BufferlistBuffer, EnvelopeBuffer,SearchBuffer,ThreadBuffer,TagListBuffer Widgets -------- What follows is a list of the non-standard urwid widgets used in alot. Some of them respect :doc:`user settings `, themes in particular. utils ````` .. automodule:: alot.widgets.utils :members: globals ``````` .. automodule:: alot.widgets.globals :members: bufferlist `````````` .. automodule:: alot.widgets.bufferlist :members: search `````` .. automodule:: alot.widgets.search :members: thread `````` .. automodule:: alot.widgets.thread :members: Completion ---------- :meth:`alot.ui.UI.prompt` allows tab completion using a :class:`~alot.completion.Completer` object handed as 'completer' parameter. :mod:`alot.completion` defines several subclasses for different occasions like completing email addresses from an :class:`~alot.account.AddressBook`, notmuch tagstrings. Some of these actually build on top of each other; the :class:`~alot.completion.QueryCompleter` for example uses a :class:`~alot.completion.TagsCompleter` internally to allow tagstring completion after "is:" or "tag:" keywords when typing a notmuch querystring. All these classes overide the method :meth:`~alot.completion.Completer.complete`, which for a given string and cursor position in that string returns a list of tuples `(completed_string, new_cursor_position)` that are taken to be the completed values. Note that `completed_string` does not need to have the original string as prefix. :meth:`~alot.completion.Completer.complete` may rise :class:`alot.errors.CompletionError` exceptions. .. automodule:: alot.completion :members: alot-0.3.5/docs/source/api/overview.rst000066400000000000000000000025701217172710600200670ustar00rootroot00000000000000Overview ======== The main component is :class:`alot.ui.UI`, which provides methods for user input and notifications, sets up the widget tree and maintains the list of active buffers. When you start up alot, :file:`init.py` initializes logging, parses settings and commandline args and instantiates the :class:`UI ` instance of that gets passes around later. From its constructor this instance starts the :mod:`urwid` :class:`mainloop ` that takes over. Apart from the central :class:`UI `, there are two other "managers" responsible for core functionalities, also set up in :file:`init.py`: * :attr:`ui.dbman `: a :class:`DBManager ` to access the email database and * :attr:`alot.settings.settings`: a :class:`SettingsManager ` oo access user settings Every user action, triggered either by key bindings or via the command prompt, is given as commandline string that gets :func:`translated ` to a :class:`Command ` object which is then :meth:`applied `. Different actions are defined as a subclasses of :class:`Command `, which live in :file:`alot/commands/MODE.py`, where MODE is the name of the mode (:class:`Buffer ` type) they are used in. alot-0.3.5/docs/source/api/settings.rst000066400000000000000000000052601217172710600200600ustar00rootroot00000000000000User Settings ============= .. module:: alot.settings.manager Alot sets up a :class:`SettingsManager` to access user settings defined in different places uniformly. There are four types of user settings: +------------------------------------+----------------------------------+---------------------------------------------+ | what? | location | accessible via | +====================================+==================================+=============================================+ | alot config | :file:`~/.config/alot/config` | :meth:`SettingsManager.get` | | | or given by command option `-c`. | | +------------------------------------+----------------------------------+---------------------------------------------+ | hooks -- user provided python code | :file:`~/.config/alot/hooks.py` | :meth:`SettingsManager.get_hook` | | | or as given by the `hooksfile` | | | | config value | | +------------------------------------+----------------------------------+---------------------------------------------+ | notmuch config | :file:`~/.notmuchrc` | :meth:`SettingsManager.get_notmuch_setting` | | | or given by command option `-n` | | +------------------------------------+----------------------------------+---------------------------------------------+ | mailcap -- defines shellcommands | :file:`~/.mailcap` | :meth:`SettingsManager.mailcap_find_match` | | to handle mime types | (:file:`/etc/mailcap`) | | +------------------------------------+----------------------------------+---------------------------------------------+ Settings Manager ---------------- .. autoclass:: SettingsManager :members: Errors ------ .. automodule:: alot.settings.errors :members: Utils ----- .. automodule:: alot.settings.utils :members: Themes ------ .. autoclass:: alot.settings.theme.Theme :members: Accounts -------- .. module:: alot.account .. autoclass:: Account :members: .. autoclass:: SendmailAccount :members: Addressbooks ------------ .. module:: alot.addressbooks .. autoclass:: AddressBook :members: .. autoclass:: MatchSdtoutAddressbook :members: .. autoclass:: AbookAddressBook :members: alot-0.3.5/docs/source/api/utils.rst000066400000000000000000000001751217172710600173600ustar00rootroot00000000000000Utils ===== .. currentmodule:: alot.helper .. automodule:: alot.helper :members: .. automodule:: alot.utils :members: alot-0.3.5/docs/source/conf.py000066400000000000000000000203371217172710600162160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # alot documentation build configuration file import sys, os ############################### # readthedocs.org hack, # needed to use autodocs on their build-servers: # http://readthedocs.org/docs/read-the-docs/en/latest/faq.html?highlight=autodocs#where-do-i-need-to-put-my-docs-for-rtd-to-find-it class Mock(object): def __init__(self, *args, **kwargs): pass def __call__(self, *args, **kwargs): return Mock() @classmethod def __getattr__(self, name): return Mock() if name not in ('__file__', '__path__') else '/dev/null' class MockModule(object): @classmethod def __getattr__(self, name): return Mock if name not in ('__file__', '__path__') else '/dev/null' MOCK_MODULES = ['twisted', 'twisted.internet', 'twisted.internet.defer', 'twisted.python', 'twisted.python.failure', 'twisted.internet.protocol', 'urwid', 'magic', 'gpgme', 'configobj', 'validate', 'argparse'] MOCK_DIRTY = ['notmuch'] for mod_name in MOCK_MODULES: sys.modules[mod_name] = MockModule() for mod_name in MOCK_DIRTY: sys.modules[mod_name] = Mock() # end of readthedocs.org hack ############################## # 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. sys.path.insert(0, os.path.abspath(os.path.join('..','..'))) from alot import __version__,__author__ # -- General configuration ----------------------------------------------------- # 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.intersphinx'] # autodoc tweaks # show classes' docstrings _and_ constructors docstrings/parameters autoclass_content = 'both' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'alot' copyright = u'2012, Patrick Totzke' # 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 = __version__ # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [ ] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- 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 = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = 'Alot User Manual' # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # 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'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'alotdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'alot.tex', u'alot Documentation', u'Patrick Totzke', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True autodoc_member_order = 'groupwise' # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('manpage', 'alot', u'mail user agent for the notmuch mail system', [u'Patrick Totzke'], 1), ] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'http://docs.python.org/': None, 'http://notmuch.readthedocs.org/en/latest/': None, 'http://urwid.readthedocs.org/en/latest/': None } alot-0.3.5/docs/source/configuration/000077500000000000000000000000001217172710600175615ustar00rootroot00000000000000alot-0.3.5/docs/source/configuration/accounts.rst000066400000000000000000000022461217172710600221360ustar00rootroot00000000000000.. _config.accounts: Accounts ======== In order to be able to send mails, you have to define at least one account subsection in your config: There needs to be a section "accounts", and each subsection, indicated by double square brackets defines an account. Here is an example configuration .. code-block:: ini [accounts] [[work]] realname = Bruce Wayne address = b.wayne@wayneenterprises.com gpg_key = D7D6C5AA sendmail_command = msmtp --account=wayne -t sent_box = maildir:///home/bruce/mail/work/Sent draft_box = maildir:///home/bruce/mail/work/Drafts [[secret]] realname = Batman address = batman@batcave.org aliases = batman@batmobile.org, sendmail_command = msmtp --account=batman -t signature = ~/.batman.vcf signature_as_attachment = True .. warning:: Sending mails is only supported via a sendmail shell command for now. If you want to use a sendmail command different from `sendmail -t`, specify it as `sendmail_command`. The following entries are interpreted at the moment: .. include:: accounts_table alot-0.3.5/docs/source/configuration/accounts_table000066400000000000000000000055201217172710600224740ustar00rootroot00000000000000 .. CAUTION: THIS FILE IS AUTO-GENERATED from the inline comments of specfile defaults/alot.rc.spec. If you want to change its content make your changes to that spec to ensure they woun't be overwritten later. .. _address: .. describe:: address your main email address :type: string .. _realname: .. describe:: realname used to format the (proposed) From-header in outgoing mails :type: string .. _aliases: .. describe:: aliases used to clear your addresses/ match account when formatting replies :type: string list :default: , .. _sendmail-command: .. describe:: sendmail_command sendmail command. This is the shell command used to send out mails via the sendmail protocol :type: string :default: "sendmail -t" .. _sent-box: .. describe:: sent_box where to store outgoing mails, e.g. `maildir:///home/you/mail/Sent`. You can use mbox, maildir, mh, babyl and mmdf in the protocol part of the URL. .. note:: If you want to add outgoing mails automatically to the notmuch index you must use maildir in a path within your notmuch database path. :type: mail_container :default: None .. _draft-box: .. describe:: draft_box where to store draft mails, e.g. `maildir:///home/you/mail/Drafts`. You can use mbox, maildir, mh, babyl and mmdf in the protocol part of the URL. .. note:: You will most likely want drafts indexed by notmuch to be able to later access them within alot. This currently only works for maildir containers in a path below your notmuch database path. :type: mail_container :default: None .. _sent-tags: .. describe:: sent_tags list of tags to automatically add to outgoing messages :type: string list :default: sent, .. _signature: .. describe:: signature path to signature file that gets attached to all outgoing mails from this account, optionally renamed to :ref:`signature_filename `. :type: string :default: None .. _signature-as-attachment: .. describe:: signature_as_attachment attach signature file if set to True, append its content (mimetype text) to the body text if set to False. :type: boolean :default: False .. _signature-filename: .. describe:: signature_filename signature file's name as it appears in outgoing mails if :ref:`signature_as_attachment ` is set to True :type: string :default: None .. _sign-by-default: .. describe:: sign_by_default Outgoing messages will be GPG signed by default if this is set to True. :type: boolean :default: False .. _gpg-key: .. describe:: gpg_key The GPG key ID you want to use with this account. If unset, alot will use your default key. :type: string :default: None alot-0.3.5/docs/source/configuration/alotrc_table000066400000000000000000000305671217172710600221520ustar00rootroot00000000000000 .. CAUTION: THIS FILE IS AUTO-GENERATED from the inline comments of specfile defaults/alot.rc.spec. If you want to change its content make your changes to that spec to ensure they woun't be overwritten later. .. _ask-subject: .. describe:: ask_subject :type: boolean :default: True .. _attachment-prefix: .. describe:: attachment_prefix directory prefix for downloading attachments :type: string :default: "~" .. _auto-remove-unread: .. describe:: auto_remove_unread automatically remove 'unread' tag when focussing messages in thread mode :type: boolean :default: True .. _bounce-force-address: .. describe:: bounce_force_address Always use the accounts main address when constructing "Resent-From" headers for bounces. Set this to False to use the address string as received in the original message. :type: boolean :default: False .. _bounce-force-realname: .. describe:: bounce_force_realname Always use the proper realname when constructing "Resent-From" headers for bounces. Set this to False to use the realname string as received in the original message. :type: boolean :default: True .. _bufferclose-focus-offset: .. describe:: bufferclose_focus_offset offset of next focused buffer if the current one gets closed :type: integer :default: -1 .. _bufferlist-statusbar: .. describe:: bufferlist_statusbar Format of the status-bar in bufferlist mode. This is a pair of strings to be left and right aligned in the status-bar that may contain variables: * `{buffer_no}`: index of this buffer in the global buffer list * `{total_messages}`: total numer of messages indexed by notmuch * `{pending_writes}`: number of pending write operations to the index :type: mixed_list :default: [{buffer_no}: bufferlist], {input_queue} total messages: {total_messages} .. _bug-on-exit: .. describe:: bug_on_exit confirm exit :type: boolean :default: False .. _colourmode: .. describe:: colourmode number of colours to use :type: option, one of ['1', '16', '256'] :default: 256 .. _complete-matching-abook-only: .. describe:: complete_matching_abook_only in case more than one account has an address book: Set this to True to make tab completion for recipients during compose only look in the abook of the account matching the sender address :type: boolean :default: False .. _compose-ask-tags: .. describe:: compose_ask_tags prompt for initial tags when compose :type: boolean :default: False .. _displayed-headers: .. describe:: displayed_headers headers that get displayed by default :type: string list :default: From, To, Cc, Bcc, Subject .. _edit-headers-blacklist: .. describe:: edit_headers_blacklist see :ref:`edit_headers_whitelist ` :type: string list :default: Content-Type, MIME-Version, References, In-Reply-To .. _edit-headers-whitelist: .. describe:: edit_headers_whitelist Which header fields should be editable in your editor used are those that match the whitelist and don't match the blacklist. in both cases '*' may be used to indicate all fields. :type: string list :default: \*, .. _editor-cmd: .. describe:: editor_cmd editor command if unset, alot will first try the :envvar:`EDITOR` env variable, then :file:`/usr/bin/editor` :type: string :default: None .. _editor-in-thread: .. describe:: editor_in_thread call editor in separate thread. In case your editor doesn't run in the same window as alot, setting true here will make alot non-blocking during edits :type: boolean :default: False .. _editor-spawn: .. describe:: editor_spawn use terminal_command to spawn a new terminal for the editor? equivalent to always providing the `--spawn=yes` parameter to compose/edit commands :type: boolean :default: False .. _editor-writes-encoding: .. describe:: editor_writes_encoding file encoding used by your editor :type: string :default: "UTF-8" .. _envelope-headers-blacklist: .. describe:: envelope_headers_blacklist headers that are hidden in envelope buffers by default :type: string list :default: In-Reply-To, References .. _envelope-statusbar: .. describe:: envelope_statusbar Format of the status-bar in envelope mode. This is a pair of strings to be left and right aligned in the status-bar. Apart from the global variables listed at :ref:`bufferlist_statusbar ` these strings may contain variables: * `{to}`: To-header of the envelope :type: mixed_list :default: [{buffer_no}: envelope], {input_queue} total messages: {total_messages} .. _flush-retry-timeout: .. describe:: flush_retry_timeout timeout in seconds after a failed attempt to writeout the database is repeated :type: integer :default: 5 .. _followup-to: .. describe:: followup_to When one of the recipients of an email is a subscribed mailing list, set the "Mail-Followup-To" header to the list of recipients without yourself :type: boolean :default: False .. _forward-force-address: .. describe:: forward_force_address Always use the accounts main address when constructing "From" headers for forwards. Set this to False to use the address string as received in the original message. :type: boolean :default: False .. _forward-force-realname: .. describe:: forward_force_realname Always use the proper realname when constructing "From" headers for forwards. Set this to False to use the realname string as received in the original message. :type: boolean :default: True .. _forward-subject-prefix: .. describe:: forward_subject_prefix String prepended to subject header on forward only if original subject doesn't start with 'Fwd:' or this prefix :type: string :default: "Fwd: " .. _honor-followup-to: .. describe:: honor_followup_to When group-reply-ing to an email that has the "Mail-Followup-To" header set, use the content of this header as the new "To" header and leave the "Cc" header empty :type: boolean :default: False .. _hooksfile: .. describe:: hooksfile where to look up hooks :type: string :default: "~/.config/alot/hooks.py" .. _initial-command: .. describe:: initial_command initial command when none is given as argument: :type: string :default: "search tag:inbox AND NOT tag:killed" .. _input-timeout: .. describe:: input_timeout timeout in (floating point) seconds until partial input is cleared :type: float :default: 1.0 .. _mailinglists: .. describe:: mailinglists The list of addresses associated to the mailinglists you are subscribed to :type: string list :default: , .. _notify-timeout: .. describe:: notify_timeout time in secs to display status messages :type: integer :default: 2 .. _prefer-plaintext: .. describe:: prefer_plaintext prefer plaintext alternatives over html content in multipart/alternative :type: boolean :default: False .. _print-cmd: .. describe:: print_cmd how to print messages: this specifies a shell command used for printing. threads/messages are piped to this command as plain text. muttprint/a2ps works nicely :type: string :default: None .. _prompt-suffix: .. describe:: prompt_suffix Suffix of the prompt used when waiting for user input :type: string :default: ":" .. _quit-on-last-bclose: .. describe:: quit_on_last_bclose shut down when the last buffer gets closed :type: boolean :default: False .. _quote-prefix: .. describe:: quote_prefix String prepended to line when quoting :type: string :default: "> " .. _reply-force-address: .. describe:: reply_force_address Always use the accounts main address when constructing "From" headers for replies. Set this to False to use the address string as received in the original message. :type: boolean :default: False .. _reply-force-realname: .. describe:: reply_force_realname Always use the proper realname when constructing "From" headers for replies. Set this to False to use the realname string as received in the original message. :type: boolean :default: True .. _reply-subject-prefix: .. describe:: reply_subject_prefix String prepended to subject header on reply only if original subject doesn't start with 'Re:' or this prefix :type: string :default: "Re: " .. _search-statusbar: .. describe:: search_statusbar Format of the status-bar in search mode. This is a pair of strings to be left and right aligned in the status-bar. Apart from the global variables listed at :ref:`bufferlist_statusbar ` these strings may contain variables: * `{querystring}`: search string * `{result_count}`: number of matching messages * `{result_count_positive}`: 's' if result count is greater than 0. :type: mixed_list :default: [{buffer_no}: search] for "{querystring}", {input_queue} {result_count} of {total_messages} messages .. _search-threads-sort-order: .. describe:: search_threads_sort_order default sort order of results in a search :type: option, one of ['oldest_first', 'newest_first', 'message_id', 'unsorted'] :default: newest_first .. _show-statusbar: .. describe:: show_statusbar display status-bar at the bottom of the screen? :type: boolean :default: True .. _tabwidth: .. describe:: tabwidth number of spaces used to replace tab characters :type: integer :default: 8 .. _taglist-statusbar: .. describe:: taglist_statusbar Format of the status-bar in taglist mode. This is a pair of strings to be left and right aligned in the status-bar. These strings may contain variables listed at :ref:`bufferlist_statusbar ` that will be substituted accordingly. :type: mixed_list :default: [{buffer_no}: taglist], {input_queue} total messages: {total_messages} .. _template-dir: .. describe:: template_dir templates directory that contains your message templates. It will be used if you give `compose --template` a filename without a path prefix. :type: string :default: "$XDG_CONFIG_HOME/alot/templates" .. _terminal-cmd: .. describe:: terminal_cmd set terminal command used for spawning shell commands :type: string :default: "x-terminal-emulator -e" .. _theme: .. describe:: theme name of the theme to use :type: string :default: None .. _themes-dir: .. describe:: themes_dir directory containing theme files :type: string :default: None .. _thread-authors-me: .. describe:: thread_authors_me Word to replace own addresses with. Works in combination with :ref:`thread_authors_replace_me ` :type: string :default: "Me" .. _thread-authors-replace-me: .. describe:: thread_authors_replace_me Replace own email addresses with "me" in author lists Uses own addresses and aliases in all configured accounts. :type: boolean :default: True .. _thread-statusbar: .. describe:: thread_statusbar Format of the status-bar in thread mode. This is a pair of strings to be left and right aligned in the status-bar. Apart from the global variables listed at :ref:`bufferlist_statusbar ` these strings may contain variables: * `{tid}`: thread id * `{subject}`: subject line of the thread * `{authors}`: abbreviated authors string for this thread * `{message_count}`: number of contained messages :type: mixed_list :default: [{buffer_no}: thread] {subject}, {input_queue} total messages: {total_messages} .. _timestamp-format: .. describe:: timestamp_format timestamp format in `strftime format syntax `_ :type: string :default: None .. _user-agent: .. describe:: user_agent value of the User-Agent header used for outgoing mails. setting this to the empty string will cause alot to omit the header all together. The string '{version}' will be replaced by the version string of the running instance. :type: string :default: "alot/{version}" alot-0.3.5/docs/source/configuration/config_options.rst000066400000000000000000000006621217172710600233370ustar00rootroot00000000000000.. _config.options: Config options ============== The following lists all available config options with their type and default values. The type of an option is used to validate a given value. For instance, if the type says "boolean" you may only provide "True" or "False" as values in your config file, otherwise alot will complain on startup. Strings *may* be quoted but do not need to be. .. include:: alotrc_table .. _account: alot-0.3.5/docs/source/configuration/contacts_completion.rst000066400000000000000000000047001217172710600243630ustar00rootroot00000000000000.. _config.contacts_completion: Contacts Completion =================== For each :ref:`account ` you can define an address book by providing a subsection named `abook`. Crucially, this section needs an option `type` that specifies the type of the address book. The only types supported at the moment are "shellcommand" and "abook". Both respect the `ignorecase` option which defaults to `True` and results in case insensitive lookups. .. describe:: shellcommand Address books of this type use a shell command in combination with a regular expression to look up contacts. The value of `command` will be called with the search prefix as only argument for lookups. Its output is searched for email-name pairs using the regular expression given as `regexp`, which must include named groups "email" and "name" to match the email address and realname parts respectively. See below for an example that uses `abook `_ .. sourcecode:: ini [accounts] [[youraccount]] # ... [[[abook]]] type = shellcommand command = abook --mutt-query regexp = '^(?P[^@]+@[^\t]+)\t+(?P[^\t]+)' ignorecase = True See `here `_ for alternative lookup commands. The few others I have tested so far are: `goobook `_ for cached google contacts lookups. Works with the above default regexp .. code-block:: ini command = goobook query regexp = '^(?P[^@]+@[^\t]+)\t+(?P[^\t]+)' `nottoomuch-addresses `_ completes contacts found in the notmuch index: .. code-block:: ini command = nottoomuch-addresses.sh regexp = \"(?P.+)\"\s*<(?P.*.+?@.+?)> Don't hesitate to send me your custom `regexp` values to list them here. .. describe:: abook Address books of this type directly parse `abooks `_ contact files. You may specify a path using the "abook_contacts_file" option, which defaults to :file:`~/.abook/addressbook`. To use the default path, simply do this: .. code-block:: ini [accounts] [[youraccount]] # ... [[[abook]]] type = abook alot-0.3.5/docs/source/configuration/hooks.rst000066400000000000000000000123441217172710600214420ustar00rootroot00000000000000.. _config.hooks: Hooks ===== Hooks are python callables that live in a module specified by `hooksfile` in the config. Per default this points to :file:`~/.config/alot/hooks.py`. .. rubric:: Pre/Post Command Hooks For every :ref:`COMMAND ` in mode :ref:`MODE `, the callables :func:`pre_MODE_COMMAND` and :func:`post_MODE_COMMAND` -- if defined -- will be called before and after the command is applied respectively. The signature for the pre-`send` hook in envelope mode for example looks like this: .. py:function:: pre_envelope_send(ui=None, dbm=None) :param ui: the main user interface :type ui: :class:`alot.ui.UI` :param dbm: a database manager :type dbm: :class:`alot.db.manager.DBManager` Consider this pre-hook for the exit command, that logs a personalized goodbye message:: import logging from alot.settings import settings def pre_global_exit(ui, dbm): accounts = settings.get_accounts() if accounts: logging.info('goodbye, %s!' % accounts[0].realname) else: logging.info('goodbye!') .. rubric:: Other Hooks Apart from command pre- and posthooks, the following hooks will be interpreted: .. py:function:: reply_prefix(realname, address, timestamp[, ui= None, dbm=None]) Is used to reformat the first indented line in a reply message. This defaults to 'Quoting %s (%s)\n' % (realname, timestamp)' unless this hook is defined :param realname: name or the original sender :type realname: str :param address: address of the sender :type address: str :param timestamp: value of the Date header of the replied message :type timestamp: :obj:`datetime.datetime` :rtype: string .. py:function:: forward_prefix(realname, address, timestamp[, ui= None, dbm=None]) Is used to reformat the first indented line in a inline forwarded message. This defaults to 'Forwarded message from %s (%s)\n' % (realname, timestamp)' if this hook is undefined :param realname: name or the original sender :type realname: str :param address: address of the sender :type address: str :param timestamp: value of the Date header of the replied message :type timestamp: :obj:`datetime.datetime` :rtype: string .. py:function:: pre_edit_translate(bodytext[, ui= None, dbm=None]) used to manipulate a messages bodytext *before* the editor is called. :param bodytext: text representation of mail body as displayed in the interface and as sent to the editor :type bodytext: str :rtype: str .. py:function:: post_edit_translate(bodytext[, ui= None, dbm=None]) used to manipulate a messages bodytext *after* the editor is called :param bodytext: text representation of mail body as displayed in the interface and as sent to the editor :type bodytext: str :rtype: str .. py:function:: text_quote(message) used to transform a message into a quoted one :param message: message to be quoted :type message: str :rtype: str .. py:function:: timestamp_format(timestamp) represents given timestamp as string :param bodytext: timestamp to represent :type timestamp: `datetime` :rtype: str .. py:function:: touch_external_cmdlist(cmd, shell=shell, spawn=spawn, thread=thread) used to change external commands according to given flags shortly before they are called. :param cmd: command to be called :type cmd: list of str :param shell: is this to be interpreted by the shell? :type shell: bool :param spawn: should be spawned in new terminal/environment :type spawn: bool :param threads: should be called in new thread :type thread: bool :returns: triple of amended command list, shell and thread flags :rtype: list of str, bool, bool .. py:function:: reply_subject(subject) used to reformat the subject header on reply :param subject: subject to reformat :type subject: str :rtype: str .. py:function:: forward_subject(subject) used to reformat the subject header on forward :param subject: subject to reformat :type subject: str :rtype: str .. py:function:: pre_buffer_open(ui= None, dbm=None, buf=buf) run before a new buffer is opened :param buf: buffer to open :type buf: alot.buffer.Buffer .. py:function:: post_buffer_open(ui=None, dbm=None, buf=buf) run after a new buffer is opened :param buf: buffer to open :type buf: alot.buffer.Buffer .. py:function:: pre_buffer_close(ui=None, dbm=None, buf=buf) run before a buffer is closed :param buf: buffer to open :type buf: alot.buffer.Buffer .. py:function:: post_buffer_close(ui=None, dbm=None, buf=buf, success=success) run after a buffer is closed :param buf: buffer to open :type buf: alot.buffer.Buffer :param success: true if successfully closed buffer :type success: boolean .. py:function:: pre_buffer_focus(ui=None, dbm=None, buf=buf) run before a buffer is focused :param buf: buffer to open :type buf: alot.buffer.Buffer .. py:function:: post_buffer_focus(ui=None, dbm=None, buf=buf, success=success) run after a buffer is focused :param buf: buffer to open :type buf: alot.buffer.Buffer :param success: true if successfully focused buffer :type success: boolean alot-0.3.5/docs/source/configuration/index.rst000066400000000000000000000011121217172710600214150ustar00rootroot00000000000000.. _configuration: ************* Configuration ************* Alot reads a config file in "INI" syntax: It consists of key-value pairs that use "=" as separator and '#' is comment-prefixes. Sections and subsections are defined using square brackets. The default location for the config file is :file:`~/.config/alot/config`. All configs are optional, but if you want to send mails you need to specify at least one :ref:`account ` in your config. .. toctree:: :maxdepth: 2 config_options accounts contacts_completion key_bindings hooks theming alot-0.3.5/docs/source/configuration/key_bindings.rst000066400000000000000000000027551217172710600227710ustar00rootroot00000000000000.. _config.key_bindings: Key Bindings ============ If you want to bind a command to a key you can do so by adding the pair to the `[bindings]` section. This will introduce a *global* binding, that works in all modes. To make a binding specific to a mode you have to add the pair under the subsection named like the mode. For instance, if you want to bind `T` to open a new search for threads tagged with 'todo', and be able to toggle this tag in search mode, you'd add this to your config .. sourcecode:: ini [bindings] T = search tag:todo [[search]] t = toggletags todo .. _modes: Known modes are: * envelope * search * thread * taglist * bufferlist Have a look at `the urwid User Input documentation `_ on how key strings are formatted. .. _config.key-bingings.defaults: Default bindings ---------------- User-defined bindings are combined with the default bindings listed below. .. literalinclude:: ../../../alot/defaults/default.bindings :language: ini Overwriting defaults -------------------- To disable a global binding you can redefine it in your config to point to an empty command string. For example, to add a new global binding for key `a`, which is bound to `toggletags inbox` in search mode by default, you can remap it as follows. .. sourcecode:: ini [bindings] a = NEW GLOBAL COMMAND [[search]] a = If you omit the last two lines, `a` will still be bound to the default binding in search mode. alot-0.3.5/docs/source/configuration/theming.rst000066400000000000000000000202361217172710600217510ustar00rootroot00000000000000.. _config.theming: Theming ======= Alot can be run in 1, 16 or 256 colour mode. The requested mode is determined by the command-line parameter `-C` or read from option `colourmode` config value. The default is 256, which scales down depending on how many colours your terminal supports. To specify the theme to use, set the :ref:`theme ` config option to the name of a theme-file. A file by that name will be looked up in the path given by the :ref:`themes_dir ` config setting which defaults to :file:`~/.config/alot/themes/`. .. _config.theming.themefiles: Theme Files ----------- contain a section for each :ref:`MODE ` plus "help" for the bindings-help overlay and "global" for globally used themables like footer, prompt etc. Each such section defines colour :ref:`attributes ` for the parts that can be themed. The names of the themables should be self-explanatory. Have a look at the default theme file at :file:`alot/defaults/default.theme` and the config spec :file:`alot/defaults/default.theme` for the exact format. .. _config.theming.attributes: Colour Attributes ----------------- Attributes are *sextuples* of `urwid Attribute strings `__ that specify foreground and background for mono, 16 and 256-colour modes respectively. For mono-mode only the flags `blink`, `standup`, `underline` and `bold` are available, 16c mode supports these in combination with the colour names:: brown dark red dark magenta dark blue dark cyan dark green yellow light red light magenta light blue light cyan light green black dark gray light gray white In high-colour mode, you may use the above plus grayscales `g0` to `g100` and colour codes given as `#` followed by three hex values. See `here `__ and `here `__ for more details on the interpreted values. A colour picker that makes choosing colours easy can be found in :file:`alot/extra/colour_picker.py`. As an example, check the setting below that makes the footer line appear as underlined bold red text on a bright green background: .. sourcecode:: ini [[global]] #name mono fg mono bg 16c fg 16c bg 256c fg 256c bg # | | | | | | # v v v v v v footer = 'bold,underline', '', 'light red, bold, underline', 'light green', 'light red, bold, underline', '#8f6' Highlighting Thread lines in Search Mode ---------------------------------------- The subsection '[[threadline]]' of the '[search]' section in :ref:`Theme Files ` determines how to present a thread: here, :ref:`attributes ` 'normal' and 'focus' provide fallback/spacer themes and 'parts' is a (string) list of displayed subwidgets. Possible part strings are: * date * mailcount * tags * authors * subject For every listed part there must be a subsection with the same name, defining :normal: :ref:`attribute ` used for this part if unfocussed :focus: :ref:`attribute ` used for this part if focussed :width: tuple indicating the width of the part. This is either `('fit', min, max)` to force the widget to be at least `min` and at most `max` characters wide, or `('weight', n)` which makes it share remaining space with other 'weight' parts. :alignment: how to place the content string if the widget space is larger. This must be one of 'right', 'left' or 'center'. To "highlight" some thread lines (use different attributes than the defaults found in the '[[threadline]]' section), one can define sections with prefix 'threadline'. Each one of those can redefine any part of the structure outlined above, the rest defaults to values defined in '[[threadline]]'. The section used to theme a particular thread is the first one (in file-order) that matches the criteria defined by its 'query' and 'taggeswith' values: * If 'query' is defined, the thread must match that querystring. * If 'tagged_with' is defined, is value (string list) must be a subset of the accumulated tags of all messages in the thread. .. note:: that 'tagged_with = A,B' is different from 'query = "is:A AND is:B"': the latter will match only if the thread contains a single message that is both tagged with A and B. Moreover, note that if both query and tagged_with is undefined, this section will always match and thus overwrite the defaults. The example below shows how to highlight unread threads: The date-part will be bold red if the thread has unread messages and flagged messages and just bold if the thread has unread but no flagged messages: .. sourcecode:: ini [search] # default threadline [[threadline]] normal = 'default','default','default','default','#6d6','default' focus = 'standout','default','light gray','dark gray','white','#68a' parts = date,mailcount,tags,authors,subject [[[date]]] normal = 'default','default','light gray','default','g58','default' focus = 'standout','default','light gray','dark gray','g89','#68a' width = 'fit',10,10 # ... # highlight threads containing unread and flagged messages [[threadline-flagged-unread]] tagged_with = 'unread','flagged' [[[date]]] normal = 'default','default','light red,bold','default','light red,bold','default' # highlight threads containing unread messages [[threadline-unread]] query = 'is:unread' [[[date]]] normal = 'default','default','light gray,bold','default','g58,bold','default' .. _config.theming.tags: Custom Tagstring Formatting --------------------------- To specify how a particular tagstring is displayed throughout the interface you can add a subsection named after the tag to the `[tags]` config section. Such a section may define :normal: :ref:`attribute ` used if unfocussed :focus: :ref:`attribute ` used if focussed :translated: fixed string representation for this tag. The tag can be hidden from view, if the key `translated` is set to '', the empty string. :translation: a pair of strings that define a regular substitution to compute the string representation on the fly using `re.sub`. This only really makes sense if one uses a regular expression to match more than one tagstring (see below). The following will make alot display the "todo" tag as "TODO" in white on red. .. sourcecode:: ini [tags] [[todo]] normal = '','', 'white','light red', 'white','#d66' translated = TODO Utf-8 symbols are welcome here, see e.g. http://panmental.de/symbols/info.htm for some fancy symbols. I personally display my maildir flags like this: .. sourcecode:: ini [tags] [[flagged]] translated = ⚑ normal = '','','light red','','light red','' focus = '','','light red','','light red','' [[unread]] translated = ✉ [[replied]] translated = ⏎ [[encrypted]] translated = ⚷ You may use regular expressions in the tagstring subsections to theme multiple tagstrings at once (first match wins). If you do so, you can use the `translation` option to specify a string substitution that will rename a matching tagstring. `translation` takes a comma separated *pair* of strings that will be fed to :func:`re.sub`. For instance, to theme all your `nmbug`_ tagstrings and especially colour tag `notmuch::bug` red, do the following: .. sourcecode:: ini [[notmuch::bug]] translated = 'nm:bug' normal = "", "", "light red, bold", "light blue", "light red, bold", "#88d" [[notmuch::.*]] translation = 'notmuch::(.*)','nm:\1' normal = "", "", "white", "light blue", "#fff", "#88d" .. _nmbug: http://notmuchmail.org/nmbug/ alot-0.3.5/docs/source/description.rst000066400000000000000000000003331217172710600177660ustar00rootroot00000000000000Alot is a terminal-based mail user agent for the notmuch mail system. It features a modular and command prompt driven interface to provide a full MUA experience as an alternative to the Emacs mode shipped with notmuch. alot-0.3.5/docs/source/faq.rst000066400000000000000000000103501217172710600162120ustar00rootroot00000000000000FAQ *** 1. Why reinvent the wheel? Why not extend an existing MUA to work nicely with notmuch? alot makes use of existing solutions where possible: It does not fetch, send or edit mails; it lets `notmuch `_ handle your mailindex and uses a `toolkit `_ to render its display. You are responsible for `automatic initial tagging `_. This said, there are few CLI MUAs that could be easily and naturally adapted to using notmuch. Rebuilding an interface from scratch using `friendly and extensible tools `_ seemed easier and more promising. Update: see `mutt-kz `_ for a fork of mutt.. 2. What's with the snotty name? It's not meant to be presumptuous. I like the dichotomy; I like to picture the look on someone's face who reads the :mailheader:`User-Agent` header "notmuch/alot"; I like cookies; I like `this comic strip `_. 3. I want feature X! Me too! Feel free to file a new or comment on existing `issues `_ if you don't want/have the time/know how to implement it yourself. Be verbose as to how it should look or work when it's finished and give it some thought how you think we should implement it. We'll discuss it from there. 4. Why are the default key bindings so counter-intuitive? Be aware that the bindings for all modes are :ref:`fully configurable `. That said, I choose the bindings to be natural for me. I use `vim `_ and `pentadactyl `_ a lot. However, I'd be interested in discussing the defaults. If you think your bindings are more intuitive or better suited as defaults for some reason, don't hesitate to send me your config. The same holds for the theme settings you use. Tell me. Let's improve the defaults. 5. Help! I don't see `text/html` content! better: How do I properly set up an inline renderer for `text/html`? Try `w3m `_ and put the following into your :file:`~/.mailcap`:: text/html; w3m -dump -o document_charset=%{charset} '%s'; nametemplate=%s.html; copiousoutput Most `text based browsers `_ have a dump mode that can be used here. 6. Why are you $THIS not $THAT way? Lazyness and Ignorance: In most cases I simply did not or still don't know a better solution. I try to outsource as much as I can to well established libraries and be it only to avoid having to read rfc's. But there are lots of tasks I implemented myself, possibly overlooking a ready made and available solution. Twisted is such a feature-rich but gray area in my mind for example. If you think you know how to improve the current implementation let me know! The few exceptions to above stated rule are the following: * CLI option parsing is done using twisted.usage.Options, and not (as e.g. in-app command parsing) via argparse. The reason is that argparse does not yet offer optional subcommands. * The modules cmd and cmd2, that handle all sorts of convenience around command objects hate urwid: They are painfully strongly coupled to user in/output via stdin and out. * `notmuch reply` is not used to format reply messages because 1. it is not offered by notmuch's library but is a feature of the CLI. This means we would have to call the notmuch binary, something that is avoided where possible. 2. As there is no `notmuch forward` equivalent, this (very similar) functionality would have to be re-implemented anyway. .. _faq_7: 7. Why doesn't alot run on python3? Because it builds on libraries that don't (yet): * `configobj `_ * `twisted `_ Alot itself can be converted to py3k syntax automatically using `2to3 `_ and I will push those changes as soon as the libs are ready. alot-0.3.5/docs/source/generate_commands.py000077500000000000000000000113511217172710600207430ustar00rootroot00000000000000import sys import os HERE = os.path.dirname(__file__) sys.path.append(os.path.join(HERE, '..', '..', '..')) from alot.commands import * from alot.commands import COMMANDS import alot.buffers from argparse import HelpFormatter, SUPPRESS, OPTIONAL, ZERO_OR_MORE, ONE_OR_MORE, PARSER, REMAINDER from alot.utils.booleanaction import BooleanAction from gettext import gettext as _ import collections as _collections import copy as _copy import os as _os import re as _re import sys as _sys import textwrap as _textwrap NOTE = ".. CAUTION: THIS FILE IS AUTO-GENERATED!\n\n\n" class HF(HelpFormatter): def _metavar_formatter(self, action, default_metavar): if action.metavar is not None: result = action.metavar else: result = default_metavar def format(tuple_size): if isinstance(result, tuple): return result else: return (result, ) * tuple_size return format def rstify_parser(parser): #header = parser.format_usage().strip() #print '\n\n%s\n' % header + '_' * len(header) parser.formatter_class = HF #parser.print_help() #continue formatter = parser._get_formatter() out = "" # usage usage = formatter._format_usage(None, parser._actions, parser._mutually_exclusive_groups, '').strip() usage = usage.replace('--','---') # section header out += '.. describe:: %s\n\n' % parser.prog # description out += ' '*4 + parser.description out += '\n\n' if len(parser._positionals._group_actions) == 1: out += " argument\n" a = parser._positionals._group_actions[0] out += ' '*8 + parser._positionals._group_actions[0].help if a.choices: out += ". valid choices are: %s." % ','.join(['\`%s\`' % s for s in a.choices]) if a.default: out += ". defaults to: '%s'." % a.default out += '\n\n' elif len(parser._positionals._group_actions) > 1: out += " positional arguments\n" for index, a in enumerate(parser._positionals._group_actions): out += " %s: %s" % (index, a.help) if a.choices: out += ". valid choices are: %s." % ','.join(['\`%s\`' % s for s in a.choices]) if a.default: out += ". defaults to: '%s'." % a.default out += '\n' out += '\n\n' if parser._optionals._group_actions: out += " optional arguments\n" for a in parser._optionals._group_actions: switches = [s.replace('--','---') for s in a.option_strings] out += " :%s: %s" % (', '.join(switches), a.help) if a.choices and not isinstance(a, BooleanAction): out += ". Valid choices are: %s" % ','.join(['\`%s\`' % s for s in a.choices]) if a.default: out += " (Defaults to: '%s')" % a.default out += '.\n' out += '\n' # epilog #out += formatter.add_text(parser.epilog) return out def get_mode_docs(): docs = {} b = alot.buffers.Buffer for entry in alot.buffers.__dict__.values(): if isinstance(entry, type): if issubclass(entry, b) and not entry == b: docs[entry.modename] = entry.__doc__.strip() return docs if __name__ == "__main__": modes = [] for mode, modecommands in COMMANDS.items(): modefilename = mode+'.rst' modefile = open(os.path.join(HERE, 'usage', 'modes', modefilename), 'w') modefile.write(NOTE) if mode != 'global': modes.append(mode) header = 'Commands in `%s` mode' % mode modefile.write('%s\n%s\n' % (header, '-' * len(header))) modefile.write('The following commands are available in %s mode\n\n' % mode) else: header = 'Global Commands' modefile.write('%s\n%s\n' % (header, '-' * len(header))) modefile.write('The following commands are available globally\n\n') for cmdstring,struct in modecommands.items(): cls, parser, forced_args = struct labelline = '.. _cmd.%s.%s:\n\n' % (mode, cmdstring.replace('_', '-')) modefile.write(labelline) modefile.write(rstify_parser(parser)) modefile.close() alot-0.3.5/docs/source/generate_configs.py000077500000000000000000000047761217172710600206070ustar00rootroot00000000000000import sys import os HERE = os.path.dirname(__file__) sys.path.append(os.path.join(HERE, '..', '..', '..')) from alot.commands import COMMANDS from configobj import ConfigObj from validate import Validator import re NOTE = """ .. CAUTION: THIS FILE IS AUTO-GENERATED from the inline comments of specfile %s. If you want to change its content make your changes to that spec to ensure they woun't be overwritten later. """ def rewrite_entries(config, path, specpath, sec=None, sort=False): file = open(path, 'w') file.write(NOTE % specpath) if sec == None: sec = config if sort: sec.scalars.sort() for entry in sec.scalars: v = Validator() #config.validate(v) #print config[entry] #etype = re.sub('\(.*\)','', config[entry]) ##if etype == 'option': etype, eargs, ekwargs, default = v._parse_check(sec[entry]) if default is not None: default = config._quote(default) if etype == 'gpg_key_hint': etype = 'string' description = '\n.. _%s:\n' % entry.replace('_', '-') description += '\n.. describe:: %s\n\n' % entry comments = [sec.inline_comments[entry]] + sec.comments[entry] for c in comments: if c: description += ' '*4 + re.sub('^\s*#', '', c) + '\n' if etype == 'option': description += '\n :type: option, one of %s\n' % eargs else: if etype == 'force_list': etype = 'string list' description += '\n :type: %s\n' % etype if default != None: default = default.replace('*','\\*') if etype in ['string', 'string_list', 'gpg_key_hint'] and default != 'None': description += ' :default: "%s"\n\n' % (default) else: description += ' :default: %s\n\n' % (default) file.write(description) file.close() if __name__ == "__main__": specpath = os.path.join(HERE, '..','..', 'alot', 'defaults', 'alot.rc.spec') config = ConfigObj(None, configspec=specpath, stringify=False, list_values=False) config.validate(Validator()) alotrc_table_file = os.path.join(HERE, 'configuration', 'alotrc_table') rewrite_entries(config.configspec, alotrc_table_file, 'defaults/alot.rc.spec', sort=True) rewrite_entries(config, os.path.join(HERE, 'configuration', 'accounts_table'), 'defaults/alot.rc.spec', sec=config.configspec['accounts']['__many__']) alot-0.3.5/docs/source/index.rst000066400000000000000000000006731217172710600165610ustar00rootroot00000000000000.. alot documentation master file, created by sphinx-quickstart on Tue Jan 10 16:45:54 2012. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. User Manual =========== .. include:: description.rst .. toctree:: :maxdepth: 2 :numbered: installation usage/index configuration/index api/index faq .. toctree:: :hidden: manpage description alot-0.3.5/docs/source/installation.rst000066400000000000000000000042721217172710600201520ustar00rootroot00000000000000Installation ************ .. rubric:: dependencies Alot depends on recent versions of notmuch and urwid. Note that due to restrictions on argparse and subprocess, you need to run *`3.0` > python ≥ `2.7`* (see :ref:`faq `). A full list of dependencies is below: * `libmagic and python bindings `_, ≥ `5.04`: * `configobj `_, ≥ `4.6.0`: * `twisted `_, ≥ `10.2.0`: * `libnotmuch `_ and it's python bindings, ≥ `0.12`. * `urwid `_ toolkit, ≥ `1.1.0` * `PyGPGME `_ On debian/ubuntu these are packaged as:: python-magic python-configobj python-twisted python-notmuch python-urwid python-gpgme On fedora/redhat these are packaged as:: python-magic python-configobj python-twisted python-notmuch python-urwid pygpgme Alot uses `mailcap `_ to look up mime-handler for inline rendering and opening of attachments. For a full description of the maicap protocol consider the manpage :manpage:`mailcap(5)` or :rfc:`1524`. To avoid surprises you should at least have an inline renderer (copiousoutput) set up for `text/html`, i.e. have something like this in your :file:`~/.mailcap`:: text/html; w3m -dump -o document_charset=%{charset} '%s'; nametemplate=%s.html; copiousoutput .. rubric:: get and install alot Grab a `tarball here `_ or directly check out a more recent version from `github `_.:: git clone git@github.com:pazz/alot.git Run the :file:`setup.py` with the :option:`--user` flag to install locally:: python setup.py install --user and make sure :file:`~/.local/bin` is in your :envvar:`PATH`. For system-wide installation omit this falg and call with the respective permissions. .. rubric:: generate manual and manpage To generate the documentation you need `sphinx `_, ≥ `1.07` installed. Go to :file:`docs/` and do a:: make html make man to generate the user manual and a man page. Both will end up in their respective subfolders in :file:`docs/build`. alot-0.3.5/docs/source/manpage.rst000066400000000000000000000003251217172710600170540ustar00rootroot00000000000000Manpage ======= Synopsis -------- .. include:: usage/synopsis.rst Description ----------- .. include:: description.rst Usage ----- .. include:: usage/first_steps.rst See Also -------- :manpage:`notmuch(1)` alot-0.3.5/docs/source/usage/000077500000000000000000000000001217172710600160165ustar00rootroot00000000000000alot-0.3.5/docs/source/usage/commands.rst000066400000000000000000000017411217172710600203540ustar00rootroot00000000000000Commands ======== Alot interprets user input as command line strings given via its prompt or :ref:`bound to keys ` in the config. Command lines are semi-colon separated command strings, each of which starts with a command name and possibly followed by arguments. See the sections below for which commands are available in which (UI) mode. `global` commands are available independently of the mode. :doc:`modes/global` globally available commands :doc:`modes/search` commands available when showing thread search results :doc:`modes/thread` commands available while displaying a thread :doc:`modes/envelope` commands during message composition :doc:`modes/bufferlist` commands while listing active buffers :doc:`modes/taglist` commands while listing all tagstrings present in the notmuch database .. toctree:: :maxdepth: 2 :hidden: modes/global modes/search modes/thread modes/envelope modes/bufferlist modes/taglist alot-0.3.5/docs/source/usage/crypto.rst000066400000000000000000000036471217172710600201020ustar00rootroot00000000000000Cryptography ============ Alot has built in support for constructing signed and/or encrypted mails according to PGP/MIME (:rfc:`3156`, :rfc:`3156`) via gnupg. It does however rely on a running `gpg-agent` to handle password entries. .. note:: You need to have `gpg-agent` running to use GPG with alot! `gpg-agent` will handle passphrase entry in a secure and configurable way, and it will cache your passphrase for some time so you don’t have to enter it over and over again. For details on how to set this up we refer to `gnupg's manual `_. .. rubric:: Signing outgoing emails You can use the commands :ref:`sign `, :ref:`unsign ` and :ref:`togglesign ` in envelope mode to determine if you want this mail signed and if so, which key to use. To specify the key to use you may pass a hint string as argument to the `sign` or `togglesign` command. This hint would typically be a fingerprint or an email address associated (by gnupg) with a key. Signing (and hence passwd entry) will be done at most once shortly before a mail is sent. In case no key is specified, alot will leave the selection of a suitable key to gnupg so you can influence that by setting the `default-key` option in :file:`~/.gnupg/gpg.conf` accordingly. You can set the default to-sign bit and the key to use for each :ref:`account ` individually using the options :ref:`sign_by_default ` and :ref:`gpg_key `. .. rubric:: Encrypt outgoing emails You can use the commands :ref:`encrypt `, :ref:`unencrypt ` and and :ref:`toggleencrypt ` and in envelope mode to ask alot to encrypt the mail before sending. The :ref:`encrypt ` command accepts an optional hint string as argument to determine the key of the recipient. alot-0.3.5/docs/source/usage/first_steps.rst000066400000000000000000000011461217172710600211170ustar00rootroot00000000000000The arrow keys, `page-up/down`, `j`, `k` and `Space` can be used to move the focus. `Escape` cancels prompts and `Enter` selects. Hit `:` at any time and type in commands to the prompt. The interface shows one buffer at a time, you can use `Tab` and `Shift-Tab` to switch between them, close the current buffer with `d` and list them all with `;`. The buffer type or *mode* (displayed at the bottom left) determines which prompt commands are available. Usage information on any command can be listed by typing `help YOURCOMMAND` to the prompt; The key bindings for the current mode are listed upon pressing `?`. alot-0.3.5/docs/source/usage/index.rst000066400000000000000000000005221217172710600176560ustar00rootroot00000000000000***** Usage ***** Commandline invocation ====================== .. include:: synopsis.rst First Steps =========== .. _usage.first_steps: .. include:: first_steps.rst .. _usage.commands: .. include:: commands.rst .. _usage.crypto: .. include:: crypto.rst .. toctree:: :hidden: commands first_steps synopsis crypto alot-0.3.5/docs/source/usage/modes/000077500000000000000000000000001217172710600171255ustar00rootroot00000000000000alot-0.3.5/docs/source/usage/modes/bufferlist.rst000066400000000000000000000004661217172710600220320ustar00rootroot00000000000000.. CAUTION: THIS FILE IS AUTO-GENERATED! Commands in `bufferlist` mode ----------------------------- The following commands are available in bufferlist mode .. _cmd.bufferlist.close: .. describe:: close close focussed buffer .. _cmd.bufferlist.open: .. describe:: open focus selected buffer alot-0.3.5/docs/source/usage/modes/envelope.rst000066400000000000000000000043211217172710600214740ustar00rootroot00000000000000.. CAUTION: THIS FILE IS AUTO-GENERATED! Commands in `envelope` mode --------------------------- The following commands are available in envelope mode .. _cmd.envelope.unencrypt: .. describe:: unencrypt remove request to encrypt message before sending .. _cmd.envelope.set: .. describe:: set set header value positional arguments 0: header to refine 1: value optional arguments :---append: keep previous values. .. _cmd.envelope.encrypt: .. describe:: encrypt request encryption of message before sendout argument keyid of the key to encrypt with .. _cmd.envelope.togglesign: .. describe:: togglesign toggle sign status argument which key id to use .. _cmd.envelope.toggleheaders: .. describe:: toggleheaders toggle display of all headers .. _cmd.envelope.edit: .. describe:: edit edit mail optional arguments :---spawn: spawn editor in new terminal. :---refocus: refocus envelope after editing (Defaults to: 'True'). .. _cmd.envelope.send: .. describe:: send send mail .. _cmd.envelope.sign: .. describe:: sign mark mail to be signed before sending argument which key id to use .. _cmd.envelope.attach: .. describe:: attach attach files to the mail argument file(s) to attach (accepts wildcads) .. _cmd.envelope.unattach: .. describe:: unattach remove attachments from current envelope argument which attached file to remove .. _cmd.envelope.rmencrypt: .. describe:: rmencrypt do not encrypt to given recipient key argument keyid of the key to encrypt with .. _cmd.envelope.refine: .. describe:: refine prompt to change the value of a header argument header to refine .. _cmd.envelope.toggleencrypt: .. describe:: toggleencrypt toggle if message should be encrypted before sendout argument keyid of the key to encrypt with .. _cmd.envelope.save: .. describe:: save save draft .. _cmd.envelope.unsign: .. describe:: unsign mark mail not to be signed before sending .. _cmd.envelope.unset: .. describe:: unset remove header field argument header to refine alot-0.3.5/docs/source/usage/modes/global.rst000066400000000000000000000054541217172710600211270ustar00rootroot00000000000000.. CAUTION: THIS FILE IS AUTO-GENERATED! Global Commands --------------- The following commands are available globally .. _cmd.global.bclose: .. describe:: bclose close a buffer optional arguments :---redraw: redraw current buffer after command has finished. :---force: never ask for confirmation. .. _cmd.global.bprevious: .. describe:: bprevious focus previous buffer .. _cmd.global.search: .. describe:: search open a new search buffer argument search string optional arguments :---sort: sort order. Valid choices are: \`oldest_first\`,\`newest_first\`,\`message_id\`,\`unsorted\`. .. _cmd.global.repeat: .. describe:: repeat Repeats the command executed last time .. _cmd.global.prompt: .. describe:: prompt prompts for commandline and interprets it upon select argument initial content .. _cmd.global.help: .. describe:: help display help for a command. Use 'bindings' to display all keybings interpreted in current mode.' argument command or 'bindings' .. _cmd.global.buffer: .. describe:: buffer focus buffer with given index argument buffer index to focus .. _cmd.global.move: .. describe:: move move focus in current buffer argument up, down, [half]page up, [half]page down, first .. _cmd.global.shellescape: .. describe:: shellescape run external command argument command line to execute optional arguments :---spawn: run in terminal window. :---thread: run in separate thread. :---refocus: refocus current buffer after command has finished. .. _cmd.global.refresh: .. describe:: refresh refresh the current buffer .. _cmd.global.pyshell: .. describe:: pyshell open an interactive python shell for introspection .. _cmd.global.compose: .. describe:: compose compose a new email optional arguments :---sender: sender. :---template: path to a template message file. :---subject: subject line. :---to: recipients. :---cc: copy to. :---bcc: blind copy to. :---attach: attach files. :---omit_signature: do not add signature. :---spawn: spawn editor in new terminal. .. _cmd.global.exit: .. describe:: exit shut down cleanly .. _cmd.global.flush: .. describe:: flush flush write operations or retry until committed .. _cmd.global.bufferlist: .. describe:: bufferlist open a list of active buffers .. _cmd.global.call: .. describe:: call Executes python code argument python command string to call .. _cmd.global.bnext: .. describe:: bnext focus next buffer .. _cmd.global.taglist: .. describe:: taglist opens taglist buffer alot-0.3.5/docs/source/usage/modes/search.rst000066400000000000000000000043521217172710600211300ustar00rootroot00000000000000.. CAUTION: THIS FILE IS AUTO-GENERATED! Commands in `search` mode ------------------------- The following commands are available in search mode .. _cmd.search.sort: .. describe:: sort set sort order argument sort order. valid choices are: \`oldest_first\`,\`newest_first\`,\`message_id\`,\`unsorted\`. .. _cmd.search.untag: .. describe:: untag remove tags from all messages in the thread argument comma separated list of tags optional arguments :---no-flush: postpone a writeout to the index (Defaults to: 'True'). :---all: retag all messages in search result. .. _cmd.search.move: .. describe:: move move focus in search buffer argument last .. _cmd.search.retag: .. describe:: retag set tags of all messages in the thread argument comma separated list of tags optional arguments :---no-flush: postpone a writeout to the index (Defaults to: 'True'). :---all: retag all messages in search result. .. _cmd.search.refineprompt: .. describe:: refineprompt prompt to change this buffers querystring .. _cmd.search.tag: .. describe:: tag add tags to all messages in the thread argument comma separated list of tags optional arguments :---no-flush: postpone a writeout to the index (Defaults to: 'True'). :---all: retag all messages in search result. .. _cmd.search.refine: .. describe:: refine refine query argument search string optional arguments :---sort: sort order. Valid choices are: \`oldest_first\`,\`newest_first\`,\`message_id\`,\`unsorted\`. .. _cmd.search.retagprompt: .. describe:: retagprompt prompt to retag selected threads' tags .. _cmd.search.toggletags: .. describe:: toggletags flip presence of tags on this thread. A tag is considered present if at least one message contained in this thread is tagged with it. In that case this command will remove the tag from every message in the thread. argument comma separated list of tags optional arguments :---no-flush: postpone a writeout to the index (Defaults to: 'True'). .. _cmd.search.select: .. describe:: select open thread in a new buffer alot-0.3.5/docs/source/usage/modes/taglist.rst000066400000000000000000000003621217172710600213270ustar00rootroot00000000000000.. CAUTION: THIS FILE IS AUTO-GENERATED! Commands in `taglist` mode -------------------------- The following commands are available in taglist mode .. _cmd.taglist.select: .. describe:: select search for messages with selected tag alot-0.3.5/docs/source/usage/modes/thread.rst000066400000000000000000000076041217172710600211350ustar00rootroot00000000000000.. CAUTION: THIS FILE IS AUTO-GENERATED! Commands in `thread` mode ------------------------- The following commands are available in thread mode .. _cmd.thread.pipeto: .. describe:: pipeto pipe message(s) to stdin of a shellcommand argument shellcommand to pipe to optional arguments :---all: pass all messages. :---format: output format. Valid choices are: \`raw\`,\`decoded\`,\`id\`,\`filepath\` (Defaults to: 'raw'). :---separately: call command once for each message. :---background: don't stop the interface. :---add_tags: add 'Tags' header to the message. :---shell: let the shell interpret the command. :---notify_stdout: display cmd's stdout as notification. .. _cmd.thread.editnew: .. describe:: editnew edit message in as new optional arguments :---spawn: open editor in new window. .. _cmd.thread.move: .. describe:: move move focus in current buffer argument up, down, page up, page down, first, last .. _cmd.thread.untag: .. describe:: untag remove tags from message(s) argument comma separated list of tags optional arguments :---all: tag all messages in thread. :---no-flush: postpone a writeout to the index (Defaults to: 'True'). .. _cmd.thread.toggleheaders: .. describe:: toggleheaders display all headers argument query used to filter messages to affect .. _cmd.thread.print: .. describe:: print print message(s) optional arguments :---all: print all messages. :---raw: pass raw mail string. :---separately: call print command once for each message. :---add_tags: add 'Tags' header to the message. .. _cmd.thread.bounce: .. describe:: bounce directly re-send selected message .. _cmd.thread.togglesource: .. describe:: togglesource display message source argument query used to filter messages to affect .. _cmd.thread.retag: .. describe:: retag set message(s) tags. argument comma separated list of tags optional arguments :---all: tag all messages in thread. :---no-flush: postpone a writeout to the index (Defaults to: 'True'). .. _cmd.thread.fold: .. describe:: fold fold message(s) argument query used to filter messages to affect .. _cmd.thread.tag: .. describe:: tag add tags to message(s) argument comma separated list of tags optional arguments :---all: tag all messages in thread. :---no-flush: postpone a writeout to the index (Defaults to: 'True'). .. _cmd.thread.remove: .. describe:: remove remove message(s) from the index optional arguments :---all: remove whole thread. .. _cmd.thread.unfold: .. describe:: unfold unfold message(s) argument query used to filter messages to affect .. _cmd.thread.forward: .. describe:: forward forward message optional arguments :---attach: attach original mail. :---spawn: open editor in new window. .. _cmd.thread.reply: .. describe:: reply reply to message optional arguments :---all: reply to all. :---spawn: open editor in new window. .. _cmd.thread.save: .. describe:: save save attachment(s) argument path to save to optional arguments :---all: save all attachments. .. _cmd.thread.toggletags: .. describe:: toggletags flip presence of tags on message(s) argument comma separated list of tags optional arguments :---all: tag all messages in thread. :---no-flush: postpone a writeout to the index (Defaults to: 'True'). .. _cmd.thread.select: .. describe:: select select focussed element. The fired action depends on the focus: - if message summary, this toggles visibility of the message, - if attachment line, this opens the attachment alot-0.3.5/docs/source/usage/synopsis.rst000066400000000000000000000023031217172710600204350ustar00rootroot00000000000000.. code-block:: none alot [-r] [-c CONFIGFILE] [-n NOTMUCHCONFIGFILE] [-C {1,16,256}] [-p DB_PATH] [-d {debug,info,warning,error}] [-l LOGFILE] [--version] [--help] [command] Options -r, --read-only open db in read only mode -c, --config=FILENAME config file (default: ~/.config/alot/config) -n, --notmuch-config=FILENAME notmuch config (default: $NOTMUCH_CONFIG or ~/.notmuch-config) -C, --colour-mode=COLOUR terminal colour mode (default: 256). Must be 1, 16 or 256 -p, --mailindex-path=PATH path to notmuch index -d, --debug-level=LEVEL debug log (default: info). Must be one of debug,info,warning or error -l, --logfile=FILENAME logfile (default: /dev/null) --version Display version string and exit --help Display help and exit Subommands search start in a search buffer using the querystring provided as parameter. See also the SEARCH SYNTAX section of notmuch(1) and the output of `alot search --help`. compose compose a new message See the output of `alot compose --help` for more info on parameters. alot-0.3.5/extra/000077500000000000000000000000001217172710600136055ustar00rootroot00000000000000alot-0.3.5/extra/colour_picker.py000077500000000000000000000232161217172710600170260ustar00rootroot00000000000000#!/usr/bin/python # # COLOUR PICKER. # This is a lightly modified version of urwids palette_test.py example script as # found at https://raw.github.com/wardi/urwid/master/examples/palette_test.py # # This version simply omits resetting the screens default colour palette, # and therefore displays the colour attributes as alot would render them in # your terminal. # # Urwid Palette Test. Showing off highcolor support # Copyright (C) 2004-2009 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ """ Palette test. Shows the available foreground and background settings in monochrome, 16 color, 88 color and 256 color modes. """ import re import sys import urwid import urwid.raw_display CHART_256 = """ brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ yellow_ light_red light_magenta light_blue light_cyan light_green #00f#06f#08f#0af#0df#0ff black_______ dark_gray___ #60f#00d#06d#08d#0ad#0dd#0fd light_gray__ white_______ #80f#60d#00a#06a#08a#0aa#0da#0fa #a0f#80d#60a#008#068#088#0a8#0d8#0f8 #d0f#a0d#80d#608#006#066#086#0a6#0d6#0f6 #f0f#d0d#a0a#808#606#000#060#080#0a0#0d0#0f0#0f6#0f8#0fa#0fd#0ff #f0d#d0a#a08#806#600#660#680#6a0#6d0#6f0#6f6#6f8#6fa#6fd#6ff#0df #f0a#d08#a06#800#860#880#8a0#8d0#8f0#8f6#8f8#8fa#8fd#8ff#6df#0af #f08#d06#a00#a60#a80#aa0#ad0#af0#af6#af8#afa#afd#aff#8df#6af#08f #f06#d00#d60#d80#da0#dd0#df0#df6#df8#dfa#dfd#dff#adf#8af#68f#06f #f00#f60#f80#fa0#fd0#ff0#ff6#ff8#ffa#ffd#fff#ddf#aaf#88f#66f#00f #fd0#fd6#fd8#fda#fdd#fdf#daf#a8f#86f#60f #66d#68d#6ad#6dd #fa0#fa6#fa8#faa#fad#faf#d8f#a6f#80f #86d#66a#68a#6aa#6da #f80#f86#f88#f8a#f8d#f8f#d6f#a0f #a6d#86a#668#688#6a8#6d8 #f60#f66#f68#f6a#f6d#f6f#d0f #d6d#a6a#868#666#686#6a6#6d6#6d8#6da#6dd #f00#f06#f08#f0a#f0d#f0f #d6a#a68#866#886#8a6#8d6#8d8#8da#8dd#6ad #d68#a66#a86#aa6#ad6#ad8#ada#add#8ad#68d #d66#d86#da6#dd6#dd8#dda#ddd#aad#88d#66d g78_g82_g85_g89_g93_g100 #da6#da8#daa#dad#a8d#86d g52_g58_g62_g66_g70_g74_ #88a#8aa #d86#d88#d8a#d8d#a6d g27_g31_g35_g38_g42_g46_g50_ #a8a#888#8a8#8aa #d66#d68#d6a#d6d g0__g3__g7__g11_g15_g19_g23_ #a88#aa8#aaa#88a #a88#a8a """ CHART_88 = """ brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ yellow_ light_red light_magenta light_blue light_cyan light_green #00f#08f#0cf#0ff black_______ dark_gray___ #80f#00c#08c#0cc#0fc light_gray__ white_______ #c0f#80c#008#088#0c8#0f8 #f0f#c0c#808#000#080#0c0#0f0#0f8#0fc#0ff #88c#8cc #f0c#c08#800#880#8c0#8f0#8f8#8fc#8ff#0cf #c8c#888#8c8#8cc #f08#c00#c80#cc0#cf0#cf8#cfc#cff#8cf#08f #c88#cc8#ccc#88c #f00#f80#fc0#ff0#ff8#ffc#fff#ccf#88f#00f #c88#c8c #fc0#fc8#fcc#fcf#c8f#80f #f80#f88#f8c#f8f#c0f g62_g74_g82_g89_g100 #f00#f08#f0c#f0f g0__g19_g35_g46_g52 """ CHART_16 = """ brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ yellow_ light_red light_magenta light_blue light_cyan light_green black_______ dark_gray___ light_gray__ white_______ """ ATTR_RE = re.compile("(?P[ \n]*)(?P[^ \n]+)") SHORT_ATTR = 4 # length of short high-colour descriptions which may # be packed one after the next def parse_chart(chart, convert): """ Convert string chart into text markup with the correct attributes. chart -- palette chart as a string convert -- function that converts a single palette entry to an (attr, text) tuple, or None if no match is found """ out = [] for match in re.finditer(ATTR_RE, chart): if match.group('whitespace'): out.append(match.group('whitespace')) entry = match.group('entry') entry = entry.replace("_", " ") while entry: # try the first four characters attrtext = convert(entry[:SHORT_ATTR]) if attrtext: elen = SHORT_ATTR entry = entry[SHORT_ATTR:].strip() else: # try the whole thing attrtext = convert(entry.strip()) assert attrtext, "Invalid palette entry: %r" % entry elen = len(entry) entry = "" attr, text = attrtext out.append((attr, text.ljust(elen))) return out def foreground_chart(chart, background, colors): """ Create text markup for a foreground colour chart chart -- palette chart as string background -- colour to use for background of chart colors -- number of colors (88 or 256) """ def convert_foreground(entry): try: attr = urwid.AttrSpec(entry, background, colors) except urwid.AttrSpecError: return None return attr, entry return parse_chart(chart, convert_foreground) def background_chart(chart, foreground, colors): """ Create text markup for a background colour chart chart -- palette chart as string foreground -- colour to use for foreground of chart colors -- number of colors (88 or 256) This will remap 8 <= colour < 16 to high-colour versions in the hopes of greater compatibility """ def convert_background(entry): try: attr = urwid.AttrSpec(foreground, entry, colors) except urwid.AttrSpecError: return None # fix 8 <= colour < 16 if colors > 16 and attr.background_basic and \ attr.background_number >= 8: # use high-colour with same number entry = 'h%d'%attr.background_number attr = urwid.AttrSpec(foreground, entry, colors) return attr, entry return parse_chart(chart, convert_background) def main(): palette = [ ('header', 'black,underline', 'light gray', 'standout,underline', 'black,underline', '#88a'), ('panel', 'light gray', 'dark blue', '', '#ffd', '#00a'), ('focus', 'light gray', 'dark cyan', 'standout', '#ff8', '#806'), ] screen = urwid.raw_display.Screen() screen.register_palette(palette) lb = urwid.SimpleListWalker([]) chart_offset = None # offset of chart in lb list mode_radio_buttons = [] chart_radio_buttons = [] def fcs(widget): # wrap widgets that can take focus return urwid.AttrMap(widget, None, 'focus') def set_mode(colors, is_foreground_chart): # set terminal mode and redraw chart screen.set_terminal_properties(colors) chart_fn = (background_chart, foreground_chart)[is_foreground_chart] if colors == 1: lb[chart_offset] = urwid.Divider() else: chart = {16: CHART_16, 88: CHART_88, 256: CHART_256}[colors] txt = chart_fn(chart, 'default', colors) lb[chart_offset] = urwid.Text(txt, wrap='clip') def on_mode_change(rb, state, colors): # if this radio button is checked if state: is_foreground_chart = chart_radio_buttons[0].state set_mode(colors, is_foreground_chart) def mode_rb(text, colors, state=False): # mode radio buttons rb = urwid.RadioButton(mode_radio_buttons, text, state) urwid.connect_signal(rb, 'change', on_mode_change, colors) return fcs(rb) def on_chart_change(rb, state): # handle foreground check box state change set_mode(screen.colors, state) def click_exit(button): raise urwid.ExitMainLoop() lb.extend([ urwid.AttrMap(urwid.Text("Urwid Palette Test"), 'header'), urwid.AttrMap(urwid.Columns([ urwid.Pile([ mode_rb("Monochrome", 1), mode_rb("16-Color", 16, True), mode_rb("88-Color", 88), mode_rb("256-Color", 256),]), urwid.Pile([ fcs(urwid.RadioButton(chart_radio_buttons, "Foreground Colors", True, on_chart_change)), fcs(urwid.RadioButton(chart_radio_buttons, "Background Colors")), urwid.Divider(), fcs(urwid.Button("Exit", click_exit)), ]), ]),'panel') ]) chart_offset = len(lb) lb.extend([ urwid.Divider() # placeholder for the chart ]) set_mode(16, True) # displays the chart def unhandled_input(key): if key in ('Q','q','esc'): raise urwid.ExitMainLoop() urwid.MainLoop(urwid.ListBox(lb), screen=screen, unhandled_input=unhandled_input).run() if __name__ == "__main__": main() alot-0.3.5/extra/completion/000077500000000000000000000000001217172710600157565ustar00rootroot00000000000000alot-0.3.5/extra/completion/alot-completion.zsh000066400000000000000000000024111217172710600216100ustar00rootroot00000000000000#compdef alot # ZSH completion for `alot`, Shamelessly copied from notmuch's zsh completion file # Copyright © 2009 Ingmar Vanhassel # Copyright © 2012 Patrick Totzke _alot_subcommands() { local -a alot_subcommands alot_subcommands=( 'search:search for messages matching the search terms, display matching threads as results' 'compose:compose a new message' ) _describe -t command 'command' alot_subcommands } _alot_search() { _arguments -s : \ '--sort=[sort results]:sorting:((newest_first\:"reverse chronological order" oldest_first\:"chronological order" message_id\:"lexicographically by Message Id"))' } _alot_compose() { _arguments -s : \ '--omit_signature[do not add signature]' \ '--sender=[From header]' \ '--subject=[Subject header]' \ '--cc=[Carbon Copy header]' \ '--bcc=[Blind Carbon Copy header]' \ '--template=[template file to use]' \ '--attach=[Attach files]:attach:_files -/'\ } _alot() { if (( CURRENT > 2 )) ; then local cmd=${words[2]} curcontext="${curcontext%:*:*}:alot-$cmd" (( CURRENT-- )) shift words _call_function ret _alot_$cmd return ret else _alot_subcommands fi } _alot "$@" # vim: set sw=2 sts=2 ts=2 et ft=zsh : alot-0.3.5/extra/tagsections_convert.py000077500000000000000000000040651217172710600202520ustar00rootroot00000000000000#!/usr/bin/python """ CONFIG CONVERTER this script converts your custom tag string section from the v.3.1 syntax to the current format. >>> tagsections_convert.py -o config.new config.old will convert your whole alot config safely to the new format. """ from configobj import ConfigObj import argparse import sys import re def get_leaf_value(cfg, path, fallback=''): if len(path) == 1: if isinstance(cfg, ConfigObj): if path[0] not in cfg.scalars: return fallback else: return cfg[path[0]] else: if path[0] not in cfg: return fallback else: return cfg[path[0]] else: if path[0] in cfg: scfg = cfg[path[0]] sp = path[1:] return get_leaf_value(scfg, sp, fallback) else: return None if __name__ == "__main__": parser = argparse.ArgumentParser(description='update alot theme files') parser.add_argument('configfile', type=argparse.FileType('r'), help='theme file to convert') parser.add_argument('-o', type=argparse.FileType('w'), dest='out', help='destination', default=sys.stdout) args = parser.parse_args() cfg = ConfigObj(args.configfile) out = args.out print args def is_256(att): r = r'(g\d{1,3}(?!\d))|(#[0-9A-Fa-f]{3}(?![0-9A-Fa-f]))' return re.search(r, att) if 'tags' in cfg: for tag in cfg['tags'].sections: sec = cfg['tags'][tag] att = [''] * 6 if 'fg' in sec: fg = sec['fg'] if not is_256(fg): att[2] = fg att[4] = fg del(sec['fg']) if 'bg' in sec: bg = sec['bg'] if not is_256(bg): att[3] = bg att[5] = bg del(sec['bg']) sec['normal'] = att if sec.get('hidden'): sec['translated'] = '' cfg.write(out) alot-0.3.5/extra/theme_convert.py000077500000000000000000000116261217172710600170320ustar00rootroot00000000000000#!/usr/bin/python """ THEME CONVERTER this script converts your custom alot theme files from the v.3.1 syntax to the current format. >>> theme_convert.py -o themefile.new themefile.old """ from configobj import ConfigObj import argparse import sys def get_leaf_value(cfg, path, fallback=''): if len(path) == 1: if isinstance(cfg, ConfigObj): if path[0] not in cfg.scalars: return fallback else: return cfg[path[0]] else: if path[0] not in cfg: return fallback else: return cfg[path[0]] else: if path[0] in cfg: scfg = cfg[path[0]] sp = path[1:] return get_leaf_value(scfg, sp, fallback) else: return None if __name__ == "__main__": parser = argparse.ArgumentParser(description='update alot theme files') parser.add_argument('themefile', type=argparse.FileType('r'), help='theme file to convert') parser.add_argument('-o', type=argparse.FileType('w'), dest='out', help='destination', default=sys.stdout) args = parser.parse_args() old = ConfigObj(args.themefile) new = ConfigObj() out = args.out def lookup(path): values = [] for c in ['1', '16', '256']: values.append(get_leaf_value(old, [c] + path + ['fg']) or 'default') values.append(get_leaf_value(old, [c] + path + ['bg']) or 'default') return values values = map(lambda s: '\'' + s + '\'', values) return ','.join(values) for bmode in ['global', 'help', 'envelope']: new[bmode] = {} #out.write('[%s]\n' % bmode) for themable in old['16'][bmode].sections: new[bmode][themable] = lookup([bmode, themable]) #out.write(' %s = %s\n' % (themable, lookup([bmode, themable]))) # BUFFERLIST new['bufferlist'] = {} new['bufferlist']['line_even'] = lookup(['bufferlist','results_even']) new['bufferlist']['line_odd'] = lookup(['bufferlist','results_odd']) new['bufferlist']['line_focus'] = lookup(['bufferlist','focus']) # TAGLIST new['taglist'] = {} new['taglist']['line_even'] = lookup(['bufferlist','results_even']) new['taglist']['line_odd'] = lookup(['bufferlist','results_odd']) new['taglist']['line_focus'] = lookup(['bufferlist','focus']) # SEARCH new['search'] = {} new['search']['threadline'] = {} new['search']['threadline']['normal'] = lookup(['search', 'thread']) new['search']['threadline']['focus'] = lookup(['search', 'thread_focus']) new['search']['threadline']['parts'] = ['date','mailcount','tags','authors','subject'] new['search']['threadline']['date'] = {} new['search']['threadline']['date']['normal'] = lookup(['search', 'thread_date']) new['search']['threadline']['date']['focus'] = lookup(['search', 'thread_date_focus']) new['search']['threadline']['mailcount'] = {} new['search']['threadline']['mailcount']['normal'] = lookup(['search', 'thread_mailcount']) new['search']['threadline']['mailcount']['focus'] = lookup(['search', 'thread_mailcount_focus']) new['search']['threadline']['tags'] = {} new['search']['threadline']['tags']['normal'] = lookup(['search', 'thread_tags']) new['search']['threadline']['tags']['focus'] = lookup(['search', 'thread_tags_focus']) new['search']['threadline']['authors'] = {} new['search']['threadline']['authors']['normal'] = lookup(['search', 'thread_authors']) new['search']['threadline']['authors']['focus'] = lookup(['search', 'thread_authors_focus']) new['search']['threadline']['subject'] = {} new['search']['threadline']['subject']['normal'] = lookup(['search', 'thread_subject']) new['search']['threadline']['subject']['focus'] = lookup(['search', 'thread_subject_focus']) new['search']['threadline']['content'] = {} new['search']['threadline']['content']['normal'] = lookup(['search', 'thread_content']) new['search']['threadline']['content']['focus'] = lookup(['search', 'thread_content_focus']) # THREAD new['thread'] = {} new['thread']['attachment'] = lookup(['thread','attachment']) new['thread']['attachment_focus'] = lookup(['thread','attachment_focus']) new['thread']['body'] = lookup(['thread','body']) new['thread']['arrow_heads'] = lookup(['thread','body']) new['thread']['arrow_bars'] = lookup(['thread','body']) new['thread']['header'] = lookup(['thread','header']) new['thread']['header_key'] = lookup(['thread','header_key']) new['thread']['header_value'] = lookup(['thread','header_value']) new['thread']['summary'] = {} new['thread']['summary']['even'] = lookup(['thread','summary_even']) new['thread']['summary']['odd'] = lookup(['thread','summary_odd']) new['thread']['summary']['focus'] = lookup(['thread','summary_focus']) # write out new.write(out) alot-0.3.5/extra/themes/000077500000000000000000000000001217172710600150725ustar00rootroot00000000000000alot-0.3.5/extra/themes/mutt000066400000000000000000000073741217172710600160210ustar00rootroot00000000000000############################################################################### # MUTT # # colour theme for alot. © 2012 Patrick Totzke, GNU GPL3+ # https://github.com/pazz/alot ############################################################################### [global] footer = 'standout,bold','','light green,bold','dark blue','light green,bold','dark blue' body = '','','light gray','black','light gray','black' notify_error = 'standout','','light gray','dark red','light gray','dark red' notify_normal = '','','light gray','black','light gray','#68a' prompt = '','','light gray','black','light gray','black' tag = '','','yellow','','yellow','' tag_focus = 'standout, bold','','yellow','','yellow','' [help] text = '','','light gray','dark gray','light gray','dark gray' section = 'underline','','white,underline','dark gray','white,underline','dark gray' title = 'standout','','white,underline','dark gray','white,underline','dark gray' [bufferlist] line_even = '','','light gray','black','light gray','black' line_odd = '','','light gray','black','light gray','black' line_focus = 'standout','','black','dark cyan','black','dark cyan' [taglist] line_even = '','','light gray','black','light gray','black' line_odd = '','','light gray','black','light gray','black' line_focus = 'standout','','black','dark cyan','black','dark cyan' [thread] arrow_heads = '','','dark red','black','dark red','black' arrow_bars = '','','dark red','black','dark red','black' attachment = '','','yellow,bold','black','yellow,bold','black' attachment_focus = 'standout','','black','yellow','black','yellow' body = '','','light gray','black','light gray','black' body_focus = '','','light gray','black','light gray','dark gray' header = '','','dark cyan','black','dark cyan','black' header_key = '','','dark cyan','black','dark cyan','black' header_value = '','','dark cyan','black','dark cyan','black' [[summary]] even = '','','light gray','black','light gray','black' odd = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' [envelope] body = '','','light gray','black','light gray','black' header = '','','dark cyan','black','dark cyan','black' header_key = '','','dark cyan','black','dark cyan','black' header_value = '','','dark cyan','black','dark cyan','black' [search] [[threadline]] normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' parts = date,authors,mailcount,subject,tags [[[date]]] normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' width = 'fit',10,10 alignment = right [[[mailcount]]] normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' width = 'fit', 5,5 [[[tags]]] normal = '','','yellow','black','yellow','black' focus = 'standout','','black','dark cyan','black','dark cyan' [[[authors]]] normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' width = 'fit',25,25 [[[subject]]] normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' width = 'weight', 1 [[[content]]] normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' width = 'weight', 1 alot-0.3.5/extra/themes/screenshots/000077500000000000000000000000001217172710600174325ustar00rootroot00000000000000alot-0.3.5/extra/themes/screenshots/mutt.search.png000066400000000000000000005000031217172710600223730ustar00rootroot00000000000000PNG  IHDR sBITO IDATx^]XTG~Ж^; {{ϯ%FcIb%؍+) uc Es{P$ɽ;s99sƓH$jey4e$m(beMdGqR98888888Tq|iQԳ-'<<0nLTϾ}}U`R $e+ZQT(^9j*i*yՂ̗Yq%ߙIbNVb UW&թ*l BTI-C*-QZPxtE` 9#YP6h OZ|\!"A"pl__T0ׇ^¼^p/9=[4q1ԁªS-OH9r<I:Ao0$T;S1N/m>ky0%0LXu2K)&_6f`Gh끶0p<סT=ajJKNA_iTƄ3eW`Mq*DPK0s5ȶ <8xĖ(P`AjU^FOxgTdP4 b ]O{ Q;OFl|3? X>.@Hgy+Tku:l.rF!σ%c0LdT.uC=-Tx,.0F|8ro]*6ǐ/:4$Ob0h ~naJ4pՖb6Kq0>E {IEk} ߏAaGeLƪ͍*Y!-,Ƃqy,y XЭZ(LT =7ѷ"|cLW)c R->lO25%%K*rg/S“^?4 |Q"\Yp{J<lmIub&hFCwd8ɊA,ơ?-5|W%eEV'_cc@H<a`Wt\dʱTz]O[˓+/1}2iq"YZM၌/c([Z!)Xk&ŰiZIHPb*>/kʘzQNjALZv=F=Pc/{l^_F%K tمoV~`jpP[W> !#Ң3{=uGGF"??Vt쀙'GA ̈́С!Ҋ:bARg0 FLw)XR<2ͬ6bhth=ME)OjӨ?L`Y!{Dcta@n2<Ů`ބAaeER¸F)eVzçJiYW Mi+%PD'4%Kjzį0X21C4w:G;|4wub }9j7\~P}%>%#vf`Nǒjlz`tkǀ_b,n-Fv Q֘.W,OU%!ߞK&Zu]r7zLE{e2 LnsV,5M^V37/4wB/'QhbyHƉ+R_E2k[Mo'Gx#_8bưR)YHf2^сڐ/z8|WɨqI[xeaS1c7m,lmm^22KSF|.d1 ,ش3oSK!yy jZJR"GʵR{hj` HaN?wN*,`9I!/oz(zbXAHxIYеTpNG**k!XUkQ'+D3 CjI cub"[?&h7gaO/(R2*o5.=N7_AR|tXmA Lb&hem/ >0ꕒE[asbu1FCXև/K2t i7r-16Es<ۋpX9Z|;̙jJ#/ w([ġҷqiM8h `Cbʏhe\1(KJ6z,4j0{ȂSaHR%q4<;d6N&ITW'51m5܊a,*( oF_aN .0S{и=v\Rt5,%'!P-Po}0o7ưc弒\d<]"S_&HahUdbt(C3fXzWEo : Ð3sChӿd ibc䊱lmqР[q-2p.ýW >YTd1zL/mY҉*8L+ Ʋ=`D$vӾL4x;+pw+FS[1z&+|ܒ0 R?^Z }8t<[mao+Hd ݀FMx~Vco|?,CMl>uU||;~]Vm\OX9k~"UщJZ^RoCcvP,Z!!Vj%Y8$L̼esc>@!%MBNVEZ1ۅ)f+,B#a#O#o+N]ѠO0\$θ]:ߪxp* Pڰh6lm)xtcTEhUT/ty$,U8ַh-I0CA%kN>T%QKKkk#yC`>ތO\K җ"]ApVp-ֆEd4%mAl/#<2^RlrA,KYrWz"$]„=u-tqU!Uќg4A+*+ǑpC:ǒɭ"jWy"KհCk0/r#!PNHڴQR+.I @H`)Y2pQ l[s qU,a m.2o@grow;b{ydnSr|8 έơrTbEz\C6U?Gc9EcQ P"By+nr. {r8r`Pp9* #!!!!!!!!D@uŅK&$sن=zΟQD XiONRC!Ζ^5QyZ)ƭx?uJjl V}bq^V뇯ȸW#Ύ U(#nn۵僿w%鶎,,m{m9X|HNkoWT¦nN|c +AmUui\Ϻc8L5`>91Ʌe" jxdI@Z(;(6. Ѓ&9_/6d=^q%F9mUP^2m q.j'd81!UYSPSrP_y_^T'R+3o5ij79 fT#")0"ZI[:`(bK6Xq*UZ.Ϣ,ﮢS7%-{' .?1~%J{8z:CI7.S/ȘH>շ]BVWLr]['PM Fs]g]eic |.h!,,4E~%љ/6s|[$*Ngn= 2AZbxL߇KIaV\b=8L*rz{ɂ:cyf˸&LB=KhˆE[uOY?~~sq9ֵ1ğuٙ 3VuF ?۷ߣ/ ݚPh6uFHcn\Partù^ oc>b^y K kqt˅yT]ȹ]sz|^mwF ڗ(*(ޏnrtM;-]d mW&(L<0̃6!\Gs]UulljCnY-U.H[拢̷oFY7igTxcGF$HxRKkKȠ U]a{{YYZokCR|>w"3jb*7ŧ:m&YtRq{j2V~$_Jdc|SRnN.b&K:Y{'Ҹ+ϲ˶')7ɶ.ZWwx0^6,%^ngcu%džuWgC%" CtdKY}&m~vj(meOxkBn5;ނƥ~yﹺljE@.d"oԬ}K{5nbdCn]pARwt4њ6΍j{'=N5']_C k}|2:~lM-7;R>˺W3  HR,7rLA..;9BS&3J#r_0pJY eW0x`Cyetkn('d=hqUꘐilek*}=Lx{~3)m_E{t^%sc"Lh( Z</NKf)bN۟>#Ʒ)$l_ŧvtU!u16:YdZRM4aQ IDAT>+bU 6X̗Qyood˞>b-x?!PR•NP@qS9;(770ݩeptRyGM4ux@lU4F6r#^Nē8!ԑgZff֦MQ:(ȅȖIogꯋ7^~:*$Be͋w_H ^V!Q![|58MSNZWſ`!g~꿍z=yvG6>>aJPﴶ/ { /j(;NrfQ5moyyIf7ӔeW:9ߌzH:jn0Ԓy<13߾<ݫ$yI]pUm"G ]lWFCxmǶ~6&(wK4L`)`4+/I5ijr1I/xv'Q3ǒTVu6 ˀ^PJ2F|mB^jRG#ݽc|?\EGMծj^N.owrl"Wb^A2D!)jKGY6DO{Q(.ĽFA#YS%0&tu)k ґ<:k" cEҴ87+_A0- GW47 NW;hFCƘsʢC4./6bKTd4([Eb O÷K~iyo=?ekN>{ըF^G>2#hJ˄ bMɥ3k3%>$"^ү.-%E lޤd׽MJعL|`!5FlYonTSYm)d͔{8xm̩7-0wf>MgXY̳twhwGfa8SQ$@2XHRKFqT /R``y:lTL%QR5ҵoӔy bhwZYXTg Efa])%G2v44$0X"O3XٗD RYa%E`Rv6Xgbm/Czz=C6NR7zMA˻j?>jcY' Mjlw&z/!.5OۧO]䦏rl"W^Tœ K2iT67]jEE6VjM?R^EX5ƞ smWR:c*:1LS,  ljX^l^,'jj)oUիd]>GxoY_ܰ{BbYoO时^Xꗃ?`蜓)jN )<}ްM,XslE[>v6 /?apǻH0Z8֣bGaҘ1<džNLq`ZSζvuxiij^:2h C^nNypVsix\Hᘆ_YҢǤU =$[P(ۣ꟭ӳPd_uzZ0⁠:v.Q J/^mX1kDd//]эt~{oY{M~13k$+_lFc"=M\mbN+-tja+%fѢ +U7x ɒ{~fWgJmk7ɵd5cỗɉ'ef9|@,@I2  7Yl|%A,hAFr,Z8O~G"u^P=U+bQzT6 I 2/vs|ǷnLz4fNGsi[Ⱦ^5 ЎlZ7xN [\i$ J`yK7R3a mI%*+(+/b\FM`z(UM+s6ƦmM HIT#RDJYqjE<Ş X-ƴ"ٱ%u1^J^ϸ8 Wv~iBXu/Q:aʒa3.(( +O(#Y.]~B{(couj8)CꙖrOM\?~lM YAoϮx¡ӃmeXv?ruGYlmG2a Q]=;vtzj}Yw[Me vf@eD3.ԙEdbYEYWFV祻8dD} !F4>Һ܌T*l{ ="2 PvAl[Wo`!ȢQ^K{<Ț:B{y&e,rslVedC\ 4\n 5K>N^zDdzpnֲcRΨm7d$=upqoSlo|mvb'#|&=B/v%dcOېC妆bJIyTn mMHP vȎh)0RH6JU3.!sTEGq>phYui֮L"lpXYrgP{Xp+_qQ BqUpTV?%WÄ_YWcioUY1dMѺR\^9߼v&κ]1sT_hXOB\ݷ_̩̺X|;=,wHAX2 UkULxZd,KۡD5n^N8 b+!!!!!!!!0iA[[-@%jyqkU %Aܗ{? {NKz{"ppy9wg({<$)C+7vq4)'*LE1O.S}]"ӈ>}UW~ Il=VzZWAUdK/ry凩۪8dc6JnH fW΃V ÈydLYw%\v+q~bqԮO2oϢ,^rgЩ̭H:R.?1&%SGd 48@~Dm\95 |.h!,,bUaleVx xVkʆN'yڑ]Lc{5{&urH'5\ƶ'\Z1pg,u 'ӡs3_lZtL͕ԩ8Zvr5wޔCndcR]l9G%O-JĥGN9a ɭp, hcM3Xx`p ?״¹`C2ۻD$䐨KXUjAV$17.T?lP qcӭRt|eTauhEj`Tʷo8%^:oq4>"W&EJ]kS#$E 璴c|.s >ԛ 0f#][ 0lЖ*EP%)c1@qbώŪN_Dm"a-:lF^ <+|cn,h!1k1~+=e90OB﬙Ͻa~GuP^j+tᛏc)؀+o߁x<>ugF]Gf YH,9oO] ; Rƒ-><9%9^Y`g  ˷!kկ5Y8@g~p+ ?vs쎊 ߾DzcqR'Z{ϲב}=}BBoue@yrkL(<&!1r />r7"=ѥ^&`Bfn_~701E4NLa\ }FC99VfʤQ _ᠪ^rN#ND[Tq%+~w9fYXkqrlBbuYqLK)03 A!XF=0i/0EL'=MbU_6; 9Sde+\,imIEP!!OȴwXp߃j,٢ ET#PΔ wճۊ%Z] 4-%m|p$\i7Rx XΊJ&m]q+]Jx;0A;L +VNv7EAm'G!OhtԖ℣u5m2;VVW܏2BX'Yq 53k6v½S&.zֵ$n=WVzX</9լΔkڲx7lPP+߮'Ysad oy+d}=Lx{~~eΤ+~4  ,]p3VR[!}/$秞IK+IO֛~9&p S_:|{Pն<KNGD^Np]Y'~F8~uLHO75U8eФՊ3]ȪS-uxZ sGX^Xk[jX졜]+SYL@Ɋ5sM/KνQ/}"N;B4c Y{Lđ E|UgIxl]Q{Y` *eӢ @[%s'!YֽO}M@reb_KOYw~/ke}!V#E.Z֔,ר772e_o`S],VFZqal0}s b_MJDV? K6jE<5xV8tLm RBec*|W\4]F%{vX/S)U/Y_Usy"~^V|8]ܒ `ɏ9}N}\YLHwGC}'Nb/u8wשԲ oz'dJ2\b^I@^R씘WMONX;gbdŃ0Nll[tvUtawE.dSH*.*" j(;N"RG#I4ޱ[9ٖx'0Q<%zC&߹j{IqKv:n-O-ʺwK2uXw;1mߧZڼzroߗ8(5*|ԔM^&,_VZbmCEZ+I6ne)m`SX2Ϻe>={]F/@K+i/ ZaԫeDv}U )|x[F۰ gZV=$;ڭe zeA.-h0^\MBȋZm]3^f,cݗ 痎|ϬeyKq"\$@t]K g{ qoQP™k+x L]ХArErJ@z$YE&iqnV̜Htt>"؋S48.L>˟J ≮"Moԋ%jRl;\coVwy}*ҹ ۋsSĜ Kյ_|StPl#D&Ѵ0}xԫh3ߟAqxͣӥԇ% Dvfܜdy7_հ*SxʪTNUfVB5?1պnf m1νiyiaLh>d.kᅵ~9O9FMbHޅf>,e7)7/{9תCˑ{M_"GbfG\GR{hn=U.9ye|~j|Va/kKKLSS2~V"i'?/o_fQNvI [*CIk kۤ`5V 6>˥1٫Z6zt@Ism?WNaLa1EYҋjZ+#hoe:eaCknګv"}ȉ}оkWѳGѥ+ߘ=ȴѠ7fw:$Ը&NxWvW?&ugNnRs牉QTs xb=jÐԒp$k&㾟H(ݼs'WG^=W3:si[ x؊cɱex ċ^8UhFLJ.$(I~ځ)Cϡےd11zC }wEρlY[(}_n!1ٌ% ,Xb+u&^sz]ffuLYVLC.Hvﱶ2]nO{ɁP?D+FsYe*;al쇜NߜAjēiX+I;9G<%j҄d+^P=y /S>cbzM{ >Akj\ Z;Sʽ U T<oCBe^(3d Xb)oi)0Rp0T*q 1. rK[(wqHqyVc=ƤK3@%KYT%sũDGP[1:_v۴2L⤣A@a6\xL4ɨ&$}7q};CGRX42QwuxQ~^ܕi% :ӾsE";e* W-^ZJ\b%"P+.*Q8*zMԱe!7ݤoOJ&b6RĚe{9>dAx(kB NrD@J*9RUK-!!!!!!!!PfSp5rpppppppTMLblX dSk]7vaZQB{}0r:`u)}.@`NDwg(C<$HqӯnGWDrk}mV lYq%'W ˭ LN,*SyNld졏F-fңֺ:ظ4@gبJ%7]i+?LV%PrAb5Chξ"ל ǩ׭}4U?Û9D};r݃X.Z0 9^_6T &sk3c?3{>pgZ[rWh:S2^6ݵh6^SfK~k{x}h:й]K;- aF8:kQ܅ZP3 XXs?*n-JĥGNa ɭYT2U]mr"rH#K{Iv̍1{/{MU& dAXwm1 7dGHomN:f&UTRXЫ(XY ` >vƀNpśp9x|+ƌB[q"R]kS#QنsIgT_{c,;O(Eʢƌ@c{t t lЖ*EP%k%Z@qbώŪN_D%,tsS,ocٱ7|R'˚i]lh/Vv]p%A-M'E>,LdI@.ȼܥڱ`E8`l/a0i#;x==?8P*KsЩ7o>MW ^7}7:v'<1%%12:Jޓ5>z? IH ;5"l)~GR^(mw#JC&}G8bOy57w?04̯T7g2A. v݈DGz9@'x5a3_ ײA +ĪɬȬu u^P`Slȓ=*6[S\E C"=!:`yKyWas7[YZYd3u?ҰœŢJR;."juвzwk GY>hmNIH·&[J3^u.ָb.HBY栝 RUxE{VNVXS5K57q>DK:yf =ߊ fs/;lOLo>aOFyۯScr',^͈'t& ނ骕{u&]cS^g<-4 ~W&)E7RdYJX[Y,MMy)/r=(_mR/9ZssU:&LTdWgl.e' -ɰG=fYƺ/'gѓ3}÷}f-[u̳^{/' ogꯋ7^~:*$bLߴI :A:Г0&^$20:Z=4QOݨOSHYě 'uRd"kTo;7_M|/)y~iS֭ȯ['E~2#³<ݫ$yI]pUue][zqS9xtE7y)JJ55eDgӼySu`6_l u۱s).|z|ǝܕN.C]5ijrqZ13I0mQL+?re;m~rei3q̻",c9͛ԫ]Ր6\^a˒EIe+0e|8 G Z OQ[FΞc_CӆiԳ= Ņ(Wk}/7w|եTưFlΫ$?+X24-'։g>I/>>,5X,`$+{ߘX8`[zm>zɘ9I^E_V![>H yª`p0SΨ3g sZ,Lg E:=eU9}ݑ7o^]H?Ӎlޤd9/|&s@/8e#&qv0qp:,NR]bdk^~Y⳥XRcON7"έ²lB0aQ͆.ŴZ(zC](L YOB.1/IV6߰y{mLHۜ"K޾+䧆i{]>uߚ,Y(~3J`Kp0l?R#q6(NYeMmn:Y'L=Ź&'*Zvsh0{n!p.t<@A";3InNbY(`cVe't?& UfV$S"Դ'% y8Ƨ&OBC$㲉lp){B1򉚏VpőA_V||n!:bu2x^ F#!P^ȑ1G׊Ybi!CsY!3[/ĊAJLZ,{vziVζB]];^ڽ45gWj'jdeM9۪ a xt*P4xFd]e1{*NZqx'n,'4!E.=\uC`l" ktصYE.n2ٻ(.Q((EQ hDc1$1j]D cb!M(^cwf/vf^O{;录dMMk+e4@#NzWedYÒհ(\5`?Ӂ_u?~pl 7-5r:ؾVJڌ&XO؉D 屗 3lu`j/ uĠ/%)}\ {BDw-뷁ip"xFԹ}o%p_q`[L;rNj8N|=k*#O|%8x ٹ^2*Y{~c~J|ʎ vv\&e n\{Àeg՘l"1/0WN("=#Է~}]:`Ȟc)}Xp ͼc=a#Iϯ>Xt8#EF͑EN"rmj=uK~/v> \4C~_ (_+8n >FJ͕R\o iĀf~ S$4z4RH3kŠ +fn%Koi2/5,~BuPc(<7Rx/zZ4Lb9OnY9O :I\MM%)+ "='vW߈\Ej7 {A|!H-5 7Ac3TOaqebCE1~ }9DIR `8bPjN>.!1E_teAWr\NISr+qQVTqme7cN϶3 {[㌀NdK,״#^h24;%@V ,-I{BL$DI}Ի-v,U^= )4z-{/ԈPB@w' Уe6;)WWEGyi84Vzx'VвS`x|B|\l\c5Q*^jPk'`0F#x' c1림$NL1F#`0 F[ `0F#Pݧ:V#`0FNgX *L{ˆqR`pZ(?qS]ޝg}@͵t~F;p"/Lht0AF,@EVO5(w/!e5h"jќ|\hFd]cPǟ2Բu:NWH49h͹K/]Sa~಑ELynǯT]ޣZ[-|]l"9vWJU`v;'xEvuYOsRvکC ay WO磹U:T7ϏM*OWV|PT v`okKF} ʈ=’T[KRrQ_1Wʌ2Yef8ՋݒU޳殗rm'nbk[4tz?xh~i hz@sF Gho/\ dfpm` N_Ё>\ aW?Vp!Gs:_ǼBV)N0ut5BD݆;OJ -%GPXK *f zmoOU3ͼ2¾%Y6Iyl%5n¢ IDAT!RbcP&v?#*hukr6jܾ;BSܮAWN4Szl\&IwLlկCh -^ɂ%"%<ONNK'NrZAl 0r1J5n%:QR:NO+jfxx+"#V0 ʊPE ŗ"O?<rqYta~IVW𬫳-Tނ~l7$'~#Ƕw%-r: &f_Tʹ2ДJ5䨫6 жfw_ZJ~:VHmR [.-ɮ=~G蓅r4= UI$ 鱎znf{m0[q0mTӗߺohV%7n.~xbvAQw\Dj%kب9eof v>dx$G6ON4k,a,1@ , %,эI,W80+00dQ*f9:,yZM'4 5$V~!7h~{>TdC[ˬx])ٖϩ]iq2- ?<}wU]Шg]9~5b/>r vQs\ ~_z꫊Sb^IUa{FX"Wfm4#uRyOym|س#R%ndIv*~N' }S]\%.x5OdAOgq_.)ͼ}MXA^F2든j)K- -eF֎lc[;ݼ{AMѓ:]MUxQT1 =ND^xKU1oJ6&niv;g̺[~/6/=L&*>@]oޚ~)qWW }SMْ{+ƿ1 7g$>茜:a ?R HG/yk*A>[0>! DPTQH@Nk4ajDM&G_ueHX,v _9_6V/_ysIW;;9;8ͺ{ߕnbVgIp U )+aw\18;Bkf.R,uI lS wOdrĕ[n\Ok~iYV+H^*иqYPvo1`ӌY2&ӁAV+MMv*H|\l99X\FZSV*= UG- 1~rԅ32iV ț(Z~tEG^ZjkљӲ=3s~Xy5lē:wعۀO)zQڼrqDAA2\86n,Fe`Ke0ԔA WѨ\tnC`CrV*޺* B-eow0n&K:k1ht\F<3 ::6ZbWVVHfz>:| Dta@qz#0FS?lwv2VǓJ RJfZdmcz/]ޟ1Jx~fl%9 2#lÒʹ[riϜbەe}hֺltNjyKc-$U ´cd2X˻U碁ZNύDv& 1̍UGiѰZlI5F! |!0K<)Bi~KSĿfz "ґ< Fo^濪2:b Pſt8rq6\&S-c]̮{V k?);o@WNLmG5kޫ}1Bg-9cyFr6k*KQrn GQ9L.Z)vqqq=Sro Uf9m?lپ N v=+#q<8#<@%aQ0WF,؏oot ^ROܹv`h jQY<p]70JԼPI;HA:dnL텖!$߾ rOhTBvtye60 :Nψ:פּnliGx ݉xAR'nG\~ի)2lԦˏD~%y).lJۈ"~mk~J|ʎ ]Y;D9i\7a2jLHd¼{M-z*EDO # l3B}%C'KÂcMh aUxQ`Yz:2K鳎E3]9_$/'_<۳X^KX#O%2ͦ]ޣi2;h%]%Gx;/AES63hD×I A׆fb@ Gc6?o)|d#e hL #\mg%gq,{a#2mR-htxω;pUOoى" A|!H-5 p͐SAV>]A@Ë or'I-H @n8Tsq 14.窐+ *0'ϗrҝNJϪՕqѸDM+Y%41gqF@JF,ߩ*3״@ՌyUi|@sE[͵f\=a{&~_swhVTw;& nn=rjqj/hR?=p-AI:W1;DȰ~q |}B F#~!Ggekk x_ZLQ-q?'?]e"[&6bY+~ q=`0F#B@lSI+P1F#`0h&F#`04g5Y1F#`ΰz! FUps' P6vd _6u߾䷢fpZ(yqHq=軝HTmAmo$юܻqVx;&SୈAƸA>F@b\1eƜ-C&Lg '.譎2Z0nPR^}b\P&rDžoDe/8@ezBLe=6޻!n-uU1Z/JD4K3x&$M'nCq A@9R- K6kJT V2_rhaN]:{rvb'[]T?EaYrui*<1 oHN>";MQ{뙌9%1F4֍J LeؖfL𬫳kE@RX@kYlyW"wm`rm&E=(0 ʊVL}+9UvQ4hHɨY AەjV Mق >=YLnǒVb\x `)9:V"pƞL~NV3d~b=&DG%/}-i6*SB%*%Y3jF^LD&pz㥞m3[q0ȥЋFxZ~q(8td:GysD;My0ͧ_~||`;a Cc.Nk ewMҬhMHmSxHh/%mCso\7ua?<1Rq̀%̟6‚E%Q<1j^t&l"J,Gǀ%Oě=qaJvOWy`[y-ȁGUҫrӓT4nlyYdջͰN9m[q.=)kJfEW}FT6Qst4O옧o9EfvpM. =ƃ`!&d(ta&PpH*"@u_<5܂ U*dPvWɽF`j3?ޗS+u#b ܠ翾LP.MUgrzK[75::b k*˫k+J*$]S-d0R2U`R4[ڞW ٝM,(=Ѭi4m^^ -#+yEɫ^7F6yBrV8 ^բ$0H ERvXJ}V(2zc*jz!\ڷ,M}Uq2sovOGǗS~2*H|\l9F.#KwK[$ /%&+i6**#if*٣^]`-[l<_JGmnwe}ߍ=)Yה T$662*:8@gF 2Mى4lB/Eld忕O]_ $ `]2Y7[q un׆XWL8jA脎RkC,ٖsSS%h\u 4Sp oa80~8f@HRJfq[JHDǻ,bqSw\|e^?ԛYf#Mb}w%fJF =ۦ]}[bfA<hv_Ǝ=^&t_}3%gh 5ḘQ~Q--hY,zaeĆ mK_A T(˻5qO dJkVJn+}(7ׯ~Ĩ JY _O,}}U/NX:g\6 d6v݈yzCMKJAZVlHUVĥoMJ K3-[_Ժt<5>E h&/Hxg{(Ҹt/M }ڪϠA'{23[.:YhaѴ^$ O)^+yTxm5`.|us/,[R?WQ$jk} *"]ȩ=;€$>_(+a~($X|a~7!CڥW'3}n{x~fl%9 OK8rdg3N3?]L~.s{X;H"F3[iyR<\#n*[rѬ!EF[[GETWa&UOkX$^ϙE&9jגW.%5kYYQ KhY rsla󌔧,L!׫—>>|4෫f9x煊_66 ķi{;4SaNzi4 4iTxAɐr0 a{VZKU:%Z.@+ְd5l9 dMd؏oot ) ˧:֟F?s o :)ˁg`ؾ&[Q#I2،&XO$ډD bn% [a*ڋXN/E%})q&KgWLrFZ7O9TZo2ؚ8` ?/&lBe--~ KJ?Cj/2 \il>v Ϫ8hm긽[Nj/k$|+?wp뗞;Vq!#&Ϧw=.OSy8]}1 o)kliGxj3pwcU9y~tKޟ#|:Jב[]5YOye(SPSO˭l~Z_ 1Rf%f=Q{(ߍȘ{?[]jIPdkS#1W IDATFLW_DZ}<{24& y;HnH3YUK7hy@/ e^ʚ Κح/򿛂b G';d? 3؟WGƍ\-K(yYⳢ4݁&IJDMtRh(Aɑj0gtj+<"(¹~`_aX=3| v/fs(X ]`HQK#ȵe᫩N]2^L#|| |5|?XMPA_JR{ NB,`'+s9j' 2|\(jh 0I ڶnƜmg@l2غgDmtR:bAe>%0ފxsR޳o_ k9bhYNg3})A8^&<"_X74qj2dE@w'Уe6;)Wʅm\Aj[y'';.SYà5Vrc)b/zVz*bZZBKвS`x|B|\l\c5Q*ui7=F1nY WU#k%3ǵ 05F#`0@3BV/ n`AʦL{&4V`L0h6QB@{wH!:㌼ mo${x+b 9xGGk귱2!OCA_wfY)1>(ȶ:hUC"JyKqC ȷZb{'.|#.{6,c9ۂx?6x딎R,W>y N6Q^w hۮ R~TH L0ypuVRKNIw` Zqfr!(Zb֏q=pKF .7`[0\z$lxQ,woWx򨙣P${fJӕ?skuZ|#dU7p l0-Hd" R#.ws@X2Uf^8u_qKMG6:>ĴRa{֍ek6,{P}P+g3X|G a4~r/ \4vmۗOD?isNm;$J&t5d`okKF} PHJ`{rN w_H-9%ݡϟz>(P5T%&_S {2$0"a\ @XvR '* I6+J14vLFR 'C4J"D&CbdUp,\mwˏ=]Z6t"Q+cڶo{|sКs8f_##F:7BL0x;v-1y'}ݰM2 wL1GZRStݱGƨC{עSTFlA$ .g,LkNx6J9ɏBU{;Fe rcǤםt[~bҤV2KH1 `]Ӳ 3-TX٠E'Cyh =BnX^A6>*}CY:10.,b;i;>Ƶ2t ɉCV5~z&cNe n-ia}=9`uC)8YQWWm:smZLQ9{cӃf[0+alWI& JPw Ϻ:[CMLJ6_+̶w%-r: &f_Tʹ2ƞAYԊe%tV)%ՙd9%Q8yLy+Yz!C3UvQ4i騼4@&"ڦJ! d͐Z v!y9224uP<dh:^깙lafD!Cڪl4RKKS 5/+f e]@O4?(*#ddT}İ3ͥUi0lFG41:tjl4&iYY`c,,XJXXp aNW`a&Trt XN Ɖ qI^RvkHdCn#0 d-|*1J5sj5KBYVhdC:bLM߱sTm|س##PV,/3g5\bWoGdW .^`ƑRȉ|]Y\T~YanTWj@UO$oiԽ/3*h<[N[-ȹi VhXֽKI]>QPɿYevzҴy*BZ?ힵo5~4ys<;LE[=Mw`:OSw>wˉL?ڱFn*_+_rԥ$/ ۾޳7ªRɄѤj<ڵ-H3yyOkS</*V ʫR)(UKc լ/+z bv2;v6Eu: |Qyi`2e[٘;-98Wn7?oW[L2+^WJMMſ,]})4Vߣ7orH1|> 9et:0(8@M ^PO |9a|lwC@LR*|:5-3B!es篤{M=_ Ciɱ}9?B15o* ~+ RTUxv+Wz~SYc^+-ÍHNaMeyoMuE k AQ~[ cq1QRҪ50Z43t)FzWT4[XfDž͖;T ٝM,(nHàiiYV+H^*и1UhKuKfQ^§U˫[^5 hʣ(JTU|aTxxѢx#VGd[ѧxɿ*|P`* {O^Jn}i\)wiːf-8܋n-3|Թy^bu홙Ø[&`V@S4UWzr_i-1}ו4dsOcpa!fiԈN*ݸfuQV(1jʠAbKԈ\[7LI$J)UFTFkBXVF':dIK/u'&.CHAe6r$F-1S2b9.6…m_[׎4=:ұ#F\t6~H {MEHryuQދ:kvJo8gnԔ [#3QDAfm %Ha6`x`"}VKPYaA[CzXR0RP^hf5E%#H&< 4mȓ%,ͼdѽ7ZGvY(J9dL$HUKR׌4H!brfsY`v.c6ĢMgm)WGNxrtS8mL[gcV vu&9 mEӠzˌ2TbOb5DNG=m VI\h {LzvЅI |P4WM9PI`jn@0[O*1K˿+kҫUWty+Ѧg<Zs 4P@D>ycn /袇ˑԴ;\/`tu4w-/_>w[$2j$! |!0݋n6Hf6ѬW/\b |ckdaeA~io6huRyT ,zdrt.YԅCQ*ӋT\x䠁m4y*1,9cyFr[-^YU6&xY)ҸR" a{VZKV)w6'{G/2*R]5uҒ-^Hը:d@3-C\3'bcl䠿'o2p-G\`?ӁxSw@#_u?~pl 7-tP\ }7LR5/Tf4z3AN$$g[tc/Af( V^JvzA/*1KIʿ}4z˺`:u<#uGo[ 2>.dy]xU-%J1 O>1VK춓$$,Cn7DbEgYz:2K鳎E]CJ&sֆEn軝H1RLuo f҇wip䉴?W]ٴKu>.ΓjzQ* `[Ͽk_{V&݁cMh mT!G~Ŵ#xDŽG<ރ׮ﱌ]~;pHx/~x~J|澊Ժ6\ 0+fn%Ko1MFz . x<dSHHPx_jiĠaLMOB*<-A)iuP!O#< #\O{aiUJ )4kKR"*T4Bei6B8ᆉ-,z%Q-Ǭ |^Zر oaGWqqV/יI! wઞ߈\Ezi7 "W2׃xapan.%R a;-c3TOaqe@bCE1~ 6/.!@>\ݽCW `: I͆rҝNl1[5w"#f @i25`B[ͪ:4i:10\oX%KhYNg\*Z,qmuKBe;#cNXQa2!6؈@pu75Ȫ+FFک,fޓ+DՈ?."sXpյCdrԍL8A(8/=izz'܀*~6=kQhF ni^,PhI ّ!3,`”`Hd`ϸ*3m ؜(FDpҴ/c9?`JyQ[E9AJy |!bRO\0V.%~SzŽmRxcUޛ7[OaubJ5*ɲ{2l&ny=9'ƻ/_Q9Ꞃ[Gs WŔ]lT=x\À]S<_N|YsՍjR{_ C16O4q%c=g]/kdbǞϋef6礶4SyDh DX&tHs%L/ L[#DKP :0' @7l,.(#G P~u  6y*w|Ba-&&Jqٶ !"O,ne"^[#6w_-M [^w₩1y'}d2 ?.^$Z06[9Ă/p851~]s>'qzs3 KwwS /եGGC zX#q~n-izD ݱGƨC{K"k(eCyy[3t!R[ޓ%9.+:hݜ?pw\Q`-L=Pz2Ľ$>'7M*Lӌ6Th59J*p/֢l`,dkT'9ڂw?JL'̴,Ȝ`t7!ԊyN/>A!)1QXq~BLZ'D-1"Q>r-'N’L2#dHҠQS0@ r񟫣A#  zь6A1hN+uz ɹCg4+U:vA>ͤ[&ZVgK?ro-jm^CeAKw0fT0Q{"-~ה2+uZǛX&n:ckv~&kd K3-l;pet9+]mTC\O@5j[UAOdf%6t4bJ9/L.;,reׅ;:|C慯n.!&7`F?f~2fC,m۱"3|~osѧaPǎe6r$Fk&khDy8΃PV;X3 @%|tX ė $ h@1as݀a~:TbЗV2CڥW'3}n{WM' x3c3L].i?^E#1kmdN\Khw= [2 :m¡\—\gkdm@Q*̿شhX"ͤC^z Fo^濪2:bZί0M ʫZ,i޽d/)u`z6D# xyrUK,ge2u;z~=2D=/Y~&nN>>|4෫f9xEׄ 7Ƕzl?HyZa+hFehcHVAzbu;Om.Γ'Nj<˸ޫ1"i򒑏 sq2 ,=%Lzx~YǢΟ~El~J|澒cB4h-ETK|=kdيqzP_{E4=Vu77|lT1MąoѴyK8?w]$KDmd=H{JޓKf[^f/A;?zGax7}fQ)9@ Azxcܚ5mە1# /t{/1WlTV[I_q[3ͦ^.".jK~sJ0'>Xo/4IbDo0M@Omz G#Npтo׃xapan.%R a;M, J| (*!@}9DIR `8[pTsq 14.媐+ kttg*`Tɭdy6bN0fO L UMۼ:8##ԳW-SJ}9 zb%^0R$ X)|+;QC bY+~ .ך-`0F#h c t!ьĢa0F#`4E[ɚ"a0F#|k`03^e` &TR)/,×M(@e +B-vU/?}1sk Ѻ*6՜M %a+A5:-vL6S8w~HdRaϸ*u ؼXMV2xBz4`MΦb44BB "MTV~r/ \4vmۗ'¤VKe#?ʫ܎_3I,>oyMLB K[ 75%ee(%~[cҘH-Ϗ}y1L?zՓ{hn@T|3C-j1 ~9v̏_NOWWI#@0<n)8 ^ycד< -?vL7w 38 i67He+d}? v&8.BQ c-qaR80\ Ё;*fî> jVҢ>ZW`U4+&@g3B-}Pn-Ҷ ߇sVLӿ4ˁ!]n$A LNf4 @)C rˌNiB,6=.֒I˶EMLψ;[>5h-na)v>QEMWJmW([5}i0|ŤJoϱʶHTLq>{LwF KJsNZEZ]_8l殈̼bAFTkIk$hq 7ZrJ2^qo%7%0O6Gi1h`c{R7Is0;5(5v~r-~wfj>ZB)g_K&R"5#Ж$;~1RA{5iǭFnbʓwXܤ+|-}vޕ$ %o.b}lSsEIp_&-"PEbai>?"x!~~.E! A‘ D.ZOwd/)y!.|nz-d+$̓t">2D^tށM6WsN/%`ӓ0'5VvX3̚L`×1Nf`)m2n_mBdpSqwۍb/x -^-b!<OMۖNw1G+T2"/`j6jGI->ƺ$%&N@*ʥ̱S! '$le2 l\8pC31B,@bJ. .}eCuv. _ c]BιK2pffnf-d t3`1NSgLjeh0h=y+0j^v: &q|pr΅v{i6@zv<7c;voj~L[ፆ\a͟Fpچ&: E% ûk6\-}!I#F{mf^+k.!7Odg=ڷgQWtmS(V2p솎7d3XF^AQ%;z)LD' SɸTJq1!`C!uoqR SԵtf0\nAĪL8=\L-1-d1$E@ENNs2Y\,Ȭd2]&n 5M2)?d"Hjr=F.\k3Q&' $ Ngik\c."6k@XK+XL&x~Mfa!4"$Ef8$'dCV(Lq#0"i6UIl  ƈId{Ed eQlb[;k2~& TcGrK~S;;Imٴ 5y/W{M4Rᶤ0vFע}; ^k l_9SFIy J -KhQ{v#xWr۟Ok_xz*)V'n(^F_̫ilxvfo+o.E"AI5 %e $EuYÇ_VV,P'eIy^ٝTXu?XË RɈ˸NcUbahϗU!3 wNc,聤A"s])‚ؿL'b|4C^HpuZ(7܄#DG+ n-'0y=Ghk>N1/o__fXM;T*')2o9Zwy ;K:;)"O̹~qa^WzL-aWXȔoFcr=m"`A=PSb?\;_Setm| h /_mv[:¼0 W2kZL]V, pusC,9WݗP#5T"̱oM;!x^P irD8$K%W`S3I}"TWN'7%B6mgվKN*vQ]e#Gsmq@9m`eY+,lMdLMOO˲c\>\}ˍBh !sHBm=Ə8*ioWU5"]Sd*]fig! e7ֵyb}}9m#:Æ_|ͮZ^$V">[zGZ]}->սiYTSgp8O+Sznl7d3O9zr$ˤ.豑wT}a1uifBy'Z\>lhG.mR_*Laѓ;La[-6ĺKzo򍦚CtE \Չw(`v_KMWD@"/^hҳQ BﵫFsz'MkĶDB66Ѯ{R4ˉT>NIH̋uEyEOvH3,LrxL{V+XЩ=$'YgMG9^t"Kr!2w#.m%J I %th0W.k$A"HEZOBR;yS}6|ȀMFjkwG%LN'^@///{Ȝп,"y/Iԥ}JL_8nH \nlt1+,\G/w7l ֟F}hd~Y:ؾ$,B#'zbY)uZ} cVX &V+"b*a.oh$DdqHR RL{|\0HzS!D Ku\ĸ@/DS>TqA ?qG27#B79Q,n*dYt% k +lی&4D05"aQw2/qV5F IDATG{Ȣ6^ۭ,X|CKep&C"Ȍ=]&DA]8Q9HP͡l!A-d#vz,'2_\nqArhCf;A$ 37C8X:e(ƅE_n#RQXav2ǻdqӦDm6lk/E+|w39.E# A‘M|#XLnڻ?dfO0LH<0Mc$W^TG &Ps夰8,\k[i-NH*@يg R ;N#l8-~R G؅M W|Xbf(J_?%Mf'(! ;9t])(Ɋ 0PQ Am#+ A(2"ab*qi PE?ypH@1 ae 1$H. 3)W=$vC9[x |;҅K/@ m>^,C{j7r'(bxYoksjtc Z63g-kݱ&ڪs(,{5q˽WpZ75Ӵh*ݺѲW7.-G[|_=Wte{@y۰5`@ 3}"-r[΅ e3и{xOEBԜ E &G }W@I.BaIq۔w ňk4444444442~Ϋ}Pв @[8зhhhhhhhh>rpG(-> V/Cna - ~B“6dI44S:6Wz"4ڈBXeK_5Ԝ~44)^WbCqwT- tSʶzظM6[-6ާ.AEWpBɳ,S9yXAjV$E$'{\8Uazl>rBPEXo+"f4IˤŒ &׃eV &0}un um Ů+e9),P)2:"B7u5"b(^2]Eq)@M+awjKrJCS7 t5KdԬLUl 6 ZՅ>;LTrxlz,)$߈YI_*,J}WV2x"wuRˆ{\0^աOW~2nG}9чXo"#Jܻp2+eY>Dy7.瑐h"WX7s_pml_\n띋V}tSocce)joyNBL^u뻊?g#^pd@YEPV/"H8 /Ҍf9Ѡ /`-E+oz"D-fG;^_cvTr t&`g3B(Pp2T'qP1vQDJ4qҲo,xb;¤ *| n4dwqLxGo\-,*nbzFq!U8HہdMXHkInYF9]_L[_`R^;Ԑolx7=Ζau<ŀeG2ee1jh&Vǯnekd&AByh/?^CB6P֌};o ~OM xMߗ2@i1h`co{KT\K70;5(5v~r-~wfj~ ]Ns-29dCWܯ>FJ`{D9C0^֑RCf_+߻p]IyłM -tLr"O] $VsJ@u2Š'd TInҕM$er6]Ɲ &W1" {>J.IM᧡< ~?&T8]}v%+ª.A!sC2(ȋn<%y>"x\-Y C!A@ܶtí|xֈ<_!UAT= K_:su]v8fygr΍W€$)Rz þ?U?:/k9-)T 7uHxs?CF`s&aռtL 3yj7%$c0j͠OlAy%MbaVH^϶I"''@s#y(1v2}}b9]I=$  WPTI^d.0/L :V2A)o/MV`Z=[.+̻ܴ Mtp 34qh컇Dͩӵ8r/LfSҵG<[&4q =;d75?|-GQZ*"6\dHK)0ƲNZ`0䷄?T޸3 K,]#e󧁣!`q,fnT'Ʊ\ cUœdàMX-r,'cwZe ys0> oATKJnw_CkˢJKv:d3M<|kPE|+'~fH ڒeyK.YYZ!2C].x%j)}{YS_d'FTBTw;D/޾37F+#ҳ4e?V*y3y\?N 9]LEE%kN2%Rԗ]c1mvw.v̬9G.6(%5n+2Ӟ vxX^aҋ@ͩnTQ&d'mՀЫ[fۚs,fV"-=Is(i-mġȀ_h!w xy"wG(ǂʋVRؖ]!q"N¶ L@U20u%ۗO_B52/naKt܂QaTjQR@TWNvܻ,mPq:F+j_ %'O\QHT7S_saHu刿(z²5lS{SP]*R͇J 6XM=Y &O_( -'odՈtM\ǂYGK,9Wg[2R\JIE>YMy`XSDObF9)~9yR.mZqkpt}g~5jC*Zz,&0wOi* #'}Cw}3ώP:x tb,>DB$+3F[_lT hXHh?win ZJf9xhi5|}ws4?4ǒв8 bȩ|?cS\5G]\&)"~ QYp Yș\ӫ^pH͢cLާ_ϷI=~XŬ@Zm>?p>GרgwXܺ*`jjfO69 p,7|Rs:nͷE^Фg(Y5^^^kȜп,"Fؖ=zQ7&uOʟ1F9њ7?S֢{;`Qg—XTA)5iGvo?|̐}oնÆx*A Ҳ^ W rb zDO3l \.:ZҬG|6&#_cn(iɕD̠Z;N]ڧjf)_hm;@jr 0{6a7 uPOܾ vBdM3xoa*ȈB5GbMOĈY ne([a*}B~Es[Dlג1.PP=yC)_ey~u"Q|A73NH#DXF xp^N\ISk:Hp_nx脜0;;lΝ席:dFα|_Lv m\yВ~M1l55бћҳ+{C $d1zx"t2ams BUE 0]YJ!9O1z-=X$,-J~)cGYaV/AJ$I<_oZ$8o0ͽ9#R"D K)Y S6PNe%/tb5kyQ_K6Hc( u$'K-/i8qss*FQZ"\A8(InrRXV/]zm2=1 I>9Z9 K}%.8x3pدoZM8T_z! .L 5XiE: ~+}tR#I[L̆0$iR&-Ȁ c@a ZS U47T'g**v2n]NWnP'WJ%7յ5G>;3s">`}:%$'yRT4o& K@M#q!qqKZU{47$[g.zdx[暖#JΠ|-g^ECޣLƒ{Z@P#ĚgJפ][Q--=\E oۢ+FV TTaЦxk»lolOglK )ki(J ÕSW|RfOqR不 aCGFzPO( TA@~-~G#ԡo|H c>l@hhhhhhhhhhdC(#@=rM7 V/Ct6na- ~B“6d4}zQ#qO5"AW!6")EB8{!fvs#/egyWzz}HݧEw $?n/+N dS!`*XM**c㊟4Qgx"i] Pe=,>t1ƅ7G&B5, uLKD(ȝަQU C?A#0AMl ^2u` XvQsPd,=(G{_?ʳ#$Ji$8Ґ oâ( (Eڭ8r@fol_ 6GR4$֫i ƄV%QЌnDK%E:@95j5Y?_Vjl~ v/Jy|kMUCE7!#PÃ0d2`F !2il3LKn+W$M>x ~9hTT&+q ;~)H}H kozmMC /ؙL El#L g&™P!  b6l, a%e~fh^ECQ)0it6J!$߂J6pk)}Jk$UQQze¢b&gĝ -ښw!ֆ+%ܜ#4tdB/N_}Tp0x׏9%E-<,/$vO$ m x"f~Ei1h`co{0;5(5v~r-~wfj> !,煮kSrA ŀeG2eemPJrak70&H.^iu~aiYinܩ ȟ!p8~z…sK[Kw:2T:Cf_7bʓwXjܤ+|-S$(Cp+Raq #}1)-m^. "cp_^JK5$:"C\$q):{(-YN/%` sRSih!{Ϭ~>9|xiuNf`h2*I9G,]B#@ M< h72Wz} \-b!<OMۖNw1G+T2"/`j6j{\ư%oZVrPpKg.n,LuN}CR,aY#\TS2LËu]r9#Вq_K9;Ϲ;L>[jD'egQk}`+QGfYmf^+Tk\ѵ֔d mofJw44[Zrw5/qh;8>89L+J+5_]I?Tb1FHfbplMi+٠¬O϶IL!<8_A bl: nxi3kLVȭbaQ؂n,&i1 Jf[O Ǜ+tV[ZBKߡ G@OGCdaXݨOc 9ɆAZ XzONVBR-M58V~a!;>D'dTXkHv ^xYXiNu`^~ǣC[>ϭrRWEWOb [fC]J+)%vs+~[ܧ_ξ-kAtbua.-'q5܌/~V/jwZ3Zcgt-ڷes)(!2KόL[>v ɮ:ä<߆K[N?oՖDT2.49um86Tĝ$O#Uxg;.Dz>ၘ놊K muje%ۜ%hlƚTsH&2=@R$nUg/l:]Ih%E)se\Pӱ*yWZuў/6/=#&ͳmyx@ y&NeɘCw~w8>wg>NJD6>rfUaK AE8iknC9r(P=PEuN h^gG(P[ Ar:C:1Y"!t<&8O]$b;z^=:Zp;-_\*M d3,vG p2En}l#+w*''pZe`ɯh?//E#*-4.|dwd 608MdTRV+n^$/7-bowvRS~F@"/^hҳGӉ3ԁm٣0>|ml]kSi[8$,Z1Ƒd}|㗊4X+qQ%z[_hѓ;La[L_M|=MLnIUTI;r>x1Ղc-> z +[jLݾF`gOR!*|)$H&yLxNm58KjF\YyOdA5^^^kȜп,"F6hwO顃)rͳLz}L=suǕ){D꩔~E-W^a1+\G/w7@vJC? >p2l  x 4u%{lwD[Uj:Ěq4$K/Ca, VhY+"b?BI{%DS>TqX ?qGrڛH*Q9=Mih%o]9T= Y[蘳 '=jsoU;Ceڎ)(WP, ^ce(ƅ䵿$E1l*9QQ7 O1z-=X$,-J~)cL$&wxDbf&xi_ۭ,,CCKE{ƚ#J Oa"A \Cc 3w}CA"]WEams R6X S6@[.]IqT}!>ģωԈEMLL簹E$29%!HRZJJ:t"",[ʍÀ‚20-'m׶+{Ԡ'9[pО쓣bİ,Gl$p$,+lی-gWwh8x3pدoq -&/$s SwCq VZk|Aa`f(J_?%Y m dRʤa (,AT}q 1FFu@.쬦>ӂ+YW{P qo%<e%S8T\,Ch:ʠR`-* /D4ՔV_ku&h4kZRl&쉽g|KGŷwhK?_8 ]{CyST?Sg⻕yj4R:Cmze`flh_1z6W_SZa U |<ViIi`:;PgY;;R}FVi(J [8E6Sψ\ʊW|||}SiJC^CJP̐Q]N#@#@#@#@#@#@#@#@#! k^Z 7FBN@44444444p`"0D{N>f*@pk/$ML³][;9/:?MB69Pdqu#\0;|5ȥE?Hp!2zNcԋ~V&1NPcN>m< UȨU]8nDK%oM-@@1 0 ܦr7Eq)%jc{o_^`d D;ԛ9Hf:eCJ=.]hUZSY"QY26*]6|VrY㤝vR4)t4ov支Y)Hա"^; QG]0tPVa.q!λq9O:oiM8$Jc[Ɯ|l[y40{]OۏcGkk9DL 6iic,/__3Ǚp!j(uIlD8s*C8 wCX %i÷_kWPT~G4:AoJ6pk)}Jk$IQQze¢b&gĝ -R&j`kCu גj -oP0.6ݮАyNLG_|^:?ilV㎬%"%/5/MϾeRV^B\f 1&WݩGGC{71̶HTLq>RO5#rdCWܯ>FJҫ{eńX"FKPA,jHd4 w,AߜINCEށ6֬dŕ_~I "O{K lONj ?_3-'O_c|ZA=5j Q}t۠!M"vX.y(.3@˷Wqf5<|E^גuí|xֈ<_~UAT= 74 K_:su]v8fyg[:2dߏ73#WvSQyqDu {smiӺ.kpd>ky ZV?*\cm!5{eQlb[;k*zߩlhgR]xEΞ=Pn/YT4")&fVp3"YޥkezڒeyK.YYZ!2{Q~"uT-PR^ ; s9I&&I'0vFע}; ^k l_9ߞ2] 2݈9g/~^*'" 9Fz>K2UI-Iy J -!f/$@>QVsnBBeFm%ZS(BnNotӜ;Xwpܙ0N[_b K ]426՛7#qy ;-$ QW jll~D qMl,MτV:W ,oF _Q*)OKLL߅L4MmMEZy"P/<7sD`}1at52iRelkαQ 2f[LdrBTJ7G6R6H")"lilAK,s%݋ b 5$Nlhmp`rw>wR//uv]{\됡lԒV^?lx|iwWӮ~om!.CCE6m?>XJ-پD~E\С2P*7l Á[;88ݲA*޺(Jl5uȽɒMڦ c2֑RKM {%zHޮ+/=lH?pF֗"FkJb撪X_kjN f}uEtӖmjox"xd*|V" QŰplid bHTSgp8O+Szn zzc Ґ)}< -NkԆTX0˹} l+g2f|0oN; ] e7)m?ڐL6!kd>gG(aS[ ArA:1Y"!/6*x4,L`q$4Iy-Iw`%3zt4w[>@;TcfhYx i3Y&}wD P\{^OPL#ϯNa' /8;fi_#`G-O,Gl$>.,+lیv3'86QaC㤇4bik"aiaVO #>θ席̌c_e"vgAlkXJq&0r#z#N/12xXߴ|2^ۭ,,CCK){8NN rң6VvSl/<0;Om.i{J6qa% #d{$`)Ȍ>͐:M\'nN{ҡC1EZWF\"t6` /L\ɕlРzA;R?zk[ir$/YfԺФ"oKbFŪ8x3pدoq-&CB/=H"PR~K:b-&NPfC ]CJ"6G6jO1.T!F.ȗpUTՅdp:-u{F 9|+YeSmZVr[0@|v^vf!63zN Iި:{&FЪL`4mZ Gm%FC@fžػ{SCe;6vDۖ>d'T7MurV=u'!C Zsm\p0ǫC-h|?Zh+}4R6Yՠ0m>G@fOqR不 aC AE_$=ڴ:4A5qMMz444444444|$D6 !χFFFFFFFFE?WO N#@#@#@#@#@#@#@#@N$^(j= 6Qe[\n텄'm@e+BMvM/xoI0oQwڈB2uE0 IDAT N_x)gqt}p,|! 3) QE(6}.=%X[BdӜY .κ8&WE5%P -U-$t BBv$yEY O|Ce[bl\#&ѲD}6Zh;m&@DP ˹>Ÿv /`ѦLCKHdzR#~)†0;|5E(#(JRph)d~# 2N(rjO`-<׵r--8n[QrՁe6`uDمnR%cAЕ+ ԴR.\iMb ).Z ,e0 GW~Y ='߱VEaEnaH-ٗg(f-5̲b'^&k-0m&81dpjY[pPo(L}zCUu~9hTUS kZxt~'voۗ}HPOS}WFjmzlxC2xدPhUyf' 㮽 *^Nݪ Socf26*]6|VrY㤝vRԬ 1?Xک67THEId=:>*6H+G8UnKsA7h9dkЊ܋:|ǁ@lD J4GN *HwIT !ܾhnVn-,Ҷ ߇sVLӿ4ˁ!]n$A LNf4 @)C rˌNiB,6=.֒IR˶EMLψ;[>5h[lC %WZKe?H[vlӑ hIVXv]q6T?`&k/1}攬ÃFK%Y('f {6rт\~Am-PL;wΜϙ3gfX1e2b\:{@qLJ趭Xqt~$Hu1tiXHrr ?1 FZzwx 5MHBE0H6T\brЊ:+àj/7[9pa0H潘wC\&!ۭ>9>";VʽV$bdN! &$V5zS".4V܌yzbD'#..mlS|׏eb^_ E?8 m4d;7CrM8G(B+񒙆E,tf1,'z|}?~1F7lw҂1Ҷv0nnjhPvgn%- /A`^8#zhYKq Zx;)t &zWxTYټdnWfGL > V۩&ŝmat-ϐxɘ$ %4B YKU}ue5sjSJ%Hr(&quY"4J;Y iwf196N%Lk+ QEm /}9춟YGކ`Rfe@jc/YQw|1R`HBC"j'ߘ%fOTsUq:d5BY˵W_k@…^?sՁoAmɆ!?"WC[a4xӼ d˓Dhv{J>Tls5H2* 2XL&8@ &AGphjP$!Q*x@)ِ!\ "pK Ȫ0`3 d'8ҒJNo&K2BQ5hSÛah;8{fR쪩?>UׯL?gR$j1k)eAt9fh ՜X3 1E='Mq|Q-YoK(z,:h4xeS\I}ZfݕFR;U屌-{AF/J zXC$(I9nڈ^ov5eꢜvu&z0I/$r#epH!AԨ \(x-,LNLKH[v"%Ø3JCwY6%]P`%{%t@lxUF^A^q4,Yu)zlD.r,8i|KZ f. ڃeP'> h釭\X7a}h7RW&\TS戦s"AwHq0o;/ w#H`y`{v^; Aa6sX,z Qg⠅q7!5aZOb&;9,*6x̕#S>SCLfJ6@4|k B(U/7`~u՜q2{00Ixjz`:/?LM e޺1g]E,1(K ٽz&]E.)K&A5^Ǽ; Bގ@GIkbU+;qE%oEă6:raHfڇn; Hx+:KƔńy! { t6MP!#2B[@_I ܇; Es\ GiHm1@1t`㓆&bJ.:C@-xE{I<a֋J!zE904{w{jy8 =Yb ?DyEPVD2z9[y֋Z4S[؍B "<ʱVoMw5A :+x; 丣ƊF@NjxLS,3 {roK9Ozrt\4f.n+%xb~\o3 C{ CFg!Բ7ingg)0hqh(% ? mT]n4iE @A3/`CWlud(ܢkM/@#@#@#@#@#@#@#@#@#! h'& .@#@{͏)MFFFFFFFFSG O]."f: _~S b,cS!Ði$Qrѣ)(FMa1d?GJWDe@[3H.bJ8qG)3x!)wiAlཽwS ̲"ظMtlBl3.T=MaܢL'cc2p:IwԎ[ U$Zt4`>?Ȁ8G?A4㒋l"u/9 m}zq"<"y Sx^2Cue: .gԿBQ_e+CfS UlWa0TUe!)i_>nJ"mGC_ݻ]*yT {y DUW02IU0CE[@0*6f/-`c]G&XQ"5rd4?i-"sQt뻨߾ )&cRKfYW3@ΆU T`\25AEِ _w:qq3sEi :;,(͢{G3+Lmב ]sҍ5c;'%Dž/S4݀Ŝ2]";_7a܅Wse:E}YV \2WFO7uLj PCL+W=~za6\YYkv 7!WA0a;r~ p-C.t!VC?JL χGd4W=9\2M}kBcze`a:NC^6KƁ j/镮bh`uW0F&ou!udm23'%˛ڊ.v oDsdNh.9ꍸp(]?|n+}ƥ 8?dS/\0c{zy쩱 PܽG󵐒O%~<Ώ;>`HVM.tƭ3#t:Ïzeh=yczo!Fg IDATe4Dh`9ImlWUaKd ]pY Nɩج Hm%MPLgLmAt^2ae %˳ԧǾYA˪jFw?=Xd%$PaǢB6KPAB[YܱaJ"W]d0jԭF1rz?7HXwLqöO&K&kDXa#JsVCΎj8g@NW :Âe'~<1߂  C~&rEFahy#.7x.G']0mTdY r0?ۚ#>%hPoV^e?{i{7y-ϢDY[z鑯*U͜ BS ̄BX% 5kIBU3j 8yykdJ&^l!c A{RH ] (=qZ]s02< 0{x>:דO$˓}Sy}on)81z&e̳\}Խw;ʊu*h"<TUkˋ+90JO%+Mz3Q8FZXdjaSE]OawrBDJ`i )VVsm9,(lb`t7}KYbΫe  a?OIB*x.9qa^xD5eb޿)bbAbJr^pB[O/=SݹCN QҲgksǼ*cdԗ2"We>n76{iZKԽjTE\OPS7_WE4V=_muq}(.9qPt1ٗn5`YqxJvGU 'd(^XJ Vd<%rճ<3)gMڈrnV߆ܘ3GsH{J'6F¿G_Y9gpm>!LIKdzLubʶ4_(ԔB1TP$Em5u]p kST L\g6A.4JhJK*%xn&vA[hF9ӊg&Ů\{}ӊ ]{?} ':=Ry+Ǎ5}٥,  ,9iR6Td-::޾豲40Pr 4 9kIAyr:_2 y*ZoXXDJRyyNt6&Y: O]va=0r$23hԔxaC&uZX6JCT+eC4d7s4FOe֗" _[4礙fa_ K|2Ә}gL;ܭ*?wƓ|1=tCfT= z Y|9jt(tMqvniY~nܜQ$,Cm0R ήy*x7c:ߩY17'@ֈdȅox\!n"3}f{ bfȱTES>n(噐`}LZЙq |(@c(rad0ևeK]Tls/iջg'muܿ"%;_τP1vY9X$|sajᨑ *i0tw~z݅m/Q)zy*L-q7: ]L]Kx8(r>WE6ՐH ɱ{j3\J*-I{e>LMq Q5h|f.S(?~~q-)%׾-O?bZfh;8Աi_N1Ærr`irx4TD=v#T vBa1l=`,aB9EOq& Y "mMH c`z֓Nέ+ |.QsB)`j:;2>%ŽwX|啤+SN;x$/+7:/ޫW9Yx(zbO eI&9aٙ?mJhb#ZU[EJOykU5j8,زaQO5QŗFfJ/Fwb0>CLUd.Y"YA2/ftNp'OsP<5eTL<γͧzc>c.[ųU7!33E;71K޲NgfK#W'Kqй!6px'D!q% Zŷ6Pdꌋbg\P3θHK) #2z9[E.@#I 1$L>&|LIc,O^`ZfB@b,v5TbؙˢÒ}Y|l>Rw樂!kBwB/WK T|n3r7\C~[,('ꣳH{ hh>2L-K;; -9Cvv~Eki/e񥤮3dz)ζQ)3 4|3|-Dg9>-inihhhhhhhh%7 >:3? %-? 5ذtU`[P>eqs*<:!y-Fi$R~qWgqʧ"秅FMa1|~Ɵ#| k 9Ou{5{KivX^h|JU˫ӶM?HgJ8aWR#/mUCɶ6M£Y*]'6?*0~=,@e=DqMlghMg\گ{vøE#?:N N lt<&Iv 4 }}.lw 2dKzz:|ud@ M\L:C0XM`h9.Φ!FĆe8hӋK1KFsb';LR'eoQ*6$DSR~`X\J Xx *D#␵*yOVw0*ZڪRf#WTKT,_曱Rz{)%,+eT`\RzEِ _[p/zW>=R,K͑HU<6-A웣 ! ʖBʃIl6k ίb~$bR.$u"%YbQ&a2?5FCZ$^&!jO&ꓖgBXr5y>CMv u#'Boy\G,M| N5CZz:`k|~/62 UGh0[Pij}kBczՓgm:c搗 1Id=$V|n̬s\9|%# MJ2Ǐר+æ0Ǟ}pl/90tx{j+?wMdt-?{y[O>6q,/+~pU=8D4j)60I=*@P.ѐor/ o8}D8 m:r`>;(9y.JEK. (ӺZX%Ke{)9'6ȯ4_Xf:}:08?m$+9Sa9C (<B Og&Gzm>\61y .GzX9 \1a ^ gS)m#kUc IwNt\EpuMNBŐ9|zIewGPXCX{=WHbCPdAG\PI)|ލEv04V;2-:4Ecdԧ^Z+ڣCGCia~=i 'HʸJUQi/|F\< b2 Cyc> Y 5 '_k)D~ßF[TWgC*R1s7ҵZ"4>8\4^2>֥qFt`2/mvRqL$+zX'X- D\7i` hhmZ Q LHFg{E34da sbDqf0m өXm6h%,E۸MG,C >JBY@Q40+a*ERQyjf͸~tg}5U-<pjCI ֔I;;ۆdt[|5Nm0ǛMC1R,R+/rkuS) j6X  $~ |o L6 ě捸H%%^t%B;Wؠ%%RIMy4^(M 7ql[UXct7zXnhMBy XJż~Uܮ1Z;7..őgX8sO(1}x]XLE}3QmV+*~߼pFv.ɮtr+xŝGcv"yU{4JP]Ɲ,&j$_{b5aN״{5%rA, UA{?ɩT cY6z<j ^wv0{=}Bx4#S0t.w&©?8PҰMjZes+BLJGNF_u+ |.Q%34zd1u[CWdi9:*. <}q,*\iAP:%UK-5JTfrjL(U;ECO\A/i]ڭb=7>0'<. 9A[ryOSA*ly5eL1Í()_WO elPW~W!`jٌ[4\#x7-oa25;7kdr=leŰIR,F4b1Ǹ4rAAO(g&8!MLՁg3984Is0QDE bWfe:ѰCVZB3% xx)Q\viKr$j\Rpv-<^ .q>5¶LZ)4p.y @[8yݑx]΂n}pxcDJ3 e3{K $P)ćB&;IpwR.ؼ$fs ‡ |.qd$b)?=^GU0u &v_2o3.wE-?0{0I)sbøgo]va,QqNL+48B)|Eݭ" J°A!kzL<QkzyF6J$>-D$5z{/MoG!yeS3.-h"W9SJJQlDew;Dueţ3.L%ӮSI]]Es rH͎?:#%#Du;yr4{E /0>rU N5q8"\U!/y+f"576z v@F9Z VtƔ/,& i D&w] Y6C 6r3.d!F4/ ^bK'Sy8 =Y Jh y-IsW[ \{Cb5;Iޣٶ37MKNK@dϸhtvXkΆY޻'Y=b{vsvF3~U5&so!ޓKTPB|#۶7Yx)C I6ƵS4'44 #.C#@#Ьe,D 35,Z6="b11/=ncم7\\l/hhZs k]1ma%Phhhhhhhh> ?fFFFFFFFF_ lX :hp<擝޽|X67(67u@kD@&߰Tt&#ӟo:Џ>%Ue<+ᄞF^3ߤj_ՙfMy%h FlffS'/ǜ>+no/ysߩ[,$8UuOny`ziF7"on }:}?ݣ䔧v:{E+]"MK#  -%4L'.%2%ZK&A'Fnkwve F*'LdW82 Ceov2K$PO O1oo;hŌ&`JuKĢQqm]^K]%R|t?T]J~|4еjb)' ڎ~ @Dw܊ ÉE_DQO¬ `At,Sظf;[ȟN oUyWk^ {ba't YZy]vdC/9g_z*$e)T 1lfavu}!C낓Nw;܀_lM1Sp֬q:=ֲUy13 oXþPi1r4?z%q}"g+'B3¢!(Nzp)T:REMȻ'yGijVû?˫w=ݤ[.DYK=*_YbI"K\N֢gЯuSa,Y;v7sJN c _| ;~P҅1atq'=09\2F}kBczښBU N:Y4W4xXk]}8swDĝ*si8t! h`=G\pe1usKψC7Mjh8D4!T>)_`=Ușm:c搗撅9C<=5V&ÿ?y2{'s/ovkVw)7e'yq"S|#S32ϭsmp= b9ȈuTd~ljЈ;clPgJFHp'qNF]ڀ *6P~tKF΋e.ǀCeՐD?tsyv,!ߞ oGϋc;Rq }~3{oI}[jPt#$a4X$ʈL\y|Þ[)+?J_؎Q3#}6LxEhq=1}q`Lh]ZonDK #"R{Ld17CT*__i\Cv&W5z} s#}Jmn{{D\Rwk=nNBŐ9|$q!J~7 y)|ލEv04VL Sr3RJ6I5|6Qa͘'VLt_~"8B;LmJ0pT f3.%Y_MUĶE Cڨ0FCs3 :FVsS+/ } !Ps N3W]5 j퇏uft{{W4X 2/mvRqL$+ᠲZeyj6sjCک c> Y 5 '_k)4sȘ$@^rm?u}kbR[NA wVK[Q j;τBm6;!r f j5Eׯg]E^ TX0<I.bb ]\~eyɉ?PC]ZhVYK|{E34tf sbD7,@eip8v#;/)t?{y9I>+=}^X%.xqNf5rJ*VtM;.LPSzk,dɩ5munc侖cѹH#o<ZPqčM4SI1"cԔ6?VXoݔ\eGoI8q _$+u޻ޙ "t@BC`f"| (|W&SV2P/^3s4_S?]́!*K @M-*Jy]+5D&VE),&CeE&0bꚒ^7{Os%\ ЧjRIMuaԅCeA;,@ίΝQw M>z鑯*U͜ BSKjBaE;V^U)Yh;->>g.cvrG;<_Q~=Y[uҦ> 6(֧Tf_m,K T 3ܫgTA!p9Y=I?@WJGuwνF`^PQT\*^SZ$ "cIsΎ=f?u.WҪt.=mh̑w+l#;~ݿo^Ƴ 3[V*s^.#O`x QўgcfWm`YϾ"|b3kaS(9FdL6VvNN,2ZTqeL%KK(߿-j%/XRdA.,sad(;P'}H4dpCVFZ&YFGYGȖCh _S]^LL}T B [ Qe.@isLe~f>CaH1jC( +_2UaUYϞםöm4yv=,# c^a߆@MYA@@Y7W^Kk nh{5wW1jKY$Qiy,cK}D۠#נ_ i(k%<': \k☼ꂬYGw=VfFJ"?1IB:е] ?|jICr1VVp{V8BCfrkt-搭F Yh lȆ 0\R$fM"S%}! e@%s^s'\ G^Y 2)*#2QKH[bMe *N;]I.c_-~y -sLu_K~wwq3__\NHc=Gk7ph*K=T{̅!HVĿQkk!1L vuy%XLAyQC>eĂhgލ#yS f. $eP%@4"r` #x M=;hslVo^_9:*. <5=4QQm33.ٱևjf&͌ȒڂIjXƽ"S`IhWD$|2}3]aU{Zi5Qjj9|^=V*I^P&< U=}ʼw͸ES9Sw;b&S7_Y_8->:b@ӞR{@m@i0vԈ'Xʋ }`9Fd$[EXFw>Kmm&)0nʩ?(aM;}bONAH6J$[;$ٽFCH *0쉓Hw!ZE(R6m/>L1r T?g^;H1{x5MAOBІ-D戺*1 : ǡR"kynkBbCl>6 ck|2g:?b[O)3=]dڵ]5{[% h"<1KhSkFFFFFFFB@6,tXc<OcS!kXڕPvߩHk VkyS@K"coX :<8~(~qWg56qLh%lɭŸ}uReiv| +E9>&Mlo/Eh"YZ`![n-.9M>?d&nnM%گ{vø#?;Nw4ys鳯Q+ '<] On'oWd+2e59HVU|~‘r\BTO㒋lR^_|.R2%ZrMQ :/=Sx^2UG03c:T6A0@14mFNp1܄2A)D+8 1 @)26 KS&VJc qSXs^ {, p(fԴ7-̞ V ܖ4|ą2}5)s+O/vd-=$`?#_ݭ[B"ʗDTAYIRz]j*t̢џNf^Rĩg|ڱunJlTZun/s6sЛ\+rj,mq,Ze{7[Eٷ88Q>MPf)g+aR[)l`Z;XIlJivhQń?ׇp2mpa?.܄\e@w9z΃@P Zl:`k|~/P5I3޾K\HM >, H uf<YGk 6T:wˁ¯!-D0k K82~wٻ(G4EEE`߂/&1%*$5.jkb!(b,EZ.Jw;`O̼};;3/IPV_/iQxt@hsMI"ak]{zsuWvvuliN>?D@iPӣ('\dIO!MHbdo["ToP]>)hB.-L5PУ'撙?:I MCs fMxfd`ؑ] Ѡq2 GIu_~b|}~LEl4t)l3'V ҨT3p@arotje%=3V6'"qɎo? Dž_!-1BOL07h̉'Q"YjS̠ܘNI(XE,_(<5LnyICh$\nC†FT0'?!6vndÁCyˡUI IDATU@Hx⑂#jJ"Q:@6c%E>sHUC)~( c%wY Q@_G>4}{3^KeC~> {'S S1sLo' ȚFa5ixg,sEmC/A0%GCƋWr--!:4o~RykA$/8c&Z ^V=KnFb`rmfxE%_Y`وjT%Dmw7|1Sxnׅw'c(򒳄'`̱LE'{N `o3ͦ]JVo(#J^H^2%TC#"|Km0u:6DPՄA$:#HJRA`ȲE[ f^2`k ?f *8Itl~RYf_Ą7'qeWT"9Z:dc=ěT*tK܄][|e1)zy}gh%zM-s򿯔t+m͸,(mI`s]~Nڵ5b.Zih"#OP2D(Pbbi\KC&*x8 TաEX$a0픸RLݫe,,(lζ"ܛG6\ĖL>"Uj,I//Ps=K{U %CeXXey;e"}u60̒by?+‡Jm45r{fƲDjYSE)L?a% ΀R@S,?ŕP$Wj: n r#, g÷W`QS <77jPRX[ 2&57(_D0ݨP) KBm Ė+܀D/P R2U [IX\]IeEm}o& Eۤ#Om͇wUeGl z2yx٫LjZ#ҫ$uC~ޗ9qdJ UmֹOF _#?5ާ' % 3[s91U H*>^>H*kWT2dEgqGe)ȋ 7ULWfZlQum&%՚Zhisb'KEʐd, X0 MIQTS~k>XN&}n,.+k:PRtW2g|‡ j܂kR(TTdb7h_'i2.7) -%۰Ac/&|phmHؐu2<3ԒK@Qc $[LTӑZSTV4ܬ^U) 9郞JU@@gWҷ (MoC"$RKc~9Pf1 f^X琊fc3?(VAt+ -<t_|Y`lȦ2$zM[tU-ԯVBd tzNZC/O~U -3Ş#8-X{9s0s-#?ouZxfl`p:90':UiIj ͜uDRѮ{#_9y GRWo;|kh+-6Ԫok}UFX\5yO =i ߒGp>vp q^CU)iﷂIYjع)>]nbe9M7ZB-l°4tg .vma^Qo;Y]T9SNpLTpLnv9{=K&C|80Zlfr{zxNF񸤁+g~q'l;JKe[Nlyz*IcHdO;=&q#u$CnC† TY=кd^sVjF/KE ?R|$H")E Ф56S7X^8L K.6+ʅ1ހ}?t _*!u, )7 #m`NlN:@U ;!"N_!*.hM61f`UK"_a2K/ 8-$z1Kqco,GSY8+U4tYu.ۜu廅ǟw&ٟAގږ3v|mΎaӸgၛ(~PϟۦZVƱZ$==jq*@ZdвY$tEzG]N)޻1לe8|j|3.\Y۽C-#H!} `[/B`<ĆC/`'(_3.47=/.Ef s2ïnhom>ŎYNx_-ѡ+O$bc9!ʧ=SJv?69[C¾GЖJO9M>"Q 99$ve>,ae2z8A{IQ&v~>lAB๯F7{i}MaObw uL߾?G6.A@56l"˾p!tnC2H8$)@ဦ12 Q $v%2^$BGV ~[;qUZ@ mGkn mSGLqt  &j,wqX;m{ … ufvłs]!_?oC7" &, oʆ [ GMRH#$K*s%(B!CUb>t[΀VՁ9{7wxr47CiӃě{m8=%>'|/( F%w;*0'Ѻ*k}*VIqZ&Iۍ2Ā: NTN1^˾X!}[04rawBJD-c@RgPDIV=g>=|lʗNl&8^=p<^$  T(--E`w[CҚ=! {ŀԤxXV'a䖲@%A<;Sή+z],TU KTs.7t,pkxU1 A=>"̀ 0a:x|ū6_-@;'shhhhhhhhhS!+.Fբh44444444)m9=eVfffffff6ڛ\ k!(!F=hV>pW~YY]akْA3PqFDo*jk5K-6 pBh&v:7:kME(t7I Pj`M'io0kÕ:+E"o윦|q!Ihy. Aۥ6 {+o\v ݗBaC[E_w\?TˁN_}&A(j2?DyD̢ R$8G x8,? =U@TAFowLj5uX5pE}x2oFŽ'Oů1LFQ--Aߪ&0FQ7@#2)}F v+`0-WN7a4e9vt[o%Tvlh[%njX{=%U:~(ddPPIZvg孛3ecuj_f؝JEg9lȺyjQAo{GN_I 7;Lo!Npf@QEBٷyyP6IV+UކbimK+֔cJ4u'c=oCJqٝfR%2bђ/DKD5 memZb`5& KcxW| S ks<pH ph0xeSH#ֲ +@RW;̙ =MXQ)G 7\2IUs;kB"C{R!д3esLsv&aGb"w5%i, vDI0%^k?kߘIQV\;%I{m;Ok Lg椄^p*),_("<5άaV!|ǤiuaZ϶I[撣;dLoPѰ RZl O܌tP'{JON<=ɼ+pᘄ_' eg#)fלy ?bpl7tԖw avRtf؄#b"$$ ydif'cZF?Gxo\jH\ң=c? /bA HL qW$SE~r{'f w7kB9l {~ʏ bpº"D re76: .DٖoG"Fl=8|+SrL4?>O2Nf[~GjuwG}74NQ>;?Qxᬸ8k=Ү 73eTf9CwRn]:}! JŘ3ÔDAb2ގQ)o:BZ^rPp3;.kf) THv&\NQ?NuvF] tB~< ~!H<2nΈ0lPKfuGjmLϲz/i:x\Gc1hL F) 6b)W[j3k0Ѵ^9c&Z &2vIukӮ{5v޿K&ыD"* .0tV߈;zSѿ/ktC54ւ T Kf[/y`ɵ|eecff)&CL.,bٝ]NI41G[߼ / j䋙mt.={!_dv$OiR;ʀa't {3֑{\$+ %g OL;m_t2~I"!b IJev{n,s"aHq Qo\Ҁk ^hz޽>~~;vQ[%#W8YJ{nOmamȕs) K?`e0T^OKâd]X)Z X~ScŅ$N]2xɮ`ݰ ,a(>`z%S *Befpdcqe m=.FqH\g2٘8*?1^w?uG'-w$u!=ʯԽT |9T܁^WS+ĢKG3.'B+:%Ns7 !*H0zy,9ByBD iVXViDT\=+EdTe?ȸ fTԋzˢS[ݸ呁_2o `]e1WOW5FYc}iٴ};؁ۂ*"o/.ܭ(_)Z dwȵUה=O'5.!6ّe幩O*s $fzȀQ/BAmLJMԁws=)-h ),<XTzC@QQ$i(>¹3ft6# ::YF<-VZLqЫbгhVŦ$2CÊ#⒠LTe;bg[6,Z~ֆDc:K3LeB9SD:0   36S|!`}@hkFR*̞6RPH@Q'*{^Tp%h!1IMY]?O1WPSϪ[$;QpDBJDub`c7b7?+2h1sI3t$zH3: {.?}i"H+tҮtJCsoCN@ n r#, g÷W`|6HUJi/5ѕTVw3Y&yʓhF]*;:jcAsL^7^?9W;3]T"L-]V5j -*/:l2KT2LB;^wlɋ\a A[ۦLV)υV5%VϘxDzrzQ} Bk&LjZ]KGKUOο4v HkEIuf ~ IDAT˜ 0Gmbe( *l"+V7Qߪ!$i@Qgk&pC96&M"7S|XE`:(EyeLY.Q$[ĦV?k7pp#ZIFG JݕP <8H9|r[;RN-WڡN"'CXl6.?OnSX,Ln9^iI3lA.rOaIxpqg*J}-h́06?,X 6 /%+L91nnmszo= ulfh.i!tY+ki}}˞E)>]nbe9M7Zj;:[IcHTY=кd^sV֫,)|to 0bs:Fae .ugyz*]CX0FԷ٬.B[EXY`5yO =i !k t* HRÄKxn۵r6Mon #߲mL Nho0Zlfr{zxNFEbqQJ§9?NwuN@jeEg69Ob^6D:qL/".̞GeW3 {8R yEĆ p^(zuA:mM݌/WHz-hmF$bM <7Þ)p!;bKw\~ wC'=I2\0̲ >.tDY)<{). r2^ض f "A^J^qU<fE^xTeչPlwzԕnMmHmQ#?>vmo>{aYm>3Qm!g/Bu`NaDDd 'L88TePV,!qD?mo8\a+|۽Ig\0{J$_z,o2  dRd0'#1y* pMw93+T{7暳 ǟOrcϽx^*Hoilw҃784]uгh 6ղًdg`Ig#)Օ'pjPӞ<_` ڽGP7I]YY&¸MBg\t"avq=ڞ6%]H.E(5q^@2ڽ{hwB$uyLI9M>2g\EJ&HE|rZƓ,/,ۆm$| IG`sbq ` {=#O4/(&ֆB9:Asl\3glqWi-*KWd , cL hʭy{P Ûz%sc*(=`zθb[@goԹz[8TeA=lJNe~7;9 *VO-/UkIVԭqhuXVA 2@{٦\q [N\]M3u#3#Q~88, Δ0p;%{ Fy /m{ ^򏴗2EzFU}4T ^2Ct>Sή_z],T}bo,)Jֆ :PPĵ6G@W@3@3@3@3@3@3@3@3@31 seZmSC3@3@3@3@3@3@3@3@3J ^r+Ixh#hUhhhhhh!8yup ;e]\{ G! a_?QZ]/@|R _=LAg_(I*YL]؉Eޅ󎵤p ;Z BǁITڴ 7/ R$c߼OGqBh&PHhguӂ`Y5P5(o2qPGzk5!e%6qm!,ì W~t8@sW,E|#&)!H ~. =^P =q\;zbV[w~G%3tP2₽uS 0tf&xja2KVoV+w\?Tlba@y{tfߗ;yE%K {^Ҟ*=^IdQvFmoQSi++*ϛ$eZzt7IIne;Km/"$<FRvgpXs~0ZȘDN"/_qm:O crĩ&Kd2}V_~}ǒ#PTU)a{AA,u{7Rq[-NԲԁ 9l$7F}I Kjܴ(2jU.ʾ٭XEy& |ĺN~Zm S `쵟7 Y!.*{o]8"gįlEEu:gE f_M}7 DMIUo9G^&=:`DBno:I31,MUeP4,e\Ljzc\v K7@atP õl i$ZVq`~EPYJw93) !*>XP-EAERJ̘:˧pOVKKpo"B8070 %BT@H9Q0ؚK"z@ ^<> (X!sz+.%EYq; ($2.> 8aICӮΔU3=|CLbbLU ; zCh jSlrvM^?>/f' >9aOD uK Sԋm>scҲmB|2 ztПza[Cj9^5'^q?VRi:jŻ$svRtf(!{~ʏ b`XMcf'6* Dp^>v! Yx] YF~u1(*2BFU(|r|s}JeLɿ&00L}3pV\"DNm0ĔHm_t25/ymYk;lbjSшSczte j$E[8- oćZ=}թߗ5j[ ^3(x@mz/zS,uL + bAvz}P}Ņ&50/X7DÐ3 NmOiԤJbCDJ/ BEa`JQ5enGbE`CV&Жd,)̮IsC VoJxQ$Vg[Dʕ `; t4yp,w}ruZrWK?`e0BT^OKâd]X)Z X~ -&^/Xi7li%LtÖ6\D0KIXJIIL&|A\ X? Z/1"J%!N Q Z-3ARh$It+6̾ o%dIT}Y*_Ylرs\C[ˬ.˒կYz[Rþw`ȵ{O#ރQQ/%cwȵUה=O'5ԅ?(VR2zP^ՂaYuQɓrea:?'ɾS ή́?#4X)t2c M* :: 9a !^Lkǂ:%xX[l4Pѡ=/3KjE}mLFau+/#}>Tô25@eDoGUTЗmba"OC7J/FX"7np kk:],l(~ ,#v3WxT$X40/l7~>O9ܙR*MK\UR)SE˨> 4/z_,T[SpVQ,)׀ !~g<`uy!̥oم74Wehf陛Kc)4tw+1N!OB+m͸,(mAOn4EP2D(PbbidQE FdM&_!-iSTvy;]]x駳UFJbCDPN/ ֎EajR"%AY퍘%rkY` $dD ڔ$z IJ`Y: {.?}iR0(:w_KZOEҊK 4YR\A6e[Y,j+J6*68/7’p|6|{_hRL]F,P!B%$zkS0 @-W!^P)mnqZ7%&ʊ>&7m<4Us1`ƠD&/4AHX՟Weg`|cJxsQgk=W/!йumʔKiN͏\䵉Jr_Vޟ18fU MuʼnM=X; *Nu2Akz 6a$k@c D* D8ږGZw/r_=e7An)I74W&rЛKDM.n/~^'B~w*wpx(n34E&]SYcăW62a6qvQaɚ!$J"5YÇsHJ2Z>jXԖv5)@lHDL,>O CCWe$,*/:aϰ晡|E"(fMI44|@jE%w<h1f6lFr|Z^^b ΰ o <)'bJ:\\mɮ}{D>T^"sh1F[_lDGg^/y)"DjCj8AwH7h*B" (Ix7AY H|5EW!*X@QZ Xr2nnmsz_U?{>؜2s]9B5龗=W 3n?/f\O9||+..`M^Ci|}:wlVZlUT1k j$L})ҰPR.n3k1 &Ohifg꧳:'KRSWmܯ#c'IbV-p6f3==<3ո0{J$_zGv>B06$<.Ti4|oDrNkr%fW:LW` /ؑ1ĎU M߰j! * nO=w| xH5^Z)z4Iٖd}O"lmö^z'=xC2 =܌Fhme`8_g]&;,\%tH iE':<儀S*vjqB3L/BSV'7 sBI IA CH/ "_}2yS6'Te¡GhWȈfv/~RɬMsЍ)IҬ) ҰGXQ~>= neFI G}U ~[;qUʰ|+KێVb60dIp(-㤛.L hʭy{(MR7!.rjRc*(_Rԅ0pI߅Iłs]!_?(l1$@HTeb<){#$ uEH_1D3{J"Q΢ЄiΪ9T2vk/-afwrN' ZWoԟliov|I nCiY1S<^$  T(GB3@3@(Og ̀xXV'aE,OkE%F0S {$.6 6[dԽL} ;:1Yz;E ff@cVTc肚0!Er`KTмŐ-P>qM3t)wuw!i44444444m%Xw7&N3@3@3@3@3@3@3ac Wǯ{y֣.=xΆ^zHwCg ft?G6g1NvTkZu_=LAg@D*#:]j֫ _e ai^ k^ͭD.l>꺾!4; |Y-`&XG"u :𛤆LtlZ0ަ7eyns"7vNkro(aa ?af]dnmehoGwv7e:| CA%cmХIp]gk{g777;D~"{|k9޵vn\9~){ nWÇ^3VFSq)!^>=˼O<0 ‡p 745rvb ]P\BU/ʺ?Gꇪ DiA{Bn,tʉS&L>&}3{zW)E*WE[ib؝=^Xn =PrG;1@~ 臏7u[#T|gįlsB&7X'83 "![ <ލV.zKT~K` -Fh'CLi(=1}*+R4cw} ͧ?۽mAԿ 6’-0Yx ;cF¥P }@0t`; ¡ AaLܛ`*0E_DPYJww93) !*>80pK)}+.%DYq; ($2.> ָCӮΔU3=|C6;݅o[SU[[SNڿSCbuPpґb ߧ#?E!6!xݏIC´m:^]X>zfOѩ1h&Qy i`M5቗ɻٔMz3 GIu_~b:Jom6|͙ \A+ȗ;0MGmx7<' Ͱre76: .džH'8^ MߘEm)`dl_׷kFF ?NH{R?&e6X~ƙ)5*kb-ʄ0 EǽqɎo? Dž_v|UfӠG99)6)GroR[4Rׇ=ڞް'"}J S|LJoJH|sR!$4Ɠc[2nΈl/$ɺz$!Z`s)$P5=`_/ҠD/ +_m>q^H ,V&ku yX2Dw*'\yp%HaQO`a.-Z,R穱BVz[pd_nXE0 =|"Dަuj:g PYlQE0Y]V#R"~U3@\UI,.￟ܶ**J\geΩXQV$*8@bhV }ݸ呁 L0}5'[ϕ>0b#{ǝ*QM>O$Fzǽ3-+2݊EHq(>¹3ft6# ::YF<-n܁^WS+ĢKG|;NXZp}wRkʞ'QUuݜjdkJdQAcA:"dGUT6bˆ{,IJQL"7KczEeAoǢ ~R?8(㶗%D\{%Tg=uipU o" Q~e"/I(n+b b4 % =ہFYgڝ]lJ"388Y/. jTl ӿG:vԣV3$fg,Hș*-)c M* :: < @I)>{l ♩RkJE!yQ],5?T8$5e"F&@G\YV*_JNM!\*xv;ׅW][#bfPDTD\SU+UcX$a( J||t*,=svil1Ae8yeģgJƑQW,,eۚqYPڒ$}RYeny]Qvyn#>Z[Ӧة6?:vXOgQB) ވY[,Z[J^mba"OݭHA,ݖ# ꄗL(\Xf[v^~JS_VfԲQ݊7iLR^(cgA!oKbc݁^F8f>"郉#I6C,!F| yWč_"@HE [ELfQMa<؍=#-͍jEKڷ:1UzNkvf+L뻏?C9Ɏ{WJ5h%juJ` K(t3{!S#XYŕPz1[-+Ppal8>? xUJQBU~DWRYQ['Zdտ{it)u|TO&/+I5 ~)v5#MHέr)cOr.&Eg P2]6V?7#ao?㵢EO>_SY20سML&.kusULH ~nܧd$5C~p;Eeb^'z<3q/`,aٚ7& M"7S.t"㎗ta_F;X  e3d jeba*ј^| ;Eye^jU2dEgqGe;UjLeF n]*㖢ZL J9i?NC>Ɋfˀ;b:-ĮӔc /?m,C]LW0t٢C@A*X_'q&uSmL^7KƜ%Kj ͜uD&EL819)ٸvp :FaL9ܼ9R4Td8$M$!KD2!p3{]4JM*C0bk"V.HT!tݺis4k !E:La<4 \\#k {~kneX>ҿR\~ wC'=0{^ `fY#ׅm&bUrnBԔ ¶M0?6VX R-tBg9|?)=ɝCv[vY}M?noˆ|Anńӷ 4e6dOhX.C5.F]=~ IL] F69\c'R.\Ko>2GA>3.˾doU99~ӳ'a:Evtkh 95%FwUf违BK%]L7(ğFDʆNxDDSI]]uRs 8˾=0)KOzˎ&#>Y]vCV LF'&.ĒLC,2I`0[RsR^72'aC\I}c_5VK]^HPe.JҔ$D$aٻ(}PwQ)J6X-ƮX>5%1`4c1VDbK*".Ez]`A gwe;34MfvssϜ%!W߮ $$TiiId+IEA^/^WUz< _?NDe>DZc\%,_aŰ;X5n:Ň?lF{t^5|s=; V]`hQXWHi,2ajt[KeqaD['{仐X߯/`d8A:<+W@K0u^wkYW8l.KoopV ]Y͐|f(1H- ul`xvceeаz0)J'QRаt 84g)r2q gQS+M코s#=QlWpWIn5,$VS0FM"+e}fgcoo-JWކ'Xi(?lY6NbA?;5NJxoNsc" 8y`84gyo㺐+Sͥs\ k)7mRE2l>ObE1X|hu[u%Y2Tz\86'}/aXtLșjKWzŊD1ǖ7i4J}>̬ĐnݥTKO NFz)-2p~_21v "8Yk(. s,Y;~p65ܽP4z%ΈDWonJU:Oïd:ʄd3pHpbAۆ G`*'L70* `5q#|O *%3яN$:#(C޵ooxwSU-Ft  &m7rA/}{+ ^H@Na3EK"-Kg/#v^#5x>^v0uӷo콵=k&t[mOvD!:Qr8wk-p4 d5:%@)E,qzЪ~ŷpux'\?y6\1G/-hL<|m '|N !} IL(QKtI=ۏVWLZ g\1ϩź/KHx(l^ ì)>t1Գ`,4*6m .Ȩu_du=GE"!*s [4±oS~ CMUbԦȅ00@ogԳ?A\!{1G|.S%GGF/RQ%og:\w]w/3}PVx9 2u jt^q.?Я IDAT3?5?\r-<[}-aqQc 4&%Ι]ukz7}~97tp*N?;BP5a K悝)`"^>1Ij^v. r Wilqig\8l$]7OS;mc7Pq0pd nc8w 0[.y^T)*~;K<ܑx?@,G/ry~\LDFcƣ##}w"V>W<_tQK{^&"|{{(` P,.ŗ"io҂ ,zF{GT&.MHI7m2xQUw~]%)ϸwz}.m-.%a"擜 Q@XIѣ QvHQUwhft_t\ R&3h;.?Eo΋[/)s7NVzN%rsqS_Ë7F NHQmqD\K56='J:Δnvgn WC}ʈw5X ?vLC&~7fotmr=̩V>u|[QmeJl@e֭A:1wg]5׾G胡KghĚB"R-.Ӕ'pW403ei )ٹeL3a)~_TWK8|&6U C+3$&&(JfXyF6fZ1Ж$" 3[ɥw[ (@ [Je\E} %ylW}K96(JKIj(kL}/obc"ɉخ,{CIUeu.iS)W1yV,;/_|I9FR\\=v_qT_*]\Y9)+4ղr2Q5Kϫ{JC*/~YQ 7 7.)Wi""+osٿ?Tibq-csQ Vl]E pq<,Xz7{NUes7`>g9;>puW%{@;dn^Ә/69n-te8 Ϊ#GɭRFɭ-e hگ/.,y8]mFft:LT"lzBfq]WWup4]Ked91nZK,],(mdXY~Q$TJT9JY@Йz]eiv*=fP[ARVZd6}h  -SzŒ 2S \ 45C!>ԜPi߈d58Yy@Fq^cL$b# RgHTb~:5-2pLdDkCxCמ '>c9QB/?t<.vѝHp:ofm]G%D+0vEsЋNWI-yat|஑7Μ,:R/L"Go`U&rcr5R\ۚBtT8!0*5i|O *%3яPEa5Fb;-@m HW8zI!꾽J2K 'rİՙeiPTMTO)] arw ͹д_s5>Q2hَрˈWj9 I>G6ݗk>|`3iVЄSǗObT)KMPr0bb1]4,:;hP*CjpfC 2Vuek:ht=y-"ͷ70@g` q꣣Ődc2Z#X腑.;6yn&\NzӮ.ts*.@$|GX捠73k4d0#6o=vZIM5l!i~fвͼEBNo%oM O8k5"{xƅV͂slLn Ǿ=Lk9fBy2;.>mݧ@qt4m8 0(|Ko=qľX/ig ue %*ʀ<*S4s+_EzftMK_g%,N1J|,AF̞J sf׺)jiToPWL hXÒ`g6e0n?$r5S@R CwvƩ,NθpH%!np#vn`r)F{^*?hdܽ7<~QzQ0,ݎ P;.g9zbsFM_4$ZWJ Z:fڄ)Go$:@تq:wG?XO0x60$WkEɒ1_Oysq-'hHmհyo$Z7t#Lt/\ɞ;538N_9=%v~yF rQ-4 HAˬWo| ۹a;q>6Oz=֔<&hS  5ӗ[,_R@ԉLm`;N<&u#zMIzJژ0WcFRZX(Ga*rx=mh|&t^medgN#KRu^}{aFx|><.Sx<o(`6-ȱ"K`LHpq0*  P~]&c4ׂ\xLFO84 tX \̌F՛ClADPB42|_!u4;Fy=h_1yS!s}?oGH.t X ORO,|ӎ>G=wft !تvuSŨqM\_[KϲϧuCkl"7ܼS;utvS $q5}51}D?Qm|51lqzO+PלQ=cGL,殤8ظGʋ=)Q26`$Wnx[/_=N`Қ%FRa5ͩ$e/RQ@\/mFL MisIõ"#^1b T9*t*O<HTv 4ъ}2L î*㛹raG^R|^MQcH>}\/K9jutw~Aw6AS~%]4-&NME}x`\U 9  IDAT8,I=zN,r8|=.UMn=`fC_i1Q4i穃TEto@j06=ɹue`Po=.o̴>˵yfװ7ęqW̚{zDxL?íNK3n1\\h_0,*6@\)>j&uU5c+sfZٮZ7Ӓ)/9h9~zolsFWQ L\AD_TfDkaE'+O N.zG.,dRzP @K?~9^xbu>ڊL{o!ރDNgNSoP!1cTT*r5ʘfεq5^JG7'2BN <^a}F@\;B"Eu91w⨫( 6FU¿- {pt`H/%Rsa(x0v'i(9|݅;EY\X C `*x%O͍m#2g9;>pu~0CI{^Qy+ w0 4K ۝6;zV"Gy_ܭe]1q2NV7782fA%JhE !ΜoUY /̷e=D{c{Y/KӔ #%NG !iB V%LXK{~7Ǫ mV/̑ Wv6t_~qoT7gy&. w_Ģ_P?\Ӣj?dbܯp9m_3ڼ%< 0yKAdGeHϺ4k'ny8{6`V}-GDz ooZw.b'~iv-g:xϣ zsEK&Aj;i;m OQ1JY|'8D&ㆠ;WtCz0 VqQYRީΣ&Z4*XcvQ l f&Q'J"jnfZ=J&qR5>nZK,,(W)>*GsinATX0p6O%e}-4r皺 ӋؕViDmo?e׏^N<#+ׁ[Dyq@;lU,\R<`; 7 W`iàI0j2]#pf0IFGX^*( zO}3BWS(C \ ԗ4dC!>ԜP |ߝHF[#ޛʏ:5DA1q,6 HN%F;o9}g*%9+s͹8(G #eW4J_o,D+bEYYc0|,>`.r~OS32Cz)=Nrl>ObE1XɿpzUoxmiRĥuKw^ykdM$#nL5SD,fo d%őGɪzm]Gt%D#o#0f^X5D:A2 8k7G,ͷ]tax%;h$'QC1J?7:ִm?3ד~|ؗܦΰG?UznwsU8/HW8zIQ4xU"Y99#L! $CQ5!kP=QFnxv1L63.oq/4B~}Fɠe;bkG>g/#zo_ZBϑaO</ج8oM8Ey5qjSvvN%;-w6c73<2 u@%sM>e3PD65 '|GX6腼QzΥ}ٵ^p78cGc2Zڣ3.hr1m`sXo4 y}c)oy 4?;Lpf?lXI12Nq[3u4ҌvhjB Poh,H<oනpTiR7A d734ѐ=˜#>)z(&>gJPHV_@r湖nFi1q,a(C3}``lSP֞ݥcAʉF* /\RUY]gXxAUf-K9F/&4t4"# UYdN5\PXJjAzxљ"/˄Jژ0wI$?:cftLHJ ZҢpZ2_U ҥ^hh]fC)QMLMs7A{}>(6.&zec JJSz{p͈5'~?9uEC1vsto^?;טzh(^$nriRs<;ol ˜$] "C,~}LlJ"AYSs (x g(Im-K/:?W2m3'% bt~f^pLJ/ρ1>೿ .+)/ټȱ"K`HILHpq)  P]&c4ׂ\xLFO84 tX \̭4EQقHCBȕid6/;B6rMsjj~oc>W:a>WFzBwnN ){𼌧͌C3aŴ.h^~ˇԼ|)Qu ќj+'\.R*ҧOW5|˒GZ$4npg oh?e]RE2/ ~'XvWCd8 `RBFIҿ{1 [?߀ o2 u3{4jD5,ciS). ߀`8 l^3>"zs%*{\ߘi3}&)Uk1F=ao·3SBl5S ҵ`5bڟkbRw;N,nϸ_=~"]it$%RGZ%Ka5!j?.C3qqǿX- wV! /o܉<m q__C8:0Ѱq8؝Ԋu:eqaA2 'Hw_O䩹1m \I<񁫻2􍔲D#]~qɆ@3e8 δ "ɣde6ᓪEMXpBIpgoU6֧S=U~};`.c6\yurc*͋e:mZ'P?ͦ 4d@R"eކ9^0԰knv /Ie[Zg코ۋ,%oLb y$U6@U yVìk;ˬ%\ G1@#Mn$j 6ǺZnfg@ϸ`K)'Q#VR(ee^YV6L&mJ}x?7u_rS lJm4'l91ƚg^aJPٷ9zTs8)F3ؗA?-222222222>2 ev%jYXXXXXXXXd$22222222fzǪ2222222f@3lX斕pQ[ڲt`txpBFe^&|, -em7 /̷}zs(k:|wev}o(WcJV]jMB' ;0a=,Bڲ"`LJ0Zy'8݁%M E]c%TќYq _@Qwq $Ӟ^ '*_ Of UijUU5sz, P >.,롧ڦOhdxt1 p8SΊR-<z yix+^U R~ul:=͸/jP{R驧mP$WVKz*N0 .{g \GtӋ,Ogp IDAT?S95`JootIYi@G_S8FQ> Wq,rا‚^}8A0Ht9z??xreTb>nrу+"j`͜JP[T>.^=v{Z˧j)ܠmC/?Y-V%hyoêB.=DZ V&ttë*cmpB#ZΨ4N^k$S$ݿH.A~y-=υa5WpJc3ap,m4 FM> `TAH&CaW`b,=!̘ ]M@ ȒC4W=-?M> ı;Pϱ5'Tbw}#d9Dy1pF<0H!}SQ{՜wN>is3}h*AxlޔLq?( 1t;]Btm>!WzŊD1ǖ7rwt053+31䬇~;F$6)]si"El__ð蘐3+WbR!$5<iɭ|HO7#o{"C1v "8sk~1gy-#N[S̸5"鰍!*qF|q|onJvB娍dI-"̇='"ίO|rs(sO89-v0:T}Q$Q/bT) k½8?!U¼ 8@bp0P=FqF然5[mՕgĨ捩flSȣä6RooGax0(l" !&$3.MtëtfnɁ1luPJZv/x EՄLADA0ٌ. Tq1ϡ+Bs.4nOm Z#ƹv4sx}<2ǚZBϑMO</T i8oM8B=Q%JN]]Sҋ*%UogvВQSxHĚ8E.Ϟ)ַrxw3nVz!uUui%}fRt@4:M=;2Iui=G\NHxQUw~]%)ϸwz5BDe:oK7ia=R/z4rg@~TvUKt8JfFшO!JRiuUQCe5 TIfN}Un}~6/bѲaN Hht*$y%vPSP(@|UH*9VR*zP»<#,$+)''2lBZ/j>+W3w9(bOUhcFu*z*Y\[z6P22-ŇoJQ?k;邤O,j4/ -Tx4* j 3wʔI_NOQ4O= uM͹f3N昪:0hN$ɧ(t; QlCPMz awARfNGk7K"I"$RdFK@˶W];v{ +jGe Y%ֳbgMy0 0x,?`hrU1ȧQ'׎1ѰaKfIWP+bab````````x7?k{7`P0 0 0 0 0 0 0 0 0 4fsa```````h?v3 0 0 0 0 0 0 YÆ%V-lPųakAuߗ{BLHJs;l7ֿ l}TL ;,&U(sTv6߻SQJ~6;6🯄c 59?]վC5;fF!/:N 鷯"T aQ7D(wqk: _2.Erk곚%YI,͚`0@c{ޞ|P{wí':O O mNJDU4eVBb24,-ŗiQ}W;p o~|i"5belt)ï) z,nm;l'8 DhEoUABrF߻Dy"$)ܦZKnN VN@@ʮ<KEK[%eiZhڨNϏ;4O䘌;+L952t]% ـ\8RW%WC -@7d98uHRm_΅x۾^Q^[ 9O;Ŷ?f,_^:쫈6]s$?;Vo^nS}U ]UWܧB* LM]{^5v߫Os7g;_~>PcP+_4`}Dise7.D`cSE*BI,I_Fԓ$)y]jhWO/A#8EJ9'=|wm{]ijGtp fvwm^_0뇅@quEW:N|D0>z`9*\Yta(\ K9èI G5#Ȕ⥩Xºe`w=)L !_G W޷2?AÓ׌%sMFtd50yضR?=`d Ko{lA q೉Q_rM 7qL {oEd ӟ\eiKHtE {Orde b/oP7~̬`ϵNrAk 9ywȹb1^ͤAh+\  >l%"kw̽ӱnNz )Kw_KpQ7 dN64E%̈DW1'ժ{jG;;G E@Zd{3^Œ8_ mKW_yXd^;Coq :7@JREY< tۆ Mw|dH*4|{; ^KCNt%C1*='H!.IF0~E;b.ДV{zɠa8GK.gOFtmPݺhl nEzlIcry3uma1x´kisעFDa_ǘ.m"XhC5V,D-j17?s&NTmݙ+TLkQ4 Dzcu@ąÖtt ܽۂ$/$B3HL^?I2"BCۨFC3dyw[{i&|J^8}1$1)$vk\bmccC=O!"A(BpL,Ad$n;>hs_|oK/IwP^~N*=忺T?zל[Cd X),(%s'pj\!P1o+S@^?sՁ@: Cw`R+Q2iވ ۍ8^q)',C|&LTHi5NOk?vxKrCUw4-Md]-㘎煘2<~roӝM=VTxRԛjQIU}֥P8qN״# Ģ[{gl5$o~S-.ϸFA}geuc^%~iZ`h}F8B\Θ En8^Х‚ g)-ams}͞{+^$DɅϩW Zgg%Dggu50)$v6Ol4ZyM|Q 牲㋶tKo{7wW/=yjVuSvK-,EA%lQ=t4m.?6 Z&-N/[ T Tf_m",$1rTԭa!^?@T!jg]l 0!xVtLx)Mz$0^8FZC-S\#GKSuPIFc( =PuP7NsǒJ {,?{|wSN&Ӕ!~MNǨgjŁA]280))$b9,X/(͗+R(F Y年Gm{u`\ j4[K'wy_T[`4w*?9KȒ7v⼀#9yV"+Ebi;|~mʫ&3낫m]:BmEnZBFҀ'^YJODzuho9p /_#@.MXJ]I"Dr",JN}FnXGt{l03*`[m@ JBiR6OOmN;"KF_T/J9&3ڵ73~ &~w~dm^?gܶhxc6 M ]M?Hƹp8lqyЕj saN{؈\X@NS>(τt ]Ne XP'[K(/a0ևTK]D0ȥoKl~DՐ9ϕZBֲpYmJ,EIz-U\􅏓ѶM9jN$WfrhzvY#U=J]ɾ{֮& ?U{gLX:3{vjanIOJzZ6>|"/6!Ϳf{ ]OlOBf`"<ɎW3R_;O>דy_?rԪFܶ 6@;qum&Y)=Ȥ<#> h8v`6_Y؍WWpnvsUg 7a~ vA4na Iq^(3,멽m%G7&‰ݰy=Ld'E\J&V9Ä;|R&d͚{lݾ?\ Mfƅ\:˳O.O,H8&SL>֓fӹtltݕ~ީ˕䲖D z]OPhTlMr{\>h (.}trղxSz'a4f lx|ԁ]9jsF"kE ar17>֮FRȺݏIw:=n5qڟ@ņwM4I'ШYt{%$D0N=.sw-@ 79Ǡߢw"$*+qaZEݩ"r;OZ~S_2f. /5pU1,v℣D #٪B_VCX<}Kh8 s`N(R+@א0n u'Y6ꍱ z{\PD1v9UFGJv Owɷ6:&#'~2;j݊C|/(ܢ8>,3vzMݡQ   K9EU,mCm.Vi49z  P1r(0d _*fL ۢYS=|mݕ;{/HvXW.ÏI8SUdjvB{%8mr!-^\%mYiw5< ųg2`P9Wݧ<^3Axɨ^eLjvJ M`@Kf?*˚;#000000000.2 u~`b````````h9/e43 0 0 0 0 0 0 0 ^+3 n5kذtU`?x [ʼx|<: !y-Nu*b2a`x/;,&8]- $]:Z{V%2?_ ǚUjĥ䇇Tm:}=%IP+d|YBpB}Qân P0Xt-x,A(Nz6͚`0(Bɳ\''Ow7z0T0ĠD'$ Ұ]r#>dhcMt}^Yҽ57XcO^kK{+Ҳ_|;n@87x?^K~ɒ3 Nd%\&nԺtUCYGt ;DNDdC ɳ#LU2P࿲+RF],MQM8TCB J'WUh&H!)w@ij$dJ%qSK_ 3љz]$y/BGEviWV".e%Ft-E#Y+tWm&HIAK7N/7J|]sB 8&v]TJB}?8,t4(,$Օ-0]\ ӲS%mf]ay٫n{UpICunl}_JͫIG|\LjZTcSETjc^4sgΕp4;mC8TͮPI$Vz%q4UT{zT8$%1w`iys4o]Y[/,A^•ːVB<+6 7!W` g5 p-N܇T,a20DPiOΆ/G â+UOoG\'~g Ž́՜P \?{4m,L_==Θ_ZTmipy)U=;"|d]c}W;]Fm_O@PhtLjOfk:jtafru$}Y1 .9DRW)&Ϻ. h:gPk8t?4HFֆiNmZWCa375 s8Cלd"vk(:ӌ݁RbElqQv\/`:|W:+̈CGFDa0O!֗qO0E\&^R$mwsu8Kl^;v2˝\]%Shv*,¯=HK; ")y/)&V=7Fk~~pxrCUw4-MԤ/[0D?t=/ĔΘ?'6y!-D [gMÌy #(biZV-)at8kڑ}webQIꭽdzzsl)H, FUZ\q䍂3vT?}U.EHRԢ⬤\&2ʁWN -"x6: wi+r%ʃ>I"nŶU *-(>%EKν `:X0 NL_YJ5DGmy71EOz[Q-hRUyɥbocJp!g{?zƑscW۔W{-Q̼^uCSO9x4j}/5X"ٜ(U:5+r8,+)ҀcshꂬWY'5[5EUF7<~]: 7Da\.@C\RP$ך-JR ;i Mt=c^/CbV=E/%WuQNH.X#PzQe<|JzRxWH,9: ж80j.jNa{nZg1b/lNv\'_\wE!<;߰ &, 'uk 5U!IɈ'(:3)$JUH"i }!I%gB:@oG@K2@:0N!yW`jˇS`,OoYD0ȥ4AZpH]!r+KUO_2/ue=~TDyn,_wWtԘy sviFeiX7^YB m0lǼ[eSY@&\CjJp#|ɪ V<6[ߚ?={#]y/Yڬ_ Vͺ `IHZ.B+rˍ{9EC,N`avLMEIz*fI^h&\٢Ys\;ulզ_Wuq?@n /qAW(oP\vKo+ח{%na7[![uвl^gy4)@'J"eClC;-.^$:6FWworHySz'a|bn#>@ ˅mjf)1Z_{LGh?ZAB~ORdEf0vx"dA;7aBf3NQ6˱a|o]M6Z "{\ vux3+ !P|!>LGoHsO ˤ04pH![($5)Ⱥ$<=4d6"lDž-gG}Qc{r8tW %9pX{5Lb]y;p:_$݁2b2v\d/[tN0"N"nl$0H$mM%mT ,I!$T!1x+ÙcX G*c v__VCX<}Kh8 s`N(R+Hʋ ! a@ v] Od1mc%A$"4c:ra41  4{X:BXNnnA9 '%7R7[cB+#v9@ҔYK9nŏ"3 (ǀCA&[T1s>xdb[`[KFք ;|lrg\s3ǘ(c䦱H3 0 P2^z:#NEFblMg,L ݧ)}t0񒛁DF5^2 $g`````````:҂=.>@^"1 0 0 0 0 0 0 0 |( 0^RL93WԬaAV-|_ Ҍy|<: !yͨL9Daϝ~d04ac<+hԈKs;ǡFMa1X66.$s?]վμi[E)QCȄiQB-2e4@5ebz)RbuSD%mclpujĐ;s-q9XPlU5Ba±J)w:O O /1uGGU0Ai}𲂫ڮ}vŘ_ShjMY^PGݓZqJ@ʮ<KEK[8,n%>5BLu_Sy$I,MQMhM*ci/MC뎩 \t}¯ovRN1ZÄKƎjYH",3KkmUNp^|j{ IDATZEӥ=GC7O y;FvΓJ0 }9CLP.7w[j{r.tlщMB\VRh4NhjKk?҆ǼI*~CF%']Օnƅ]}s)nMv_/Az}/Z^I?}՞Icec~Oìgt6AL"L.=eU]P;bY";6@V܀ٰᗟϿ,Tg' XQLhٍ3UY%`Tk\J+(|TmzkcWO;Hr%õwI!,'`Z6X>+!Lw}ś+1,u &A\W8wePOΆ/G j=0US<L3/KW?E΃!t20T,a20DӪPiJm !p$_/.d" K Lbbgn\qc'hw9pk2s&˛Ƕ,A#<>Xz#c#qn9Yz\u)(nR~̬`ϵN͸sñd"7=~kݢWw3$A1;;1֎QƵlpde b/oPNŒ۾b53y5|'ȻG=.]3LTdL5a4] g&`˝ d , {֜LȆLd?Iʫ!ϰ3oN1 QespK~qp6z=0?_Lb!$hsKfGd $)lKע"´ ^2_ fcw) Q.x8J\]yN N LF 9~y!9Pq-\V9}"6, FUZ\q䍂3 SN)BUEYI99٥b]cM%EX `uqqBE,?Z:bz;N5Ⱦ2$Y9[SQy19VobߖӅiم5 IJIIDE3ˑΊeA0DE&-mB>bK@z񳨕W<ȓ`&MH/a}Qdɳթ8O73S+~ŝF6)rmLT 'CHX)Iʃ'Pܶc9fϽE/"ĔpIV3)j碂g~쌩Ko6 1q/3It KU./u_Qr6(! wL+ME߀RRᆐ{LQP0lx=/ |@Fq$V\` 33p!ȉr `^` uhH!I0 G> xSA<+Mz$0^8FZCB%}کVV:$E=PuP7Nsǒ47Z85{,ZԑTs,Ԍ/ǸTcp.ND͹~@4-P%UUX$./Qj1HV ;&oGv\ܽq`o8} WYXԪybKV]u%Y\[]W 3%9=u4HRnQQvnzaIP %qgԖ}K΋5x(ؠf0*78/mڳT>mXPP\kc/ª$غ̓"$xt'I@MjPK*76|dKWX} ԯ?=3qy>d9WWi[M[]TZTѸ_7svX0N3IpmDHOF:6[/!DP:OR."#.5`481 ~gAjBlo,ȅBK:12S ``mIJ?*CHR.)P 2Ee nҥ!܄4'wy_7f[ev#G6yYfZpx!='jhpJ+F Ui H-G\BPg!.-$'H%BqI=z Z {K?"+r8*+OWvF ]ǑŖ#avqe/ *h@ȍ4\T)p"K-MiPrzKP_ZEe%b ]G o),8ِT%51_$FDR-ptG3~lNMթ_9 }Mp Wvu{%p|oPuG'_NqbɉȬ?⪫s ^L!:owN{؈\XpM=܃LHh%NZЉq|HXbQ0|X;a}h7IW$4zJ<`?!fA[yd*0 /iA*hP:J&u&B%CE6?RejHDnݠKUO_2/ue=~T=J=?5Vur& TDᗀ`'vZ\4"%yU%nd+o͟V7Cr{'+}MmsSfHRdrSAM_[/K P?HX_EIz6\X>jX6gakZ kIDpVð I^hc&\$UIyqEnvq/odq4HB䆈L7 |g9:pY*ƎQJxLK[bв{ώZtqInju=`䑣|5M[P&䃜Zs^rtK3.HoQ LI \u;ē".0N0sQ:ˡbxpug{f7gn nul{mT9a6{ZPvMH azNΕHö({Ggj`XֶfEw 5xz S@IUG.t7`uUB\4R|K #3fu-OF<{lDee_ǣ-O d8.&_T# DysƧ ?lJ=/Yn {IC2Hbkq;">%);g.\ $ _7Bр'-|ٗCUƅgx˼23n}~5;Cu,/L8,1>sVUЃgd" ez[Xed 6f3N昪:Vـs(<=-`?~s4gTe;ĕ"&Iax%!\cq,Wvqaoa7Fl~u,B `9 6-5X"b,!{@}͐) 0 |=NZ]^$nu"aCsB`E[X ^Ã% /sNp8 N↑3-M_m<~!6OZ's&),}n/`;TVGY%\ERE0JUmV<8Veq5?fph  4ڣ * i 7@W7gスdeJmj]Oet==q#.&珋qq9f{8 3]_fVZ66t#Yٚf66tX9}˭b>1ёO#wOu  l VHo2ے\~,%uo>+d````````;~".>Za000000000xN]0Hٍ]````````h ԬaA;V-lL)i0 :V!Y2Q7ħFk}}3]&ﰘT0Q0ߧĈzQ>ژ·ZU ۞\n8nqwɈo$_F,=Ӱв}ArC&EHhDW[c {oa ́B$ت6k„cr9(0T0Ġ5g+(힑q ,MQM%t;tAWjt/NFr>Kkm[ijP]t+_4`}DiV <&X{:U$-] <5=wXCMȕ K9èI q5k5Gi$HSuUC{NS>:B3@T=qc'hw)9pdɈ=|w˛Ƕ,A#<>HXz#c#qn9YdA!LP) o.+(̈uF:R%GZ2?%?>^͚J"60Tl_HL \piGxn1 cx~6ȜcN2bߘ-vopDtȕ_F}))Lp߰VDJ0 mZCNF]sySDHJK?*D_vz]_ z?N/WD޸ii"^,Op2Ez%ƛMkK:Qcu@Tأ( jn)ۉs..SYGKIr$2J5T:o\M'BB;μr\ɞP B0RRᆐ{LQP0>PԎBɉ!`C/踇tޖWPH`6qʍ2Z6F8iZY!R7렒C9聪ۇnw1wם;G\hp ,hUkm@aZ6 PѷЭ~U C^:#Oly@MoEeU ˋ+Zbׄy\M^^"Τ!L⶛fJ/>͟ƟssSx]gkk`tX\N۞IxV=vAV+VYI ?jy4\\_L4a9n"k{RERe;Qc/_|$`l8;ZF%5)q?;=1 o+$w6DÆaydcC2CT Ũ^xR6Fښr#.5`481 ~gA*tޖ,~XɬW>-A έ_L.uwo⼀#g?}޻)iȉfc!jehp+iu<|dw`Qm_ b bSAшXPl'J4~&v`1FK" ATAf;۽@C#w;wݙ7 ,uȓ⁇FޛWYI:,M$~نS۹p#]ᇝB&ZdRÌ, ܓ_gS%\{EFUe#VlleqdNMf965;=]WEfJ ̈* oKfp(MԬ_lfm&HVD"}c>ӁsɪR`F @&so os_־F͇v 2/8\|l+o;*x.181.6 7jlS^T?7PV'ysI(kVD:K>,-˚DJ3nu\gnߺoQӌ"BERGYrAE d#lOj|cՄkwkwjb>Ѱo~+.x Dz pty1*1ocd/RZA1>u!5cN|w1 fGʪ2?W~>-.G3Վ}xzYILV5*>AQ2$JPkޱgfo?O&iݙBS^]L ]8(h FOSi>f`^'}A ^SlFmgV"^Nf) :L_sEr`vg'O&w$Lu/gLv|ja]\fϯZXY} ^KO&W^-^wiۭwW6 mmJxj[R]f4rrsqMVYA$UƓBïk,Zxx:gi yԮ~j[u^5&ד|DZ.mg!r6fqHܓ'N9&s$xiz6.GD^X^稗&W(e%$&ŝ0غ,o6#4l1%{Yޣ%Q^a3zs};jש'ObNN5j ϓTQUkr,߾66PK<9x,.[`ف2nY|o冾yO1c?c*l^|UX5 Ӓe?I[,#0<2 %"|)Tk1-"ⴺKci<: CKu^`/FrIjU[i^J(JG2"WZ [t=+*P3Z Mb~<閇aqDEr͏cdLlE J ""0:LEl#Mm.AY%;a,;d Y=ASAT2WńȗDX%*aV<(մ%@ PJ2Sͮ$W |6bSi T_adxʌa4qx-T/Xݻr3ThF܆Y"E8_ D*Xֿ*\My-U 6YRξhc=VÇRG`$A3T),\U8Psd׋fThGD<<{Y4OC%k S )Z Nh4$R`D#MN mfGXyzػ7`*FD-7w.Hd1+ٷ1 ;"AyɃ+ysh 9$6>c#.h%@ PJJvr^YX;Ӗ9‹9đ6bxVp\^5z"Ÿ-r3$轜ȇT0k`$b0eHwA%x<1G哅 ~\ų>_ /&6+9(.9G"4Hg"R" 8U*hphL_j,I$Wo؉pN*h5IIyI^WDJ[ SEqxGrMي)<([`9L dJ#+&$G x"lumާg"mN O<ֳV?sÌ50AjcC Y،Щ5 Yc<MXTr 4*㨇J!֠+yɫ,w-"b+,."Vod*]uJ(-%)0~X~?%<5]^  ^ l#0 B?F#{W(H+1*qN:7#/-<5TC 90%xW I̹Ʀ't!ȁzvGH()"EU LڅWUUȌ<]zSxwQ~}X !p?l q=Am&+ Gر8\ۢVxV Yf *G0.8*Ap1  0yJT CenDf@?.bA=^EFi]+؞AxCnƢT;` zo:2t\cJTkDV zaꛞ_M>?Ԁ|tS4g29jgPs"a<*^ =B P%@ (Xڇ=y>^lυGhw6A蘋]dIh"^$v.ҍ牰`,+Q㱢Ruk{-61?&o#+#{Y_Hn:j2 w_ }qh}+xOc>xE`T {WtLJ u%\_q1ܦ#IYX]k rwn6TB#E`x`{$b8!Bs Y`o@ixn%fMF Uo!Aģ^8] C4ej!l4ȭLnK Cqm #l:8 4艛.TK4x.rDv,(J0:]@dm G뫞:aЫCd:^dλz9,pc$cR4#Ky<&;cg 3KP7yO?cUsayM}0A1<H4[1 1چOH(K`&K_zeJK3 JB̿ 1ԍ{^*,R:K^ah&o D)ORf34f 7ʶ|_ X~ʉJMr󲌙g7L/Ɨbp/&gפs#%@ P# "0Jdzy^_->1iN}^Ot3 g&¶7"u| YKFvOٸmawLvpkŌh콁~|i3敪aK}w*{Eq֤#USB;d.C'2P:G)Jxo?24asLtހ u|݁Hd6T)UÔp9UAF⛟e-DO39EE0r<ƫ˕- j(ޕڑWd$Xi@Y5C P%@ %@ܿݵ dA'V"䣪g\gg! /*JQ;H6f;kb8M|n Bd` i쌲n*Y1]q_ﭱ=#^$/9G ;I+qOZnpy: 0"yp\4&ÆMXј&؅oӑ@[I|eنB8!7,q#4Qe%gfGFA-0]~p bz0Z 4~Ͱ%jP0b!⫂[m { $bo+t  W+:a|C~V%gSJd5Ǖ N3a2%2*Ym#aesx%~U(J(5»[oTKCw?%p/Gګ^I{<&eF燗Le0[y E3ka(G~ή*if6&2'J66Vi"tBLKlq̘J3;!01qO k\wirT"t{b9xEmj:oii;g" irƲٟ!^.sF7>(r'tE580A:7_$|a-ht(;>h 3d:ܨc+k5L3p,9R'аcBE"J˻?NӶGzֹ9eKNVc&Y2 j4o>0ߊEMq|KyQdݪ `gZs _Wʓh=};ebPE!#ŨbѵH2N-/͗V.N8YSқo0|1-M%T(2"sQ_0WGZ/#9i=z0)7(MK} mS~r6Y@Px$D#7~~w`jjGN,`q˻+º[/.u"~:TTgR5h@=uAQB~YklkSY@s_^0-O9&nCcj 3 ȸX+JhCiQ)Mϑ90v[vA=X Ms+f6TwvфK<E;{)R؆WJO^T@ $j-5o1xa#27s:ՠBKK*ٙA{U%o ;+DN7}ZYqT6>*o&׾4=hFi7&~D/+~ZS_TPmGUw)AbTrUh?0L@bgUܸE%+mk"q@(Ed0tM?,V8gr2hj=Kc75mZ6ܒdh6Ԇ򫝦5?-P4 Eub) O X K^ i:vS>m.i>|skd>k2g%u&Q] _ Ɇsioc q38ڔTI!2+g:@>Z$5h@oSqI}R<6=S4q[1BC-Os.wX>ymU_ΑXW@&\ xQٕ00ɥ9& `ˤm;\ٞcOqB 5Ie1碵zb_8M$04/q3őÅ~zi4)m>"Dig*ݽCE'E_nyDtt(7A *z#n 5*Xw2PffrȎ!l 7ğBT-#7LGʔTBJf/ܘK;ED!e_JW:d%?bfNcbN>!t`N+/}#Erb/lY*y0]W=޿3PWE{Ȓvģ70#weT#p\{kPq6& `@G Zh#/VrpWV0}!)˾ ݕґp[v6$ W{,0=tK+M0L^ӋtEm;\fvuvǴZDGn WxUϛbLn_`jH4Ă wW#5"& t1L݉T vSx'ڻJܗW3t,-t(;vIc[aBs̸:& `oԭɩɉ Iǽ&X(Vj;h-n0L0$|oXUCL0L%K K `& `r|Q?0L0.K/]dj&lIɍ?ȪNBrIujŗa9 Mד .5Ra&Х-.Gz;Zľ,PkwˍtF="tl?v[}WG]6A0u8*nkE젠;NjE7ŵYn473Uu\lBi׼G+ZQD"M9{' l&1e9e?\y=Ѝ:IGc& R  o(vse!z!gn u_gbHPQu]ݞpU>:A讁+ x)拲GɄm[\¼kPNMI&Y=P_zh_Zl)4<>;cw;g!>^>&D0WITn;Txl4W| JFqDbSjRi4?6WkAq#$_װ3ZAy&,B/7 b}\u&/ѲX|3dHc:q脆/-~j}xaZYi:v2}2z-w&AӶy06,z\]YDh9z懯je^agujkRK z\cjڑaď(h(?3dд{}y!3xC4MRPd1V*/xrdXt-/ˁ/jb\ʤѝp6'ntTe{\ii^^ȷCMC}^V E][Թ 0LhT =ˇ[?c_ʗBӻgVvFxw)>WZ@$zP]ApPT~y_xZ17j]atneUqlj%5Eτz&l0! -ǿ//+urR' Yf}!uOҸϤ9^wxZ:7?n-3>uGeT AX]j]U,me%h݂=ԋ[ ݻVQjE+6ɆL9.UI" xEYѧ'&yX4H[kk)^Rѓ>UaUl֦UK'VPWQGgsR}|xm#/yP_]g:w^vp& @~ HR584iJ@ s Ȫ˓NVrX]冭;+jh[1]*M&n@cj 3 BD# ϗx,?wzgS*D i`Z\&f_>Z"E|B !Pnɍ_ F~7Nǧ.kӛVM%ϔ@%2帨 Rؗ~XqdAb[2Iu/t!<F i. +:,Bh j*I4?)~ȈG^ns|ZTj,3L 2q0Lh/v㳴r (14WUn@:ieťˆGdTrCȡkj k$_9wUSƎ8igQG5G:JR/˞~̖E so1\.Гis]V/be-@Za4)GS۸>l t,}VBݔfS2ȦP6-na2 jÖ~I3͇va퓜GtM$ʉXkhhlk?Hr~;Tp3`ˡ1`sL-̴D1dqVtǗ$iQ@nOX3p -@AݣǺæg &nK<%uҗEW^TvK-0L5h/λǕ9vǸ/dP²sS=fA/~sVRл9WCY`K]6:+M?sQ љiNHi>n%fZqFb$@~SIIJs^:UaX띴b$FqvB DI(5Y=tWTa4 ;NFa>\n8D<-˽G¢ȢM)JA2b\}P JTxK\,1z}~3'˱&7 hzmʉeWpђpZq/yβjJ n#/v;ދŒH Ub)i8ȽF,u80@B`EZBε*BdtT^|eg:^@w/%@] Iڻ(>C׻܆]J8, & `Eey;"]ѧ`;E6uxljOz•0`Z:qLn+4~ "lK_#L:) ILB3-ie-K+E1#@ӱQ6q}txK4&tS bbVĄ$^ZqMaUpaL0Lx͗}*= K `& `&3_MdX1L0LK,& `& b :aNW-2q;9a]hzGuBMv1k-\tB0L\AIi:oˑ]B݄V[GfM8i'_]aPxq;O:jaM'5{3:X !S&t{RWx]g(iǚs\OF_>M~4rf.x{M _ufN;0LKTTyl|-Bwџ(5b}i#~O=>V_ hwϬ Е I5-{3ԐP֗ hXImq} d7++)ُ[VWY/TPWQGgs4/@}|xm#/`EWxfg0L &&50w`Mx.AYJXfG/Bo5A?*ʿdܺTs"F=& IDATWB4>^0-O9&nCcj 3 ȐX+JhCiQ)Mϑ90v[vA=X Ms+f6 ;I ϗx,?wzgS*ԸUS%a2/ Z6`C6,~37 <3iOYZR!!ɸ٦vX~JԝWV5tXR4 ^ +|TKdyJL\F 47ӱbmJ$lʬ(8jpנM9}'qgsWH{XwLmvP9;yx<6rw /\,C+M\R %DW^Tve; 6 `LZL;~Dwcjh5 %,9=cv7g(m Ҫ#x@y4/q3őȅ~zi4)m>"Dig*ݽCE'E_nyDtt(7A *z#n 5*Xw2PffrȎ!l4?r 9ZF,˽o'R U$/ܘK;ED!e_JW:d%?bfNc-d@7|(B7?.RWV4_F6o^ز+8hI8-|ow^q8zZ {@uBRXV3w\M!K n#Wv;ިŒH ޕRɏ@q]De6TTgc&F WJ h#/ֽrpĵV0}!鍼˾ E $O]iDiA%)@ې67`ޔhYޥɕH0L#z苑`mˌfVVܗv/}Z- 0-fwhi]]f|L""϶T 0.Oєc$)LB3-w/]--h::+mioН7 `@`ZLĽqk\XjG͗0L0W@@|Ol#f^8q& `&0-f `& `]6_t`a0L0L@57|A9>骥zLV.4=`^OS:+T"6O;(&5Bֶc& BAi:oˑ82_{>!zz>2S|MnrĹM_@L*% {3/i~]~R^7vbPtPk#OV˨Zx]g(QuSV4m칞N+}n\dr'=M7>(r']D9" `[M5QjϱuݷE|Ԉa-ht;cw;wN,+g7Hc&F ӕHxRM$pS\0(%%8MJ c+ʎ5Ì/0y#ܨc++)нǒ3-u i?&d_Z$rL/s4mg]a5f. )L;XvF܇[]Z$ _ʋ"0V0_($D+=͈r _Wri[=%bPE!#O˿Eqk}Qm;lN6g隤o0|1Ar ̻;`H&?01j](S3`R/BS՗~MIѻgVvFxwu䷨%@7!z2?t?;055#'{w%Wu_.s;\.Et&/ 8xjМzRPuAQB~YklkSY@s_x^gTEBBqQ?M I;Q+k谤DiMAMeVyNC&2»+,)f=uj@ӵu}|VȏSxVթ&~ `[O@TDPn͍[aQ+^]!B^VI+ t{JD C(ȒZ? iNL8:fwvϒ""|~My 0 aji D MB7~d?cDyɋz!Mng完bC'9j 9/YIsw(AWWBB(+I0 Mt#nG* [=dѼTfE \Q#ؽ m6;i;{>B*uݣǺæg &nK(oΊQBw[ eo,)yOҤ feEftgoBח?\ }iMoy>/#l뱖W@5\Z` @!;%Ҍ&F'j\.ξa(~GH%Trc.}J(_鐕&9^bn:Pn~\Ё;hzmʉeWpђpZ"H.uꄤf(CF.v Q;+㺈[l^QU `@!@+)4 F|_`)+\kJa6BWgt X&@ӱСX;9 #0LS 0-nLNMNLHLN85B.#KGma& `+ htHPLSaH!7 L0Lx Yop<L0Lh/l `& `6_^1W֝"$7pW@q;9e|- RdwXr 8e˯oz1L ;u ]ʇ̗j ςin\oSa5#D~Dz:͢iȃݧ`ÅK BU$oX8+\;p|l$"KT ˇ«y3`Tw_ԪMŸ=+ `;vB[SMw BŇe˒\+|& `VcFu46'j1jM(xTYW (g F: ,U'&x^pq \\^K s66H^_u+ZRp>n-p:_ZyZ ;8Tf_s>oL_l _p `ڧ!'~u% 1 '¸ep;1fmt$Ue6 /r ?CVX>ܕ<k>!aZcf/[Y?/W: `J?!ySka/$Zyo,(STdCW%Wm*kLq`+<=zG}8C@FCIJd SF /%jEҏBvA{}um$ƈP#m5i; TQ">1L`I_tCye `0@B ˇ|3ȂYR~0Aj}d"ߏ(b9B܅&'ޯ| `ȳF ]D@O,Mp==}!Ξ'}V@D݋.@P s!:'=ih%s ~w!w`.4Y5 |/@PLm>B94S0g.݁p!H$2Ÿ4O7n߆[884HD3z[))D|"^ AW-ucm̭rRr ?R $hBy\G[Q0S60L@X_jطAp'0օMvA ُ9ZYXFM5z862~ASЅϫEepg:tM+LJ9d^Pǂ>` dZgZVH: Ų N-M.AW*a3nMS`PP$KG =9ِƥ#Y7]7R_5 /|QU Z&. 5 dSf? z߉vo1^P!9Cn8z}p0LaH?:x,S$HЄN-p`1r_4B:E 8E5'| DjQ1X E"@ |htF@'@ Cs5h!tπz&Yi"+=i@B)6ėؠ'=@6.5l҂02TBQh9Dd*LCWVžÜzd4CMC-H|~|U ;M8Z=4ZپRk>pe1+yњɧk\+yEj1L- ЦX8@D̘ P KMXS诪 k "2|!#- S{%Ex50z88 U"3{-)Z XZQKC_/(->˱R4e l_HxV݀cFyJ2eò0TҠn Jiv拺 MDVl;/BдRS6`RU(x} Y0`(5+qs0LP@[~`M0:p IUcN@rb*)+*nɅX ĩ.|9q~2P=^ =Q: *hGS n1r`lP'N<8v[ q_1Yžp+RC $ x3$+u8 UIc^`V)r.MI4 'Gy >he -`&_/j?ݾEa@|HI3WVcP?0Luep jK?[oe(lQnMNK-/QC „~᧵Mf`ƊMφ 5&~w#"_%Z>k'zNZ6=a>ؾFGu`35ѯi;0ʅi4-N)<2ոȚS9,h`4%cy)(l߁|1 @8<"K(6EZmE0ᒥX_٨?i?XL oP'?K}yEq7&_`yv0#ODQq-ʾţG*]. ;p .U|6 c <@q%Ơߎ}F>\=&7ڒ"l)~ _jK b`fp4h/3l Ȯx GT4tB8-?sHcF[^BM9{'xZ`x IDATWO;e9e?Yl\y='n4|}PԙN:Yu2& %zmO4b:fRUYDȮVݨ/9)ݕj~T cK0ziɆÞܱ}6oeʼn|jkȚQ{/-R֋~>0)g([ +vM Y g|E[:n0L:־C+n,%W)0opBv!fo]tFsŐ{ɩ'=FRQ+;gwWja:ۛ$aFƴ\w?<9ZBIL&l%X]ۥ=cVτß4ԗڗb;oe;YߝZACL?v?Q,+gۄ s0L0.gHX3<[a4es0c]0tWl F=fatsϧ/?R2iwn]kf8|< 73#`㥅A9d0z~rT#"^OҴpV[!ZPZmF;0Ç33׽@²ȣgJGvýb& ~wrnG2ETES,֦UK *lUqxƄ#2&^[˪h+YaU}:(:Yr<& `@!?`[ ;H:EaxzVlBtD+)k@H0:dT<8pof:JV/ѻڷ\?uj-cQ\ :ԐQTH^DU#k谤@h j*%Li~Sي6ʧ1OLR^9D'xtl80LUri]PKXS ~Mƪ)cO4-RNfND5_ u[3oi7(N]2K,7C&Ze?Ejmy-{h|4][GE 5F~Pu?8OamO,`uY]{=X ]& `o.m 92KvAzr4NZ枏0&X%/4)6o,I3Q{@ {wu/L1:<6=S4q[1BC9i󐅕YQ<-nxMRAϹt6cLV+W[9Gb! hVx撺470jʮTpa& ^/- ws1L:/CC^0nIv6$ ł]Fe]ju1`0LZE/}Z5\E;4S۶J1ͧ80~+úy=)ϼ|y9P-\t^̟=G#ma&vx Cy[\Ι$oEB#3ŧ&G45_z9(?vwtE=)?)qݛbP3k#O+*/Dw&g@Y'J:TGu^k,/-N,Դz2:X iͶӍ:ܩ57v*?0L!VG5h6'JgN(=RL?v^q= n_j%kyC&7=ͭ? 1_ydX!Ē,IeFO>-ٰp{S&:,5lR'v ~u!kG=wQ3g/\~6& a:Xqo_p `@cFFn=x({Rx[iw>[T\0km?B0-0>]Dpax5Dd}1'd"F6$9dW'N8f4j[mjbL"JGFd4x",ƒJn}nCU-XVz&?$оy3l=jg!>^>&"`&%6$@\w*<6+cċ=m)lF|^Qv|ЮfKy$?-6N? 7bݡύ:rl_jc:q脆/-A/s4mg]a5f. )L;Xr/̷b{L;wINExĴ[|Mg~jB34?|u_U͊Z"$Moon)/ EF=d)Eqlx|t|HwٜmJMq٧bj|yc $ǘ&Vmh =ˇ3?cm)~r6Y@P&h+-^ p`>Gnc70_d7Ö_\:~ep@Pә :+AsIE7 eMeYf}!uOҸ^:z򢪖ȹj!k"KMG5ut%rOM:5dC4BB5VRD~\=JGMHdU,EW 3l_kLGMah.\|>&Mt)kdU I %t/GS;Z p l|Ga[vra=-5LSb40ӀǥRPZTJvӡsd0]1}PVC@܊N;h%ŝ޽qٔ kۣJ'/d*^  VPˈ  qOי4,UQ|} cFF!<RVÒ¦ip45-5v&t(o;F'& `]@T[㳴r (14WU hNtӧJUCFk2F]|!XyfKӃmvcO53GlMV9R Rj~5yʔ5Bw (׍:A_Nmv[ín&F+ RiFZFX4%d:zRڗJ ŝbȨZe?Ekj kdb @j R֦hXuVmac]7-V4][Gj[ X]VkˊoLժMQ*wkn ZU\" *:_TC"`&ȥ@'X 4'qQNV&M S;gɃrR>{ƼMˆ[r m~Æڰ_~Ӵg&hN̟1e"K߀Ec7rQLᣝ{0@X$'Q-]!?+3qu%jJTH>ӰO(oΊQ]6tq0?nG7X\=Kжs.2KYq;Ѿ A^_p1^tRZ劣G4NGr|8Z^1Psa>h5l>>x'ef&–J3S(er;!PuW/ܘK;ED!e_JW:d%?bfNcbN>!t`N+/}#Erb/lYzr4Z>Da>8=-˽g!)JBwQ,;&%vQ 9p\{k͛T1M8%U8ek-oNa~w,&84tM(2ivHvJ˷!Wmnt%d,0=tK+40L#z+`mˌCvɏQY7%w/\7Ğ͎©i>}׉oyb80Ӹow'U]B牶x_^ ɫ&@ӱСX|mnoo ƸpLZEi1udrjrbBbrq ʯiU +z_ޘ#e0L0L'hY`1L0is `& `jZp!L0L:u梋KB7`Å\tJnEVu܉wPL)XB$?k"V5)5 U^,:+.GzoΣ/ fc%0ިG" y<^v\{1ꈂ&ݷ"1zeTtIH;"`ۍfNnM8-?sHcR_6qJDu^k,/u٨=דљ{WOk1n4|}PԙN:*_ `>VW|yKBw[7>QӈyK^:']=,wǃsqF?ٞed_ͯ *+/T!cP+skh/>z,:e#]XuJT)ʇ?}ZaFB% /Z Ďp5dM⨽Ǘn2¿_8gNG+&Х X8%GU}+t7 م"szOD)(.+vKθk+y}AEE#F]@7ɻ?{ձ^D(XذKL,hKO5$ޱDQQPTDA@E uww٥qљpΙs~gvܙw_kqѓ9׸'q q^fv5P_V颷sR>;]5n))o"}/{J3O~ITl\8 Zhl7a7pÇP#Q^Sòm}hiИz. [rZ/kds%k]X]('cvr ö}'xsc럆[%>93A" B@i !m+>0S!A3[pa.}/pCc:`XP MY g}GZR邘roeq;-YrN\ YkSS/^34'yD:#9:zx6w¢/M>Ū]8KjeiҲs:[nW `FyvA{q ?u u.KO?x6 94>=@ҟH~/N L}W4$DmyĦ R^MndR4s"#3!ۇ!oS^}kS&,Sn8^ ʍq5vLO$KgqV&bIPrZ&~{0::,uA/8ŤM}E{y8:H}F-hwqqr-[oʥd Ғs3.EilV_EhwI=РV ZRP̀fAX exZ \.ȖJP`ҞTC@CM ;aT.L}|;VYKqenXtv%,H{tzٗZmWڻrbG=mRg$bjܺ֩/O 0T*2" '_aAnxXl1nl>bT*[MCPH"f)sg~pz 3ţ~AbB4OOYQdFAƻdP(X\AeAfR& ^1ѱ.QŒ7丝dF 2Q{_z<>ڍwe((2xi\ GĈ( [XTb"AFfOڈrSiܦZ`B 'P0ÓGiư׍3÷)i^ᆣSπ":-ctU7C9ZF쬼Rjjza$(3&$f'uajlѰр]Ѳݢ;hc;S;> >L[JEQ##VR\Rg~+f+`$p)Llv\ z-/NfkG/"ַr2¤F/}Wdl\T؍szInn~FNx> G ,#?IǴ?{Y&M>l-Ŝ';~7]w? >S]ǩ Lv*lFk6?}7&Hl;AANL5zl wmt !]g ss ;JAsϬ~۬Kn y1l|@h\BzDJ[Ϟdf5hsO^igHOY6zQo6'/T!d@#@2ulN拮?y26>!{ǘF g.ϨWwizpd=Qk/[6Bz m~zeFsү(MO}ʦ ('/3zW'g!=u?JvFU)DT(3te/4'm+!Ƌ pd| BSw IDAT~1u!@m&Vy}wK ޾LBx|/JTNZekX2gD!@T#5oם0QCKVt )R~$|Ar3!@!@0Kzra\:B BPE$|QEB B _,B BPE_HkO WAmOģ]+zJ/Sϖ;Yj}U_Ïd8K!j~@],7@s9jsn+kf|}kz/XERI%.{N޻w]A[VFC^)C{B+1NehюbFB TJlݝ x?+TDQf[?1JpC)*,zP^ ]'}|De&ded鴞rM.f:ζkԸ˂}ڤ8,err k剢"!@TY~aEo߻'v1(2L|T!u~lvW5'St Ӵ[D_Pld\c{{/ecJjȠ('cv: ö}'xsc럆[δ%>94PH!@ 6P &?5:,Zxίu(-|7w<æ_s#b;x܊fMǰr3[˖&|jS&3m8dV{R>|oN)5jE&aZLDjXQSҀL08d!:_Ԧ*)An{@lZ F&C0osd~)&m+JG _Ͳ aN4=6fܶi P^d2Ca-2oǕ=H fxZ 07=^&lֿETTE!j֮TS>Q )ԹEg_ֵpN xyZ4 ԐW8P.Y6X "2az,~PY N[7h$:Ǿϡ/â|. mƝ:41Ъ|΅Bf yyIo"NӡӾ7+9"#d3*f[(791e9'7 5n~V&\ԕ_iEQ|C&eRV ҙ6sg0Q=k^1e& Jԅ#BE?Y>`$p)Llv\ z-/.fkG/"ַr2K :cnG`PЭ||ſϼ[6@Q֓}id^:y}=Zfc u`cwPнkk;J6|TdIu.'Y2Y x+!#9̼t2x{ǀœD^ԛɋ1ʿP!"Bgl~fUOh{*ez闟,VjJN!Bv?[_UYD<j#w9'mݨj̆mIV5>!@L@%—j'ֱ1+尵ll,jtƐ!@@/ B $P>|]5ɊE!@*J/*!@!@ r B T _T1bVÌ84])v +/ϠOpM+D{OP5 >5qQXއ-J/>Q!x+ cttl(.%W6a]|-0u~*,w (*f9 .e^C=wޖ[udr7R^9l$>\!@( 6<x=$3L*fX1uu_Be?+!N2 5r悜کKTr RG2gθ uU4Y )IIU`aOp?OTe1̾h6èij4&BP7 Eyus@oq a;H@[y#]q[nNF/vDG-Y AbA9pDpB+Qʨ+v 0!z ucil_p{ͧ-`Q%8uSW zul*EH /ӟN 4d6Y-0{ vq^)NjHyAYxzaI]„6Rza%u@Q%l© Wq/F'2Am.}h6h: ng1sPq`dK@f_n†;ХS8U΂%]P/Vp[V!V!{aO>kGOE= Y-z\jcU{HX`kY{Xxnt/jR6|aАCpd<4Y`i`yL0@fSKS. Ŋ0,WUw8KN 嵱 ڋFKr K~ 'O^ֵFS}̠%[}ف “^WPy0VK6*j7ܰd4Lkɔ GU%/ 8\$ڎ¡Ab fұgKq\Pʦ;{]!hrEwgeOz?B>5y]=xC1<C2[`P(E !mZRk U êS6S.f ˉQ|A C4Ə>ըyA:EDWiHӁslƺ#?k}15˃*"u7j\ 0dQ7p&xZQbPA QS0.v5Mڳm4٠>琠y]E 钮9|1jѲx6Gm2$#OL!@_-b1!@!@%P{U\$B / _< B ZN/܁D}B B#@—/bB B K-w`uOw&kV<%0< />LkGkRBHh.lj}B@ `FpCc؍Sn)HLJz^ur'63:Q՟o]XJ$S+B|Ip}K?W2gS0JT:6X~=pΐQc2U.4w7yJk!PT-|an8 CXɪ:q,ԫ8f}[zӿѩ3l= GMGq.ua>`/_EW?9y^#lل7{@|=vyVR cGk6* CqBY`XMWNd|J?`ܴ݀`YgLIU`9G7O)u[\'.)ƨؼN?K^pY#I2mwe10m.% 퀭8YxXA1L/gO,]Xz1ڲ|WttpERdʠfon4ΓNP4.` sU/Kɳ}r 5z Kx&zzk5 qM85׈2T&E5'wJfC_)Q(E a]*Nl$ka9xl^&#]6!,R@)""T!u/u"m,̰ d}4%PY_;wJXhYǁLzjb%Oy&'[pHQcͫ8yl!rS:ڰm n ?AS}-K|[Faz/~( 4=:5赇E/q 5h n+qYe BZޗp1rC"ƥtAM ë>&)9l3M?e%jSuzVȰ\_M[Iqςz0%!DWmO* 'pKcl}[pKMɵK!Mϱ=<5Td/%G>^ᨃSwfC#;נ:`8-DPO>a?`;M$3 ! _Q1[07jACMH\aRt3hcQJs5$M|O7o8ߡN*(᯼wHM'Fvܥ8H ߇A"Ī Oɰ7_z|;NȢH"H@#^^˰"/zceŪs+1Bg@Q ۋ[pߛXNyqX]7_e:5N.*q!:Aֻ{@$6D-,kb؆<]%+p^-"-.pQ&8rwr3õP4CSh3zCQ=z4d[v1Z +Zf.v|MO x#awҩK@ ֛GTN|tc6]uٍaSqc7'Y5.z/G'T@7Tl뮊r#j@3l͟vRmyuLq0jJ}r:)_v+_\!&@—&JgL tny/Ko°b 4U%@T3D/B B(xj[wB BP@/ lB BP5$|Q5}B B@"|hd+ N>@&@!@%LoSYZ->@ZBqc[JShIE8tOR' _\+ԉQg۞XD sРE[QtE%%| *Y " V@c8\;cbɻQ|B}?s߸-7|o!,J|Xv]rBȅOG@'i``o;UM*a5ʩBBQ%u:vňߡ9+>ztR*cGVsnu-oi+H_jB+ de|RO":4:'0ajg~;L> /T! qH}0oʎ;7^%Y A=|ׯ qGc식3Q:Zza%K`$=vƖ!W=鉿W<s`֢xA J4QO); EMn{8u^iF]<@o +v 0X<|zIɔ.` sU/^1(&ku3,bBpz8k#;Jh٭L91Ξ+h S4@e  yGzHS 5 &`\ٱo$fZl,oOt.];dn64@iJRρ3mREVE c {N&beqgu:* ?X"BW!]!DKDo#s^BuqCIuzc$ *YjppLl“5X}G(ƠFPQ.f_|D0.7VFk4o䊐'v, cH@l u:>Z " "r6Ҁda ),K렵9žU~Y`B^!_ʬPDNbizm>s˔$%:<P 5WzY7Tc[@I[S;-M'ɠu%C1E^+J T. J1_/ YYxGICFIyzfХc&ZgHPG:R+T.=4 wTpe#\WX Q9 &é|ڈe>DxF H,a44)!0!JnU=>3'N^@&5*]09ԯ_2(@Фg@(K6Gq< XΆ^tv S_pz['"Fji9,I"Jњ,xdx ahkvP r`7DoXI{4h2jh;2/g&AHSGDY*h,ͣ-O!yx&yL4& d(m*ˠ,fr(( v񊒁I%td@LECo䑈 EOE: ?]fw?ط #JR/r%(sC&>e3d4n}tlX)HmLe|alڀXV6y&'(Oi(ImxW,H+%A]4q$jј &2[ +lp)"炒;Ӑ_52!ԃ~aT RLR?"J*+%ׁ f[%/dnL2"6O* TW ufLf,\6)pZ ʕVR3ErUrK35v3 GC#`?PKwH Fvܥԏ]C FHS܃:O@Cmp5Q9:ZJePS0n)C Cs(5$C60cS!"<P8A.Q+!1r^2 _wPf>"e^k=A#"<]>U7!K`A5;E*7Gx)@ b_3BVz=gy6dR|LPAe\)*@Qh{z ԯvtQי7/ZD>ɰUI5 dƏ?E|!PM-|=ҥ8݃o͜w'|&O@X:V]W܍R;-8{~8e!Q\[:'~$~h^e{%fRI[l`RP&tB ߐ0jb|,r毸Ǡ~ e'ႯM+Yl 6˨`rn<փ (&x}_^ 4 {@$6D-,k%(;HɏoOo0\~ǣ~D Na[ "BrLPaen`r*K T]J3dĤy_GҞzeVTTWl"֟. oLcS7tOLNhEdƭvu*h ]nR\ MʫJTyuLԪ%%dSWwO bD@eVɪT A"qu5%bTyS=>ozl>xxO5b]X^{zK̖VO3-ȫV_QR#B B|yG5EC!@5F@<1IE!@!Pk ֺ(N!@|H"+GG!Ptxޮ&?kO KYv*F-WiW1Tvއ#mIUT.|Qk0|*E9Ik$Q؜v#٣zG!;]ZRM_qnYPRuuӥhS\%OZfFCFV@VΗe?|S_[9AYAݚ"i1eoT84kwe%iatE>>:d@ҿjVdz)4r7Ԑ]l67}vcl:UR-(Eעjꟑ 5h.f#jݸӴ&ʌR5#}U,S{e{ʰ#tmרql94jf,:ݗ2Z-ܷ1u $}A*DS(j„fOHOr|g-Ll%Iێ.z ?1A/=Ki>UZ($>w__pI%7|YāISgW4ԯkС|nO\>3x؞(EizVgJߞm+=Lw>{QC,KRV.-IJ461ҡyWT00}*goºo>-)LsiO志$*穩/13NZٹ&9>Л*6/=vԋ9_-˵KB 1!U'',*dH'ɩ  _R}0TBnFx7)k7GHS'E`sdSq\kkG2dCґ9wĪSY!L䯘!5Bc\eq5ԑSIR7yvEf~A~YQ~vS1j30}]|<^&qZP+钑Ȩ+7LRE)f(GArsZJDpj:ԳnA#*?γwDWCn׎:.-z݇u5Ԍ`b.m3wf5^޳测?*P;̜1rS@O?S]JZKq] P튯)ַ_r{ع{|ӳko3bf1.qNUR9,rmĶm2_*V +:ā\oicic7|4MPij\-CVݻYm6te9ǩc7:ޗ,x7[iߛfֳo:\r<{Ѡ%Û\c),3 4ZQa7|LRI6Y-7veiX%J ~|,%|5l ?2d$8{o:_5:uh#*v]h/&OLZ^ &lx-y{o*-~oXW0LSʏs;b>sVZlw- _( vzL<&of6Oky?|Dё0MJ*`kZhdLCn~l"/OzKPȢyH٘w|&jtڎUR^ɋ: }c{"K?hfٴJn4ߡ=w>/LOcUX G *9Şa:+c=g' )u ?7J/[[n;bԐhޗ ]?aS!6 i(dJgo]ܢniEMe2vE_[RO4mȏ?=-#nmoIQ] j2e233JMЫ +Šr~ e5ɹ&lL_*|"Ώ?5u19^zzyƱIǕy( wzL*yZ~u^̉y)*B%PY}# A{7O;Tu"@(*N%YHmON)f |rB($@B B(ޗZƓK!@ _>tR%!@!@T _B%B O@/: B B/UG!@' @—OTI!@U!bV˱?m,>$B B P@!?+^hCE!@!@ WwFmG,98[Cu0,oB!@SCsϏ]zb!@!@T@_Xf&: xsQ%zO!@|VT>|Qo6q+f"x?G!B *I@fFtǫy!@!@|~T>|B Bjʇ/*t# B BK @—/FB B"@—I IDATB BK @—/FB B"@—ʝB BK @—/FB B"@—ʝB BK @/Z,ySC=ʒ+P۞GۻjUHi~ >? \)OϰVd2Ҩhkު^e⭉I!@!'NRKK^^e\qKMլW,[ }Gk^['S.^&,,iA>IB*% *:cXUN;eFySq D >>݀[aJ\7OkQFln-lQ&䣇qD[[[~Kw~br\w'i9~CH4&ZM:&)@~V~g/ưZΚ[ί\_&Vێ.z ?1AV]GSFUzAzw_ە_ RJ פ_x]WQF>Y*Ln}}/ћFm譀7.(,B#MJXG_We-a[}MN;v!*t8$.!!.̮F1NC?q'QqdSS!A⨶HHZ@%~)vzͶS;c9zA ˮgũ7|ob5T۷(ay{W \פ1ĝq/5Ti0s1 hB 5Xr+4qbJO[B]ԓGFr[/2ve@#v#-}id_gciҲs:UX]<~~z%ϐ/I'=~!k^׶GJ{#7= 6-_Tjv#a^,D/SL/\RjY3y67V=0'p 3n4aj-w7n^u]#4m|\gxV &7{k+HgI+*Pǫ _r=.G$zqy󾄎kT"XlzU.i95ip',Ia۱Z+K&GZ5ԳI)8^u6N zR䪡dS(}˰NrorMS:RYըI#Xh{t{8u{մNY) H!BPizTBUj~̌Ӈ{vv7/eVE-n]I gԀ'jYES@aX.Y .XjXKn᧎|9Gnݠ> Jʠ`4N@݆K{ws5*LuU/$%dXuPGW׼+ѭ x - Rt~r|҈ _o>w}'w5ago{Sb/%7V)ygiu(^\嫡Ȼc`QW|ĨI;biQK3&M;cW8y^ը]!@|aN6&xۄaeV2eg6f fI(3 RbVXg/}ڰ^~~hEeɒllgT_O M3'?1ܟtp]׷V.º* UnS'[u\l:plVDDIi,T)2cl![52b%%NMqLf_ q;ٙ7eoJR^ܳ{/bF|է[!Grʆ ma][4l4`WdQ sw聝o+W@@1 _HZu|KqMLt56<(s&x7[iߛ)qdFWl &',C4䦡9gId(.ۊR-V[V8K몫:к'* E@0='ZY!$KH{90{w^ttaH# yfW ]>84|ꦧ?i8wslVvy85v9F weyPH>ekgXQ(=}gu7S;mM*2q򔩳= NُS˒^Y:cDGIQKX[i`gc &La~2gij:,%Itx IX&-UQGѲ<}^@a@# ddߛ)ILK吺cΒ$Լu_ɘ8B>z-)AʦM=f\]iɰ~xIB>ߐ(ҟ Z?ήeZVY;~q7':kGh젳#?V%eg'Y{gQ7e}ߋOKIyrе[mЕBUųwBAa,[heQ!c[k?zB2I1~*~D?ZxǝܔV1K.ÊOrNn0ELV7st3;,r >BJG2kܙ]C$km]ɉ|l KXX~ȹWS4Ge۲+0 |M?ۭflþwsY,j7A@/$H_J@ "4}Q @ :H_:PT @@D@ @#K  @@} `,}Q}@ lJ(+{{ o'p8 @|/4݁.'9>^n] h X 4~f!8Fv1mO>kkt ]iݗ=Ɵ̈ q-#&ʹ7ju,vP N%SHV*V+ΟZzup++ygH >g<ڡк?0-mf݆-:wzvPk+Zu37m~bNb]F{ҽY"os2 jjy{[[>/vj꒢V7[liW63|њ=aE;}Gk_YyU D֨)}'(_^Y_JdYHݎ {=KvfPX]R\%$:qi7Kn-fm`)oV1M^G#坑сq_Ʒ^q%eễBz#!9Źg6NȭxAcwS{n> zrcvHacqذϯ޳e1ag@#Ӵ~2ȴ_zeT bty^,)xWe4U^{SEQqj$e_I?9pOW<89i JaOqKmF` CiѬٯ%]EX.?̘bfߧ 8VNJ;zhBE+>lijnuStDv*nϹ&~|#ޤ=~zknf='(y撟 ~w˼Y]Ѻ/8}jnq3')pŽ0 oAuzı_쿻?52¯_N=v¢ G 4hؗg顨w^hݸReYmN kHjl /bZ;ݧ|5} 1lf+T^XVt$OaAeQ2!IXVh8ZBe'%G5/ov6QtFtܽySˆF,TWRYJ&6Td=:q?Nxwĝ4I%i%,z}zL3H$#9K ,s]g8 km)|0ipj@P,ei8^\w|aMQ?9 F*KJsR^-/jcC)dGI5+-u%dK^0k9M<;6Ow9BAY?йtW U@T$#Sw8"%---ꍶVE T]x.#'4jsvqj̚CCfŋ/zT3-@ _AMe(Qj3 ͮu)t0IG $nSy,=yqF/#nts9E1|.GOԔ旒Dz6:>=­j%y2fp]F0}l˘)3+ȡ6xq' NF5ʭЃw~91a7ۯEI+JPc7TbD3/–!2ҵh4n|n^)Unb>w)q@,ǯƒwU/Qv[IƶhtBuqċP:>%K_(B_&wZuQz  ;w6תٛ4عm[;8"e٦ջӐT3mͯW9e^ttaH# yf<ƠI5qfkDH-eKg[k=_aR:v1$)VUڭ˙<4t|ՠkirGzeʍ0uDftu5F+;2~ZFѲ<}^ @@X(15{ NV-: (dus;GG;sC_TmbfȓJH H,{ Ζu!zT[ڟL9(dD|CNLnJԃc+% MaɧI\/'o9ʭlI'g]p_J;˙/LsV6"_-q(erbg_-qiZ_t2/Қqͽ$u(G@\ˈ"#lvՕ _ُ96Lc%:ٺ{~4/[2ugvc X (g> bOOlzV7 > JAF_" `@ CF_pi @PX @|(|( P/ f@ ""{K{H\Q&tl+m(SC@][㹸~P m!23sɭni}P@ t7gMJkry2mE5I_Ed^f.mmm&x?pntQ%=KvfPX]Rp3C; iUjJ&ӡou  Ft;O?Wukd"U IDATZ#?'-/<>%3T6`SO998Fto͍/SyZ擠gaq! ۯ;Ⱥ~ZP ݏ9QA^珔Y:X+T҃oM'husGAaz'=;';9x[XHxSZ.Yrt|]&Kw$Jv;9Ma}-"/+!.b(]Hfp]ƽ2W Bl4k9s/䶡g/ɼmSdYES)h濾u`)j.#7\=!BL{ Jo񸾋0TWd#^b N>ZW7=3cQ W?a?h}?(g4۴Xl0y(*$V'YVF#,oMmкι:  >}u%8PIf\n /r J@@.ʶN]e_K+TE RHV+}Vew+gBndEi>e a%3_5yI\r f 7gVg]i[%57= #k J++dzƈp 8Ou>FXoa l|NuM3>= *J$q l822R"  ZM?]|K~mo=%yu̵jI ~AVPBҗ7=!&]vDgĈ;gՖPU(Rљ,3+섷31m0Z\HHQƲ-|jd쐞 _'lP}b2@+) @(YmGsc~3dߋ{Amlȣ |tMoMTINQ[",K!V#Gy]2D*(Ջo[Ѓ{zկiv={yf-kFGw&JҐiau3eV7א(T f݀WD6e"(!,qYܐLc2i2dEJ45Xؠ@#tKقc%Yti?GKuɃB?ªJ;c, Sp|򵦣Cn`c4y7Z}Ÿn"; ]}Oхoeq3ii5~MPyq-  4}FXԖ}GWn@P/7 21kE1s'DM!NM"ƞvgtXK6 OVaJ=Zw)a{,dnS&G)b}dCQ'0dOX?BĘ@- @=HJԂO%u<b WǗݿFjc5җug^A,OI';}!ͼsjm5?a`T@|/'hڏijcS+ӏ݊*%*!}, mC/?g4J6AIn^nVۜ"~y~AVPBK_VRf׈RLahO  2 (Y5O8T 'jUJبQj/LO'j/;upJ{BRDȧ42n7]r~P~܅hЦ(mymuQc'o @(ǽ<]GR/$U~ʼn <^V;7\°sq"M}cxݺZ~ Ia7>O3x @F@ h\Gq /5r`7@/mq /5r`7@/mq /5r`7@/mq /5r`7@/js]asD+DQs4@] @@y43Qhke|WB=]`"<EC5 @@(זb:4݁.'9>^n]voh(“@%!tݲfO_>P @(1鋒84 @@I@$3@ }!J  JB% @% QRP P(I @(H_z@ EIfn KLb)8sɜvhJnJmG2T)2,#/ˡpNcwbaj@58@";tI? JaGbc n$[$/`X/y{Q܂*}=C36 9Nb7Tnu2{|l˹5ӼǔU̝\cc/}uT:T! bbQ~? _Vi;xlF}EHD,j\mWCX< <^diw=uuM/ݏ4vXTA|dd*je3.gx kWnԺڊVLMs+ _H( kɽS]lRB 4 +<dO|uf{]MY[tѝu he3.#Q-'ͱ#.ƫIvnkkk73|s˭QݖƩv1 "d oJV"|>~N=Ñ܈3v']O|cFDczsIгظsõ @$Eрj&Yϳާ/4QO=p/+}.|% GzUvrύoDJOgu8MuUn^ic-?ΪT ;ٽޓQGm>).Iw9H2EVMa럱GP"^[Ja{+(/Oaz'=;';9yBpҲy7O64 ?J0R׶L7~A5\`J}AmxSZ.!Ge2~knx)=K~.x 2fͺkK6H]x]FD;O?Wuy]R{O֝ӂ]?~̉ 2<tvSlv8ppFNc{K' O3nH(fHBqX wTඬp㿢D5=X`F#,݋< ƁwEwC>Or :G#P_IDk+xx9te4FBG2/+fкL휬:*v񸾋$̠5} `Gպ~?LѺM d$S,]F$]w*(KG5<0mE_9rZo6L[ԋ3gO~v0iZ}g{PT-7:2L^%YTZygR|w50ݾ&L$+R-,T$zUuoeM{ Jo ,T&2-;Iaaƒ dЅP gLӑ ~:/!"Ƴ=3cQ W?a?h}i~?OP0cWGk|2/3JVaJ."y$' =8 Ljxlmb4Nىd CEf=u"v/ V iXZO(PF@ssO~mr-?D+D,U-&L -tE|*$ IBaȒ(:~ӝg7}>@Sh 8Ou>FXo(pGS\#(Kqv/Ho\w|aMQ?9 T5yI\r f 7gVg 0M)*}b?yiafq= *J$q$uUE U+)rg7CDEB0ߠݖqaEv]5-̺pk9;ض;:d%K Ew~^Ԍ;28(4*h~;'R%N]xL?v+ D+DLXRARVj E|Ԕ)NB2֬<1ttL4ߌ jVۜ"ɭ_/Ե2YuI+;oV04T$l( SP^RUPX~" .%̲8N&KH]%P^r~~M_UshӟU$>Z%h -sjR]%`ZS_\ wmJ"vQ"/ue'KYP־z2OVܝӜ[X(-\0[G#]z)5P9= 5L֣LT}b]k+| c\Pܼ YO]Qph nͽ-WrC5XV^~Μ5ۋ!iYu)L`q|nWJ0zqxc9YRW*/#:.KWH<[rZXXCPs*ƚ-ӭȊ|ioɢDa?(=5_?Ϯhb˘>ߙAw9e.go~WoM,A]M6ټ3}fޛj}8(mym$W"eĪ)-y `?bj;C!,K?OX}–Tv6#c!2)%޾sƫ"3CKbp˹{Nf=֟O$q0Ռ).d`{m >twho|S+^+"OeH'7†]pܐi|]]2 UV7AmTJبQj/LO'j/;upJ{Wd%+n޸gN'bfTN>"`!Z!𝪈;w6תٛ4عm[;^|JrZg{OE-[;RBQ;3~Jc\-mAh},~ik~ξ)H|򵦣Cn`c4]*FUYjP5_Ƀ[4l͆ŸՇۿYjωRn=j Iԅ+!P1,韎`ab24/މCa~2gij:,FQuf6F6]#JxHR[P%hrZi馣ۘhаoҒ^UPѺCX[i`gc %S ʱ"2]‰:Ib ,luVJRp>uwc8AQvyl/;t;y9_Nd4q;h㺉^AkYy98--xsf(t[ĪNp8,98Fth$ uȻ"O!EiO{'QC<JtWQN7ӅgɢOV$ftJW扖v5jM>w^bz[t{fگ88AI;}*D.uC1ίIW WkLuGfwI IDATuՊt-PԬyx)ڣb'a~aGV\M33R Z~ _AB3#s7< Ga.e^uWg"͐zjy#6gD 9c.&>1\#X(56x[82"/[;\HQ@µVLeF*(gd*;ĵL5zD:yt% PeFMO{[Wus7& uyH یжD0L6w 3{ZRrK@B[e%d鋚wmeל QРX+S$,Myx;T>pJ⁆ZJb !!Aۑ7g], K%QPr|M@3_Ʒ^q%e! QX+@ָ SCy ~To_dJϏKvxkbݾ#[o͍/Eܞp%'>@6 G. ľE`Z}I<7'|tQnѩ - Ǝg2ا^|,,6.^PͲXIN999+zHTy~<ރf4bѽo3H$(DB 1e,vf4i㵈L^v*oקFdI:(( $ݩe2#v-/<>%3ϴւ tXHw4.x-uE<.Q9V$Q3peU/= vt."r?{2Mpl}!+ұewoDr)u"@ 't?*5 {rmfMÅ0x>6V^Gĸin@yŸЕAh7B^͸EMԝiv+b[<8 L Zӧ߸7JFj7Bق>@E^/[Y`,ZG NFX]=I>_f)wS:Y05KIѺȺη2B3y$ wYzu@f]P &`Q 󙇢RocR܉9;Q9xHSȬ'9]dvq QCɊ;L'>1(ڣĝ-yJl??N)lM~?ykEB¶tU !_W o| ߝGYeVXzIiNyB-u8tM^oY͙oٱy&I5%Ar5y!}1D}Vew+gBnn *$ IBiP:j|w-Vf=8[d: Wąuմ3Y,c/ l8D>)el[j!H)Լxiuv$#bke)7.*6ąU{=Ȯ@ջ 9amPŜ?|xCދ9\$ +n ۤ'/ol*qȷcӍLAEB*~šhrԻJZU E/f2{[K>Fь iR4w]ĩKnEh(BHV/ 4?QZ(AMeivcCN[ϻ6%A(wo#@PQT.Y/EJu̵jIr ~AVPBK_Q1]y@mXkIU5%o /Aj3 ͮ?)t0IGDzж:s!^ LΪ :r٠Dpcr U6( .|MbȖ5ů %..$vHqBAIntQPp B+糾]0sO <&Id+Q*"h"p"U#|U6C[v(]VFLׅݨE4}95Q0M-h ϋyg6qѥ:C'w//1H3a%K_8ƱxFb"Z!k%p:*QQ5l d*+2~+Vp]v,Ah>a [RײdWwiB"~cb׼ DTeSckEM/yp몗IUgf]Fm(,x%Wd(mymӒWFR{gz=Q{ؑFS?J H1kJ^ 4M(4M,l8(r,D$%ˈU#6$EذJȠtb򹾧V-3D02lInɖ{feQ(i\4 V {Uʑ%/;6N(.+~# K v-ir!/w.eo_{٧r ^hCPqm嘁c|ӲGk3R/;:hYh(BX+W/_k::0=f] /^>ekgXQ(=}gu76}AVVcC5dj-~qn6Wrd#0%aj3<>덭}abX5AAZUoat.6 AoW"šWO.ֵKɇ& OY{eX+O'o_8B8.+z#ф'7=$(\dm܂*!ELuƠyLOŊdIћz$Ғa 6>Gڪ4}'&asN&d~ؖ[dl 7;OD=8r6/UųwBAa,[heQX@GwLN w5 AAY؄\(< n~se]A_ ~;9 raw`e9peFr dGL1krD(RcDW [T}'c2w ./~{QokeMKͣF™G@TCB ꇜn}5E3dz>I^M'1<" Sz昔ދv0 Q }) &rJɖ6r @(#^?ٷ- fZ@T׿t@X G mC&چ+H@ ڑyԎ* @@ =x@PqLI!>[?΄G;h@%{Hbʗ >rw[ y&:4{LJmQ3}J @(J#/ sOc}Y1 @GI_{`K'w@ hA҂XCS @I_hz6c+Gׁ@ ZDid/N~.Vj- 2/Б#3ѸN- @L}l@ چ@ǘO_=ݿt%V@ @PE˰.*$h(j@|J|* PbM%{qZفi@ EIf D @B@ /J0  J@ $ }Q@@ Q%@%!bPC@]5oq+sɜv-D\vĘjnPz%A >oi&$5Tm7ǥxYW&I7nDgp=Bdf:璛^d+j>J;ʧK.BGOW׫Zus7527Ygۮ$DGzl(+"LWW[fS9'fX ӰՋUV\} g?lM4#7D{W#" rίӇ:[v .ejgKY|"B!B (Yf=݉vTAE~4ָ{->w wU!S8XV$ogjhXOפ\ӠG7nSqG5y_zY@U%qإt09%W$ўAեH vvY;L(.)"vcn1X&)Y/[2@$H@1e*6Tr{>YQ)uz=J"n=S\\*ﭠܺ/VqҳsC{7DHҝNZ6/3FuE4 ?JLLe_2}+0or jz(zc7^xgoGcGu Aا^|,,6.p6mۣ` 5lO6av>+D h8&O" mNb m7( 'npc}xAc{K' O3 Tvrύ5AX(q PF"1XTDG&_$BhkT߮O( q.s,$U/= vtK6}2/n%)VօɬNMѕ<G)oWlsWx h$Ţ{OI>uEb|-4݁.'9>^n]3D+DL+L{ Joc'jQMq3Erhak_2y7 dQ)jIHX:İ0ݾ&L$+ -,T$zUuoeMvWv񸾋$̠5}ϿDI?LswC0$i(lƨ㉉Gi4Cy1Cޓc41^ eE[2zp5'{cnI^d+ϊu!e!j_R f^Ldo;_gwaeփE$%$2ȥAYʍK~1DKrf k}QfȐY%`psfxvlYj\J$.G}˄$ayFpXcWB*/\и4J$֧QR!_W o| 2DT|ߌv?@ZMU}+Q~ @uU_|IPi&AݏAKkr7R5b.zZA`e! AQ*Am !8}XL(-t %Z.F5hȫ̗+( +2i9gօ\Yƶ߱_hM5@fڻ.X%cJ4EBK%Nk3񫶸y~W;廭\3'hm07F|?Й$ ҘׅՅمd.TR>f0M~eCMTLS z*yݦ/Xz8h%^_"ta)dZ[&{b8]V$VTq& 2DڷK]OTpSqFP'AMƎzx(e6wfG]3trueLM))dՅ Be8(&Z>tY\6^]hB|?< :1o7}N- G >JnH{Bϙst{1D3;^;(Y5O8ؓ Q&,*zOЊ.FCW/L۷j潩ڇjrޖ+PYXFQZ ?bj;Ci?q'>qaKZbf9 ;Lc2H|d%KyzFC4oyM "yçr=!xdlEFDJAUt5¡e۔U9/Z!pSkJ^ 4MD3ǢM"@Z]x9H֯ Ȍ1dEEq -9y/1lE߬;H:2Ϲ4hQBOkى']L^Yjz* >){_NV.{_tSQ8B9T dI8*_lxU.()#(k*?ܚYuQq~JL|毪g>f)|p*V\W̭g-.=&@M߾ ,jMQ^X)JT;*p~s H5*,rg'4Ulfkƴt>8ζݺѝ<{pEc~}ؙ{B@X-JeYTYfNfn?qrC2c]aoUGd8FQDw Eؠﴵ>Awh]Rρ,vHqt:s_g}{\hwkׯG hŽ"]{D};̲R1=*ir\B32uw9QaVn di7Ö4Wjض^zu`VuJihꪯ)8Ge+{Q{<*kgh]]Yk,}p`d-&b]gsj Y{EZ>'26]!*("k. K7R%%,lcՐ/*|薌'7+;j&L(gf&=CTjz˒Ԓ" D e !An^'J˱]`Y 6]ǭ' /x=>31a6aXHq ;O*㳫[붕6i^B3Wl۱Qy[ToDIgQ2R}Ref[~sڶ w׾tj'Z$ƪ!Y猄]LncfVj)\7%)R|t{ʱ\3e-Ϫo8cCЀD D '`bGiWÿ9R(ZNh'2>!:q]5to֝b %Ffk$24պp <~jͩ^7bo&F.Mt3nn&qGnݺvhզmN,%IKE:zr^\{TRS22RR^p!8MI"@Li-(QˢD2EvGoLT* D_<*.0&Yyd *Yep/4%bH)"@ : N,&qܵ\OPuӺJg5 }I "@ /ޗ' D"リ7()L D9r_9H Dr_^I"@ ENܗ"GN"@ GܗGD"@@01Ŧ!~ڑqREΚ $D"@ /uug2(kJD D|m±Aik}.Ѻ 'ez B"@ 龶NQۂII:OO''S&D"@Ȭ*bModqRT2 D"`w_,kx :lt'w7'e2JE D/w_kۼlA%4NxJ'D"@ #`aՠTD"@C bbN;MA5%D"@%@H"@(&x*"@0/ƒ#9"@ DdU%0KI*"@ C(Q"@ DNۢ&}占,B帞+e`*c,0;cEzCxvr3u ZAJV~c" p/$+k}u:䨳 g1:Ǿ)B`a~S=qx(Bs;S+})7&#P 9wʹz4wrhB2GwQ1K p#}쭋WTYNA/maX_"`;ንR^ sLLq2V~o#tCD !cvJJgFH9Ġq=bp_{=C`osF=dEt Ń;௚|ϡwl˱@ r/U1ᢘ Ze/t \-{?]8y߀nӛJd? lok0=MkQ4J]k.=J?ULtDK;gX=>>ۅZV,߾M2 ;vtA6!66f'1`O\HrWCy MRnhp}BUI^gw86z*L_Ue2S,N]r4Lʣ]wjChRM!Ϥbv #=$_s.hmC#ą<뛕|1>CUZ^+t݆n)X3 ,O1~Ja^rcQ=J@8(kjf ",Qq'K)!Jj J8WK R<୨ 1My[v jīKCayiգT2#C]D9'PtCmSyC~~ApzM:h&2QC/]1} lkl ;#qi$F [? ˞۾@\~:?h9`pL"! ܗՐopfWH</B-6slu$s9 ֢ ?Frdö!KȺ,Gtv }1vh0-XPj] suI0a>My91#Q1h3 >iY/vC.xx<:>B (@i%,W.@/#6J[7FnJ|j؅,۰ã4W y7+a^./;^9=ÄM\ 7cVoIs:)2Qí\!ܳFdI_P0SRm1ȱ lRKmQh7)tz,rGU<_GM>U b.&<6)_b&d/ݻ@ɱ nz6>vHNfB_;pMn|U9~-v3is(J!OʸΡW l rcL<qX#6ʑD?o ih(+cmk > #9Uр&>} « DP],j*'0u^F-o=5}B~?T%# x/Br;qN\±ÆP6Fs̚xo-ĩW%A3Ḹ vm,R|j\1C%ؼ˿BV%pٟ/y|Š5 tPZ>!g7f뢙 -lSN ߌ[PYV+ GHPB\ wr ֵԤ^rFġCh'ab%LU#V(ݐ[+ $`M [ e) >|o+* :Xh4V ov|>^ rr/xP km^2Pfzez{iمLT~VOT[Hil%^#R /G Vhw`fߞ1k)zz TU?`&!xy04 r5%򔗮2/2J+úHAsLgqaJw{DŅYSӦySwi!z+eoKί2{hKy&>z/꓁Ƀ,소H/L4Z: JCUOdy1t,!ST8-.caRG!ǬcWqcntJTͧzU]"X [\( /񁸃^[TGA?\Hqʶq}DNSr jpma*ZB_YT9vW!6WEmP,UG*B35U;@pPU5d꒓+)"TF5+.r@3t,e[.󓧼4C^^{Y`koA~0(;ŀ^0R Uq+}Mwᒒ0Q=v(!3Ȃw95;Ӎ؛c_vZy`fC BIv/N?SA Le}\oc8C@j6 Dt9o07YY?Vؙa쭻siS,B0|-0/AZ)Fix`vY}xĐ-8kAg._`yxg{}pg)0#ammcJ,#x*4C߰5Ii_#8M!Q m8T KwE -7ck ()( agސWbO8%<E97"w^^N81J4E7I5H\DRgXf9b&?lgN,HgoABgS^RAθi:<#TL~~*x&uJq @pNG}6G+Ch`)tm8|LVC`p,W56ŅI8n SlCTN?2h #" >9 aw!|"Nm1RC@> (你F; KQXዻ-{=gRZ5x|%ʼn iax_,ޏqhojVߡs)ir^f^U2G3G=5OQx6KhB9J})>)Lo6O9M$%W%mt*hpmUmatymvmq}X^Ϧ]uVJ܀G1b>dzz̝%7h#FpSt_x wDM\S|Kg,hR 9xs,i/ _6䘨tYzq*O-HDڡN)״,f$|ݝڴFe †Qu_p_X7'8.c m0=N?bm0|/2xbU=vr%gT ].u/d" "p yqG3T"L(4_wavLU $a!O@$y*N܍TpET_Ӂ^LJ$KNkL 41cfc $kgX4܍(d2|¥.\\rA:;W|w:v_-[*1A[/%k/`?ht,$ca,JjIDAT 1?)J #{0JE D"#HT D"@^@!5 q"@ DLQQB"@ D4?j{5eyIENDB`alot-0.3.5/extra/themes/screenshots/solarized.search.png000066400000000000000000006575211217172710600234200ustar00rootroot00000000000000PNG  IHDRr!kGsBITO IDATx^|Ǚ6I")QUHJd5K%Dzĉc],s˗DqO'8>E$({ N$:oMXd<3w Hy0aÀ< xXrmyb{0aÀ< XxaÀ< x0a{?X+aÀ< x0bJxLYQ0;| } <0+ (iq^6.sdrXӍ٪VОb:l Ѭ*N){׼#!4_kz/&D嬵Fe'Rv\{:X Q^߸yGGf˻̠=)GXBZihT3m\Jg[yޛy=JzI>%÷D4qz@BE$Bc'L=vE;>T92n#ciz{Kj}ŽggFFs܆h[kf2nGd07^7&HJu-hrw$+6V&*rqVvyJFu眅^֖j1 8z5LX7bPZZE&;ĉ88k8LVD#,q,Ep7n$wp55vxqū3CM7ﲤ]Ul|npj//O#XDK_ƒ~]m^ fE:zU/-ږ.-By c"c-VŜ%X,] q`)g@/2d:wH9$Lo{Ѡ2ݼ%*ǚh0ZKHB ~%A 2~hk:m!`/0eJryO%-&d, =-جj'Mӓz%ˡX]䴙* ȡ+03 :T?VZvfSyËڼ5>1O5fIɚʂ-jB5c ysDkkzEwX=k)-oVNBP嵴?=d,^8-4 cQyOxDKqq`mc)+1P3OJ,a.B_OWljr"in&{ O%HD!%/b/^ORɄWsZt6U2^<&hG~UNE?#$*ݟfªAn*6.Pk1*:l]JIsGnM/Ό^P+WӾCڋէ⇟aCv`d^ u䅍l2aPMմXeDfn iE|Iyӷlr@V-=9,6)"E^d"ע`%Ȁ 9omGO,dΫ Kj\ʊK\0,]Pywv̫}{ͷkgl @1^R -ӭ%ɓklIT4ڜ{jئqRqwTnсP0]'tv}͟u;!DHEuMecZ!fo  wtw pAAH􈔸 <D!º hTD8=nң 3OtOQ :6(H˧ /ijN w BJv͈囓 #noaiCL70?BD$i,\ 様ys<f<9m۪|,iP~c\3G{ ő,#38QD6:|ctVn^Eƨ3ҬG#D. P,15wSgƄ@ h+; ȻǣXeۖ{&Blͺw'8<~k@K/MV 5Twu?c` ު,.ҙB%g&zbY>Kwɀ=G^JHc3'_|!2O'l 8wi6ך23Au_mɛ7=CC EwN靰B\oRȨ4ւUwsiU杪}+3Ⱦ#yY?OtƲE>>0Z FCG8#"’IЖmcjLE!>>qAQ5[ܙ[(<m~.ueHN zOm`IBId MԽ墮g~p`ةt}*kwfe=, Dm?BUmòL484-g<[SЄ4@φTϪ[.uݝ}; B,eLQ`p 2RUrw ٵ$N٘sj-gQt] *ƌ LrNQy]kT) )T(a.Q^{bn\KzD/Q-=^ثGk#~hcZ fŸT+ cwϠL3t}dlZ Mڛx&;}ɦp5$ X#EHUjU' W\m>qo'j\Ulׂs;.‹4zF/VߚYggj60wg05@9߃ilF=_ k;NbJ*?nĒqqTZZo?opBz> ep0[E7jV٦GM+7* &>Blͺ} HBZ=o޶s7Au-ؼSiKC[Ә˺3QTӔǗҟj-Ai.z׾ޖ4NHS:3Z@&Ԯ` 6 ż,ba\Cf\mu,ޛ2Xs/Q)V ^:{|BM:zpG2+vJu-EC=M7>imz͗k_x|C0}`GB2>dBnmۜE[9fo`]\]U;-4̪VZfE"5.dkd]p_K/c_8$aކD_V[_LGıw0="3#9!+KI}5reG+[NuFoEL1-`xBr\RMh2Z@oo.$yH׼VA{vYѹ,-ߒɉ?vt3e3Gny[18nww hA7@='IϧX4d7n+ *"Ծ k{:CF3LgKFJequR-^n&ݠ4L½5S/!qEu;!cvQ ΞGNcashjW4\@Nap `o\N;cLZshl.(sF zxyp9LԠv#Ip([T;t"އN]NIǃfFes ,4q&:Ug'0p&s6J'z{&З:r[b7z[b**d]STυ0]@Ap۪pIXg? NAIG$x5 $B$N㇣v]]D2{V_m'ҏWyHTXFr@fIQ"J״hH=qwsvF|\XM1XJg-`""H55Td@MYAȴ [bGtWAd0btl0[#l̮U}ٖƂF Ƥ-o;6ŧDԴ5рe[\P@k d۪M#1uz0KW+Ʋhe: njldyljsP=by` G[kO+($^up^EM BdI1 ; WReAJkgnCOsi, ~\NLFfN4OAjJ2B10kW+ F2E^PeK ɩF5D.Wij5 #NȲ+Z͢#c uKG8 v*]forO0s7:;\75Pjؑ|ؐtcXqŗw+P`>(05_tlviԃu2qD؆8NϕCSF4p"J `SѶ $Nrh{Dلmęj޿rcC' B 3jռʑ `F}؏1>*E\PN Ẍ́t*LY y!&Jo)4l71tp[`$j#\a PrtPJ&+r瘪~CVUGyW'[r`3XPn5A^QIEkV TkvyTXaҨ$X%Jb6bk?׍ȲXyw?ǾTgwKSyD-|]Lت+he@Lu_d (n'Ƞ]t#139Дۍu!_`,B:L}2[m[ ]*րJ#=3}o~08CglᦉBMJHX!Ucō&;&4SCWIp,eC(r{ hQ @#9`[&j{(-/f0f>Pc0wu8Z;W=>2ۚ4TX0)D,t_F@B8Bl搙1PTf$u p<[L=FI vxH4E—c)/]ag?8' l{͕C }oثp+7\3HϢRx?[w;2G G!<`G!?ߝZF]|F5k@kU9S=1BDU| K?b M,~ 7a+vN{E\}8޾X}]_ڃHx0MV![\i!_{Cɣ syѕ*SF:E:j-𑂽Ym<12`#5ţ $@hI3=hzӜrRAR/+_x !AP-ARTTXL]SG_ϛLFtd;BQv_fN3lrֵ-܆ёZep7Dzpǁ>JH0T"?_hT>-"2C Q%Hbbs !Af @2^)?=J; )x:XkUj 4 EsD0s[°ɑS,ޣ=yM桮kJ(:A;ƼȠ>ҋfn͡- O@8wɬQXQua:‹ o3%k8w/K>X>xcۅ+0Sh'Q.b9Gh%3Y\ne3fv bk '/aѵ4DJa} w˞ /$QXzj> ]oxr.Luֱو} b!pk)o[D>nֈIˢP!ڣ%#&k~&RzRa.#qbU }D Ŵ~S/:ҹ]ll vg 71Jă̝s߾Vז rxl6yัij?"679[ D|H"h #E3::S⡗uUY~;X6},MeUk7(A%s?LbkS+IܭGϕ6Xa1A7H vw{n~OɌQq`xd^bu 3hNk18)BΞvk0XC\7)SEJTǹ0m r =<=^K=`f܈z*+HlqZq*ܢkS~ l ؾDb4 ̈}Quv??i_ IDATsTisʋ* 2[e?5}X5vǍbP+Asf2'pFma!yzL7表޳]\j~"ޏA, je EXB}6-CX(6~틦]][&fZުHT/!z g!s߲9QR /ne 3VHdpTX$-#=L|$p:" aNSK{ZPloւq.(AYwFV^NT1Aq\đk,9⥡ em+3"ohHqcyRLILfj`A\g+!][2Ԭj@ $#'PK(悏N0beό ߿vIW',Al-g>~t6.͍.jK# '& 0a^+.Jm~4"p{S]G4ʐꮂ4PwڡΔS rXw zpTJw#p{RnnxƤkS0c^z65`¶{R0p@4|4'Lέ34Q~8+cs&JrI̽P\uێ:4~vo'ED ?xiz<:%1YP_-4؜64Nk_[^)j7C~ދ7J Ύ_]7Zov̹7X S5}k?dضmuIybyx mΝ|Ռn3x[+Ġnt\m$1$b;J2 Ѷ3<=< 2[+!XTrP~xeEWj`[(Ҙ`Hq9;,D77:k)B=pj^= x0aÀVŀS~|UTz"y0aÀ< ,cVz< x0a:0 AU5 $V\FrzpD$aWa{DĈNU45XeL yFn֎cCqXGf˻̠=)b=+#0A5jrrwyTc$E)!<]'ĄyB1Lh[͒E':3:#āVpcjRV"-yFŬii-iT?}~$ٳqE;>T92n#`Whفyԝc{yFe3`z$Պg2?9n_Kk҇pFe 3>~7g"C!ok/}|![~m}3{øӽgowA4qD+QLWxoA5]3a KAuUi0efmwv6`nRoL};%qZ;0z Bv~jY޶O\!QQjz Qy05y8ZMp^RE"t Z|d)w~,Q;U}mc7+o`Zځ 2^8-4 cQn?+)IrfKV;.QtY!%wwX8ApaSna5'9:R>ym#z"3%ɷJ{u dT+mzFG^^]wgmJ,]$hݬ. sN׻B4ܢοJn/z${GVY1`88PJVR3OJ,an?\+cKjUZϼZDͪN5 QDY}P." fmGO, ljʾ@ժD5=ѭ;o7$ez>QN. JJ'a,cwjtz}nuUeqavt)V9B O΍X :YKf҂9ZXabe>S|IEIѦcݸ}aU*9tVaDƤJ֥5MY[mwԠ{o*oQ&vtۮ]Qy`Vﻢ(>^R/W_- _OW] &3f3_HSO  %l/GV(2~bbQ~/ďOF$Tg>q|hL}YVMzsHNHUFdKIKBm R>II5Z9>fo[='ႂ)q/ Ͽra+Pި}\g"65Ma (}dVVX7whTD8*ۣ 3OtOQ :6(K-X.bNnM WuO,?c_ZrHlv~O664P `]U"o\lq5:Pt XJ@XWTڞ& tC4Օ5{s6[9٭69zJX~\ :nz`$`S)m8Xӯ'Zn4)I^x~7~h~wÜQ'}4X[uR,A'o{-J=Å%߀\jƇ="|K?i=ψݞчm| 5ֶ6AVnȆiёs 6,AT_w-l֍.{spVbBbOj]-ԤG VPVT߮A̋ % ?/ٮ)ơGOW+qqy1.DW$VU- L ϰn$WQNEDj*4Ό6aյšϮf v\]U;-4,KؚaKޡ2JXz Ls bY?e Y)o#JpCr I644KWaoCE}M\.L)TG>'S5¬TWCۀW7s/V`K)u 3ɻ6}ESoN<;T 0Kaᧃnc,Xon3ln, Ep2zw[/AS=7*y'6,]^Bߒr9W?Bu`NȾo4,MqsK͍Cb3D y':kv.iyWP<:=3&z!. oʝUP-Jy),QNM0D[ kC_5i@|,;\f=qhs~=4.-<6/e{~60U+u<\r82 |V!!BU:d^S2ҭ[xOoO7 dM KLQ2IRqoJL؛{9Eot1a3N\.UNG)7N4v*Ŕ*NW^-5:˓ľ~()զݒ^D!wn`ᡧv־t2ˀN)I Y?:[w嬖 k>V\uCd0X;+ ənM]3Yg#vyes^7/;>g@dh'(d{υ oXD*Ir[E`նBd܄%?W~r2i/rMY2*yzʠUCҔxvZ@-6׷Y]`Sx%#25n1 \}m" Y' rlBgxe7'YlCN'}D'I kXFen=DSH0LO# 3&CBRkii3̱]G M}&!t0lX],m{.0Jn{F Wb1xq2H([a$;6aG  43 QțN51BQN Y. ׷e#cR krLLfJ1Ĵ֬4A|g^6N+K"8 ".UJg,XߨQ; AB`[yQ ÷;Q85P:"dZ ޿R"=_=vy.'Wϴ?Ŋk;ȇ cC/V+xxo>(uo4|`2;i L`;@&C1PWnLr2?t(Ij0 [Sp1|pmFm$˜"p 5 oLRj-@1Ngb /GZ],).Ӹʩ7Jk\y46DPt^VFdX?6.D,Ffpp@;4q9&𐦞oBCoMp[Y9Eo2.2 cd=+d:_%X$@M!9D643#Q)t6Cb:e4b8K@p`xlVUۓ1Ð=n7׹sg5v~T:d;붇fF6ShXᛌXS_XrZ {vf6 W {,~`f1zyb>sO݋&̿]b^@#zWBUDm-4@4cB1JćpzJZd#" M ^?ȝ)ȁAXi>ϼl0V0DpDT^kKsYZmaVQ"`Ma6G[[ 2]0UݙYFR:'NW:[W~>LkFmZ|w M(&>c9 1`{ΉZ .VҀMl?0 (6=z| ;{ȠzŊIL b 5K,DcaY$|Vv_s~QBSm=F sF]s3Pv(AfDe9"Ύ &;{,t|0\"۶,e,3~W[=7Li3$el$Y#G949/J-5ί$E |^po(w%uoaRnZ2릫"<וfSԩTGM6iUMz$J˺gR,&ÄB4z;Oﺘ"ifHOE@O4I{dr" @&|2ޏj~D7+V[^NCȟ? dcug:7/Lh+'cA?B"B'0r/nNE@)f'@*U`2dکm "&D9lMlm}å ]L4v9a&VZ^WiU:8E`$ !Q^Jm6 EsD0s[¬S!Deyz,bC|[ytX 5sx-:r=vQ6Nțb1iQ^\%ox)5X; jRtk10:J(l$=㛛iA ~6_cC b vm\h$Pq&oqDw( w׼ p1ic~ygo6@0,|[v`A-tH>o gcӋ,Q1qԂpJM aX!64EI j4 x$j?~Q3Xttڒ#dpX 4KiBdhVܠ9E>4>.%qX͍P 0kU]oº-gkd#՝$XݖNa_m7ZDY^t8SEpQyAWwı8N8(N%H !s89r_ g:&LM?7ʒ,l% ?LFU"lhe a\X'!֛*y%OHqoUr727 nƶ+ϦyDXmCo;D/h#*}i|SKyj@ޣFf<_$3E REDM=cy DD:EWM1,MkXw8%s?L;Ʉ%p>*Zj_2ts@%&-˰KTzxd͝b@f2M0'4yzfcD W ozd8eb &2-*0⭭>K-3׋_BR*tK( 2ߡO7L}QmҀ{+C>>ig˼`Vh5:*l!N$7m-Nv<4"BAqJ/NZVo"BƜ٣ukW CE2C"| 8~QOe-NK7:N[tw/9T.p>@2ssbT]iDnڽh*Mϼ ? 0~AA(Ҙ`Hq;FD7n)o8V~u = x0aÀ1T$!À< x0aÀ xJw|aÀ< x0@Z옦VaB$V\Fr뫡 x< xX€Y_0GH贸x,=0`1(zG5F2]{p}XK)̑a!_xdnf8)8&;+Vw+A{Rkr+ΕG2Lh[͒E:3:#āܾ Ĭii-E]E;.2}"6pz̎ Տu_0 Bl_ '}MrKB6-e^=eXV}Fu0{=*/h♌Ïs'n6c<{q"M7Ots5_UW,xFǻ3m K\z6ՍWɧoXҬ/j8%@q%QyI!jRuQDDjAuYVm! Wb ܚ/HIM jwdMcMGmモV&oLYR|_d)/fIgKF K_5L2CcB LѺα1=/Kr^lR 7Cv-1o7X;SlֻMC\i>1:53\@ ʋiwO_{Vl9}ɓeۏxԽy8Wԝ~ܳ6}+7'ܩvl+pՂ]?},jHS/̖v\nR†çܿU^TfďbVv a+;pY%%9TVVw^<#Yjt+wj%we7/w;pV ?v-Ũo5=xW&_Ujg3(F^fk gXN_G n4Uӷy~%'_(5gnmGO,@AΫ Kj\ʊK\]Pywv8JPo?pt90iyk#ȇ}ZIƱ;WQ5GB\S|IEIѦcjF7k WSu;wopv ?:+aԄ(/#xraMmsV`}ֽ?T7(x%/™ f3Lʖz,cea&61ƦnM#/%?Im5O)TJ=$EKnK; 0_aE4=2b4\PPS)qo/ ϿW&lUyϬ4$41axISc!6jCyq  ; P1=p#Dtwpev0UJk[ oRbG\‹zgHQ~ԡ^'!).>"º rHlvb%2+`hTD8^^VB,:3Rj xT_aٽAYv͈囓gϬlP k 7%Z"nb{jfq̀bJjMeƇS; У 3OtOQ :6(KW^ ݛyWGո~oHTkKۍP$1ÑzB@w_h@EG:E#eS SYUŘbzq65r3 tC'\{wGT!Ls@U0jf שt,eۍ}Ma]<19;Z.)*߸ }{)e2|G;W.5Ac >K7MJr2^n~δ^RgWA[^9wmC[G$"nsfg6 ~Լ֣C~gM۔E*BХO$(^^YS^|$֏ +,nЂ'!͐n\"fJ" BmxcּėxD%U#77BB}_Xf"BUl<jL-ʖv=kVk(05&<>\8@,J HKya)"`GZqJIVe1{e*#cϵl Ҋq/6e"dS"xչA C;hۖq7>i Pup__S-*N9-&*e fЈ`x&;n]u[vшZv5 آh(w`Ǖ?$`'ػDR(J(YR,)N#.q\b9Ͽ|/Ν/8|8>;qcI,KDR${IIt,; IEmٙy33o޼yxG@5Cėg4t\lܲ4:lcg]w6:W[ S9JlWߛ K?`nvLV9'*>ST7%ٞ_VZה*^׀V}B@؛0a&)ᦡLG zv2)h׋nbɪk(0 @ӓ(Z0TkܐD* Tef=p[u$;LFrwwsѫ^f?0{sԾОUlg?wMĆkTղdqw V  0S{~S=d\$Mֹ#G5W_^yXB /,BNiSUA۷Ix8{SY}lWᙼ?$Vݚ R`(>V>G}PBpr̜[TLq$̕GEB&ON=K">>JS'#S.L?.F{J_+}!4!M:yR5~=Y)4ow&},ץg:#bijU'LO"6EQBhZ 3x nM$2%?!;ʯ%>hW/#{p΋hmvNճi&l!`[E" bx,~%7`@cw2[@xղo ӄezR8å!/}*1+kRC~*+.rf)|:{WKX|b*+I v8|s@3 -T/s0^g5rH#`B&39*zq6,צӓ\"s}(2TLa7)X4%Dl!r2 "X{C@Hq1E%!!ѶKem bR1A)|E+r+[lCc47-F@ۄ llHoGnRy|ӕaRq0gX`∙qRXӄ!«VJf?P0ُ}!̪,9+ ? aғafyCNPȻ=f-0 ϩbBy Y 2 b+& 5Mc)N_svaS1ǑZ])@*OpTAg[ WB.؄ tgby a . % M$"wl t.e7"-.yn]nltf`3X 569=,V >WKJVFx8Y U B7># @Dn|z8ɖd/7vya 0ËEv;@t^OCt&V/zK@MA5G`J<=/RW6̛~[OWQ#s_C* –yzl--2uiLf:[/Ͳc| `vd 6&)DL!HqBCw[? U0[#|7z3nKD] .,#V`ڲϏO˽{>R~rPmJr>ݔ즡U'@hc]#7`T.UwL,9tKć^Sb2d3lygD^ƗB=#y D8%e݉!(WIƼvIir %]l( 񔪃K_d*)d̿Vֵ-E S3`uʧZފwbkߗ&ȅl ϗY9!R@>E48IyXqI-ĥaiWDz 擎U ?faSY KH+ ݼq7f;r`ŗh<]Z|NnTkzomk͵ u^"i t$(׭ 8ڭՂFq 6+$ײ+M=[to`(tpwOIZ0B c7NYs 5+̪ӣ`AP{g,w4\le:5w7+-:+dAγ~PcӍ=-`oٔhNwL$9ԨmI7] \/яOAbcȊrK_Ӱb&{\۬N@N t ۽0B{諾(ٲ Umh 刱At̎-p,i,Ny7c{I= .ljfI'S/6"$Q<pWᩖ@!#<&_ԛ@U@ >d-zȢ{-Qxiǎ푛xȼ~y_IY%,@y͸~:;8`D''q5 8%s 2&"L'-hwu) ҪOB؂%[m?ӕXo[Daē5BK-!8x@7nWJbuRD94~մ5oi乗+@٦|UL8ҀEs_@17܂Tc־Wj0M?\՟E/lyN&lT׌{ٙ-Ѭ7XR}m]Mn7+:hL@G:z= 58>*!? IB>Vk^OHeeP`.AtjO!naQOX uR9}HD7p sQ`{0TU:>dcsߨa96|1{7}'ga cg IDATM*+iiFqFv}O\rf[q1XE8qŽvpG?r(+fQҳεմNaG6B)C?B@ѰX?42Mw2.lSM8)'xɸqrSs6BlmEc=&pnI{<# bt600k/U BOwɧDS<>ҷvnj"maډ @݋(Ue$E熼j3@L\֐2HJ%6?DP!t(#+7:KmK/͓j`KhL^2|"$EA6XЬPwߠ vNu^#am]^M6p#ȊO}۟0'hN;ESO,kp I e4s_ srfWoqA{oعCx<}rSݿJI}ofDĻSҤ2nTs%9{6l E-+ou9#{3?A87 z{0kwI.}ee?P!(Vޥ ֶAܭNܳG♆귪f'2|Y4D|ѹଝ?ԞVա sz?vl-]8 T)=gedxM/^jk|֯HsSFُ}(P*K:?.5tWF۲߸=FMW~vZ ʔ3ծxcR^KKcƉ3Vck]cs/؋~U<ܩeG/{Q9@g'{"{s~;:tV'h 6;p0m ^ݲy=7I=;Ly<[_Lc k΂ʖ/ʡ_lj14>,Od9Tqx|V[mM\ 0T        [AX5:,w)(((((((J\[<(((((((%:yp2%!ʦ#`T#!贸xWڋݬh[h,20%T:5Pi\ 6>jûA:[U֥Jq@sC5HH̶0yB '*9'|Gb0XW^݄U+=8 v/s :[yU#^΍ H'د.u?f߶Uz$-|*͉̃G܄IXW3:;}^W='a5؝("u\PuvϚ,3jhQ%dh!Kf/:U \Evz-8%'Wk@P <1…ɡBW!p}:K"aH 2 WEx=4T7y>?rB:;²G3}Z( {Bpje7Ljj'6:d}Y`=*Aԥe,/YCh'F4F70$-Tpc;43x~UnMcSBB B;.[S¥dd17왴 %WxA*.m<ž ̤ȏ[}^nǔ@ 3O^o_N'_G>AI/5xHI啸ȟxْU -%S~}ns^[%D<wk.ko b.1C?;5_^UonbDƼ-v󝔋m3HN/Jr'^$\a8Z ]I]znow~|(%6]M*^Š97Ȕ0ЇWbcdg=0/FyYr?UNNfo(|T-H4_DM]5)״eMskU9SC5fέa(!:{Q ̟/|̯~{+U]M^i[5iē%G+$^|+U7Z1>fE_gnjWC!$G@E(!?Tm Ln`*I`'5z/UD˜H զlJ)3vrk"j~BK<sOhz*~}W;j |91 rƛ2X[G:V_]fnYjd7O) `aR۠6ⳛ^&`ٮJx% Zw9`6H lU4nF:q 4Ŋl/+kJNYu@Ul  "+*Ϫ3@XXtJtoOm1{)h׋-u/tEcjaxx!m@"OYvuf(|?g+泷.O!1W< s7]ԼuQb$͜ W^FX4յ ~=!Γ$YCj/I)unʈB7`l[Hڴ>N^:#U o(^>dS;D?lV ?A箟)nʿ}t]uvEӘy B<a摋Z} f !ګQ4VaV<,M&dhh..GU tO4`We$Ɣ,Y٢C\1":xg7|4.M0/ sEi am<F#JN-> +`Mw_IFMo%.#ċ]KV`ғ#,S 0sZ MS@"`h;[/6hzMm6qHa"B?1XM `Nk_$NMs 5:VDv T*C$|, ̤JEd:bk,][ΈA4iLVsYt=$ qޝYoopTG{q,ƳLp|B˄,#>ʳsr{iuEd{ p5ͱpHo0؈bcC/$8G o T{cEPd4~wbo0Ucg/lXSkCn T[RI.1`6+19@r+YHE(d&jy̼!9]L^3Y!_6kA YA x`,h/ϥc~b0o ϊWW.tۮ468%?#i T*W7 )hԋ0laz-o?`ွ8ꋿ0g=(Ȼ J/]L[1<Y{,L|ᰚbq+T#SތRBB#F0@#Z<L-V`q~ǧb{[pOT\lO3ÃNMIմոht _G"V"L.~΢nشW5(= +Fuڌ/b*o1S~˅נMgĪB^ '抻- ۼ-F(n}rxe`9?${jS_4lVn%_ZZgq k@{x7ȫ%Pv.C;:Alh9'CY#F}R^|(uB%^Iw-@ ;K_d*kk*[if'hu`V]"v#U`ԑ&ƍ% y;&3M@,U DL25Eʹm? ^HE] 5iG] G?..M6+$.4EeQPn ԗn!iQ vWta\9X)kwfFcUۧGj,CΒ/-+ԩYiA]!f'waѵTEFT$5 37{j !qzj{#nK_Ӱb&}6b[F.?>@{諾(ٲ Umh 刱At̎-Թ,i,Ny7cŒz4\m \Xdv~!ʢXJ% R3tUl:TYIK0Uń[.~3ۊicr vXWk݅#?CY1H:kl!B~Џ?P8u4lYtIns{~jj?UoI9n rSs6Blmc=&anI{<# b@=۵*zx'~`wuF%m|%maډ WafFΓl͟@DD25 R+i[LOgȬ2癘"ɜCI4=ѸOpH\k s訴Ofk`Nќw+ʧ,-YVn7\-;be0|cו>+ ڻ%~ s C4gGs:|B8j%`GWV{bJhUm`HDM*VkJ5j0=6,^@i0+zǶ ݅@Rt,{l L5>ئzKoնlV >|^rzsC% i1aΦ);3'X'ul{e#hU9Kw{bsEc7`Êkc0_:'uBW)tϹ3$ٶQT#LXW|C箽_{C~^@.:(,:unч w#;NkX!QrCu 3N\[z5gK r TM_-WAqf3e|ͫⴟ?\-T_~pž' +xYs=8E@,;pws#w M؁Ahçgn+fNߡdʻM_V` 2 tT_@[{h?W~karYt﵆ΫՍ^mB Whۛs//?:E{qv)a__FJG'aqt|ނ2+tf}(nQO)(`SUA<,Fݗ<<:,O:UUCse+Ddon_7Џ_0nʯ! /<7JFw?<Z!p7NSWnSu\T=_"l}&aG ϙw]@Q-r*tIdRܖZsJ$d9_T܉vCKi}Mss]R\0kphvbn2d!#D7\͚A<ƒUK;3uMXšD 9^xk|^7'Ԫͫ1vn &N:0p IDATdUzFϯe:\9y0鞫XG|\ w7sf580eE$n0LgW,ÅcЂZ;gT Y/V`A#%UdĔf/M9~[7 Xg^& ؑ.m|%A͐;BoTD_a& ^,fzي#؄%KAYNXlv( xV?|GqzI&5ݓZC>t:3x~UnMM֩p'`Ǝ˖ԬpR727왴 %WxSA*.m<ž.nLS\-?07+{'S$.8J1+sk-tQ&Brvt]}{sC)ib%:;tf M(6횆Fڸ^xz`FCr*hMƗR+w O?|} lV'.V'd~VˎK\ ~v,$eY+g*IZǀ]vm\tT^_`q)nܾ0;rQLiQ0/|o>Dg.TD/J%za+~Q GJ-5! GoWHY~uMSvPX"WbcdgD\R/_Ua) t@M7JHK&嚶 "moSZkNPs+Dت+c^T4 -&Jx7pסgcw;*lVn$~  (gՍua< 5b!ۻp\FCqG"Riglo^,5umFOK.IGgS0%EX * V&&M[\7NS51a;q_U<`<3R9>f8|APp@ERKt7auLC?&=t4#չ!GJ[)k@|I<1s[Q )&zPeS_cI>e^Գ<7[Y^w3;LLז)y:CEM_n!RP|3bՖ6 b场I޹j6]43]<ys{۶=vHtwht9((geGN0 bz䗱{i\/}=խߛNEߵUcX ؘ {-'lfUXJ' h6 N6ٹ[X3R=4`4^GHdx*EM#?ottf >`HK)&(2P$ S}fթJ0KMK rfFGX@>(~#YC5%~U Aj?$gճ=.UugA)|+֟cM\ ֎^GN>cR^! &ON=K">>JS'#2٥iVhOik6<& T'O/p~B1k&T΄Mjp f1ϐ%>XhK,"+%y|~K#= 3uwMA>۳?[ 'DDy&6ڡBc[tӽ:vCK! ih9Po*c}g7>saFB>0%?aebb-&ShtQ6REen'sz{q ,ٶai}X$*MUBG`  @uu&CCSwqAh>z"g]=3w)M}:UmDY,I1%K+EV|(ХW"- 7K74LKC\Qyz'-vH~fCKi XWҨ1ȦF.#ċ]KV`ғPc܏ M u LXэ 57!irpe`99#Ѹ~q< Y $h8 >aSǟ\CB߿R@iْRlmm!4|c,S-S:_[b@wY g jwK:Dۺ;7x{ &8X`Ɔ_HQ⎡r)(ixPaRAiXY \6yؼ G_'K} M͊/eL-}JV fy$w{/[`bS0e Ā & xsK4v̟2?WZl>-YJfnGpd׿:v|AaI%B츺ZR[MRqׁ` w ,έ  yvcӥKo6σ`1 Pɻ=Z,LՂ*28!52iP_mXq\1A6X:=5s'7 aG𩼺 8y;5a^Vu~z1r.᭮.9 g4_Upץ!|hceiu6U!=1!KP؋7`QXYc|z6gfozBBB7b%p1=~04fX}~| ,lZs>R~rPmJr>gm!HM=ӰY,~˛n}X˔D@{x7t_gpD;o DȜB̡_Ē+QE !X:ׄ]"v#U;P)US7MIWsTdĥ<ٸmA7onUuGRǷKϾɍ*\Fv=hEta0 E'JTwlN0Dq RˤYPM?d|#9Z! ٠M}-=V8$-J?rS\C1Ou`_G&   ;XDcqSX*By潛#M*+iiFqk7\.ĵ~[YDEqŽvpG?r(+!:4Uծn\aBbD?O -Ε MM7A⤜`ˊܦM%/<5[z>XϤnRH8`vʷA"^u.~i~~b;ÇZ4i' n3((^^v8OY4nȫ6x/e y L]T]{x **&vD =~"FЂ'\ :cYa ͈Jt 7D ̓9Gs")(BxdY]VrBβNf10;>'avƮ+}WwK0>,*ht*[,:unч Qo|0^i +$*S`HT⽎vbƉ3Vck]cnS֮R}2(yf xEoyY󇣗) 1k/?s~8a nn־Ew5bVmܬ{:|D!@!@!pcnn:ԝ~Bl>v`陷e'>8{nzhw(NwЗ~8Ȅ -^<& L^kZ+$ <^&iۛs/huYE{qv)r*X_#m 5[\&Xb[Ԡ,0[CYzJ!J0K_Mթ* b | Y}uIa >l49 f"jZst )(p0PoPPPPPPPPnʯ!C(vУRPPPPPPP, տqJu%E6w^kNvtZ\yw9Z}kleY7xzAQW_-i(ESw-tK ޕSkމ_n 4-4T*Є # AXi77;7KEvPuȸ! VeD1xծeqa^P/ݾ` 8tWzbT4m.޼/h`ߵ>˧z=E&k2_uJs"/{lka580eED^p0T]ó ޽ jhQ%d-Nj+PW/EנuU=㡧fKvWM S~}nswB\l@ܭu2n/uA $nrb%C铉s;vp(vl!|2]WPJWG5o3UAk!SzZ@WC!_%nJOMW1qT?|,3no( j\hg>rL :]+z2Z?e%.m>;Sqm2U,3U ĵn#j~a+6KgAz$!R:rts|Hb=ߴ݉+0@gJZy{3<Bg.TDpixVVO1o\RxMR)Iݻ5*9qsy5A0"Q־܏~UGC }~e/&?HvƢs[8 OK?DE$ԥYrM[)|n;@ƭEгɋT`6pF7mxhċo;溎13,F,*P}_p{>'hŠHKroy*iOyk}0`۸F mvͰK)2c&5t\|m3HP?chOHuLxZ])侯Lk(RcB!BcvOl]&- V \f ʤ gݵ-&Qp%Bg$0%biMkbŽ!\ A}[Q/~Xv)fl20όw _!eQOht)WI:[lO̔1;#S-'A [WV֣=^5wmۏh=:;+-4v[(ӓm@SIU4C4erb&K=d_۱AY`GNiǤfҖ)F4?׀p\]gO+;")xb涢 S.M_3.ǒ}⁵km`j<+az`ږ.M/j'KmW+ԅLZN VYuz x YQ'mo"I$H޴͌JtV ^%CG*_rXH'hA"̊پsWM'CߵUcXo0cYTX!!/q[Nx)t6VO@l;﹝qN6ٹ[X*cƒޢ_lvK) O:/zQ;.p [cuIS.wuqzyg d[(kXl|Vtˇ4=UwUk2b@X/lpߠO~['(kj8gr¯]!\m$luUzǥ;g{~-kǁc/;.)VH u е06L?jR;uPg5O};)i7Wh@i0 Ѥ IDAT~~@RN&b}Ir~*$ #n[ \`+;~b[0奔`XW^Kڧ}X\ǵw{eYvaқ %8I^'Kqʳc;c;{lu[B T.{sڽwmw>}̙s~sf̙vm(dͦx9U=07[1%6q<4XǏ;'!&@xbN+^"eeyU6w䎘l~`Lɝ곛;wHIE{E!/=dU.N9<q϶= >ܑWUi׶`kbzBT.~|SeO&qmgz{9{J _.}!UB@c CT+A=V<|_VqMvuK mBCg_I T-qϮ^mF~̅cƔjGYR.7d~n]v~Hg+&aW3?nw Ӫ  Z_ iݖT:5ђV7^3OL-;eۂ0KGy$ |݉N#kf^,E6n<+9{aPW6 hǠbg}Oݦ1\CU̐n n#mC=53tث|רl*I1k'_?M@u2BQU_ 1{N>v^uNQ{/`z (tMD _x:!o.m|!kz6=viƇLig[+ ojnqr3H఍^]Uɑfsڂdfd:Gg, a%AKz]$"SѠX1kBeblsfϻ:{SYUJ5/~w<ì.I#O!( "Aa 7DfE:jRyQ 3u+/\sڌA4n i Z9R=КOazPd|ƂnR?oѳbyk&{e[N]Eĉ̋mTe[,8̐E wpX mdRP@{ E,<ɵ?9 Znf1,dF.p(8FΔ5 2rcl%-&Иӕ-ɾ]ṭSU b[uZӮv6cA@6~)^w4 a(:k)ҁufR9t?wVnMS %=!aY8{0 1X\Nyഩ\g4DE0Ynf3`t&i׳هTZ LȾC@rF&*|+>D6BtcVH]0842m]A@qSK@^+ء3gl`؀&'5rL]mVyRK}boeV_5a3Aܝ5vErӧ3f0DȎA<̓ >l:9 D%v zH nO 2D; g+QYDL"{!v3=̻"k/\e{YgG`N]$AguS-|yb6ob]g C"}ꡥ*U#!b64X`2ׯ0v!l ڈ{iIE<Jș*@|2 mi>r?tƅub%K?pe"kg[Ԅ׬]l6C%D΍N]aHv Mg m-P.^$́6 !hT" a!;~I0ؒv|㟩x9?4dá9_t#vϜ4 &"L0iL9BRӠ;T~uC0Ka/Ck?NT `MbG^rl-fb%ja^:{ /e/O?4]]ҨuW=Akq1;jjcE =QY-N@l˶MS '0xU@-Z$@S[UW%Za4)V>{r[pMi13q b I.%#g<d JwzKgOP{Ni$ bRc ))%MC LUԏvup86)@@\HpL6KE9/XgbE{O/O̮2zk3n]y3v|bIyX&lPpXbԤG:D}]3 2x @֙4:!,nxˊހBWwV}b=o{?(p1WFOϵP|ʬY_,wCk紌hSJpc q9GYq ])$sOv0%bmGsՠ ʡ7>pjfBc{ 1U} $ v%=R `sZHZxEԨmeH$egkB4y蘲`JBڂ܀$ oU X Um>t4B z/c(J$m [EB><6MΣ \X&hf,$OpWouʂkS7*s߁CZ"< ڡu> OJc0uQ?T\F+bL   *y M2AV|DBo靂eIVa*u}dg5UԾ:-4*}Fژ/- )S9C 5uˬW# dY~—}68uc=}ZGցc*a0#4fIK&k{YdjC Gyg9y&Mnp6HWӣ\:CDWZiUԺ$e8ڻY"}} * CmV",0 aPkݿӲ-M#W)j.~]A;X:{hVO1cV:ˋ{-dϚ@ u?@Q9wqY܎O-hE~y0/?>tS"XȒf+#ʳ{&,i ERlN3: ovw^+ɞ~ nYWeauѷLfWff'{uyæ{CA/쾔ya$׬ u(N]Q?žNrܻ݁T&8146OPzB(Ch߮zQ) |vUHdy5ݝs|IWIfO egβN`L20;9?[ʓ] $|Ǯ[Kޛ5QPq9Dݗ!\+w[ϩ=^=XVlYXDipfء~O4~`ޞ< J(Q}CV{K <=Uٻ/?2l,kuR[:SXm۟ WJϵ,Lmf_ ؀h[,UK}+=yQA삒V=]A.ҔsMJ;7EGޕ}B+EQ{KY.u{gu*# FKcD7ҲŽ.pԢ!D M})¹ѐfS B)A0wݞ`Fkn.^B, ;v/<oB~ }[ƫLT)`;>I~8/;=HھMw_O3[Wh;3IiP nb,zXɊ b0[}|i9EK#:WIx`f)Y"Ek2$zqzBh|+~υ Kc[٣X=1-*X~p"25?8}z{#|C2su %\--4|ּ{#o#J#_ƬWq2&eS^_/0o,Qҍ_YZ'[g6S\[gl}rlYO?x K _qK5jt_l< _/-˃wzjK#Et579x_PpujƐF(cD3obs< Sv,|;eNmɷfwkz T&^5]F }-o UVlUʇsE"E)hй̀Ҙm~':G,6 ''~ARݩS.Y/Yˍ}`+/i,]q$lON]V5{ͻ\I,c< &de181?J M _GJy)πSkk,wVh`QrƓǍ YN/\+!j9}VJajշϴ=>j9JO,òsBMhoz"r߼4_,K 2APAM!dK6AC?[WlJ𠡺TEoxHJp*}^R+đ pUmάD-m%Eѹ DPBƆCf|7 [43 b':R^9G;Kȯ}6Ie#[^Hq>PѣWpixTFu[":VH#wa$d$TyxAuMc^Zx3fW}?]R˽fi+{ez`HŒ~+TzK4_FpL/k0H6LjCgGS֨K$y}Ϥͽʇ94##Je>KϮ~A1ӂ81{:fY;.w98Y^ҕoZWjK *Iꌊz!@i`8kKĂ,Y9=xIJ=TU^8 oڼ` Jشwd)IpZVqxR;bvM?o{쐐'vEl<fUXN' C^rWj ޝ֡Aذo|3_-iԐGJlxExg\|v?r4Vn]l}E;w5}6V!C@ T ^C2=뵬qHlkp.7Bry: /QlcF)\f%:UfX.gfi7~qnsq 7Fq4nvV##uM9)h ܜ²Һ 0SB$(q;UGrrÐ- gi:gP(0:~/:Z[`%ߖ G^"vyI _#P4\k^@Ewb4{]oUeȋ|W)z1ml`ƒg9a`.V^ͧ@'VvwpX-Y]\bմx3!^sy({̔GJ'PC3p4ا)su\}vSq) 8_zﺨ7d}ţ瞬 ߴQ :ǡ7ٶI IDAT;R*mڶg c,9 _ ǬK~aI:Ж4ӑF.-(-󀘞##TSI)%)ę-+rDΞ—km}f&P<~g'ՊpO:%MP6H/]jYT机pW֖ֆӧnȐ>q0y䋗mE8$k[}YiBa4/olnw Ӫ D̻)rn;-/^<w D~>ڋ~{G11vA鵽zv My' @X',wkpkV 4OA̟>!?Rovijgscszdz-oV: ]GRf`, 4  I~3eT-K$E`@1kBeblʇFxԅƒթR ċOj%aVmQ^ΧDB::8=ݓ{pCPH$aVJPTS&՟3r|tlÜwDܐ8:?<:UmvXǔ,,-'Vѩ?q*L-]OaQ72{- " D4(Qo=rHbL* 3#8dLƫiRIvb m(ثي4&xcEh!2XwQg;a%k/61]LVʄ4JL>&u$8dtڠbM'pTbN/15yx=$)N͝ ZzUA`k1ٮB?EGAWyxPtcّEƂnR?oѳby6 0*Rk%/k^S}-R0,dF.x;FΔ5 2rcl%-x4TlqHJ c m L`ûa-'0񄑙 FhӁ-yD(aӳK-*/=(0lEg:\[G<;avGr5M˶JPÎL A,ഩ\g4DE0Ynf3`ڬ6 SF g&>}9< _bQ0WCAW]>Vvnd`˴vyFUM}:cB->\04wb;bP;-j4"t̛@b 6c3roL m/ox`+>2̥a(( |3g!t*r1$ tq@Lh+= ᯻/"x5}ztck/\e{YgG` ]>M=bCU9#ePY*T_K7^1|:3w3SSc'Ldl+0iPWQ}2?[$v}@qeIq4&~ PbbqK'ъo"w %wCvA G `N˦~pNmӟ`KtHwO\4ktaB \9TƧtC{ҁsjOlJ ]Շ=k8aծ-M[9-$L}0S"jT62C$CR+LS}-) $ԩ-A?Y@;>~z"},Ji0F;_@Plv5-VQCr> fhxc87&ɡ?+!{痢? SB+xGeb5!AX a:V͎7'S؝[ߢ']|cص<`ng,fRA%\CNa,0hЀ9Ẍ Wܠ)oKjGkG+)o[dn ) u_iQ!QRfk͝TQ`hTw>J̀72Ŏ&IcԍcBKHBSdɛGIvHظvx/^" B4lGNIB ){:ZeyDdiꋰ9xda#/Y 5Sd31Ve mPmcFt_nrOmc =Dv~$\r;Dtm}pW9ȡO. 75MCܘaCc`@LYJGE~p/c.ijCsofU ݿӲ-M#W)j.]A;X:{UO1cV:ˋ{-dϚ@] u?@Q.2O-hE~y0/?>tS"XȒӸr' jCџC8SLΆB[?r@~<+ÝcdOG}v?7H\eauѷL Wff'{uyæ{CA/쾔ya$Gep(N]Q?žNrܻ݁T&8146OPzBR5Kh߮zQ) |vUH$YAF)rs)bڧ@pʭ0k[ߗZ ;?PqwE$“J5̞ G-ʎ}YV rv&J3;9?[ʓ] $|Ǯ[݁ޛ5QPq9D/\#r|S{z ٲyۻ žY2f\++!H=q֬!\,LЕ3KyH7'` D.V+K0o_zAOJ@SYU.'deyHI҈<^_3K ;v|2{W`nB-JIt$ F :3w݀/(XaQRK ȶkrݗ!ߝ*bR؞$Cs %lbد#`:noz$m߁ <{܃sOy}2Rn&/;UksHm=JVd\Nؚ,QKEy1RnnUr93dž&+F\+8u[M1.$.pˏNپkVkNp87wG.&⇢G@p572_ `݁ݚw?zmdCi+ژU]*N]ߤlʫV4L%D: FtaVᙵjp/m7W։&A X k7+ͅ'헃kpgGܓ%,@c6gOӶoOٔ_ BJWu`N}AN^ljo͔ 7/2>i,ItZv1ipqѽ5 zVjfYuT_% x \X&k+6%aIcη89Av%jZ.|z܇s8AyY,ٴ㽕fLRdˢxLu⼪>cЂEQ^ 3Jqg}j^ 6Tiœ j r- ^r]_765M˅ō3oI͖ 6n?߅h)|qX6%2ϙ7-0z=5G{Z pdR ޜٽõq]ڀ!Q+И_ȾFD #I IsT#):^#ykA +`, XQwKM$%`Sp՛'fo8rNwpgnD[U]'o7>zp nVBWp: x#R#{Z>~L9M{Luӡ9",:mע3DqGY'!pD/2 m<5`{z iuMdxYנÒ_},'}n^l_ʏn  Q/d1{c a߶4Fw黅B1[GK1ì5k3hqraC~CbReJIE?u6p#V2~tuԗ\Dpjb,LtI7m ={76oU'^۝000~9`q9T?wU7e# 1#fɹ,V6""D}WKiPXp`,;~PvyCKz ['!y&W_(¿=qC}C^lJnMy(.nH3 zxt|H]7"xCaY xy=|u0g<>y`|f Ek>ci㣾/%U*G!@!EwuY8[)S5 ۳4푡tHS0c~F@VIA YKӕŧ{QX&NZyHOrG s5R[g*%icBlߴiMfgdAwvVe\ˑgTSlѪCt53 [2tŷW̾/V^Ɖ-|4}p@' tECFO}A0ǀF Xb?GL4>`eѳm B/uttK,]rA[b*Iw&Θk]]@rE7RukB7?x 4f u6kRBBE`9.LAu[ӹkbw-yw̴I{~gT#Nv+5 Bbzx46)\ex˼;j4ocVw1w3F1Cw HHש`5_B!Ngo}:X0Y<:xgIReŁb+s,l8b#3~3L Go{Nc 5\֌ 91  )iMvltp$UNvj0sɼIRҘ0r^jD"衢 AUM?4 F;5SV_2wI!.%6_q{?M r1Fy2" "3!T#BB~~vziX 8|w@%fe qü&BB&E[ߘY|;`gS\d1uTlQ)LJ̞e0FS\sgQۨb!|$ Txo3` yOfpɄ ZݫQc g.@RĂ}gG*"W>88iչ1Gx)`On;Q`V&n)`.au@PX]; =@88΀P S(@xfv/aS6h̝ZZ[.oqӨH /E57E懛 s3]jzSi|8=XYޟröh<qN[7A9y꣮[ZP~pb(Q5-eaWц]1vo}\0 p ]Пqq+1 AATpQmŇQu'- L!ߐ'i+],fCwfxL(W`|jz%~SPlxV¬(>.?.se^.(ɸmlXz=̮>H6.Yyy3MQy*+:ligt],sOkiVmۋ6?RS<= kk7&D cAwsɞ*q\^p1roZ@dWoHNk 3BSHZ̞ҎO\p)7-Z+7uԧk,/'5箐TT7Z$wSqf.v84f%Q_oDcvMaO/&D_iJL5Y\eUO~^W[ p~|nVZM5'][[KռC^ .i?D9kK=dnRd'`:]XXtmk?l;Gr`M{>asM[o;k>n$/(9":Kߏ[X9_N b>f).uWDm30<]n?nMU?PmImL9iPLNLfq}J9=X*7::^>vF ¢+=(ASuf(!\n#6a1npBƒ3P[oSbӐ wWl~g`w@!@!p C._, {瓞nk?zpݏwT O#̻}ciT{Zѧ"H쌰ԍlz$y/rߺ6ް@K'r%q@ $4IaNuƎ/^ 1R|}إ>PP|xCU{K"C|}s0?QNٔ7A,bdF=c"3SCӿ-_%poLMǦ^]`UuK3( cJKѻ=B 3S)$G:)#FG [[[p3YJ߀\_lΞNB"@eݎ- H?SԻ7륑Fx`ՙ_b –<:hVVT. 2YU7       ["R7~kIIA!@!@!@!@!@!@!@!p%\]ėj6/77܂QSPP,L"UI|./0}rR!, 1.G"6FqE7H.,Of%jj)ormY-Hո!PdLjsjU6 +HR]˃_eNmɷf q|K6C|]d/kz T&^5]F }-o llUʇsu[ľ_P %>*C!vE]PG~J1RǾpxC 6MOAnNM4f[=a]gWȔqd*XDij)Rr `!\>•e| FDb+7 R9FkNRdߎZAԒ!jP]!ʬ1UwY [)̩CWVk K̓M]T1Sr4y'LmbyĂ [anHl64lӀ+akg>n|!3DDM\ny+1sGUY[|fnFpNZLd 'g|`(k_z+6%jn*8r1(tґ7sGyM=}SUia)?)# DV${>kgg˔p9\P$2A8WgDw8R|(tK|{k婵5;}+SO`Qrod?X`|.ƕ5BK\+]V-lSxd5:@/b]C $^_J+Ghg;T8|謝^l*;u}W+c4#%p|ew9ґM4hHrdd^aCgGS֨KaGXۺ4n1fdUZ ^@`Z,G:Ǭ_Phfx(4uw#!BL_OC7O\ $eWUDFr<<:U}1jlpZ{Z V) ]`aVE SP@5/erAʮ)oͯfq]9bx8eyA|C1leptm܌2a1@ @.\9:vCbLj a X)ir)E<#PQD:Y'uvn`7 = 53i$Μu8 ҖIrPт?S9 4cxbֶɧ>'[Z =踱-#s@O[SԖT艱eb}DӵubA^ˬǁ$3^`NZ,^kiU!zȋY[nwNR[Դ Æ}篍b˽ſp0H oe&ȅZG.OoOesdfWradLTz;Q2?R 8!`s/lIiß( yzE!^|w/Tj0_`Ƅ<?v$؅^رc'8K4uz{&irߴOi4MRxlcmv̾H v@tt~#!Y:ߤ93<ߙ33G;%Mj ֎mLZtX* 6ȴ7UG[H{sj^r Ƕs&(;Utc|Y$9 N= Qht8UaXIE@ݹ^Ţz?~d t<';8拀B 1FPy-u9"#A땠QL8V<`N! `8"jI꜄(dҢVQӀX :G͡8Z_SelYdQgRnwZe@}{"^Kgd=SQ{_~95ӛRXO*jO6FYc?4.޽G+1 w@,/3@Ϊ=-SqC2{*W{0L $e@I|?Վ<0d^)vHY#l(l֍^<]CAm:kIy|.sV^;~tDw'!D GNg#F@[7y"-吾 4jnVgoݺpbT6n f!Dh Y<蚄<{ŏ4AG\.ˉe/^¤7_LV?ݙ٘Fuf(P{ǿr XSs{}Fe$X.ͽt:'Et*0U.3\*&DsU.B`^㰪s^`(-kߣ}6Q\ɻ!H~ ؀{ C4 cCɬܓz/05'S;%Фv2P&G'Gf\i(yiWRyI,YZRʁTrEA!4{)- L?a6K3ݲ*M)5? kcTS BtuCt}L0n ZڵnM@pbpmuT\~4z%9 Ѯ3 3n~4q<=qAܱbῑoph@}bK0 CmHI|jzh!$)tD9J(ߞ'&0Fsk'e%aYw_iLAQeX:7S @/PC7DAPI{*rJa$E]-}#VQp0ki+O]}^]5,i0U{B$$ 3 ATpPv(*eF0Ǜ&!Bl/mmP)YetDڊGŚm`2.Y|͓qo) -Y@?ȢЩdq` ʂy ##0J2 "pd_3]-okPBvfy'KZ&S,j1FUi!jq a'W2X W7 GUKq[ +Vr)D ,Ml܉K&<6}k߾--ђ_!ܠ(ͻ 0'\q|0 ,K&cDډ1xO Sh֕f~dOiFӬu04@^4UdXւ}?gݫo4ډJL쨗#c-f:pKR=6!.; &8\?o^0t[w\G"bY]>vB 2 F~nUH^ȂBe 'FLOxߨSMC@ۗ@z`}QǴw.bܤ9S٠6B!˱\Z9 dIFͺ0}SWF\zf`GdDw43f0T9Lg?mkG`glX\y3Eo!z`ȏ{@3*0c;ЌP,t'' j~|l\0Xyp?;\{{SiQbVZlqoFA@?g6oqOmW4կ1)z1Vt,zGe!%i)4/P)$WaVa] h}f 􋥟Oxߋ?w\/, CZu#a2]ƍ 9az Dc9d1h8%"eL>s:Ȓ)_SaJN ߈+;yII)-2%omr^zSJLS#c@q/W \=R*1A>?~4&[;U,kLaWQqV^zI~p(0lc(Kp*;n(d3SX rXS8Q:< d0Y3~A^ D5V[? !`B\9 IDAT8Q0_$4MAy^"슍iHQlCJ$&/2,A{?mF? ,4DӠP9I㟏0J6޽`c-.u\!Q1yD|9, "aјWΆ,) bJ}-^ d‰@#fEkW` ȇ!ݏWֳhûȉ/8Tw'!6f=#)/?J2BHfl ]f`>&TDȝ;r LdO#LB6;S^40đ8dJ2uJGH&7Yw,PQGvflsk0⑸YtT4+g>Q]RSyAfeml'ԃq9ANA TTfS&eNbK2 ɮ35GM}Dc 0Fr7~׍):aSү刐v|/uK-|jQ5asm$^? 5:/a<!>NJ_R'"+z]pJPE_D-H0?vLP&FTMy|cN|Baqe/* /Yf;5x}_8~tTܓc p/J+ )tqp/ &Mk J^R;X.3|Q#+ININ|GH+gK[;qfmqCwigPUU?vG ^8n(c{Xŕ/FD ڴڼIyh€h!mQHP!Q1y|9,  ,17 lږҔol㑉:5p!oT'ФM . -V@(J}RJ2mClMD yN-]anէ* ߮雖JFyO^DfJ^LpƼLwU_wO?p nP>:7ܡgBtRڝp"`cڀxæ՟}0>?\W~ְo Yu?N|.ÇLw,jVT׌ߩ6hZze^H c*/X3ti)7#ta*Gj&و%zƕ6*]ݽh뱪VڌqTcM vbS.w">ⱺә{ku8Ƙ♌>$`пAR&fSwr) cOPs55ʔA.΢YW [t8t-kz~GZ5m]b | j_m1@@CiٻL>ku 1S7]+>)W '$0[X=fHQ?FS}J0)37k-tHղpVdp,Qq7M1<}wk{fY6N6L[_/as:d1:fzGEĸStNc7-5c%U)Kj!LwϬe\ᦾ1 N`y8TR>V#S7Դ7qZ=cpuVO xߍ)#?s5?h彪L̝arTED}3L9xb_#r?I31R3>n@AI"9ôf-A[wo>$\QpM) 80 .œ*$ynYoB\+=aU/# KeJ4M]&V=<߾X6O9wޒ77ҞyytWͻ7?^/hE2]+'c9#~kJHkP A"w(n &ZcӒf5 1|w V}^9^Rw|8y&P'j`.5Ǥշ}Pbkb%qo c/r)K) m3>&q 2 :ʅ_Y]+ex Ѝ4yC?Mʯ lXȌ^prx ħZvRH߼Q&s6\z}ߜ127_%U#_⹶{@]ԍ7{b2!f4!+ä 9a Ԝ6%G58+:.4,Z BE&Fٶ.2~š~,gh/>}?Lak ryAwHVc[,hd:]Ӗa3K蹲*ג lUi;lZd䀐R2~Ov\eO>I6J'&Sy,BsC QO m3;Ruw`fs9 |aq('՗# K,꒏?HNLbvɀHlmבMږF-'#=ԗTB#Ҷ19qCBVDYuq bR03S!@p (Zi܈ 1<E nzslT :TcN*tNJSvِ &FBh;uC^ơi$pypG/`d@[$6wMhW]Q xs 8$ۀe-S2B}%e %CJ >ևmO L]>yfCsF9&$oyq6ex%f jc-kbeZYɍV :c9!R/w}ierVQW1xHܨ ff, Wݹ= do_zT[SCkY[n%Iunj$UOp:pD^c|y߸H%=}/zQ.ͼŵw hO#0D{;u?6nہ}$\S[ F!"o^ pTg[CN e1F2"h>XU?ӻ YPˍR$`,X孃-w[ތ%1$EqY[/h3^u S~tK&?z@"<=А{bHf }BdGZ]9ڧa-0(*≠dW~SaCAyouS1)޹cӳ7^ L|(?S=z_ BfCAi1iёsb%! S7!c)ix~~so;$”umX9,Qׯɸ2`ﶔbX樁mSHoH u :5OoiͤStuHNՆGmX$V%5ysxJ.GM+'?4G0TXwhV^5PIoOuNYplz:1{ _6ITPŻ_V\0Ť^ H@3B0 X ǃ~A9&# dZ"Xh}M ߖeI)]1j4uM&|"7kDB}?z=`j*VY6doN(?6ع7M3oS6ed_//kL}\npAtw ?H>Kʌ4%V'&gC#9%ȲC6]Q? A e,a3;ߣEb%h9[EŽ8V(q`z/vLlK~y1N1`ɿz2XDl`yʚuC'ó|[Nw6*2Xx["O_|>F*fݵ_z!fۥ%ڎI yL_(Ht hni:F(]pI:9Z#)vxSA$BޫYq"D;eRIy|.sV^;~tD}4jnVgoݺpbT6n f!J;C꯽u4mP%7¤7_LV?ݙ٘Fu-ی/ /iDo4bD#3|%BR,5A3Ld% J Q7!+,!l 4J"{)qW'ou`^]Ϊ',5:y8,9W`֡ $q*^2H\ -cq"QNF GO yI@h򼴫FQ$Ec,eiwʮ|t[e sWK5FeȤ OP#QrBBHS"FQyB\uw( ȑ[ce i[/`@H! +'R e}2PFdvi fxT F" hsR1!sq9dzirNyuI@ ̘AHoʦkb90!$#0J24iX!G)f`O0j}zG[y*aLW+V&x$El/mmP!YLD( ǢЩdƜ npLu U= L]܁Ƒm J,!dIˤ ,j1F{F\?VT3 1ĻBKf™nk: fAk d |M\[˥(v/ͺ6jٹMxl־}kYPf%ӆiFOPUZ%iB'JI'{L+0f%&BnyѨi aZ "W'$E9v;ȴXΊcUbOMN< ci<ͭǣ"ld`D,. רSMC@[>&j #d0@¤4_?P,cafߎ^dPl@jj ]2zLzAf+=|~ٰDĔ `s' ^oTZgʕ{8( |բa,Yt=ɣ->&{Lg^-.QmY'vpZ^Sg'/6Ϝj62ҩ445Fe3h8%s6¼$t~ˮiH9&:!iKQ'B;ѽMAv۲ LJi)yx&Əl-nb O)M9I㟏0EP IDATJ6޽e{sX|i P`{@i"Vx*!!%z#zɡ9m[F=GN~)7oR|Gqbcx1EY S5}@'k!n\Ns203'"pc̦  Mʜ2Ŗd]{gBkRL?)з|[Un8L dE/GdOF{[ y¶gGw`JŹ-Γz40pOf,iNJq_ܤhU`7?[?|FZn=ل,wݛgJjXa #a!l,¾7і7U!1xvg&#&gt`߹:"!L8vba u [߭<`]gIU%`ZLNγK]7 ?pOnʎENGRg+-P5i a]Q'F4ulgd@$_%)kD=3-%&uvA)B!8 7t}6lτQB*,o2;$׶~cϩrPH$?gwJm ۭ--濠M;~Hͫa`6Ey`Օ" v0?DJei 7LZٔbĊbϮ$: .7J2)6lMD yNE S$ T%/f-'Eq"Uv##̬h?:7ܡgBC);Љ*= ӵA{ͳ2j 2'zڵCTS;4mд葯[?luϣ>*ay{Ý܌W@Tҏ>MųKtdDu ܥ{ƺ$j S16s(xeA'֘wun>>6V{zOPN?1r;OqxIrt˔Zka;Qt @A!Vt-slG+p]d 㝅jhw>dJ@BymnҎKZ '&քJPfZnlQAϐOE и~kc2?XtsnH[ia4#cJ=B'gS+d =LymC~,J=\mھ[:#UCfA0=\wLDjQY^뢣^!ӎM 0p:ϯ9fiB{Xst#!`W[HՍ7np#]o=Ljw Y^^/hLE2j+'A\;̕7qYxWqͻ}39\NV)%EwebpA$&rl(x1sMꡂ-$NF5&]+McsP+ȗ$Vk97&O7>cZt)_7 4sGn ]~UL|xUj'> ;kF~~ԼYcZdo4JګF67d 4z:'V..+nbIyR9L `cejÍ#F 22zY(Z\= bͱxh5$.ZPemmϪzqe-n#&m#WYyenD:qwM[Q,ʪ\s2wu+R(k*+SQu\)zܻ>o SχZe b#<PΠ0k?Suwmfs9 Baq('՗# KV&2יfn^?~7GcTj]6<k sP$6H¤(|}9LږF-'#=&αQ-D`-#c3(*KdlO-P?U43'~CVY޳vW:.!ĝpj Lz܈ 1kaҳ;j#ȚoSJO# gI칆~PZm')Z&dJ/@8H='x⺶R S.NWE6(yOPW|do{cy(ihn2'W¤-/CW3n|p,򍳵eu^TX9+QJA#aYuzs$TŝG_ڻD&J0/&F8x/ V’ڋt08$9MTB6\u,}Y ajdv[u {Ra:ɻmg'&N==>[! 6S-G4s)\{h΀V4C$Sw8 dH 6“3&m;osh\S[ F!"z G0iq~o}@zs| 5Ge5Z G$/NmS%w[~V/q)IֱO4^Yd3^ro*l1!(6#N٤zBMUx10Ǣ`3z_ 6֎^LZtX 0bBZ^ZYlXkxwߊ |֊C2uK~מ# >3ƻ-e3`|q,0c3 FRSSl^8m=e,=G6)ekhȶyrosŖPhT5v+5 *o85e0j4yn+ WL6|taYߑYi0¬~6 |YdJh}M ߖeQκ&lmU%Dl+M3]ox~|>$:Y6d L ۘ6mIZ6԰Mb3EUt#FJ`pc?$ޡHc0'{3dn7KTYd+;ac-.MWOChPng)K+i9[EŽ8͡$ncA³m59$b~!S2۩#]{{4n<}2%W]?(凜#/jU.;ӂt?0Kw`ZT9@ \W+G\>'%GUc$os~כem(_fΝyPt`~yôf7K*w86I'"E Szj kԏ_#yzS*)^Eג(K|X~gŻ(5qmI楈,1EIa>\6$vNf+od ~W|3"d~xoF_ oBIM+A2tH Z}O y暓sd#BSGQ(dlAgܽi"2 S$ny>ßjGFTf:aԑߕ( Ȳa=_Y8f6@_N쒠daқ/~XP&z+l}#:1]jӴ]"Vikjn1b뉑98#1HtDvR6!h8w7NV8 K4z @ԛǫL?zu_/2p3r:y4 Co]!ӈdzf C/05'S;פv2@t;H|>zi lAd(yiWRyI,YZ])g:ȧqr-r?>!l/`pyaxkrTZzjzhA%(/A˺Je 2U'q . J"5tCd uN#IVl x:;Ӳ2Ǜ%<ٚ_ zY/0Iȃ=q+#!5l5bӕM3=8ӹ܃zJ[y*aLWL<|By@!˘O n Q7K8L1|y`փN&Y6짯S{~rK2Yw<`>\HƑm J,!dIˤ)+ ZQ$|q\?VT3 w.R (N|CT9Ǜ;׍dVe {!i>Q̝SOq3/K8 8n4u ߅ a *s#' 쩿2(b AEZ Hj-~]]gj6JsD_e%&vˑiMzyί$?'vek/]x\"ogZؗG0`:4MW~ج*-2Es͘ܣTL <.&o ~œ!9Fy*b2-愚O=P4v]gT3E/ƊEϖt,d|d8-EDս5R8a_<8o"sN 7}rao6X* ^(n}K3%RZdJކ7#{ /k N O)M<7MǙgnB$4W_k2ҹшgϟԹ@\ʩI#ģ@ډ ` qy[ow )!.]0+8W+/ttWsh?8XS ƠRԓ`JZ̘J1cxd+\ei^H5@e㛵H:~z_xyzNAH4YZ4A 0mU>b6{/KeDCDn]GmqvF`W=D -Uc^Ulcۍdɛ~"釺{ pp:5Ď􆫤12f=@1Տr|tpy,Ď!P`%5zC*5 J\yhћL&d]C&,{O}5-{hZ1`h*pq*FeV踉l&y0i`X#2K4Qi fVie# GZ2 쁍23'>\U52΁0U~a?ņHHrl3D-}JZw(/%|r8M?a*lX{OھOéxoes̗Et@:5i@}KgH6 t0]@%Ƈje'."E~SwV@uU h!ϝs4E2LN8Wi4 ֍i[w=bմa gcBe=iĝo #ZG͊Xᜲ I>"^28WVe~}xV; tn`. QO6!Dd mhLYn e)0Ĥ.(:h |Q#+I@1x9SRLHr]eg0Bs:A4 pۙv m4 {~Hlo" k) k_G𓇔S' Δݜb;:ŒO 7XT*K `Oz&lq^lbE>4BT:Iifk$@ߝt e6Kz̛P2/ f Nd+Zp#0 nژxn۟~I,!\H~<3yz&Ԏe u_F@{ͳ2j 2'RcfNдAZ+GBRw]oR~:UnQ CK|RVs۵y-Խ+j28w~8v{Zc&pTcM v.t pWFׂ'vuoC |xɼ>F|'6/B0=@nw7ا t @A!Vt-s&W vv8\o;=^WP 6XpTTU b{/VU-?[TPp3dS_4yZ-~wAL .7+nVCc1Xsv\`jLi?O9NUi+o IDAT}~uF(a|Qݨ0p{r/=,=GN 0p:U7GVu|aCaF݊32=Xe }]09tlϋ<x gf%o-khJvl9wAlܢ,o)c+}MC-ΐrǻ '-| ZT}գzNЎ`uS: s IcYpOj q.ߝ(0T]# s^TZ5#^7LT ,)liNz[׍^4Ԟ7K |Ӛ8$j; +yk7M_nQYr6Hy[n֙>]`LtZ(-&>{٭wdOQ,+4&GlƁp_5e3 )g870898Yfj0C|oFxĽpfƋ0-h){TIJ\?_]Zw\ھʺޮcGqY/9A (ꅦ/?ycJO󳥻APU%cQCAK\O% QL@.[=kuFqVNYIK>qU+MkR#anTnT~٢朠 pɑs2=.0ӛv?w(d=H FuDΕ VG:nہ;nU)5j#&EInP΢7ޒpQttw3J*Da&oF^x> N_6謢nR Q4Dx18:[]ޣ ['8,-땨qom\k|(Jv<䊠ifkbp!v" ꦚ:#:+K"%AxG;/@A>֑JP?mxčO-Z# QSmYdʆ5ݓ.훳1$#aé\עJV1LC@YLh Spѡ3R"ѲiR?EǗJ*mc: O^\ lʕ V[;Î0b5{L/ [D>DK[6mtؕ050m3s+{["8])TL7J\]YS/'nV).ёe5[@7ph:lqױgm,8bG NˊaFT(#ۗ2C89hյsPЮmۗo|$3Ӓw>yok(jRԴz YL#QwY,8"qCyߕRZ /컱[ 7^xɂT34j{nfBTWxrvnvCҾ_)p)*D&g6̓?Jidm)) `y=^߁7[ c5=; dwblQSۖ><|l+*sGZ/xW6 u(mڏXAyuI/sRS]3 ꦳A)/OlE%z[ M8hYI#Ift *40FlX2+u5v[D6M7=mHagsͬĴʫ5=4$tͬ$ r d 5:(`k֖e'l,w[mZUߌžUCv{G&5-UkpC-N_MYW1/`FxvVVGQyY}s|N$ݓAfk { t?>7TԩϿ0Sm3dvFۢo,4ΌBIXtaޟ}H.L]+ I*?/~p yf0'\^*jYؕ'.|`}ٲGVn opx]݁J[Ls /Z9ذѤj3syI ,EL=q{O[Cix8fSLw%oZ[j.n+:p$>چ/n}KZ^ArElAtxzۻ=WsdӅo&햯2IRjt؇ɹϕu}bv6OЫf}$;^Qw14/'?~\s=^́ahg+?j+^;.R4ZҫOK UQ^lJIʖ'DqlL DgU=m81>T A#]:S٧4 +}\қm\~=ܻ)XBtN`c;5N Bo;O>Vh_K)L?< At-Qzs $]̣?bP H\$Y/#,)šl|3 @LZ5dhԃ5`#[bjkoPި(Ƒ;q<=P:Jj+ ZCc+p,8?p>x"O;:RTY=dcq& ||XB?ynpǯu(3g TiٿW^027r[-i N t&@koHydoF(؜.-1o X삟Ue#d0oQY |t}<V 0G. y)ڀq|9d;ZO*^QEilӚ*ԧRR %p*$a nh  ܁LQEa*/,fV9QK#Q%Hgn\M0 ̑erUGyW)$rg(.A#f ή R6H$ X }aA;ڬj'w(:ڒ?`wgt_7va-7}6hJKS:(n QĖ`6Eg'Aa1fl8?WU]I])OM0:Cg^?'J pw#Ĭ Sh`#\ L;1 &[Afu˹P.v3)t`REÖ&AT#"ƢC>ɈS \ުegu j06c&ׁXMD枳8gecJz + |S6\rʔ}FȃP!E>HSKta$6+1ay)ǂЦ7>O0P)^z4Οf+n)n]AS<&Hyy`T쳠b$"N\DYʆƅI+ƺJX,iL@@"gP)@c Oπ)DANBeN<5Z Z:37qP~[̆zsz 'na{^ p7!0o٠ߟ|摞 )qTh_j-c(tWow>lnaH3Cll__;'Q5)gREK{#&$r<Κ1>JPu`iW7|ᖽl|poFqRm0”P ˣ$H-b0S [0蹌䥈8o{g~d)D1i/b)!(:µ7t&7wI !BJl +)p7VР.y$- )ݱViu:7 yNw$:\Oe_`3%4->i;|=N hљqIW( ^J4S EK|`Ęy'C]3OSfqt2p LpHRVs]͕u}8Du:wϾ'UrNU)dlEEl[a` d1^1)f" qӵ) AuʶDuM,yB4\Х{ljMg Gu[&f[/:mKXx觥l{6Ch;sS7AIV(U2d7AuB|L~U QĩmY:s.d{{7+) @L4/Iַ6=.*ަù6P~-twج8$ά Ml}˃z-(!hwj<]Qͅ.^ a90ŧ{~~\>g|l`7W_zGVț#;aFED|T:?H oIs! WM^zc\n@x0=#Ы˓f7֘oN#Y+*"`E)`>[4eS*EUd?>BxO&TE'9:uN =U_ΆY b% W&֮./FL̸]cVkb߸cٽ4ik+ #$@Uy%!7+D~ TiO[KQ~9+ xED21FYTڳqqH\^ P?`(Qʔ0Dc.JdA"af[P9k-ݸ祭BDkR '[YC_WvuRX:g O NRzKʌKaFE>\x-T |5Hm a}?(3 o*D4a`$g/gfU浚 @UG@箾ѻCGsLBpN;rs0g'3cr0 \φY^z+-?Uwػ=բ>=~uf >,(`g?gֹn9_yYpcm>r`55n,L(+=6, k v]j1w[LE5}h?gGKM^; ^6v]iB]5dw +9E8Z .~.)nV}ub3i϶rQ؄LaJļ#^-5Jfz fpv+QĴ3DBq~yE sz8ɕz{&uGCNBn3+A<x3pjVEwfx A<xʻ=Rx An3/Nosx A"`U_hPMfTZlI< JHtn$6!c:3ȼ!(,t{3Y2%Ʀ:ZSңHn,FlV_vYC7V&"sG j̬e VɎa"g+nQUR7S5f8Xw2Epxwa/ۙ0˘ !gHdtaQV9A;2}CRlOt,9s׃$!خ2kW+e|`5VwfW{72ajHe9OaKsܹZ׹<)VC7$Y[Qu \-¼d^Րhahٰnfz:P{/ nDbnUDIkإ5[HrWmY>9*5;LސJZ<6ARFE7|J{[<pǟsؔ~ÿ4JŹeiDn˫F@@ƮH^xj/xXP~W QL@nT[=__M>8yO%Ĺ}b|&_5쟛h?l-t#eQyC%siVx{ p6 uFqvHpn4^w~j+;fÉ A ݩf[߶jj;K"fGQ$Y_'ُJ0yΦ($iŦjz\Ɩevuj /d@6n LYq'loDHAgWR6 g#vTa4Lİ/[Ho҄O"of@zӊ7) ??atJޭo6mZ @HX}PՇK<=R#anTnT~٢朠,AisOWy2O#܎d0k[ךaD+-`cu |p\/hkMTVNUTd<4/=Yې+bz l2WF-"Awz1i1ֺYܪiSjF#M @9fsb{KRqm{F:;@ NKi%0Sb6So痸:[]ޭdL9١ID+c9@1͌6tM0,./(8 \XdߪjP /%JV\!240HU,ZEZ0˶/٨ 56>apyI8$$r IDATT2UD AT;TR5U-◔JZ!\e 1T.8765tP%aj%3ﴍa7.g UݳXEbX})#Gy.gsh-a;3+-7C\3U?1.ѷ1SO]k;+3oc:5b~߅ FSu~;"ӭ-&s5LMyyF#C%nJ:asxJ!i]K 8ύ:WWTIsemt$j0+mY [PCM3-: b6YqG iY"⛼|P;QRPk$'hx9(h׶A0#*w[_N fDo˽%Jqy}t~QԤc{i~G#n(C^ ő}7v1>6kݗ^o7YF!;S]ٹLۭ,h }ſR Z1$oSTAQ)RImn)y_o cqR3SzkPKI8!aP_1zviP 1sH&p}I' hP8/f,o1##޽R}&޳k[֠W3ۂ=l_h<gw3O;;m ⳗl/"_'0Dm;o,=늅Wx?v,pwaklP @(=醇&Elflì$9khs6ۏaZ՚nVտKX Q͊k>[C!.NVtf D5CFpb덌$;I""D.2 ݈ƪniQaZtX\mߊ.B.3Rk~C‹'>Յ>tyra %>t,l.uvUym9@jS0gP1p1"fÒY Tnʦ²r3l/YZn؎./G.lVw0n ~Qܮ ھ|ՔVL= NV&lTt'+ԧD K"Ŋ]M霠M\GD!q] (bqƟfb+>}}A/~|nS#adf2A 1OCn ut'T {lJG"" #ؐGGG4Rl+N\Mt{Wq+0xe|XLJ;Ys\i/ù,WEw8BɄYxOF'ڙ}|wTP\ cHD*zYqoDۻ7ԊHRP>O}Sro<{W ,<؎BScAHaʏ=E}K!\I vr ]SCRTIxxɒE¡,]Gw~Ku^*Kc鮈g;I]VEy)%)[ !Ʊ17`M̯7 wZQig+?j^;.R4ZҫOKiua'>M^pyo &kIŃ&; jBja JWg Iv@IFE.‹ZöG4sȄ bm2(dM@ PĽ r E<jѵKd|3S>`2hΝK~ӓW:Q&,oM:Yʦ=LG{Aؘ­P`VÌxIh lb),u'ʤiX'A^tA4Pwe0T A#]:i7?O>V86+0d44j/mk rAnTuG4"E81 ~^I"󢄘x'8vH9$fPL D$ך TG\I!WYT Ba'>28Buz wSP`llz`ݱ)]A4 8@s+Zov7R7"J# dNN_YљYܒtu ;ww UwFi`dP)bAzh,"-¦\zu=';bxc]fJ8~Ee,SjH)v<޹S?CNŦt3g TiٿW^02+`:nGO6њa)0Bi038jnrH"d0oQY |t}<\ 3)Lc'`py`-,wũkb%)jL) l.8$ LL|w04xiw65:,?v;ܥKLny5Oz'n0~E|WCM®$*74n"y~kBn7E0!EUDݒ=UA Z Z)繸 N#"ƺH;,q%qbVu:g~S@[Q @NL{q\n)d9D^Ij [~Zb8 [/Bӳ:0q耨VpEE_6}#@ʽUc.lBZ &0Œ w GLx\IOsuewʆRNt^SD-nT8-N)D/ )>2wgϔ~&.Jz=zq\0MLf*(3;o9ځwG#U0kl7{q6`Aox\p =%KNepDTήCz|m0P)^z4Οf+hL1E3S$h LIM9;BWkv6HH$H.@$:F"..tY R ˬx6s. *';gry{f ^HHkM괪i+$CƩ)@4=)WB % nVC{ܝW\NH UtBx]@D+2*1t(tX'Gzz{,pHZAW/\5x\ckk~UlJ5َb>i]$J&e_LhiE~v[æ&!43亝rs 'ɽ\;f l0T]8=-[#7|ᖽl|poFqRm0”P ˣS'ȳ, ?ŋˡe\ A^FRM2٫rsT707-̛Dmʰz L{O1~~I6 PrC$r1uy;`ʆR0 [&fTuZ,ދb8}N),N` LM%b\W=6Ó9/eyϥZ(—C5#*qxg:M P~Ȧ,^V }sMV72KM rp!pQPq%юUOmV.A:e WNRْx1 ?mUV'}9\e_`3%4-+tg pTl{KK4[Sfqt]RL{ELɮTʺ>Lc:rTg`JР D1ZZ:mNv7/} dE!\Iaedr aMZ4 &V֖j vx ' ffrO_0-Mcj= ]mCl޻V{+ѝ{+PVdT#,Q@\z'kgp4bhmEkǐ",+2@tڲ cQMA[d]NY40 CD!#@!I},&iJebHRFq^Pm+24%֦Cpn奭#<:6:o0B].>$`ήԂM`/ްg`9tӏc؏PX0zzw~d9cv#BABMx=}K .\5y鱎q=nEx0=#Ы˓f7֘oN#Y+*"`E)v'. D[7>#0d_&ɮ`sFCg?#f ( "Waq#^Nʹoy9D.X*}M{9w;s<%OE#,"V ܮ:|=Q6ϥ-!٧-?\zGLbr#-M?exy*o9|tk$؝5XPˎK?wtf>+ʊi G$o\*3^)[ʫ H/F 6qv=Ƭ:U?ݿqDz{$h҄sW@G*pw!s '[YC_WvuRXﮑL̮Q)€]{6n¨;~˫Qף) uuf;z.2~^ld9h9$$22$ 44D1G?"\ߜY9oI^sCOd*ik!/njU' >/=y| gᖞҲ|j rToNQ\kG[h}dkȕS؍ OlCum{k. jp psYDGhP/Bl$QOebHRFqZXm4{fiGR^['vty2F9?hw^LFuw9puξhɭ;>B*k+Y)()%#$2KB ;l Ff,>%+ŧSLcEA`=7jnţu;-~wSkGhƍӕ#j'M q=!]3''_}#h8DxP(zG7M~t&ozhF> =&\d;gUob]YV߲YuY~,6ڔ6K-T{;J4]Q:8 vXoO ,]zxY(8'a ]*;f*hBF-}y+ alBk0%bx ^Ԙa$t\ܨ{>vo%%H%r[^T6"AޛLK>?[z.ww  Z}*U]4VbbW%vl+n,*o( sߵd. ڪ7;x;ۢYgjk~;0+i~''(.彶t[aTM rfV7t_ZRҾf%nAy[_ZrM ~TɃnXX5Z%YTBћ2h+Q26g,ayu͖ۗڇ8-=9hl%h{+1R5{=1cWC{ɼk>S$ml\;s֘ܬ4OV.NPF[_ӧ@?j >;J ~֒{\K|G /a;="iǏ.ٔ ldw~e_|pjMNlrā;Jpo.gѬČ%vfH>.:,_,J:6h_7su7Itu7C yLQj؆]lĖծ#c2Z >U+闍FJS_Mq O`n=t`Jb`L R7/g%W"_L kSMўNOHfkh*a_j6{vFA:qsĄ٢-E?ǭ`WTFN m텏^7$H imxP<-Τwv$g/i9^׀k5QO`,07w]꧳J柡X6AyuI/sab?]p7 JypϦ~y@oEuCu136aVAePT.oM`nnoCm&շ7$pbM} 1eKGOVO]EKEEѻP]cM{l^o.,?z,7Û a_ęvPhܕq1ϑaT6qb|6C ]7B צe$3NU$ bu L=!]8ֲYa@oP[#s@n+ ͩz,M=|T.gUûDTN+1P4v|1)nܶFPdNd+D(1bLtBlڲU^-Ԥjf%6]Lj[7p_K[;!rYYEe9)kJJmfk{ t?>7Tԩ̔l YIh 0:S0 O& ەɿriϰ1ѥP~cIc59BPWӟǮ3a"^sSaIf5)H8'n c; 'lJ2"TkKm]G͡oIy\ԫ_\~:h[m#O@o}{¼'j|ѤXL+x2:Δc㦂_c~gG"?Ů{#@&Dzć.h~rseo*fk<{WM qԀ3$ %> Eac3d|#^[%!T 3%fҥ&xJ&Kި2i1ɱYwj#dyT bƀ2Tc*~wr{'Yka"nfd!(*^@>rt}_˖ŰY^938!%!?$MB )|.fbQ3ԧ#<~y4UW[~i[B)VtKQBLTLطy]̣[ϣb&gu{Vd ;9=O~:gEg*g!VHN/!֦>0SKLq,!ҍv߆t)Y_~Y37G)|yt}o/' B VCӭcC17LX1wJsJ!AIUtƁkw!❞YGK 8ؤna؉H岗rʆ3fM/p^X/7j!qA2ONlT[YPj@WlYp@ok?|Dh=c D$YKR|}y>>}Wf*c)do31mGW~)nCu­۰Fxgo$ә`5.j=p~Fk 6 a dK0#Fo$ |)aF%^ÏJ03㹎[RA88"Hu_K+ 8B Udxˋ>zE!qNk^BFT6SJ 8KU\ D VCd859ըD|ao ty.gc n'`F1ybCJA٦keY#|媎򮶏ybزenKTjrQ>LHQը5|$jOU*jP$N,05  4HmxWJ[Mabd1T1)IN^W-2̕dpwJj $ؾ_)tvBy#tUW>nmx.pѽV?q1[Cw8*'To37mն2.{w9e|M%Es΂ND"kl8hZm0Cu%0ljf.2T)~iv '2}!(3Qm;A-l* LOF!x3YȄKNp0Ù?cSh`8@fL;1 &@5j0M/G9YG?A@JBg۾өfѰ`q ՀE\'VTZϿm$#FNLs{ fРV l vpEaBu7?sGLx\IOsuewʆRN3B ), dϮ\([Ƅ({X B*w:>D^3#} u[oR*6pKӬ}g0@C*-&ETMmwMDR(J(YeH咫q/׎8u}\SSɎX]6I; V,6 B$ۘǏpf993G$X |o)( q>}.pӞrynVeDt43"r2488iTn-I; N.TuZYbP\$ K_']v@R`T Ͱ&/Cx $| {ZQHNrn1kNH6fF\l͙}m vH#U=m%s;n~Sb7jC2*EvmiM% :pB򣩽]f0XwE,AE"t84:;3?HX]keǿC|웃/>)ڱcvM.F@m!S\ku-ϦU|mEKTi@e Gfs*?!iIk {j ?6%=u9 U+z`jQ7e.xE2:f|CbbRU?YBƒ6PW(ں M־c<']j 0YryT̎2[MUfaAr. VxA\ L5tfɊ>9vzgjBhK=^,ã6gHNGR^ Pޙ,O!cw%Gt}6p ~! NEq9N?ߦfr-fRAn,jʭ)F96pjp [zʖH/|OR;jSh7f9ԗ N>5i-U}LI>+I6uf`L"N)+aG6mKFSyFPo)r$QpD=="6&Goc,Ip;k䙢3jVEIbgc9_Py;)'A:ҫ0cOTտq 3 ||ľW[]B8tIc­pZ9Ls'J 3)fͅ+[]@RfR?RVQ8 ~٧\®SV(tݸqi9ne6 7(|c1DrQe-E P /N*L_wVK <Ƞ<OuF>pРFzpnLYXD CAW<#Pg=聒6.FLɝY{+>\&~af%4?V 8¤= kֳk?ht`i/nOD8`0 9S14Z7 X=:7yT3_#hC9*XǺ0ZƮa%RQIąқ1Ӄ3CmwC"JD&CzpҳΞ--W[znRZ!8/_^*%\ m9e) Cw0)yS퉪Fg<ʍMΦ;n ۝b|pwɮ/>~I?txKǖwp?yn'cI =U|w/TlSR2"Ji4Q7IqF؜دd%lfCc`֕Ɵ?ӝF:{5oo#_i>S;}D5<[QQ;Ñ>ט/Pp+O3i5f.qVm[㫒c^6=IxmJ8pRִvߕvKpϹ}Ut :p3.k(M'%@9co҇^Ba+/M1xnSڭtƉ)aJ%_G 3!_GZ3n*ZOM~qrӳi}UPӔJobXsQ QqH_yjVtՖS'h$nܺWVφW 5+;ګ7ƞ8ھn&AmDžNP%㝊k`7ZMO  w]mpWާFi^z6Haz{ i°[ԍMn4Mhצרa9yOp %(\-^|oZۣ_[-//h Ҭ5'H ['x3jKy귧+O]o+n=٥Savb1O.SHLВM UMV )ю󵃳+vOW_,#qUqb@(tnqjX6Q9+MtM1f`RZL4*6Psc662Wg"F?y:?giW Lؼ{[muFɦ5i0%`,`u$6V:hyv) thEٲU' U`Ŧ IDAT{'_)jC/ waٱFm8xȋ9Գj&&Ͱ@"ޱa5VR! IĿb?19eaibX1?xb geqyb$`DQhHǤ Ӆ"q<(hǦiR!88#^e%ÁLmp˙(b4rɥ6Zµa\"u)5tg:N%USS^QSs@yDqYy>jGC'n[t$uʭjN/'cnLSHLMeeF+mě(2QB0f.{/517D;uy:+Ua ϔ J/[`& OYV~]Kx;*ZyNr~rE F[xáec"f%XshH}x ()qy(`{daFŽVVٸa_6?jCg)zƎGwGRd{\&1ce_ Y!>GDŽL$n>9iRj)CԐ_ev \Tdžsvoz`Kɘ*5d#Š+-quxK@8QO߷(xxח:!7[~-w e7܄;}ew#Oqy|]58 Y5'O6<9mf*iez,l*i훚XHwwxyPjQ{׽);E7;Rv/FnlMheȲtƀ+ΊTYCBйWCvMOg©x&K@sH?:0,0\fw,yJw~qcs)n kfaK\hVA!Aq:jtd r6 u;PAt.͢sn`B9$<ZN0w}5]]*wl 姙k7k;;P<ƴ¦WKsC5#Fv|Ɛt'1 8\ENia>-pUHLy"ƌN^^ȡ?tX Ǥlyaf~I

|6< =BmZGbL4D,9TFݯaڔ@rKyŶ WVR[bibB u:ŭPCOo@#՗^i[K͟'!/\p ,z];Ew: ,yyg7S ?ry)i<6duf2;Om2ypr,ܱA 5n꭭|4| ڋ|}eBd?~^~ ovWs:?qѐ!(;9CfVz#?0vS_O:P5ysfVA$2 To !>d*MIE`" E5Ӥ53ryn6G˲&oAC3#=uFe;dɷZdT=#@.bzDt_:t9i"2:O}f=_ۦlb1d4 S Or VjLX#jSΉN؎}i©22DGG!&6G,A+{['lz+;U=c8q0ˆYAc =w7N :?CHl),$,␹lad :( SX2Nd.23E~q9fUh H 8f=Ժ[T* Aؐv%.geo%q{Y` b +aQ+tA0# &zMYS a7􃊫v~ 9CLVk` [Kz]SeVMQivl% Emv"(?荿D4rJ,uNLaS$X #7ۺ/ (Z}YuD*%Khω͔sS30gru*$WpjV~n x( >`O{U.eU"Rlєs3 K2&2D J$xL hgfp6pVT/orGPDVWZ_=`mv,Vj# ֎mTO!4+Qu޾^+ A'5#M;iMa7Oy&Zs!biV,cf'ti-vpp.%QuPϘYd @]h>664YIt5”"fQ1;/`dWWn5V|.a˹&X,#q-`YH棺͍[o-8=S@_=ENM \*KRQtų5 &amSKqmA4n<2J :Ș` 8D~#LTtmZjf(y[EHY5MT6 +DWȐqSi*["#=Iy:-lB=#:O*ja ֜yաs`l}j KDN A0(]|h s_ǩBMmgw-+Цh]ZBX4Χpv02f6'T74ptp"IC^pfI^)AO\;f&m(h$A&| 2?Nfzr\w7&~"UJH[m:vڒhY6>Ʌq>5f'3njPv5oo+j;HmX|hP ""F( <"-\6F3],:&F4)ѮClf@-/v,83@| 7#C2^m?)44͏eBx>pu aG'D ;<]E['N ~~y(p.~mM&HV >xs)gH̟Y>lߌnXaL9! +k5`{g/P?LޚuˉJp fK4GP>x$$E:30&'쉔 ARcۭsKOh"E X@=="6&Goc+٧:*V:8 78oBcn8JoG! +L'8a 5V0g%_Y ZZПgaƞA8%g~YO0+pA{eU*#J46/ ۯU[<5 OS6frAS̚ -=W 8|߁@͞~ pd<}yq7A aoYI"2Y,V+\_4o67?hŒA><Đs}gPY4gt+?r*?6ᔽ6$PpdK[kܳ9ޖ# GϜ~6K<]sC۔lp )JkK:@̷*v_ ^{ܝYo@=Yqņv+$`wF+~|prbo+aW nSn%sbJgAR ԍ5h _^j"YkO8@ ^຤s#궾6~c`iJ Kj,qfV #G?~X+<_COǏ?~#G`cG?~#p _R#`S\l5Gf|VR_jG=JV *ȷt_g'D[D|fePBa2CVP>ZW5pT[S3ΎؽaΏO! ۣͰIOf^j#T5qmQE)VlF,jem踙"Ed'#4S8mwg b1zv];<Բ׎86E@쓿tJC`Z}nuxKF-f省 $vOm?I385Y 6N1fZ32 We١{j7u 9[nf%vuogYGj[{ۜ5'm+[{6lWި7O}c."B6Tђ"v` YDu]"}}7+u o[ <=pm&_/-N~}XbL{fZyMˊJ̗/e mZt>|w 0 ֹ+MSFvW}ۓk/Y3b_ @6UA׫/n:έ0T- !jO~tw fR6vM?;glJ>O:쐌sYI ObK|tE<|O-d*i*j=Ҫ)LcO~>C^Ia`^ olOVŭKˀĎ YI så^ovXv q&]nQ76u edcfbVלo=6M}bE%#D)}ӵ/G-D۟>jdʟ>dSюm󧫯k!1=EnZ9wA!ދ3KB\[H4ApvLlUoY|9pzrɬ t}/G"sWScIJ)YYtmkڈ1b0s{IuOE'U76hhx{9h_t^ؕak1鑧sŸ0-bioUo[wlZSjTfjjꪫ~g/zf] 4KUtK۰݇^I_ f: ∂e"/4Գj&&Ͱ@뎘ޱaʾ鴒 !،4gA ۅWDb]q0jGJ)rw`exb dqyb"6LKrLqo1󈢫f "a|bD4oiy%AѢNy~|dm_Bcy˪/iQ]i"@un\ޑÈ:鱚<̎*aS?Nor{:3`)o(驹 Q0=xw4G'F:v:;,6:ML#-;zx7 RbNꠈhtό*e]Su 7lA~0w_mcsHP._4o<+ ,n> X::+gYfIOm@p t4W!iٮnmho1 r7ͥ ۩̓̅ T7^ѮUk>w}6E_36exbβr`SL9vgvȹ1 IDAT͌"n3jo. 1ב%uaY[3_dž/똛I@2|CEKc /?iV_SwQhk8^o9_8- b6Aޑ0uw,Fl~á^1P^څ-`~B FqI'^G[x͍Px+6aQr!!|m ;vZYe}|yէT;bAK=Œ~92g2 :Ӎ'';VJ??1!E nYI˙ +|?}ewb%<>&(tԷ7i < 2{j>P2hL&JXkX I[f֮wH[{1>$CFl^nnWIeEckJ~vE{?^=TU^),~AB#O ʹR#nL~5P ݜM2tiD I$)ݱ3 6 7ޚPy#SuL"yA*64q ٹeDN=vJy2GRHdSG @DwąʾBԌOe Ѿ7o}%OE?\O.LG}{ _ D,9TFݯaƺQa(衧IKh- /+=GbUſ-gPD&(Z>~auI$ Eoٟ IhPGo$P T{r"{1U# 4$0#Eju)3wA;\u5W&qX5)VtジuOZ \ KD_`F⤁MN_XB6`e@yRC?6#ĉKHFS$|a%(hߟk0b9.nnUIYEGayGu :?CHXћ z t6%iH.9$$Y4} [;+;ubR=Avqp.iH b+"1sGRч^rT^׏65v=@ uMG [uKgMXn%lEQt&?d?0k1 3%<kBx~v|ذW61; *-T0 W:$>Y` b +aQ+d4NGd$x.;weQ?yt1ӅҒbiߌ^/.ym`H2hE,=~s^ڍ/|`X$:ba*ݡuV|t/u5̑oz 嚮JE'ҁp̎3@ժvYk4:Bg-LPɡaNY%cS9\0C( In df0vWth*\ |Y#w 2ـ#s-$"hW6vq7W9Q1 hF*eiPv%2phĝ|مrB-WPRA s9덊>Vl\獿 =L¹ M:z.:f9EE-3=!R鐍*=#=Md/FPPQjhlBH2?VXȊ+7@7;F髁2 q#40[ ٮRs`i D1432lJƄ;A 〺$fH #93 /6Lَ#uEEQxJq`K0SIoBO$F#yz_2?03(-Xy䖕GV b?~<elFD.A/nxB_\p{>؅&%nؔ>BC+9i W"a H(C̹~ }.pӞr`&2D:k "U*VWZ_=`mvZՎ%DomTO RԦ2a˼1Urvj \)i#XʎA6v T_tp[6wӂŁTXo_Z.3p"N{%[-M ;ҹ##oK[Z;{:M&Zs!J13DO7dZ \J⡞1s"A_x E[wLK16kV.ٱEd3 :{ 0)ྏ+!`Qةl)TL]h!})_Tֿ!.ժ@qA<+L,z*S4#e(G7Qحv^233?pJ^=MeKCS$t'5oVZH'[hyBm2PQ{f[}/(X1Ԡx3k[ζSIB({uI%q:BLiAӃ^M)LP`ɛyF+}!\uƵJ,dsןxZgqsz&H]53@uziՄ)D@9\_NT#:tn&84EF G$޲edx.8DTC q>QWXC_Dvo52Aލc٭;m ,8%i]E! Y^cbM"sb[.Y-@-/v?ex>GpC GjnumČH68;+]jKLtB4ZG&or_,^ޣJ!cvs_{Dz6?}dey{ŽNv ݞ40e5e{y?}(/吅|c泊B;}8/ˑ$E~症GWfSul2A´'˛9EśKa>C`LflB}Lyپ0iڙr'B1V'j 8c<^fPd/Gaj-f,ǰ$K WM:pWajuZpZpzlܙ 3wnCV t 8lɅq ĵ=I40koyŇn.kjvچf%fQUvH=_^qD6 o`p.Γ]6 7(`n>ط@mV*@Xt0ovhJڸ<!b'=;re΁0Nvr"4/ Ee[uK %НC&=o=Q5X~Cܔ2pNB}V l1vƮ< ε!Avo~ ;lfMv;< :CMx41'ύs>doxʏO)r4(zw򊻇w 8}aljhW^y63K3}ZL1M /nup?ͿkoDyD~j6%9C}҇ȮǓ4frR3}6GUf~v+}Ɖ)aJ%<AנRc0N"?w{pl^D מquIIFk_tol#̎Hb Jobr{5΃'`&W&9wʯYyG?~"ѬD~#G?ag#G?~ GfMFŒJJ"^:{ )PVP>Z~Wc##l|AΏO! ۣHcg΂G I`9 V+|[xu;l.Q"vgH8M2dV M6-1bO:Ϧk5P$1b˞,jem踙"Ed'?4S8mwg/ O 7Lwy2dk޶׎86E쓿tJC`Z}.Z܃NU`zƹħKVu;rUmcU ?̛IWK};7Ϲ3%{RZA=cZ ''{\ jhSE䭼߁֌:_ BزP/y^3 p[Cd@ƚ/4ZRdߎ"E7uu]7~NCm_YC2d .ogu@^xo BuZP}s]WeYc譐,]|#꾳Z.G ^}q ua[bD՞4~1kq=3Z|*@(9QdG ObK|tBPɲkDjΓ/jԪWITT{0ڳAc 14vbR}ƞ|LJR(Z'Oxc{;f2/?7i \d)\ozC=h! +]# RKϽu3톱TAfɊxnvȺkf%sk2a9yOJwѦ#lbmxƐLV\5 ּS֜ ba+} {#Uqb80 |^=NY_˦8:geqi# LJ;B%^Ree=SQ+IՍ9.+' ޞg?j'l1v%fÚ~Lz l_TĭݟCl&aao=Ϳ~W} ޵'mZS0hw `$. #ԈN5Zofwg֥@s[AMՐXoa>=n0K˸UL]}а݇^I_d͎u(5jD^U3=5iXwL? ut8EJ* IĿbfʀ5$-9bn [eB% ӎ'z: -u"m=3&)O-MEv7ύ7)&M 3H[%ZȱjzT0;*:Pև9OW >E]\ڀp!\t h 8bEL |Z_?Rt]I91Aǹ>l}bshbΰr)HF%fV)G CP1So—Wv1Um7W"O#M%w#)`b8$ +ȿ騅n>9iRj)M UH,0cya '5d#.1KXv&;.?"kyӇddY+wQ2EPv~.C,?v09 fPZމx>ӡJoGƧ!v4V *[񟕴capC^ SFK@`] Yۛ4\5b2熚 "O΃m'NET~ިuzN͎5 IDATx9xBGj!&kHhJ05=5o[ظo9É!4p$1YrubCё)2+薏Lʖ>ivDIKg!H{eS` -Hk@;k٘^ؔci >T6R^% s·Cg:;wn kfaK\dV#Vfib9! o ۝ha>-lc~ Dl\:OWuu4J6YࣱdcPm`ဋ!>Lķ" >E0RxJcu#ftccHt\㋀O0\ D,:y,+J`خl* ̍ ljG1$`獹+UKw3O¬XoEp߳<NU՞ᅑn]nǪ4@eںb fa<;vbqN׾I6훎HMI 6Ә fo+i+Mu=}%:ϛAjZI Y*?RƦHPl66Y^; \H^ eE)+OJR~tzؼ0//SppK*~ُ-PSwD9T!=T^Oz ˜fY=[͊Umӵ?5#kq`Ql  +Z }]DYD0w ͠XEߥEϯ:zgJ-ɲ3~up3Xٷ-.:= (‡Gg /{",vQ[.t0EpZFZtg{zw:>Z͛? b[lɬ. 'e\_Զ@|zs/'0;:Rѻ,v7{QݩWwGьHCI|hDsjUv %z f'XgշʫGya>iʰ;l{(g꯸ƭa/؍ḫmiAW)/ƙ## %P/=q5:PSmDZrtqd_zRmh  T钄矉.;z{MGUW^:TܩKKᢤ"9eO2| %o!PzUA52Lؽ@s%3=\TR WYW锻5sDds_zjf~"8d%^mA@iI`Dk5z=$ƉI.y`E:Wz|EY"P>d$)寗fKC0Z^B 1%I^ Y ˝ 怰"^d}.*"aA}u#wmdE_2Ы :kFq,-P ݮ҉Z -Ђ\Dz)A`~ V8^ܞ9.Cc}x1Vh ZxbL7r -=cJ-ap%uj _p ;ץS$ږ9a58ᆫM߂Vˈ C tŶʙS<fС&l+τ EEoUas˟.(VuiZ< `Ļ/3) G=w%ǟ+6COz+zv<\0]B &n5+ {{efxG|x!O8{2wGH$࿞[/W0ROt 2|WwgmJK4&WLy5EjX5@|8\'FD>PgJcm,F(D޳DmK ([.)J^Ԩ-_85 "֥nl;r@ 2s\dm ~av0L|d\˗LsV,M19:f Yu*5ڽق1FQ-K6TlaL &XKkE=ExZQN[OI )ާ"CB}eml,&І0>23bcByS7^L:\ƊS)`E=$*1m}h3Bjhh_xtݝ~ |D{G3V!nœ.LC>976ܝ8+x{mOWtM'g>{]ZI2"uQؘ#) ˔ LP(mlo }h6;rY\i0ʒ":ţV|vyf@Q"c3k\zC✧` gz/wcHy0ȡl1 點QqLD!3OZ}6x~UQ[Y)˟d8LZc#:|;1H%}xdqw[ewޔͬiUC<8h8yZO犓AfYBJ[7n\E4)ZjXYZ&JRU`\pE# 7w걱1\V+44 ^@xA[bk|D5yoI0x0.}ZŇMHhZ<,4KPQ$?*Ծ[%čZo8w!NǮ(-mCt;jC 6 2]1s;B*2=SO$|1N%,. 3hg (fcS{-i>}&^bZ|9m;aՆ wOR},aP'j,{QdHe崥CN3xJ%~5\74 MByp M}~0laꙂD!bv`43?sH^7\]"f ↻`pYr1!{sO45 "As/A=G.͎kC?ОhHKJ /Y!0`ӣ'3]+eرzB0i 7wWLQ Hz) E7.L0B*<9fF~EwH8Fޱ|Z o-skB_h(.D6xh1)TWPe$ |">k& ,[tϚNoK ~{ p>@JYTˈa?|fdyvG|Ql ^?N7'(e74U?)/e=KEح|tfxdvz V^@qEb5ؼaka$}\kF8zԊx`7kyuP/wWh> /83wk$ܒ;/}cм?/HQGE8•%IjfN[M7hmem`5 k{9bUo qAv4,`w!+;p*-8{ U}e5Dcc@kXP/ߜGrDs͒! C]p(QRH!SY[cJBdT,p>P'9V\" OafG-z20P؎n#Yj*Z<"mБ 9 a;g&NC(8ВUlP_Q[OIA PɵBn/k]|LȂO*!+t(lsܜWު4k藟I v1/D03{zhO\rjqޖ3_3>ՑgS6~68ܮ7#?cύ:/v%´F է;`dY]@}oAþ9Suӧ>@|56nT|r;uώ/{tʇ%#.fq3Em$*?VNQ$Pm+衒C˛+愥Y#¬l+hFoNA9 -Gw-#TYMU;,p^~Yc2 \;ꕧ>:OԢ,{ bH"$xS]fk N/A8&*n*J0]SUdX|6u3/~Q‘sq8eI#.e(\dzO4jip$i[Um0$_ZDz-wטe_uـFeF; g7h,+ȦnUc J5*;6NCcrx?xR8ˬn1 Ro6O_s֧ Ⱥ '/84\z)*5;\pķ7[-cYq9s׏8g^|?5*h׃m yO%U0(ѫ؊c'_*r`Z%o"pUݘXWw.Dy0Kʅ/#Ȥ7 lkO%T߼V >XLҨo%{!ksw v~?Ytld_q"6V_z6sWUU7ZpznOf5MԶ؛J@Qn/ seέ—J/-0#I5,S7q]K~N \*/Utg 4ΉM/30;U39J\|gl5$vOZf|՟ JǢ~=~-n^BfV򼴘7.li-Cn&/V WEpP"JinQqOlbx$ߕ?t~B ׏VѪt?{Ŭpb1X*7.\zPNƜUU4 |mc>[ʸz%pz˷ԟۥ鵌_2-8 XʄCA+w{q0PNL%{pSYLsƻ!o=/'z%-fzqEIQ2}H,5#F[pGc΅X{JAn"RbmnQoYT'| WHREZd0<1 H%5Qг bxDfjX'NW||NYc-A-%K8FU 0}0?C-jrmtQ"L_CաN2?wToep#vNMUuLa&,l"'ʆªWVO/ eS!7=YR֭oj?X겲N4z .cohAiù޸aj 3Y˪!qh} oXji8w'/%Hm%)r:i&C6)}*1u~sM@-?,ɖY>4|XW[5Y5%6kGt#3:ϛAjqrtЩS0^IyGBh!kW%ܽyGjؔ ZS((үJtA RmPsMיVrR]=T^Oz ˜Ffly7+V_wƬ*z~;+iA0N̯=Ɓ FU+f0HSQ` 0Å޽lv1_z2Υg_+=BKlKbqc~QO 羺)՝~P|w$M_"]K4V]0kz{aeX59e~Gr@(9&]Zr=l+öVˆG(G.ҔOgQ+`v-".*ԥ:4f׎ܶKﴠq̑C }C9%Pŕ7n5o {)evtpTU{嵫 Z[T+ǃi&DBtRb5L, )ըߓ" GC}m`x!4L(U}i`L.OhrYa)wJr40?^ǘLQ*agiObr2#>A8籸qCmW<LC)yk2’ w[b`H7Sk`t]g7IabӶqƈ&aA@|}y>2YaVb-Ɯ,_b@) Θ *X6ĨraBǔm׋!qrIqذ֒ȡLqAhݢ?[ <\왿]?B$`u*"`'5&E}VDq fla03O RG?ft~ʏy1:cϵʥ"$rg8اڞ3& `ra=b(1í3?ƭ[EhKڀФs3p1mY{ڲ"<56W8JE/j*4 Q hT`֢_c+﹐47p?7`:r]}13QS=0Ad6eQs#/ݘ!ȝ*C`fL|gsb@NC-Fb"bXsoة!0Wk-em͟m52.R(Z5x)}l!L4>e9# :yư):6Qs Ch%Hkr]hcMMzlfe+)Z/=kƶScZ@p{amPf0>"to0ߺTJńdᕖHk+W6"G$Z#{< /NJp\ ̭q0(-kHƈڱ>Cu+4d΄>\pJ`L|bI: UMa EtC$|Zi!Sni`pզoAeĄ!qb[=-$(aVj1A6Z}&\("w1+.^ltqGKC,^w e*BA%Ť!Hcr4nޞG^#Y%F14i/.Β )rE!+ e%lH(#Pd3,L1*8a ۵;la%]$yRL<^xuҨ-6+fXSL &Ԡk&h~Sz>23bcByS7^<{ɫ 4OBX0kcV]z} 3olfM[&K3e/6i\m7cX&E}}0X _2LsJF&ȢoqM)&$4-l#AAЖ,QT¬PGK}`kݖC]XH%`IR*X´ZŐwIuI0̖\pK%2%wY7 Ct?~ W.,!Zllj~)0wҊB,{Sza0LAP @;Z0Hb,=l(ebeKj4j'2:18r)ԧ4AޒC5=CD|%bi4oEqRIQ6t]I3dENm[pLWު4k藟I vY.`O͢U?yr;ȟ'L[~=pmp!fUۗp P b=OPޚ;wauO}P)kܨ,!v__ դGs) xYes9i57]";F aPnƎ [W>jvNwrwp+zt<@JT8zTcBXR $p=Z6,ogoYx0a1V?8fךFrwOk`3 mgVz>< x0afZZy0aÀ< x gVs0aÀ< xpLaÀ Lt_UVTZl?, Q;{,+޶e v !lz(jwM>?:[r(gjUTZ`nZ.v@ht$$<3{>V@UШ. ˈ 7(‡pӄv^$gVuq6e ֑_޾rRU[#AdI UY&v=v?ōvlw̡%oJ&TYMU;,Н<đro\܋J]XH9pmN_)7 8Rcj#ʢ,9T#$xxekk2 8m2,>{#\?Z\ٯ"|@G XޢBm,wטe_uIÁ0r%EaںwMCQ+[Um0$_R+Q0`P %F ɻFg1vSTjvÍ iXbe?j!y@͊˙~5XuuPasAi#ؽ 0nPT.Nꔺҟ_? yO%GU0(ѫ vCd 817t6 C-_XOt21^ ӃWoT\{T6O4G_2=ВaT+_pC@\|@/lC981%MCf1m+w_a[F6 */27 fDC ;F2 x0INLƒ ܼYlpjpY0@\oRLҙ5#F[cǴ--}m=?"%6QEuϗݗa)J<-[)5xKI#Y겲N4z .+Tj8\DtT4X ( 8Kƛ7bfD( Խuz+(FFZc-A-%K,p m-@sX,2) l++ǧ`<$ː 3|(dr-FqEURǬu 㛳*p AɡڶDDFy֧2L@n(z3!&y/M f(LWrmjtߵf DTm\ҵZӹo_3X&jTT9Kq׬ :QoUv@o+DHH8jv7CnNCAy6s+@W& Ӄy8݋h\3fFo]xFQYMtvQ''Ƭ8w j חuy_s_]{X">#n^] _OD82\r_]ptjz*=;/XaQ!b@5 T)RW:V'PT bsIG?gŧLwVj3PKH(` k&\0wE<!5"R^(II#k4!ľS >UfN!;Mu?+Ì3eZO V'r0ogQj33+7R/Ɵẑ =:W{hZIRI*[J|B}'i I6EQzO7LCd9m%sr: ۃpcӓ9VHFL\S=Òl( ?msw^Rbm O9y&/29 l.8d IDAT#?YR1/fH,?OI%j7!ݤ @ }_kS͊K| {Fs<@i PZ{ = ?Up@(9&]өYJKBO+2Pl֧e9|t n,YdωJRТ#$>'ժnϤE VƁL.5+b9j]M%#uCv^OD?_^f!8d%^mA@ic ~vF+sw/nh[$!%&S:#ڲըDBQp[.Pz af44S+%JU_S7s ;\v=r3! OW1&SʳYL]XۋV1P MFWTi=La^/b&&u͆W8/ ubZcăj bD|Ve30Q#X38ĖzÌHfi^-17NaRĝj]h'A(E8i"@,xx]ZL.VfG;98C/۟6%L}\ZOXlx_FXxnK v"qXf֭+_RiO,~` pOmXYxt]rJR* `f/f56[}Dd 7qr?IXȤ'`EGwIBM EkvQzCf`l׺HG:b )2$e6Jp*r¸4& ͊LXu^q *e`.ʍIKlP:))@r Ǧr|e\q3MعߞfFqEpSnq2MPZ9s.6T5`I4PLFbbqV#篌yei+֤L3 ^*l}/rgRwHD 0(,ihgI FtU:n0C>,cpAtxBgrֺI @Y`nibt>k7]8o&e*+ Gf7 \&{Č5h)"#>|1iSsZFGB.ܬX GsC2:Kz. LZ+"xlpS͒0 8V/xx+R FGaô4S-3:s{N{Mf*6xl%Uo<Bi&EH3 exq dKe3h,`s3C^x&>L0zC1!~ƱKjZ؝n,]d24V}nǫ`:Ubfc:41LMD{^Fqqie{J :6Qs Ch(KQ\bcآ$2X{襳H9[,.EŰwߑpK6謙5.2 l (ưwm6۸2u/$Ǯ3|pB. (0VDR(M A(A 3_yBzS0|iGQfY g5Oل΅-Ju,Ji3Z5M2Aƈڱ>Cu+4-Ө!/oD2\mXFL+U2'XBPވA6g…""]wEʟ?;]Q5WpiS!-@ 1i2W"HEp ;ϕ|!'=NB..Cc"a5+ [ UYҝV'yh `\t/хo\h7V_~i%+q<=yfgw w,pӠ15`)h`fOOef~ƪĕk~ބCuc"fR'閮3k yucKu4VvdT =_={^q2RJ#A==#&/0&tW:q NPd}_Q* U([J JbGÞ~J _e1Aq݄ 6p>x(r$SREQe.no+fe1ɇ6 0kwDK|gjKI|gvm}h3BjύƏ+k=aL%|D{G3V!n!)qHW!>i;.IBTp|6ЅI~H]''cކS'wo0hIa|˔ $+6Q>hqGo }h6;rY\i0ʒ":ţV|vEK2ʍȽ,ސNE<k\zC✧\LO.>1QB2 99 D&~:|!}: n! ~s8Fsiq n]pŋZvM٪o{̚V5ˣ>*+e0 \3v8SaP֍"WsvqOp^\* BC7 nBaUk<Qێ 65~1i~)6z-7/iNv3DGa:Sў)qxݐxuc";DI h$^=6fW(ii@ֽ\֩Dk ޒ>7dl]zдx 9WO^x0LAP @;Z0HhLeCM}tvWLlq@1{%3+6@d[-KThF(cvU;/n :_/ YIS 4R:oo7; !2C߄q{^]d1)TWPe$ |">kAA$0\"`xߢ+~|w`a?M.+! 19 *8⋪g˚㤜}?")e74U?)/e>6'X̜Z/\ʈŤ;6twnCbOk~x-tDZvz^(7i_?-!<?GG$$&dŘ` ;6޲8n]m6M7_{{on~7M;Nl;w ".w$, a3gf;ϙygXl/ok|>lۚܤLۚժ}Ԡ[ΑT_g:X#nQXlâr(%I!)!>yD[ TX< A=j>oaؿ q J|m& X}qK!n1t%_k(4SnaX94X rw #dU2 A7ov&Xxn\D2ѹ}!p%v0O ǙyO?䆕lh\jh ;rX Ptb˅WKywjt `Zp{5(qc|'8b١ϋ0ϝ-_#D{HQ1VجY<(ſp*#'bUCA8|s.V֛7_=6/vam;~Hqo~'6f|y8-h|:M}JwX-!#~պ~XjF+2i&!yu#IqMQ"k)mt-HQܝ_Zi5t:\S;_[>daÃ7iJ u!upZi DҘTZ*PJ2TAЮ=/@&k~߃f+d,J} =Խ^97a[i_噪{ \9>|/`O%R[' ML< 8viJC>|!v\>!C>|XDV>|!C`BX}U|(( H!yfI$-zxtnq3ېkftr [sAdxFD}2ZtO \G_;">*J۳mUdL4IGu7rÖhW`fHi8!z^iI cɾf-ܛ ۓU753>e"q#DŰf`IgKΓuwW/h*k vGnԖIS^v2#+äaQ :BRR=E P A:,.wVAOUI:?/+ͦd~xxfVC/ w}rr2n܍Ldx N7_[FPmkCw;-+Dλr 2o]+,y2[]= KoՍsnpb5-*!OWpUJ[чxS[$ZOB=һՓ-{YIY̮A=^3S$eg zĹU7V|蟰;ۑg5)[2ʄ3E$e>ɳ—k&^@g\Suݗͬ+?h >SbpArhzU^b3OrbM5!XT+6:= G0TO[_[`Nj%?$u[z0*C"+w_ J\:[U1p Cg'nؔMGlY] (2-cčbV7^6wAiA8ap(^a6lS߄kJo%âE9Hxmg[z& +<"4C=eh28BdM0*;.!B0F{CၖYBx4:2`d& f,%c^T]Wݯ EnHND_E;-BLNHzslOȯK"fvMhy/cx"C{X!vb09hvQ$* t!ށNjtCČ]@\0@;۽A$XB QL^+҃L \ѫMUodr%a=< \SU[s +/ˮVIUu=@e4[Z`Zӥޓ"f$Xf){m/?0_}\=; ",t9(<eHpڮ3a8A%[0%~ą7ʫdyC?u^QN[15xܮvD¥Wwd 1O~MژV_.y(x)k=Z:jS9ܬG9&Xa"j\$`OR8E8_q#vdik(#>ְz;FO4l{BAֹ&A }`jdV~+ǭHsC7=F s[acJ2EsU9q4sO!PY嗿YPAM/HF^|`Tv)<KdiESҦ)pP+] 2qwh`NT`SB 6r%`F"U}#ވQ 1 R+s8rjO~\vm).)ѓ)(T% 57B0b(f(V'^[zI?U,A ;uzG [Xmv[l$TR5tl1j,$ V7X0"z8 \/iD_$?:u* 2^]2ojԘc#!wۗYve,Rj IDAT9@|NV!::% i8ŒSVDK8H47?lIp&+JJ؉'r@U+5MZC_CXӧE+,?; b9p|Rm:,6>Cm=0쩤/Ufup?qmPzj ckq]Iu)yۿA Zf[/N>Ghuf}DDQz9w zgʹjLG rP2*  GV$ F`VC%?& }"({H!GFi^t'%! ǩr $8|ڏ/Wak "z݋lvd&lL2L^ "әqEulrȲ9r'ƀ(LDŽ2#U'o 2IYT٪B(a/W߭/_'ba"/2ȹ=B\u۽8@BfR ekrDWוV D0] pEnjH6-p$e:"fR"ȱ66q_̂Q- o:+5,x`J-HY0 ؓvvdo#f)Ts.,fۈW,L' sL՚`QP/g5&!X$VBq R):L 'J|0czwY6BK6ݡVUX~9nJመcb+0lIA ]ؾ%L.fI%!aNcZBFD S6Uh[_ϽU~ʛb `D!L`z5z2ɶlI+iS!@H$u#)xFͅZI뵂/ɎD"I4-zJ} P$JGFܶ`pˋOuWv~-g 6x$q˟/yMw~J&"N{[2\g  궄 s,c ,F X?VQ3X!jCD9o#uao#ˮ(ѕ.*5c(0؆ef -8c}WFm#lo555>@Vu~B䳍]ei-i_YK|?qbGЁ9'<64zvFoS5|}ɇK\Q 0F,35X5S3`]d!) LK%di]*kZ\N m[Zݰ"NY8#o'ը߶0CiǗ@[?N \:'weaj$ QlF ߫Q]^?1$ O'U*j|dx,}Kcc{Gg#K o!`\zz6'ܖ|!r%hVSwU#-:eЦL,%s3(+4֠ ) /r-ɞt9χ'MPHXB G 19R[2 #Ne]!dYhHk -`jA!FQAYy>qp*N㵱GԝQX 2d~A.4FTZo #zt~i%d. ߗκW.*7j_gqb PUg ?DL. a5 "(aiJ'dU"4Nh2ptOe=8 Y!`ۘ ؚvq'T^~{m!TT6X,)}_ ηCiC5j7M!Rgl+03ch+rHn.{̕I+gxJ?hS%ɏoȅc;>h#EΑ;ÍNOm=}47ލO ۄ<嵑G]>ST-08O[ {c~DQ^ plSTU{ɮ6XuVT v'۶;ogOn8i@EnR=敬38ma ySݓ:p-潗arvї'YLi-i0e{"9gYCnPje[4#ϖl=ysҺms֩)Uc2IOlL?3{(lh~5373ܺp*ܪQ]\쩷LcjR~SԺm}ׇ :'@=N)'>j@0Aɳ@\F0{|=͊fSp]0cOj%;Fk6B  ,ZafT֙ô ^ "qC/-e9nƳd p '+WA H-` '{f ط{K,ʁ#Ӎpq[>v&\ 0 vXW#̤~j5uc>KGJ{_a~bJc"Py\G,TL0_s@EǠ>LoXPԌ S8ALS7T?năLo\r.Zy=>|!C=K> C>|"S+]c܇>|!N'|XCWZp{ yOF؛ΥFHքP8!aB[03#彖M"SgظU?v k $'_SoܻΫeS`onT d[UV[BYwàiۤ* F EY"},6Fn,aYnf f 3+GJ ћ9ܵeV ԏ{D#wzAUYCWs=tKLc9BRdX;J*0~=4Q@I=DFKx?57<6 :u%wV<* t~^V׷a)=Fc"¢Hwc<Ʃ' BYؖ2\Qr#bK ~BF,c-MG-I%#Pyap / RklC h&bԆ%շI ɹNwR'I)P)`!JU7-kVXHȴpbLz˔#C`IޗF`^A zM6EnCG=kJL7.M/ Qs9/ "vLm`A7kl9-ǁ6tͺAzS!K:% zBSw;8m72/]iGb4$1qy/f?r_­X} )S[$ZOBWhdcP;VW( rVY;늶6:|;T ˜ԗ=.l5n% vtlqܪ+AOu jR6t;>X&Zj[]_v]j-SvxrOq^HEأhʉ%4f]$* _BYج{@kx׆W^5wI6?h >l2j9Uy)8ҥ7,z`^Ĵ+K R, X-մ\?밶C=]]7|b"W ]T2-n(g#Hpl*=6;r1ozk$7`lɱ03ngƥ҆@3vv鹏?T*xnx bOe/MNtSѯ ,t 3.g -uOTkUm:e-HN~\W2`,SӘAm!'`"B5U3 DV vz:щ3vWryj03z0>=a$iQ\!RÞ85)O<L \ѫMUod.ͬ߸yxhp0z_XOjTz(f0L5M*^cB`kD wW@xXi 29,Le3{SF3HDƶmix܊Gnݜw^fug/m];(nN; 5JU{aF9mhԐq9?a. !?ORV_.y /eyNyznÎUbX0Hl5.c[=.',AL[\`#P1L ݛ Hor^WY֭7S7 Dv#aZ|eΤo;]j}'5 RSS&]9/fOhZ gwm#!`{VSVD]{ϻU~hTؔhUս1Rx%H,hցvT4n%cPjp!C`{m۾].[PG:I3T 83 =u-8@d0deז"R=aaaFyUՊ M8,Jq7$طcFU_D7bCL&J%9~16FG*!:# .Pn}}6 ]l ͆~xʼNǏ{g 2liJ6P+qOz!m1SZ!BMZ>H'k5+tF YRQlT3i)VN\S]9JdW+Bix ֦VRqG )9k5NQ@D`)!V~/gaچ)ѹ99ݥUy /yʥД"frik `H涧9x>Q_{3Rpsd[DܔJn[RfN (gI,9:V5-Q濂%۵ 2$ŋ`jHAz}%!ϟj4KK~(c}4ldƷs N-`p7y7Wr7hӲdk[6&F%m)@LϱNh4t; /ۢ೘>\}Bu:7OdG V[ *'ĞƢ+­<$͉DCǓAY(n@SᑘӊkNj[c^!ξ[1cqyů g#xj͑ȵR3㣹͏/*hR$JI~ʀv,6:PUJMӹ2i/.E$Nca#~{N/ -e3s|(,0/ 蔼 -3{}dhDM*Wq \{2OFFN9 쩤/U\v UUKcOBګgoΤ2#Q@ TA5W ӕHRxd`(ة;**G|ľlm-vcE-zX~ߝ-,BwIZV:(VC ?yY(]? /Y\h@ IDAT|Zb΍|X< N}&ej%Y8cjKRj"q#m;D`k"YmgS;oWJ i N".9=#$ YK>SVԏ]p C]MR35:Oveen|h/ˆ?ِ۵ xmAuICmr)x =#k꜐UeU9B$ 围!7&&[)}׳A6T}~8j Vl^m {5?+=}!{I,Gz&-!~ a/fSKtG(1E`l7ԫ!0#c]]ƭBǢ 8)o[Ȅ{s#wo_'qO͍WL&/>e?]1yn2ibȍ<Ìe{dCFb d]=50m&zAN> 6ogOn8iEEnR=敬xc`-L!/{{Ro@Լ2LN:$I?% F)F9rCRx}@dD>rV"Ȕ~3.L |1SP!N]k/NZL:m|\;8MCY+$0i5Qq<lBDmͿ& qZe3[+7)ms͇aZأTns©pFu[] A \2&?Ew}sltr"kFiy @UM K;sufOd8Q~@j`#8@­fFe9LP)ŀk 3WV`M{iZ#61"wϏiM f"Ra7 !! I@҃Bo(p_D]sSz0GEsmo8&s%i8Cʡ*M#=k})`s)n!xV 0R>/-e-d pQ '+WAʼnH-1kH`D(46lox^OX X)!J^X.cMo{y9Rmؔ( ;0VVxVdwl9;m߳?y|)gv'xXy{އ Rak.L·>-g=tt ܻsg?)zG~K%k 0xd$ڛWCj5;eNeDՐkŇ0pg0 1;WOyWʶVG~\0do֝(̬(xG5]w ټi4)ܮՏ\2cFp?8WXŷ GŶx,<?/e-Ot73jR\ӱuJ8A-HQܝ_Zi5t:nm^m|3L_rx&MIҡ3rt=x*Zzׯ]Ky='~ ИIqStY+}|\A7xc5] OLiL*΍uN%S` "S>||LCKxDx$'*ۺ:C S3Z3L1d6_g~S|B;R+6!C>|pVzu.Ǚ>|!C S+G_->|!Co8wtd}>rJqIC@晑^K&c>-bo:FHքP8!aB?}. }XZiQ}Q;e F"AqUe>%uW VhCčeGM#M{Eմ&JfpoVծ+4+Ku=["CNVe s^ҭzjˤ?V/;sot]̰vԕUy3e'x}k|wzO~N}_o8nzE1P:{IFKxfSLNKvT;+J]~zOUIh:?/+1+>dkilJG|?j9n(tmmng3A Wi_Kݢ"62F54[fa y|qRBr TE% _&NM_p̳-SzzȎ X"T> 4!mh(jCVZu_Öj<˂y_\1+ħ^Z ᦳo cN Y)WH tjl/]{څ ^~9uz*{PR,>|[*ɳW(UeQ5h Grb&xU6o"ϰnA0U`gYWT;.HTKLq&f.]8dj%_i^zM`7~dA$DFWtMlu33q {R,lg BS,Ӓvr8$6hk ̘7X 5MzBWNC숍"/QXؖv>6T1 p◺`Ӎ~~ ;t^9BuOTkUm:e-HN~\W2`,SӘAm!TVIoWKgD̲Ye\LFstӻA:N7}!u1=(:e*b񽼞̬h4Ќh[<[ev[A qEӈNOh/=\dQ\xb5g[z&  PzH':!%'ڶ驪/~O,VKJ䶛gdBQatP91dv'NˀuSzʎK 1T%QNwuQUW(Ԕ%6Vmئ( iהJE6t\kZf Ȁ*\dUUBy[n$3n57usl{hˢn떍i!&'$=; G]>Gf5z@@ΆܱW:,7pu}4;cʂjƫ:f2 IWtR'f*:`:f?`|wBl,p.1,s3sE6U 8fXO_OV9siYkƒ}RC0aji*WR[#eTkJaHpڮ38AňlpPRq.>|[0ݺ9ڽ̞Bki<;rFɟj/(r0X3C GsO!س~%9#wrHkD#wӃ LpޒFwjj9ī~,֛)pnϲsJ̬Nn-8m8n*]BHI*lJ}Drqê{󁅝K? ͓:ҎJi jbCĥm^R{Sw2qwh`NT`SB 6r%=(D1!&aӢZK!,qx4etRd{2OFFN6UtT?E3SkX:$pޢ׻GI9 wwoVJ|7$3|gFÂ]=R p7mT%(y;[Ţ3xz<ݲw;`Kzjfc`^zLqȡbv[jwYOmsb5t%3~›U7[/O@xDLoJ V EG:F?,h03ݯxĐMXl*" IDAT슄)|spR S#v"2CF dW< ۢDIbQT6B}Qtcу:R]QNn׍;O5wO#¡Ϥ} zB `-Jmn(/&g}>I(@gH2v20%[@T#X Zf)fSeR !Pn,u^Jު֗U :xs#w_"H ~õv; y -j&3ǽyqsh q?Xj윱# 5`3cCEX4>,lH!Ek)fhb$aeæz(a"1`L0~| [UpMIWJFeKt}x[Ǖ- +HQbU,Q&rGxX~>M6_n㗗ϱuvnYER)Q{{`I :pq\ A,g 3w̙gΔt>1ELBGI( Xt'2'Ki"`ts,NOtq'^?ۤc`lop4+4ItGԄp6g2pՖY 6S٤9a״:E1ӝ*~u2I جPNQ l8"uɲR2nREQpʖKm1\-!TI1"bʂء`N:pW?z͗N@x`bEּ>keH$M5G ORmh3MOUWw@Ia0u Em+j@~#< g?pyÇ[UUD0Amm̓F?i81(b fȏŜ3`f(V e PW(+Y^.+ _z>0Jhl(hX`D W~,]k!i ED  yׯ+5 Q,3<gJQ ̌ So_0>΍L9B,>dYI+Dl1f?cp@1.5Nh)5)Ut{`LgRX{f&: sng!Lc=khL{.?̕myρԡ " cAU#=`F͙LI\첊]$X1M6ţq!rAeQl&xm #ȜACWD{EY\cXf@#h@[ɳ8hg$#pp%owL>/0A͠QB lMá<2c`S␯U!K?GaZ -yoLZ j35!FJMlQ`b+${ЀwjE;r '(+7f[q0uuW͆ 0U"Z`PBB7ާ%/u1`_k/x>G3k:TPt$UO[h.۝ZQڟD`ϕuhjZZ0>Nf@.1i':9ə,S?]& pLj(XwWIiOk,M#0pyhV g'z Ib0Aj$2[ \DWW ]ĸfavJ<:ޡ=6i W*h$ݟ֪*7a-jgx~ ܴ6!I`Rڞ6p,k,9aoWCpvcP&CTPF'dTlqpm%x\#Y ["xg >. cJ=XS.ޣJCL){k nꇈb:U4ʺ7?UHYV+U)0gw<˔a`z(gs]쮔Kf 5$>VA w\Gߐ{_wMnM8GO,S_r (6*;5?&GjXA9EWŭƊ%C"|89ũx_G珿|h?0azVdgi xa?H'O Lo_<Хdq fsw]΀Ť}+W쐇s>]Y1pS­y` , q} J}UuV>#=x(cӵtIkUK|XFT,䃠9aEjT+qP$?v7w}☐WU!̋G吢Eٓb|vPޛ؄O(|t)S{]\ꕔfo|_{cǟ̧ M<V3s% z%D8.qS-{.L~PEkg={7\0^Zș1فt(r_sK߾=qfȌYZ+gǽlZR+¾}U1 #~ígw،0;-`z_>g!ejgn$i+^N:pUl sSٸyӂ ζ>v% [N}MiBV?Q<GAp?{oCӪ0"fQ$jӾHOڛ7w+BپL]h{kHH<[X=u7՗h*krZ*IU-jG4F;))oߔ,=3H >no 3]#j&z~] k霉Lڶ8~ (tG8`öNܐ;GFD {bWIU,# MIFՋ5"< ]#5 KǴ5MBwYwm˪Y Cwά4*->9$ eW#Q߬'msCʨҬfGl灗PxMsɱŒҜW晡 ztgZq5wᙳ|0(1@cAwᚚZ=$fȼ fUX͟*ma4b}86a69:b$̸=+0+5\{UJ06uhq N辡yͯ_U68 ݐWA]qͻu44G9E}KsmhQ/'좰 g\ m{@p~ع={ c%]C5<[sy5%0KN\ efVn Ntf%]7=U7Rp.=jR7ۇYn6lIQۃ1˿VPsB=h @|6;fҘiG]#5 pd:t)RwqOY'O8̊Р֯U9 볕,1#9ʻ/N.䗓K̉A W߯W:B5^N "f>} ܎W^5"|'e,q)\]x]eriP9e⠴ 1ii NOsEX uz{)%pMTpgLCqݼš[9[Pd]y#)/gTǟr>@ĵ+Q"G|+5M9 ?U}NuwwM P sN–~mm`v\&k4SõFp|1d߱Ri^-##rqE)gvjcbEE1m_H{W qi >GTjF[@E'b!K(q.}ZLb%V⺕aEפg c"( >tcb'{;vV|D Xς@.Y0ʻYy]aJpŴ+$o/bEQ)B06628!Qm6x3 ㇆GvԃWZpm E/Iko8%R( X)pr2B<+mp@9<4UUim3JS!<"EhB2]7xiq{YS-74bKU^ޣ ÅIE-8^5wRQ%Thn(22чzaÐ 9BXv11P%%x((F^1R59A< GgS<S~~\|4 6Ѳi (`7rڕieE% Ovؔ\& /{sB#sa$GΘ+W3v%L Il =%SV s|7}fɤb.eEֿ>L\SmE5U`#2kݟ6 vpcPw &!J`Qbq..t͹HqVݼ1 싆v CqwӜwZy惙7a?URNZq6KzDm+Q`EZƱk/ 瓫 9BcUR%  6;pZ<4{'$$|Qu򍳽uBhu:O)|lB !^q 1oU1""mfUU?z?B?V-aVC-lV@HĨ8)$ Uf$wX*03j[vvTI f.uGLMLG/ Jӷ?)'IĨ}2Z9@ p zuhVD۔0ݳ)/Y _{;dv_ laBm^~+Y^X0̌BA[2S ̒n^/֫v;5JI=Or}| _|f?T%N`b ܷM؍ʌ"8|Pя3N<;\ [hHLaI_;$=]PbEpR.  LI;GC47𕼧n:>_u &חz̐mb=eWު1tʏ)g|~~>FA<៯r_|*)(_B Q#K NL~,:1 »#$B<;Q̱7JTA6BՙN;ܨ?+,|r@HGn C!L"Zpj9=i[>=so@̐{ˆ1ʪS4MA>ٚ_ڨB~7=˾'ICE>26h͉U89>Ivt.q co^crz(}űY=6l FizCW2L!wpXِN>=1˽fDDri0-0\K_ZNm|gk6-h#QRjgsf(W\m`3Mjl_Z7LwG$:FE$ |&UX/JmkkT֨2MLLSYPb;L[, <&QG҉ߝܝϴ9S_Uucc&Zv>k!m;쭤Dn\6Aa&cc=0`1`#$wzM}Uo[bgq.O=rpUKz\u/7k'KOcB1jBi 63~pqFc񬳃VBq G `f(V @=<݀4e9a_e%VA qYݛm+E`bJ6xh 8DWȻ~]?X(l՘ן/)?8Y0K3+4&OGŠQ`}6j}z nQ t6b2B#ovRT[M. 6t&1nr٘Pb&]1#bfqw߷Gdac ?u~x\PA 6-FbӏTaXɑQV~浉 8f!|0:e9W4L|f:[/Y)q[e,4VԂ1`!tF <} <=iKr;.in.,"0.9i=-ƒ>-$LނlfU5)^=9n:9+iX=*dHٔyߌrT:G080kOfG40e'E)|4*ˇC,pP?Jŷ_dG"q` 0+ V @X>.Td6W\dlIwO"/]w m)טXl]![/WG60omlUoԹ8PO 8gb6B^v,vU$q5prY*Ӹ}| x14 w|-#cKVkR`S6xb[xM[D~P*az g|O?ZfrX>\%nPN$̨{So/C,C) $b@I*h1յ 4CeKKdGV E7H*ƺ:KlI'wGMXj((eD iH|Nؓ]jcuՕإD.'UxF@Hx8yMCC?zr 堸N3]n )ZdB@)"vT+-fmz׮>[\rNn$sǠا=֝ ,ÝPl4gqݷdQS4\۠l+'8}/yE; D-CZd !7G /=2|߬p>lMá<Q0e!_?.:~(n%ߊ%bm n{g҂ {Au S"d4b)6Em5%! Y1e@׈un' U"vO.PQV oTi<> `JŬ 0U"fSed0KE7ĕ]b"\5ʡ;SC5%V7551&'\ NJ邰moV',ל8|=WֱgwV.6ii7-MHDzGd]SifnBE{da޺~gͻXi>Muo~H0V> 8pS`!CL){k nm̢E{>U8E{QUhj={?.XVyVpY"n 6l)2WD$nB׍o,,^"ihpF=wy)*= QL)Ր(\+bη} a[O'U09BDSV֩࠸t% OjehG,ݳ4?KWbaFM^!U)!}ֱfUdacUj!,nT LjBߐ{_wMM8GO,q+FAPl UTwj ~LԦP )qk$!QIQ7P8RJH\F!of|R$ ̑=~4݂O%CRN ύAdݕ\},?-,7%Ԁ~vuIݞ"'#."{mEV9~[Q{곦~ XoKL G};9yе J|p DhWw[+ ^omwZmv]^`y=X?j ,C4vZܛ3^ON-߮*j/ Q̟S#/\ҲO)J4UDtCe_on]KiƁWvzl)_) "gy9QY' 6v("].(!OBJnHS2eϮ̐HgVH dU jD)!JYBwk`SM`SNcϤ5tbrp;ZF`A)ݐxpwr}i{(7zYiQ!]{%(`;o_=˷Nb^>'޳EQgAR~3nRU(/ɇ߰uS2t2 z쌜H"%.:u#3f}k}ҳiaoꨀIW[E>-u-`/ٹ/nL;{כ|"֓R֢6#~í,i+^N:p$c3O &8>p"3e 㖥AkMюX?=ڔ3Ν9+ڸQQQvڱCڔ˵Û7)pl,Yi5NUT ׷wMJo)[%7Jՠk1C>u(oNZw5CfEga0IڔQ:[y'G4nzr+2LdVb7t3<}kz aOdNhL v-Z1Fb$Ow` }+r "rx<[AqiBKd$@ժ9k=$厖IS}#yԪ2Lc? :tI`O7콼{pUo!tOa=%?jKqLgW`8p|l7c`ΙȤmcS %UCD Hr2xSћ50$۹̬ȢJQTxH*KIkA`o=-# MIF #LS_*߳Ʌ:ARZIʒ^ie6{:Jݱ2ǀ{m !kjk#6V]c7 ʋP؜KKh.{L0pݛUrO>+BI#{~PGq)J/KW;y|)p9)/pGΎz9>8ae\ |aHa߉K@kJԎ ofZ mĉK7:xfeW="4:=eriP9e⠴ 1 ii NOmX uz{),(cAZ. - 5] 8%"P!hwQSKMY}mӋ``$k/yI" CRpPg{Մ޹ P? D1Rd2*k xr97C/-JFZo*h~!GwVOT<:FT )dn/; X qɇ7kZ&†?u|җYlj0 rMuT)ȀSwV0,} ؼ\7q)TZ _(1^:\Vݼ1 ;|i*qwӜwZy惙wa?URNZq6Kz&͕TLIC"2=Q]PSĄɬD|ߝt02nYs.KWi 3 rK/_R^9$uߟq8o}A nJISCu1.+!nVE ٞ7:u=Ȃ [o4hBM_&pBJ6oC=3_gotшY @Z6"0;WZ_F"ED*y@S  !A)5 n+ es6$f1A!@(*^LO2 9-BmM Cát²O!"!Q=0q!:~YSGY2;J$+!ֱ wovzhаCIoe_N^fIs _gl\ { &Ln{Ah夹[\\ IDAT>/~6>0vKC'fmo{Ep gxN[w=ن_ q`qQ(a@dYG܈ϥ kG òkEy\uf]}64|UR|y$Z DXpUC߻՝zBVpQhU`g'Pʀ/ 6u0k'HaOuBo&XAs݁'" LI;GC47𕼧n:>JM4)ԣdxl[)VEG/ P~ɓK?d: w\T2xgјl' Uti ĉ\7G;0GK[ qʮ'#NI E c I`g`P8 afR`D0R,2]'e}&?hL 5Wխ> u1&SƷyL=+jE#4Ph} &S!(xgĆw}"'ȊLa܃Ä}t6+aC ̞ `m 3# _#?PVӍ)rr ;@:TY˻JoUtWjNpUԬC~4anA#SZ7\AB0 L'K˳kNe) Al/mmTgy?ы(JQDA7T~kU1BG) LD |d@`D$X\#lx00m;&a47$,@hTT @ijczB9lF.&zÂ\JL{2"]ڃB{]uaW7\5>9\L -n/?Ɂ`lmST!$镽r0aaC:hڊVM$OUΥ|ub- J"8`1`#?g0/jv~_qUsG|ۖYdp\'?˓m>Bl\˃[ĘTUEniA``\L4\ј`Z a3bp03+u ~땗EYqb}rYUB\|VWغ*aV4Nra1jg ViL6NA[ fygvq&>sM2؋/;ăg}ں0 X9Bw2GcCC>&V̽j[M. t&1^r9m(Icn%{WgՕ$# j1ǂmPd#1 GJ*Fq0>:Ral+Ww~-mX~tҊ_xPjyj*Ys:^=P)|~JAΑ]/ǜ#H>~?S> `USB.$OshtG \o"pB{]=UaWyT7&:10Qrj/Ap{o mST ?4Xը1vlԜoRUd 6|}ub`hO  YiU} lʀWOn[uNud_i~4JKecfu# 2Ci5am*4*3^ x7nr-hYdzB0mMbJbB^! d #G#.j. @!zFیƪLOY 2LN4ɬr<ul @u cxV\#$ c]%l$~#ۦtU-νU8Jy_*D)40(teUMI*gu,kt ոA~ ?n!S5M=; [դ#6eVWB ]n,l^`Aã9GH|߬!6_-jp,?Uu0e!_?Cb~(|KRŲd@薼q&-H#Z#H-j,R StXAC8ƫ?0 \@DߨҚy|n]7+Tpd<. ǀa!A]F`, 1\kdda'ެsOX$9S1m.t칲=rvYMK 5<{eI5c+llx8>(ޢVq5c਍c%\;4&mWxUJ@ZYF?W 7 />s؄D'~/BhG,ݳ4?KWq"TY槊) j壪=lЛZ^8c Űq u%~ch[nŏp5b| FRY$~(n,%wァ@?81dݳBTTPvdTlqp׍ ,,^"ihpFmmSHuݟwC]Uw]ȻH{[2B$H*5OE|yoWCpvcP`8`OpXWI^+ԍ3Tf'E X#{h'dK'`Ckk6nMɑ9.`SG9X8*%\YR˾b#)hYڿj0@a䆹iM[`膴i,=g{& >"=IG~.2FBBr߱x-Wrר؈t|ND#ݍMA V=>3nMʖs KqϺ@qhb O.Z.U“vqn~4UP;2h;kނQ/x:9q )^9gߖ!U]]սUʣY H(V3,c?4`ٚzw`)hq`~(Nеǭњ,] "eR9~y8Q\94AJs2EvXss$#:=&zűu NۛD ߔYI!;a/'aB8_m\%N*q(ekrYW ̽QJ)1AcBt:93Ys#=UO>a\Ω ݄Y Ur#=hS dx'_X: f+MR#jKvUӥϹ"U] Nс1TG.x0U}4P!m3k^n^=xH:I Ya$Z +:C{. mVZ.TN^t{ӅDT$-5՛6MMSYk/I\ }6X;5_*zeqYíg491K=]qM aJ\P3x''{1JA ^+n~qo7Eox*0a61“nV6 C B㒄&SfH LONK`Z3uE7E4V@oTJkCT4g 2'k^qY|S:?%\ې ]|·KſDm$) #[7'àr!xBHV]ЎC/$.v܉ۦGFZ 'FJ}}KE[5cufK:A5ןV!+<%&P|H6}꽼b2+Lʮ&F&Sb%=I]$Y*ޡqf|ő 9nl0lQd Tq{]V olIY!K65R(/afKn%Wd33"ߗTvJ0;`:EO(5`` b=h[NߜT*ˡgJkOcWwl\]yZEzwAM L+kY2ֿ<`'&tt ,E3 ~xm.?yYT?TO$Ϫ~ oRw}y\eq|xWL1g=1rvzۙ H>?)G0 _\~8^|z \}qkQqQFB5و$1=*χvܗʞ~â:{G : Szڏ_H"ct~uN{/nCpQܤ*B҈m4(@#$ *JM)IDV1K7ep2:+/}hq!->oKg;HC;ïh\h₦Ar6AC_o:\¬N}2d⫗y!۲pYKOwF<d՚vD!ͦ7Xs&!B'R /Nbg;b?TXٹJ,Z"ܰ7Jc Q盼Vh4k!D3nYO4_Qz/T՝|]塞1<t~knk6~!5!RN|0oKhɠ)99Sx^0tB{L IDAT,H5;EE/yZ 4QT3fԦ8 O?}C(+hl%537bW/>ځ?Ҿ9vqkBܮ||oLac=jG%: Yq@TZf*i'\"k"Ȝ \i R]dRXWҨ0=Eo]Uz"}0&)JA#SzrdqM?aXtM] qS-}clQ[UF x0[. t&/H 0KB|dk0|G&`rX4jyA4bLw整6hpI/I,M/Z.n^<.RO' 0qAFfKnXmVdOsgx-X[ۅD3\)!Tq˔о@:m>>x#8uisJ-A ||X(I< |XjSG.UY q>4{0C]?ՁQ f N'mV6wbe3P"]heuV7n_J|8tS]1ҙC ~S~Fg6; a+fp0+ &0Z7PW;/_,-ƄFe'[Ru0:E'ǁEV3;x)Y|_fҩP3_}T L״h{Syhtɢ Y;buflpf3X 6u@v, 3=W՘⦮1{wWL$zVGc9D3{9c84L͙5td?f#7,İ;-HF i lJ'Jج #[@2L%2 HGmZ&Z. À!hmh,=9`KEζI=Pzr}}9 6/W)V\F"/5`՛m3p<= x0I F(F&mf%; lJW%3l]g>%1e^j~$|HuPrؔ_xsKј\i-[Du# 7]j9}"3:g+T.݃DPf0w,f޾Zj @M]O)w4t|0(K€T(bBl)Bt,ZMI>,yWה02)}^z$VưcsYaޠIMMkS 'x|1L-0V2A<(!T['pIrE2+wc'@Ipሀ1nt%@K,AL49 v } -dg[AH+orBĖ'{>sFgw4: Z ?:"Ooy?cCcPmw[uhj(2N|9-ͨt!p4CI%Ʀ-z Kȃ'À,_#fpktNɳL}u`LYV{hIW.p88!ǍAU}lJ?B`}Q<"5L ԨmILi 1Ä6Mk8Pu@ޝT&n_u=\|`1bm0–Ќ /5T"Wo5 E)B>UylX"y)$k# 9T<4IfێW n$u:s{d>]ڱmkD66k驁|iM#$\2*@yfC_)WCFtqX%Pѹ8?&M+[+:NA[z׮ ULHJZhZG8 yybL޴QʖLj=*1Q)Ur X$6X!e]_%BTPmxO_,n~eCW.8E{ 5kdL= 2@^~1<[眉?~HL},RgUU:H6'N;YEC`  Z=< x.1a_x%Es~7/].,Uٹ%d+:fM ztW1?nϭ4~ʼn ?*$Ngj|SNƒy;#S /B59vؑŧz&Ma-DcZPwl^|ESMC鳑?4͍x+ccMR>h0)M{5I6 yZV RH_>?l2X @\hޠ-^K uvlyϛm`}O>6V>-] B"mwT]Q>E%5[傭YV A⬽~L:̎ݹQvƮ+}Wg_%0G2fV3TT!< 8r0ckd*+e-vLi wg$u"S xTIwks'[~9n@xS[=XVBt4H3wq!GKe@6K,!|{r|)GU!ĭ%O++g4ywi~JKa!_som!?XX t&wr2s_6gLi+$@EH-\vzzh(IGH8Ykblw{JiS䶘!-W@0an+gWM['-k_|n[4;jPDg}=C, uU_s{5ۙIsKsܰYNts{_O)Y!k$ o؎X*?UkGfֺ^%'ꅧRn6;&L޾#kݚ*߅>v9AG%:Y~w{ Gw$Fٔ,^7Tgy3bcZķhSP`цNWָYUٙQN}?ؔdvub\8‚߽(;`}@ʖyRu=8a|;G]ܷu kοi[g\vp'؉)o;Jn[i!"p7Qp86?nAX`۳>emOVRvjT7("(9l< x+GMSz+)Q,8F f^ago_'< x0aÀof--*x*0aÀ< xKw0aÀ< ||;z^l &.}tG×k&h!Q[B):QB-Ubfe@ӘނA) a֩N[DRbICb𷅛 !7O]Mʖs KqϺ@qhb O.Z.U“vQXpxWA*R\rfkނQ/x:0׫.5Xsm" "U]]սUʣY H(ߓ}e6S0㈈ 1w{QRf:G& ą#c?5Tw΄ŭ_h7NWM;‘ӸҜ@Ѻn eb{l-zTA>^ CTfap:[n@z.߬w쉱FלI?]l [y DW#KΖb% Lcv9ވ4&K (Dj1!?W8S/4sum7aVk{܈cO,ڔ .4l{'+I^|YmɮcWAt>9s|xcnAw9/ 8~mXM\_ Mܗ x u঑GOX9>D qaV2;?x9x앹a$Z +:[LE]WPJ;f%6|͙!Pi/'(Jenaqj8]*45:Oe[tV$=(s#,,xYg\5DHRB/ $C"wQ hp#7OXŌxnK]PS7ly!qImM)3$Rceb{pg|B%0-u:jI^ NCoTJkCT4gcAyظ,`v[pVÏd.>H y_hCEۥ_ntD6mp[k-Ry`η3᮱)*aH1SV|ܓPv>#w*l=w릆k̷^H\<M*5jOydU3V11n$>N8ij ^si%h OIpDD"6Mz/=np!g}Ij>sjʺ0k҄E0B:+}VYBlSO]hZ&b3F*0WƷK}6dcrD)~\" h Ffhke3?.Q+I[+UYUf45ڍfԓm`@T4CH@b|#)TZiLAvR63&,'L^sn}PRplmˆ1Aa ,yr;t)9)~\];D[AQpP 3EH’q%@H(3BfzQ )#a]*$h\`{ԹijRErXv sY륪k7k дL. .ۯj.}QOaRS^[$>FTyAbn)?yss1k}ij\Nf6߼+OtY(xmKQ YQ[ljνZTV'ڷVqjJ=vȭFH:X{wC=VHS8fQmw]>>>f"U=I!쵌nET[vv~K9跃6 Fx yRDH\&UacMV]6qz,?%cVUr.×f]@v`:G墽v*Xu9ß۴á8~Z{GE͑ٔk0B(e?JFyxv׻Zָ`v$ [Q7F+Kvu_XX GZ yf?IJL̘3~`ƕ {D o_TYrh[ґ 7{<_KMNy(Yy xoťlWA;ۆ&3ERTPdke Ҿ#GdzƚRo M+Tt aMƷ4tCMgk}(!ʟJTCR$ܮJ fT1 g`Ebe`h0C3\\khɫ a[̈́[MܦÌ5Y#̬8YI†q@zxqw *zPPa->ea$ґE;7xf%S^f f%n=qPNH5e-Ku>! "ԋlH![/@0XSn{+ ;b@efCzr9dt0).&$ mcbFLTh ?ӡ֊$?fwPuuZF/hi'e,ʥPu> _9 %[7@a+љKZ@s^cz¼@X=BlAugm雓J*RZ8k/p}aUy #OkWOѲdyuO>MV %AX\f<}ɅYȆwu ޺E̵gj_oˢ=r]&YZDa];S! H_Eůbh{b8}c! H>?)G0 L:<] r >t;˜01x;}u_);lV9$1=*χvܗʞ~Mۢ:{G : Szڏ_H"ct~uN{/n12*fT-$ 3.)Aj5 I4n?{ CZ =C%dwkN%TmC=>5ŒAW {5Go57}6j !{ U\<~dss)NR$"~δtK>oKg;HC;ïh\u!=-[ tg3KoÔE3๴PGǻԬt`;}aL'A.+2,هI( i^lřK$zp]͑et7:+E2Bd)+iTjnzRU=ex>tZ%)=Er Q*i ;fs8($M iB!  4nrF0W]i3MZ#0qt'pTJ#2nQ )c5A,E3 82âAV"ށ`B ӥ>2u$AtY0*N 5Vx0du; &E8WN';7`GqĪB .x0[. t&/H?JE I U=^+ ֬?#).z1}m٠ ؕ XtHeB6e #,8ANir=."8d3a31z0?lܔf3`BI3LG}/cg2Pm+eǒ&]|DOZ W)nB247)./0^taz0n+H6~b?{: [:/+IBM|`i{Gk^:t6a IDATdn߿ v;8J.\xlv,9eV{#fbLjwUDb0a ؇-M`dq/` Y<|Et&oZ]g@k)10Ԁ*L'H4:}6}4,(Ĝ{5&K}W0\2eA݀`lB%UqI owT ˄lPwYbl_H 4ҧD12h#0+1mY`Sgr,9a:.ߖSxݓy CaS~=:|1nL\A_zڏOk5~ i.S$t[P0dS9FQdb4nYbA ^1q|wpxbf ~Q[a K\as<7e_x/hORa~ q9FK]s`rӺhν  M;ұc5&qff tOLwޠ؏PҎm[#y4~YKO Da9J,6eaqBJq(KG\*@yf+a +XЀt V+Tt$0ΏIiʷN|^-+cqc D`WZմB1>CZ>ө16>[e_ف;&4q\R`²4wdnd5{ڒcjZbKvz5ULFzI)%9*EM $3j1}p{GwvpR Yn:3¶ 5IYUrhi=}/*P(6)!YwX xz!p1TlkkWvf'./(lS7z" YdDEL^IFKaVȣ Djl5.G']lX&fÁuuwY+wDz+Qa⑃淗\ҥ`hICvA يEUesl]I=X:VJ?eĉ J"cjvʩ/ogdj~SAeX;&5gn;6TϤ 3cؿ~£qȾ۵W+yGvddCFv77⭌ic9I]8)M{5I6 yZV RmYd]4iCO3C^J/p>,30~y2W,q=ᡀ+DԖKQec~S :ŵ+ ="N0kWΖ7kycS?$ !ыx~@]'_`Xyݺ*ER,+ʧhxϒoJ r2w؃uS6LW6K`툽*Í#3Vck]S[ppmg~A=<+<-Jns;9$:?#fQگ$[&q]"IԍXno>פ7v]iߢM 4C87(zE7 >?;I=d|A`oߖoìS-x;LUYn0]Bx I< x19`GL+{[NuqVwKf%r W I񳌕F~l遣fL,4|a߶L0`KM94$O М߿-EDQi7 ՍT  fp'^&z0a{# ˗U 6>qf۱= x0aÀ0rʡaÀ< x0aÀ xJ7HaÀ< x0@-0aÀB |,EcGRy!۠X:mIa wlw9ҦbĕX&e˹ 楸g]N94qzR(-1+]z͕MOQA:*{/,֠}APD k,أ,9tpM48Ô2w{Qf:G& ą#cJZ;g/΂'+Ax{!Ҝ@6)ťl#5,qRC)YSzQuBfnUju8?H^)VpqYNu#;僂)lY %ly5FH٘qgJp\~s;ZG0HʊڶhVN^o_;nND+kWˍ8T3 n]}w\ޗɝStwp #^~`-EXj\I^|Ymɮc(t>9sbt`#ݕ#C^Oʱʼ}xO/\M_m R=pUkԈ7ߩ(ѥ}Z*f2͛n v=+*86sc̑$>_{=/4_,g57W63xF nxv7JF@s/9Da$Z +:}8^W 񡔴 ٔꎿou SP-.ic>+: B=؟,n}|(3l7{^U7ly!qImM)3$Rcebh__1Sgy]QqS@70O潒;b òx4a/_^;}{ OIrK{`A~ie/.9XN|J[ #JT]Ӕ<fȳwB* .r#ͷlSoeu0Ek̡3U#jm24ZtF.}_Zonw|D.M L=fFT3쯴{&r)v"PNy|ЎC/$.awG6=2ҪԨM?1R\ɪ7^q8ij ^si%)1 8WFBSڪn<̦4lQdQ7lc5; ®ħ߸0f0!#IX1Xfɥe¿vTJ ,5EIC Cݟ+&'Nola!*xW;ϯ7@yyׯB;n[;qltցy`_:YQ[ljνZTV'zjc[ũ)$s#\+2qcY X!!O㘍GEl;Mzmf+Rד 3]ZM:ƒޢ06)2D ߱Rf{-V'P}p`S-X;;I~+vXpb?W7?!H@^_Ш J% z GbX-^ gS׻Zָ~7#hzɈӃda-i\;䝪Λ$IP:{(mc/ B8za 4pwǏy@kr>92۹;:ǵx UM֬sNrBqQeK]R7t=BV:eʒ]pdLm7UgՆ.~./\/P y`˞#C3{O7j(gxϤyZ_x9%L? 1=mmJg"@腏_/lGFѴU ӽrN%{9SmO@ \z]~0"䎸`ߑ J|k_>[u :Pxg@ MƄMo[c8,MόXs}7GޙW u_GP̵ËOۚ/n-8.eefC!<]Uo$Yɔ#Ƅ/>L?ݹ*æRll1T⸼Gb:j6lK#G$}Q~>4ݵTNG#} =jVБgОKo~B;ks>l˔ۏ'C/ziǐF)grE#ӕ@6XV lJI'ňilLرstݹ:ʻ {ڽ?sF(l}ߚuݦx65!1iFW/C]okn2k& iJb,Kl ::Rϥ#') R kaQ9%O ;jLG.&O 46tI čzͣy/rM,./U娲&͘ټh cc BnB4fIBQjcI"uĩ+iG9hOtw04PWe|Ss(0T &!A{S+0<7GqA Ci>gUXaԳv`avЬ\!^溑Q$e?<V\`zu+!l?˵)zr.ނO Kd@= rD_aaC[ݿ- 8(W/~!Gb ܛ#L91X#EHT W8#-'ϥ"ο7=R>%pi:lJVgq5Ab~fm`V,Đ ۵swOJ?hvF"Iyam_3.l u4 6n>/ و@,}%J}MohVpED'akMR;AP IF+_%U-w%8A8$`\AL^ܟxz.?#)`R@&║F8XaoXAC4gi΅pJறKUUnt&t ')K=tpKUH 49P{Ou`fZ3 @lYz"cr#1 \+f F,LN "M4&&=s긾3_msD!LU&Ջ Ycoe<}QƑ̏yl \U!kUՀ!t{:,Fl6 Z˱3MjS"t>?hP]f_N" Z] EL6Hc ȗunI@pQE]u"M$F3Af}@,.ebPtE ӕ OԖ >pEZaUX F(F&mf%; lJW%3l]g>%"2} /5{A>PZjRD^nvj+꧵蚥'f|@fqa|}JCOQlD{:o]t[P0dS9eavj1Qx[s-/{J.x:Nq,f+7E IDATk4V (>x~}*AZ1kN>];0񭨅pb$z ?4k̙U*bUAI=cqY<1=I&ìS6Øs3`~ q9Fes" ~sVy% 6/"Gt83gT ؒ7X|f{".ŖpjJ7$QJo7MxlS{oGxq%7!ܽ[uܓ.L7lJߤ1lgش#|_ghtZzmzlؾ^ KN8\F+J)K~ivSa~05mY,Wv(U30W#J7܀ǮVԏuus8Ƕ)' wwo{ϜHGG̑lzpBCdK`6|u.&z~ߗHw7\(9VŹR =7xX2ˆƗĈ$$\ĚO龋WQ:9_VQ4+ 8I{㺞B5$H] 8'A5*{fReΊNvnLå'j t]ȊezK_ӈb& mbւ1x<ꂁl~O =$8 zZ|f޶7/j0AwvwЖW%$I/`&;EYEDԇp!fy!L{9#xG&n{ĈocBN!F8O4S\WF poT&PҶk|sʌ $;:bOHd"sf ڿ!)Tӿ'=iN:5:=VsFeHZ6I/FVn9 n5u4ճMfYGb 3R1]kIyͪ \ҩ031? fե[@NW5hudzf?ݪ( 72q Q#]Kv"70J?rR\;M 1&f~$+NRŌZ$x:SȄv BpC SCcोgi8|@,Pf)Y~Bj!~n(dD`V x;{#tpOj# \C#Fq=eFWS8pI]m~0$% ՟TKe<0ߘùaks>d˅Ck]_.|HiHHHHHHHHBZIZŽD"@"@"@"@"@"@"@"@VCtO-L$$3fe\A /12Yxy :;TT;mWhUtVmJ h/{-G fl) 88M⵼2sjY}r\ܵ1ܗGZ/5Nc|ޜ{6*prz(~Wk5Һ I6% X. dWVk\2) G9k`cvVƂgہkAaY"]eTW?P=tv\MZUo$pD;k{ $7ߦ67~yرv@2+z U6tau|zUW pFdžcf׉j[kTIރuU2տ9m ZpC֠~}%e[,唤 NrESviy)~+>)汖IZ|CW6Sީ!VJcGC_l 4LhQA֋L@) OwBHm=leX\]#Xy&دvsψuJu|B-^m*b^սHُ0SZʲ -Q^t WuXQ/%,qR钩ճጤf,,(gT. ڪl=t]DN鬚gĺۗY*v7?zrX ˊM[H8/~3OKV ϘwW5gVIjGlc%ZI:iхwL;\pʏ )̨oPZ;j%kj58Dcs,aLfL[8\E3\4N^k,2B\֢qj= ɴcʞ[1Dz+jSͻ ! c-o}8WtmbE|Qm:-g([&̛9hptY2@Ywl5,E,yb<Ĭ7#kN}PՇ{r[9. 7*oY&f #kZ3L.Y5efQʙTnl5 xtN{SaeQv04R6BPO >/"X]0e,nV L+?%VdqGQMpGw8- FTTZBջ b!_8Eo٦OϾ=~sG;@!aH5pR$T̈$&yDŽ#JwW.fcdXqX!:V `GaL1P`MZ9bo!ҏt74,LK X>v3o&f`v lTJ0;߻G8G M<\pt}1Dwa̜J "W+tW @ȮMP$%,Z(p9j%qhC׸\BB =Y+΄4jׇ{u, <~_/nZT͐na&J' 'Hӛ5K6h/֮{0:a]0h-McM{4XX|sЙ;r,0ۑoݮ/?Zqx͊U+\4$`ip<g{+{wup ["T -er"TPSn,C.zAF`>6¶ƴٔjQ+1%|="buߜ~.Ђyn:sʠLnL؝NV=ы/+ s#fDm^^Vߜ-+n̕-z6+[5TdwqE|hH CglzR@K (37;r*hpx'DG}Lf>$ojs]:fg:&p";7rTiOҫ"hPAe"9kn).)էLvqP&B)Ohۖ; ~5YX뮲s)fFIc+33ˡϙǿ;X 7Kagذޠh3r|l!NK`*?WTtXpv$]y<CeLA(nб!Ty9YƛuXRzGx|SE,ou'~EE<\O/MD98 5K?t`{ו:(Ώ*}Ȅ9g+OyߖDgEtqam[kwϴDf+u!J-qvs hŽڌ4@i[JT h8G3 L5͎H-ξP[ZM.1] 糇mRJXqhMnM>0csB@u )[Rau,-;6ADrfPq9k mS`_?`dB;[i?)T<_42M|R4&bRu Nt|/FB!hk }Gg3+Gg1d\ V0UÃ3FB0Dp]mWJ)q]JAwXF8x{sꤵBDL]S6EЁ9cf[F 1fiL͏ʕb\tGzE0TY '~j f55^,i2?^{f$Q{:(`W ӌ+  AuI^xLpܽϏiʥ:xw2/4 sF σpfn${yۅ%3| Cfԁi/S=F0I #pt FQ ߍw5`*ʄ;94 K&4d6q'&W|=޴AjB'z>S0fBa2ÌAk^"v6ʊ1ⲏ\“ )̺(k Fq߸EK@ؼBOt^ZFiX?67Y@w>=V9a¢xQlDFfBڙTЛ5.Js4;B.oЙ< 2aϙiyץT&HOIBg9&0 0a45EȺ ]pN1h|RGj 匠3:hwјntʕY`muR)+GQKX6V&Nl|hu 51l\K+>ǡ.&f6숱^CIioH/n?U`|O.;#1QOB v͝nQa [y>zQcz8`$nTuw}?9/t:_V23KLGp^JRnRl9R̳چ% k @&ȪVb3NRChOOΎ^->!߯3ԝ@GB >]/ 2+-54Iiphw]ІdW57 3hP΀BVfq?IDAT/ǟ} `q$J<0[SMwᲔC`/ANzg"fJKTtP a8\~.@w%XTqe #{I}_ȏ6͜xV2 8TusClP&uy<1oBt05'7s̓tFM x>e@[㉁4s_lv5Hp|^٨Bar |aɡVL!?:@\)7P|~x¡DTURZ-P_@fcǭ㚱R)p(LSooQ  <LxndfR)V9 %|W0?_f|;6s%*ӲVcꭥ`J iTy zKr9E!8ze饫g[J$:˳V&`ޒ:* 3 s{#ŬڰU¨%= HU(o ,yVfԃ]7ڵ8 º$Rr6^87_lіX7F.79jkT \jj67 zJkiiO]0o2, "[-Yq]-`$4ͷVɖI|Zv*[LXlPNNZ`rJ^ƿUqN@Y ̓&˪+`#l9eEnԍL ]ώ ta{}M&gCs@++^p"ED5i=F8-v@ݽeUY]m(uvme1 I6֨4_W^t~jL'ܜ((T7eS[=(WA䄪ܥFa\eJp~?qXl!ANt2x{vNyφ[#$'1|:ڐ(^*~h"-yp :JK('STSm ˏC$pwI#{p_۷Cm>=ٍ{928cfpZT+q7^Zr\zh;'7 ~Dg~T+UzƝ6Z}ҲZZ  Z3mo6*\5t.HQ&^"Rkkk lnjOa @DÌؼudGz\'a= ľC\[pR?0TT:} tp.Wvu",ITk5k{Ž[ rB"5#e#R l=vúTOD#ݖA% "<g 1/aIO'O׋9|yqąw>0CGZzYDS^8qȨa5SrS3-6OΎ0brh4]o:qm#qĝaIV7Š3顄+k7oewžWD@5.}8'tʃWƖ3evodV4dZmKȻF 7) >,(go|hCasw@j\jF;o`QQxTpw#O >0J?rR\;ȘkwO5fFp&iXY9ܷߪ{pmh j%fhA bpǶL2D}`lQA&XvљOn |6ä /0=){F ƙ){vQb-N4OM pvR[xe-l\79\34C%-EŐ7t0(-ƏGjJzvy`VxMu7oWp=evZ̠hhyEAAW N5ĊxhYm+[yc;svkZI XqÂ.N}dۻt(L3rv +s=Wȅ\n&8D7w־w{[B\NpC;1d{эm iD=X4J`⢿*;yfcs[8sѡ>+*'J}YtTyC|B1Р(cT3129~i3zܬnV)W;|l6GxI;&(,OývF}|1 쨞 ~L$k,q/J]uIev`TݩmA(䔜<.\Q> z ԎЙ t6i*wlzztAgy˫9NE;7k{d=c(iw "L$    FW4U9ʢC:B~I/#5GKjē|-^~q0'bcpzIcs{8߮Ndzbu"{^Dg~#/: @\̊wdFȣm]=YY^+:X5q?xS1 ,~Z@@.7;9U{tʒ$)OuO3d<8xFqV.QIKEDx#7Ufof+W(~A#ouҪߝV,D s[Y #Mojo\{'ZUto+6!b=$J`ު!`SgFX㺱n.=ywKs|\-=ȷ/rޤhPSa6r]{>]O{s/}h|U%`A- Q: kò4.magzd3z=Gǧ;Vy:HA@fX0^0m.2DDDD%Ola.*pSKs%Q淔pFhzazԆ Q6B8W\ގ#D4:euZ ¤h"%|D7B*hK(%/5&sّ:e~-ԣ9|ؤњY6fPYy)S7cCSsTYه>E(]^M.]t_Lo_$EQ~jc|о;TYac2{9` ώ[3G ΂H{Dy_p-t̋xpM4wq.JMYitlg/yDKc ͉xc-mSƙ9,@0'$LK1p`n ˠꁏ jS2gV phW#d2fVШ!)Ugζ0Y=h!A}xOdc0BLIΧlY5yf{WO}-Il1zƆyT-%٪;ϲcTqo:$" 5Ut462'%&#cӎ7O噦j-P qfSD=Iά HJH[v>nA6XΐCGl`mva[:S/DFý: }ԈԸ iפ&k扫Aᡁh[L ^+B8A`H;hgg-wP :x N&#ȋWL&p=S6Ad%d!0*l8]?;ST`g/c,q }1ͪӝѹ=i9`6ZᒊttXL| 4ؒqUƤ.?l`[b/;Ofi\k ^[.~P`Ƙc-{&wQΑwR+K#Rd \T[%K|)ǎhUW>/?!~:hWN flDŽ(Ƶh<]XzFR| ]7g'= ,G^ZZc&_?E0n^m|zF/Mk'*>6S]4)…K "'L0+2U+T&/pRhOhv ( K y*4~|y{7 F;@>0dPQOxIwxrB]+3] ߀ֽb$IHHHBR10,{ʁz3:^YaE7"j)T((6d)_.0` n8&.?Ti꼢cD^vZ1U3 @ L'$ч#^ɩA{(tKZ! C,$NQb~ O}bG$I@SE7} Z+j>|VNKy ]8o A^lg+i?H10fddZa V lvm3ùg O~& cJ施O8oK{)R|iԉNK{5JVT0()lN)~WxjN]L𠉁O|K2֥4@lA QA^Nb?| f!کV%P2\rvXZ K ze'oSeR"̱h/pD1g08GK|U1[7`W_ NO]o֑w?晇{f!ƈ|p=MS$)µ1 QQ&Uc|-ez~!WFpT#{%-?{2PȅFe_+n@O v Bm_e(69z $$$$$$$'ܳz0L0)k5A`W#kj yIHHHHHHHp@m'5Cj: oLB! +"Z+I22DDDDDDDD`%Djd |dN9H $$$$$$$$k>AAioIENDB`alot-0.3.5/extra/themes/screenshots/solarized.thread.png000066400000000000000000002673621217172710600234220ustar00rootroot00000000000000PNG  IHDRr!kGsBITO IDATx^w|[}$KD2%kXgjyi'n4ͯiyki&?ĉkk")Jܛ H w.QIQ+9K:s{p@D@D3HS?6" " @3at& " "@rFPv&(@D@D`~/hij- ֱ|zrUL|@̈dӈKݳQ%x7z @D!pH`|}O"bfZ=)J>>Xl5ePVF~cW_۰=9B~%]]g|Jkɷno33Q.UgfйڼUk"uUb$=mTGߕƌXmEf%n^#)=tIѭ4"HY8lh--+VFR-wU^S&zN]ho̫ Ck7LNCewO[[벪wKMāW>lRV.J"" :b MHm}:!P2yW$ABPDUJ1O-é\YƕќRZ D0LQ([ag0Yo>Z*`RLvE/yaLg⃢=χgK}\.4|J~ELfS p'3=OH xCÄTT  2<]j;rZ%(' A5hһj1 KWG$l/jQʭ$SzPVZjha0͍jTƂ(LvLxx8鵺%Lx1ҥut`mccwOֶBZ{$%(@D"M,A/ikG'l%`sxviqdC*jʅ1Js^̸EҦㇿee'eksV`~?¶J9h}DˋDpXE^aPWn*8FF<[h8q䤱c6=^k%MSc4L/γӂh_?]wՏq=$\ưP  HJ;b[W  ! ,^&{䒽$Q.29}5 "<86& 0 .1J85YJ=sTș̀AP1&049̃NkKCq}ϑ;pT{yr6 d,X2?r}pb3q ז󜢶h8nVսR3b[fUME;C}?;9p!lU 3B[F3"z:I} &{ÊZ8D7` >EG}*l8|9k`,^q&I'\A BD@~lH,adp;`L(JNE)o?fG.IA߯뙻yĐ5ibň֚5;1FJ8]PĔڊiz]v-Zr, As?4gz(GHE2xiL.NM()ASW, LS#l[L}LUءgǘr4N !7IRm A tL|rul2j2 Z`660x u|&_:s~SMer_oRW,z=4Sɤo4;WHS><_@DA&8pphlqĪ VnTIxfl.ƒI1pOl혜'0_%f~{a " k"@'&& +DxF~HVP!D@D@ee|DGξ"" " XP>" " ܅5D L t "!,U|7.ۆδZ)%/?\'Kzf.dݷ~7PÈ" 21?vi OٝƏhʀPToMf\Fƺ&= J)L=&" &/s#6+]\Js=<36q3X*(]!^v=fSw/.>4Y֦-uWhO կy,C:~I ku/XzL.\%UiNN) G/N 嚸5tGԶQ" "9ZFcV{ K^]YI&OtD]9|Rf];?ѽfe1c3+_r9\vYJ^X U8z﷬Χ e&mz->MiچoV֔Ж|lZ!o5Rvk׵:m\7VқoUs7Ճk{>WjB[cO|>~Q7t+I%鞼P9yq|YbxkiH:GNFWLeGsʋa&(R~[gcWizmh3ΎN#u5syH;3dEV_@buNŚ`/v )de1qqHް$2uq6\;bmM8qteLP&eK L\T6)q2*+{׵t*[Ui "Jf:.m3-dOF JZEUZkǣ0چ~qI(.*&e̜ w\ħܧ!=}W89a_tsoVc_SnQ+n{W9aAiJ 8I_G ֝?oK֖Ն⽇`䪬\W^Z, N74/e Ŏ_?$(|kӓlݵg`T>jlAhyXw7ןߵHE"UF7uC@\qba+g EBg\LI 15}pzNq8`۽0rwv]c9zMCD@~; QewH{@pNw%Eԃ!-ҐMR 9L{=m5yGWF{o9?u̠z>h`:C)U[zܾ9^(Ϩ|J/̓^^DzOaKL }d256Cǟt%ZAHlc. (-11L/I~}T?P.^%`M h,gq8jhl ,E-LV* s d:KhFt3$V9x(K}蕝AM~dQ&Ez0¡/xsHNN#'\`ֺ^#C$E}*.KV}rl˻kQ" T5" }!a$]$`U<cP8|Z~" 4 w[0PUɄ󣹅 ܞaeeq%H_MoGVpu9N`cqXFa~ LJ2PZ5 uw^p^k;3t:5WL gq$da>(, p·#vTg8Wur,m 4L9|6gfC $+#_h6j- &bfb 6y<$G0p<d|.IߘqՋ8Gn,x5(AD@O`C(Љe/ZFD&;QliJ&3Ȳ24U{E{Ë9xSxc Ԕ6a _l/Nfiٟ 8{ięO;kgRU1B MVG@vy\dqNfW`Efɖ[YkD2__׿~zlGÓ6ƺ /+ę\jsKWcaEJU ^3׌lK7S%VӸ'[ XGYIr, 5acYU," o U~ܯsKxLˤ{T W$c 6G7I֢SfKbGG ,,5 ARQ*4FkՋsK an5m/t44#KqXX0ͩ<fV*#sί#)zL/ͽnJ8 g6G\4j0J6F31; 'l "@ .cE[rD@~ 424&8kM\:'Q–tvuQoa\%8GzHaJB5bg|xi .Pn)V  &WkNJ[.cޙYXmPwRtq~~w'I \L@p[dpG)oY[ǕR-˕ '@,\?za_)}ZQM'_JV@D7`wKl]V|0wύNzMI8mS&) u2qx$fL(C)]ϚYK:J03\@&" y`/]b`;0#/xS`~JIҲYTtBl!hkk CrD陉)6wqzRko}dudT3bBz#/>]k$.)F) ͣԢtHJߡ52^;ksJDoé5y;ah?wQ?XnZikk]VniPfBQ+t6B)~$ƕHA" D FU6>dM8W^Y !c"tϘ[lvkx"Ud%|>?uܠ< mRRxKb$"6qdBYжg0Yo>rѝK _&;r07^aB(u3YAQhuE>|xk.ҠsQ1S9q2T14<>>lO.?LHE^>Rc\KMc^^| Q18 qB`qP]Ckm[,w9Qd Km鐆G e%졅&c=pt@ m0ٚɎ OUGËt DŽ9*^{tir`(2XݓCT@D?ih qwĐ LBp.-NlHT@M05F{Itw*+C5-' +QX/R[4ҺLg"Юw@˜s'y~ ԎbQ>@(<)V`S8q`;wcliU oAƪ,." "p D6Ўʁ @M)y╝|ُ\uk~` n1NQAt|/jVt+uJxte4~1coIĪ [ۻ]m{34xeA̜0'cfÿ,+%J6WNM09Y\8Y TJ3~ <\v?0nK-XW-kԲH\,6-.-THmŶpMR9Fa$>=$\ưP  HJ;b[W  ! ,^&{䒽$Q.29*ƍD@D$1mC8`tUpqx$:|`)*pϜ*U)%r&13`p+O}ZoxM%H_M/ ` irə֖#wlK0d}rml2(Bɂ3Y>"#r}pb3q ז󜢶h8nVսR@-dME;C}?;9p!lU 3B[F3"z:I} &{}DX! a >SHG}*l8|9k`,^t8 դ.(ie!" : i,3'/w ćGY)*~JoǂɤlXBD F佌qB 0~`(6W`$eK*]yv hei542,? *ڽjO oZ+\hY1-1FJ8]PĔ*iz]~+j˱@{f^]<( rߙ!]6;\൦183;eIq`$ů֎Z>0YaC-] @!k5CD@6zmu2+Y354T2^J׀NTpqL`G%{n(m͓x̢`ovӏ{ Fe#9.ԉ(QTzZ?fWq<9ؽ%c\#G.qɡ TgPKH&:q\B}pdO_sOK~eagp:^3+|(+ZG،!'a122 ckQL(+A Yk+*n3Fv>K_5_m*ۣ^Xbla<88L2@h1^҉$ѦƋ6ކ3pm14Β1ˎc  PhݕD03b#GR/OjW17 DUq\D@D.m+7Kmڞ56lYh' # JህixN082 WLZC?~\Y.%(6y/k4Sɤo4;WHS>42@D`3 ,38s\;3aq4$pGi&xrg\> ̕T f9| "lK{x:#ly|׻7HVTa"RPJ>L&lfOd+" "@Cci]4YjEX$vD@D@(hd " " +MVuF}TyVfF.%T^D6tꆇ_-yaAmb+*D@D7)<%gw?"%+isbq_֨sŋ["  GnђMQ?+ M~om:-P'5/c6&lx K(eehQ6{,C:~I;ƞBQ5Y=fS76/XzL.\%Uvne9=<0m)Z CY)?\˰5 !" f˃5xp51Z}eјFb'͗ fxO,8xU[;.p\bJrw(C]LVZ|@[6uޕay?^g͠u sS8Z){gWW%d%VϾB<5Svl{Gʃ?6.T.ZbI}?{üOI\rT@D%`S\khgPLvq^np2)m&<M_n_BEBYRz)U~xĜ rUqvXYS~j xB691CA]2l@zt pNFY^Ä}\en,gw7_{"Lڲk'w4@"" @D*(HDc"qjW6P)CՀ2~Ǝ5B?TF4ݒu[MJ W"bb.q2Eh;Ft|T/,]V:Z)J"Drg9\5z>[C/|HN7L[36O ܼ xFF\- Q-${:=P,E&Y܄$TQOT3hۊf!ˡlm+w*Pv  8&6iG.Ά9X ' gv2)[d`~mԵOA&O='\}]KG)xD@N E8Gc ` Y^v )`?5̅ላfm!1sP\U|CM%S+c:0}[~Zh1wQz]J`l8kt-[@'ԁ5ugw0_1s22qN 6[sRBQ7Zwϼ s e5.ҽ{NXt[Yw7ןߵ[|mxOkֳ}tyй,-̌wr0D[!!ՌE"Ec!t]I7uC@|׿hv\qba K(0?"gJUXs:sg EqL+(@DX!U"Q?ϽyZ8ϻb0ж[r W.03#՜iOk3E<cS +NNJTW3E_͍Qjɛr1u5˪>"'eY)M)k3Sm#vo>N3DRep`c `0TqMYʒ NVJƶ0.eK<]DiKA4,+v;[ܣF/Z7+S)$P!\,x=''nƅ;Vϟm52I20{Xl(1.KV}rl˻o % "l wWpSC- eT۪ʄ̭p -L irzÛ(1SGjz<u`q]hNN{xk؁ڀ`30'3g~ba^_D%LnΫn jwNӬ w㶿-d T=KVWXBhm%F:~I ~PHᶶb[HgHbɤlM*y/h/=$LL=v8FgcYeRsX^3wYN_P.rB/nv8K:qON93EZ;nY)Vk`8"PɏO,P^oTh>;9'׶|󀟱;iqEov3 8zkYYh J5>cEnk% " 4f@+f qt6!nnF9;9]y[.wXﲬ ]-Oԍ5A'1B뱲t!}L ^g-`rIya' q%O{ j׳`VKziƇbeKp"Uo**+S,un2^4ȝ Ҵ7n h\(+A i-zJPWDj2ѫeadiYxkNl{:I4\w],bw١R' >Qi<Ҡ3ԌD~iaCu%@QTM9h0^򌥵ߡ n\|cϔ(V ~Ѓ "< wBlC{]S-kg SF8I<C)ɕpY䪅^vǏ+ߥeh;Fq =0VpɯV-gf_ғ/D " x|c\+ Otm׳7PqW#lԦ EdIoBrr 9_SsWdkxlc!MP|lHD@ < 6lʳnq$Zua|-/ݭhZFe:$រYa\Bp͛>+TKfq" wJ5樑'pD)p??4Z֣ xv60xAD@~ `-URuܼ 6W־X3iՇD@D@8;8sA" " &{|0=ͷ SV)5xꚺh6æ6D<_@}F䴵,,9 hMaudν7d&˯MnU "D"pQ\XJaH&ݛ#3Wʋ~OhmµUb$>e|2Yb?_E?#CS:4x!<*Y3 Sힰ[3bdҞ uf0a4YTuQw3 $V#Z 8/Vq<UŲx] +6m)Pm6ᙾh2TMܙ/l2LygjfX%g\XJ\rg`"d58uܣ/+pߤ}r#Zdp,qD!?k*$ݎǴJ\iV\g') XnGDžSv^Qp/MEkJǸ;|SZ'g!(3^ w6A Bkcc\Z|K/^&=YK~ah)<绎B|[P7)P8ExK{net(@0QG(gHw&ms#] m݀~EU!)ۭI+bIǂ} bqn7IIk\—Š㕥KYw8S\ji>^2'Y=}CBNoS}Sf6G0f_lgZ ޺72&:e$O=dDg>~kbvnOɈΏ:k?TMTW#O-ڋ.)LGh.ì~14_slXH֢+;`r'ANcهuR 0䲬K_ͳK5Z}kx(3d%<-z n^33]G19Zq:>Q9<;A2⨳kg.9T5_!Fe-!7;ab(fv>LPA9" F(Xm{0{}"vpOZ' 0)sK%S GV@-'ώQ-`gI;Z;Gy (+ӓƾOfb7:ĉ]N(_*Qʹ->^:--RwFҐ>ziԙORgNj'K#* ms/Ʃv}Z`6XHVHӟ:fP=^mݒp\ť<9>'< B sNǼ w~ vXbg?˔}/M`|/+Ԧ1:0|ՓwHtpt[%R.}09"k+:#~& 3b&_DMFd`C O2S)ONJgdzc#pnƯvQ+ ~*ןh0JGmE/o^Q0p g߷'Ae ΄_($n*IVk0$+['D64<>tV iE͓1t#IZ4e#"JLԑ2a/ILeI:HŠPaF}V'V/&ϮmH)r\ݛ:salw?;H:7$0zU%Gfբ!+4.&'V?ᅴ5kvJF/I4qt+Ҫ 6w{f…wt1lʸGD쟻L ӓU)O.dyʾ) m9E" )66yOJX{5eq SGјЉ ( %\Y$pfx̢6Qů=qu"8&9yH~ {S^~ebhes1,ӭ˧,V(R.?1/Neg/Pʔ<@prᬡݒ̿r oj+:֖($JDvgxT3 V*#VA2_+[Gb˞W+ WiC!s< ԃg8m&9\%\XH~%e6rp7~z(+E/̮daYIuE{."( 0M^S]ǧʜTdĴq[9q^,.Tovքm 0oG: "hQz 6L{IB,8O$gY_<+O?Y e73 +@;2Q$e5&h5s`1{ +_ԔswS|^j4>a.{?,zwŏ27t٣[ZDAgODWVAx-z./)3I2-nmv%/ffŁ1߃s@y" @Qfe&fC BbNZA#*fjWz)ZH'p*L8;y4/1 .emWK J'5!Q OK",O+j-U:yscsf#j@DA H_ `o+kG~勵}VVqvA4lĹ>_] L%c삔֩KGrT" H4\K|^cR栒 وY֦-uWB7G,pM^1fkv'a?[[78+|@2b@ϥ^8{]tn/cM/eB/IUY-/XzL.\%uH9hYR}ƋS.~Bu&NQ" "lɔ }+FcV{b \ꊷܹd&mz->֔kf$'> |#i {>yZU P!Bc0Ď9?N\br/=sKq9|N8`:B:]K+i}y?ϪgWB_Fz晙=z>yf&d*+ \z+7,UK%Lה?-9Lsd|d.X>KJNġ,A5?[}kݿ5rȍ!@!;Q+fͳa]^14`rMJBgJ~&<[gFh/z$Z?/5͜(υ=LƼsw=;vh)+dgvRޫ3b…W:[XW16l)us;s'HU1,A%uv \8@Q{6Q=u ڞƢ?3˷oS9`hVoxjkE }!ǣ B B`N ,.Y]c=ب湰PTW#/ 9TҞ ,ECiaU KLh4Z?شLoGd2TW:d 9.1u0NtD;8Y; l7ҵn]RxB6<},,tm+keBNLH2tb-_ +d-*ŨȆYY՟_,.Oژ._tM]MrB ۜgb!{rwtsaa!%!?X?;}{%'γ r'T|];opOT&iv\ӦݒeV6~pDn]v 3|B.um^>J_ /&) ޱhHǝ0ޗPIW_k5%[ B XxYs@c2  /+8-lQ6P܋JV<ϒoϸ^W~=w n۳i[(3`ҽn\vQ+5p80$B[;D :NV;:Rە=|bMu-.PDMߔ_ 躯R2~{@!@xD`eϝJL{'t5?nKXHYqQ2;H4I`rсg 54I[}'4~+I.u#c64H0_{f wLb=w GT&\q]VeE,:Ե3$ B ^^pRtf=o!2Ԏ_I%Y}[.֔/=w}[IS&oP}A^l[ϟ:8=X:XMvHU!kE6 ,sA`¹mA`t̽gZuCdnƔJѢ|H!@!pXh%M*RB\VC<05ooj|Φ&;MҌ,s+%0r}WA~eion38Xfnh*@gb$*<}/Mn+2U޿p=[pZE-M:ٺZLL_ QT+$E8\7+^]mCIăBdZ|bsBGWں}\f ,5z&=}d Dn "3YwLUM{>px/ %2#B ' -yI<;wS^xBPdt' m<܍J6xlOv|P*#%/\dHT[襎5s7ۛBNN!0i>'&B}3'WRwtKjW̚gnŽQ)+}(Pk%0lBS2ƁNiVF)'GkkmCϠUJU@nݠ|U+UYXt ɃC|cjmonćj"W.w=wYZ0kBvn!/-jgMZ {ąDȩԪ҂ȵ~FX:/c &fk˩.;1k%|6mGjT r6w\-Z`ʵth̺0 "SFc$B`}'6T7iwy_FP{EU+LU.Os*!@"%To`[]>9?eIoaX:ԥ?+5N.U4ç x6Eu)=G6wEy.,4tWҢ^"T+]CIajO?q߲cj>:3Uw2[;³F2p˞/掲?m={$J0sM\YяO0b+[Y}bA"KTIBʥƲ*{Rdz}CkW8n -Q^=7 xm˕.0j2{M[,,lR`Y{SP"p~$AmsT@jb-HvD@KS'r;,}aH[ݐWXXYRA2 B \ |0Wyi1& yA56$ۼ]R#^{rwtsaa!c^6jAOOO[;?[y 3tJᚰo&/pǚ"oКIб>Pp,A Ny`GFEQaCf컞2/ޫgXh-Tl}LM IDATj2Rdn^z[["+Whե69kB^9PVǘn}73z,઴x/G4 Эך0&+$Eᕞ+֤iߜY4!@!Zo`6.P5=oȒN|t9:b>m:;l~X?ULE-+֕ƕ@c2VjRֽxZeJyX6ip֤B]0T 0F "qYyzkOSrMZVg8̎REjmE[<2%Xv3[ NQq"J90=;mQyl D$3w[誓\_G6} l? tb5= QG_ dxCW=F{qD!@ F"D%'j<'?f,C8PH@(qzeE9TzJL{̯ ?\S\Ԕ/hb?>D#]LFaUh_^TU *?["d-]C@2ڋbUJk>]3&cW*'硐1M-H2QSIxgrK@8ܕ_Ȍ}E5-Z[m6@gP7iosOUk[ rÒWp &#/z- XZxrV:+m@h~U,ׁ0>D4?E.|' B 4GÂ4E:un,y }2Eյڒ}*-TR{ bA|lVhBeBzWi'/>"萌uߐ\;^Yx:08aJCq`HCmM[̆o)*d~)** e݅ ھEN?Ubdj#ώVRW҆8,+ GYD{!gOn,t2O}8 JW~v|aO%)$†r}SVlKi(MI8YYM"6 suAq֑5*'qDu}Ɂ^ WݷqEKOc~1C-ZW!K!(6XXͽ=V8Eyݕ;Y;yAE1w)9r]V8~xu<=ɚ Os:NP=޴nO_ƫg'TE($;6 mLt8BL BX_͑N;L{0?RLng!<6"K&В+K%KߴvX3Sdm0/>ъYۉ\2JOP[[xHm56 u3 >w`$r:}Ԅwٹ9ydM-Fb1sĬ]XQ ] !@ܖ=Yåm'/rRW߽jtypyy`A wRJD4}VB F ÒӿPv/IY9mkv B B FI<2B B V΁ߤY`- -]+ MTފMKf/%IX|Ť͛xFǥFGJga,|~|B ۏ?'PrdNy~d%k.bTknsQXKgkZ ^~>E{)-QDlȴ4UdmZ($Ǩ`Gںv d01awhrao/zؚ(fI`z1Koө ;dcjSg25ɿۆ-顣gO|DF`l;]ce ;&y!@ I`zWVyXV6B5:e +3;sejĦϧݲ CWka3</m7A\VyOeIt"DޤAi/P罅u#CLs Z.](_,]% BX!!ZYwtKjW̚g< ch @sg͉zgo⃅[,H# }eP''=-%=f UxĪCGmBVG*ctW4޺>3$$G{n&qy)0vq'qTSr(eW[UBcU]]cjm‘J弫JYtSOogu?h,i鿌^N ʱo Թf`*ohgF]4|K;F׆#s"uH[~7ǻR0A'%d9_Rgٽp˅s^LgN(]c]|7Lsx,B [NCSY; z{M 8ii6f7zõaMuP'-ϓvwF5υz uz')f=h17A=_ s:" 0wVfd0zu_Ipgjjq$ }R;Tw6/XiX'c(-*WL&4]lxN7D tSLOIoj;Uf+.HFqFpt e.ϥ_rAEH* (\"˟g\ۂ 2.\* ޞG4&яeudAbs\bꄬ뺿gK /@Hvg }wqw/Qٳ7>e9>.kaEH2tű볲?ϿX\1}L~I.!@yH|`l-G@Ebi8J^R*'C|q*{3 q'.途U Ū(PMW=v{^k~V 4_zlcxB4wմj$ CCXNS`xÀ8t֯a.~KE|wjC=vڒytlp,dm@OIK%яmM6ȼ+iKuϯW$83x*@HW=+zO?ySCw59뺿gV5I(QmXl]ӿL&| @R0obh|_buN2Bg^9-r~$ B{H|!sͥ~'e+?}IS_!3D1G0sG7oK|,X> o]&{ڣ5d<6| N mo2ۆXx Xq=Dj/L/DR1lV+ jqwt4l5qƂ>팏J9:'% Ĥ. t߰J9ڋOnZQ>7%0v<<" Ov PfNIJn2](5õ~.X^8n^ }9GcDrW/ZuA:$bQ)wFVA ݁Վ3! x]u=Q:kQ\B fB̰ ZRb)qf<9{ hʬŁʠ5j}L\$jnߗC%޳&:.gMǏ[ BXHYqK2<~,#'.#(^jYĎ^ȵ/}-`1\1cïOţ+&@$U~+ca 0ULwP-(0nۮ_ 6AIԻhvׯw[#~"?> M+@C[V〝EՃ{bV}SJ:kDI2'2 z;-KI:+'*ϜӘyBJ i`ҳY6p쐓u?g@ʵ)mADwalL)rVd,"U!@̂RfEL`AIW]Y4013|OHCקed42 g86@Z5 .Z2EA&BE(j[U%+uÞ4)r!@!@|݀e!!@!@,U+Zy(aayYO<5:b&0CTreCqlrl|n,C9tTE!@#e%ԗ1Yi>7Te];q͖~$ܽˬ?S/ XRͽJ}iIF㗑4 '9IY=4/Xzk/>` )3;K Pև6vZ' 5 ̯=_\}ة?Ʋ^xPA7O^h8cJ!@HfͭUuG4vŬy6,,lfzju UVKZmւ^1q!rjd.8W5rS"am߱3bٗVz.(K|J?)\Y&qt岻.AcmwێdO\B1!0s'1N$dm(nke˳7 (9Q*V> B B_ +Lڣջ<c Ͼ^dS{b3.ߕ5TrJ&Zqk75[,Hd^k>wJxR!oR"s[l ߵa 5XVb =e-*4+?Be7V~֦u#\GkB;wt69*5MWvځW0OvwXHGGEfn+,\)[tMy&Y!@ރG\XX;qIk'CF@CGceb;PysŔ.z揅uWl\`K]a wOFmH׶tigÎ[Bnp<.p1ɞX [y}3Soz#vs5PPǹ1YE}') \&Ŏ/^ B yXpW>{=Ш?&ouJIRk,6[n(STR+`l:S,fSJW]a.;+;{ԂsvpLr㲋M߂7cA/#XM^|{;_qEiNJ j0i B 2eŮ3(e -܋&;NcJ9e'eKpѬ{S ]Br$f})SQVF >$B BX<Ϗ tf=orkȪR$ٖ5vƚ7o[M@h6)P 8(rv .l![tKDy*˔@mRd\ixgg!|YyC=|;Ԯ5 ɴh7pK-k!@<uZ/iZNZʢnJ=j2WjY#~r\5ANt/I8i%^\d6ӿ/9P^ 䪰6Z`Z> 7Dg)F!@|A @@9„i!͉wV JM0~ӄ 96f?0w̅inu>xt$cƝ!›|msB B)،,ʬǎFMyq PMksP͙ }S]<'Z1k;єB!@a$r.:}P엳ss~Z yĬ]XQ 7:A!@_D +gHU*O^lZ 5KIAg9+E!@ b!@!@"+o^$>!@!\. !@!p; r pj߼ZPwu>g~Y$f8qփNY;g!.O?f~3{!$m!#^AyY޽v@JL?N> %#_c}/[i]QK?|_B]L ~|`͏ -aсLR;zH;羑 B Yl?|>VWSAz?+Y Qo33]{'YK0GyC9@gS+1j|15eαvtDXEa< g?~֢2#nY7ѱf2.L EC`GPXPPHM~~^;"=ބQDCiYf#` |I8tۂXH=frv ƾ J< NєNqQPEKP fdCF Hi @Xxsʼ:O=4Ia (X)qR'E]k3Fi$QBS -!]w%KQ>"$ ۟VaDh*dڇ\"1ԯb gz4xg 42sb3,c82 xej-XX0؃ޫb+DDSo_УWXs)0&^yrg!ɃyV"55nvM'tޖ g)Ÿ?fe>,'\w _SsiYfp PK&bӆ Vhepٍ5LB  rH<237ldH`4=i>e,tPb+YwO9+XxҎR;4%^2s,H_&Kgz _Уؗ迎͏;k57b0>U&'ِa`M {~`u߿E CX<ײxgN7s!Xz tBIn j_fYqo'_^`Ɠ~z6Ѝ~v*ÂIo >^?^B#7V;rp}Y-ʠ'ZJYkER5_/Y|z"f>A,BYڦ_9ԙK^]u2 H'Y=1dA?=<:iۣ &SC_*]2i ŕُ]iistb 7<|} W>cti1u3.,as3!@,umJ^fxwRQL>#f Gg34*q.j^` bK7g&J9# Ω]nQ0aZx,Q`XdSxhܜQvt3A B  En0Į[z0E8ۑXDuk(`}"<(ij%So`W/#CTpւ޹"pפԛ }$E#(Gh3V=~zUdWqGΠ3G8"T]ިBI|dJ&B}QpO&GnJbI>=@΢6Ij*jf3Q~bTk# ?4x imHG'uԯCu6wL ]~^sظxG@ U901 { ! rK8`v6 (HG`0D⒕Horhi 1g dK>,qvbwEWPgS Q4>9URo~ Dt!y]0)Oeï0ob0A=;7Q{“&݄[v^+j@9h]hBE.;N:b Q(I,05zJx*Gj 'W@F&"_t5ιb?j 1۩Ot|ڍBE @Cq=O_fd%zPCY3;(N/YÕ!H^'S1p\٭`@Ǭ7NXyC^A!P Nx&ܜ( ,4a PK&܈1A 1 ,{{Ul,mL}38>s kѓ64Qr=2ˡoK/i|wކ@ ύp!{;P_ TYE=8cp<DESo,ן؉' [A`q̓J?Ŕ7a4% DtXpV:if_ {BT"ĹXX9~S.B(d$ 㺒X^82buЉ(uFs$O Cuik1w})Lf}-wP[VKu[{ 50|L8k HS| }-TaZ~6;&4뚓J@#qƠ=hM 3{7P虏Y=!;IA >^?^B#7VC7(k u ޛ+&V` ;ef3 t/bv9H-"Ejo׌ݙHco͛KTdA=mcSA% xc-}˾=Ȁ 2z| {Y΁o17KC[tRѭip&.0oɽ3&0/*B$ @}AF`g]ܷOj$nK쏍}XJ)>δl9Д\QtZ(޾xNjgPـq`3 pz8וP%>S!slJ٠fIYn?sbu+V XAx ,:_~ޥk_}ۼ D`[-#P  I'3ju>˸ke[YuRu(WCZ -GiAo-eVJݡ>PBz|Pd0^4 ft Uk<X^!a`=|ʑbvo gRRϭ%+>4'@%jMWM8C;eWe;x{^,g ' {#zNhBe5BZNﮌY.fcA{/!d7AYM,hfQ1pz9*0"n_xVJboZbd^3S-Z{yi~Ω@L[ykɶ/ ǫ MogQ471g dK>,5FQP;JǓ5:#ȿ̼m^MRey{`ข(X Wc1牦iȆQmX<\a'Iո8Bg*7L"tO~vWB YEH 6$jtDw\mv2f-}^sOynNuf-@*x ŃݨP~Ь|wWÆFӕ<\y"\ {៶SȾ|;6PH=aeS?Ѹf, nc~D Oz@feydWY1{Wr}7[3<ߪGo8:hJOx{ ϣ =Ӕ8/uUecUQ;0Gr}w#ROP`#nߑјy0?g}`} q uqP=2P$NSF w. 5:^ @^-RXwfqmv:l qOW:䢻^vQ[܇hr(|]U\]vU1" %j KhXGm\r_5 @ćj9ht, 3x2q,H6M޸-Dn YIDWmNb[; [fq'oƠ)h!}ABZք܈Y,8jF(ޛq]w0X@p_E7ZLI^⥎vҸW_m__埦Ik'}uv%ٲ$k$jEA\@̼ H $P>s=w9s2 ΅ ɫ=YԺLiZOۑB͗g?1NE\.!bK|lӉoؗ®nѓR8&`~żVOs^]&;$^?yUx x~Dd> p(M1(9X]$.s 9݂\K#6Y42Mq E-T7#%4&@99KXIC|6/dJ[,<}ۿpAˢϤ } p3`x<Հo~Ȯ?Yh$_#)blgj(3׬F);Lz%b ޿d >"/}d ÜLy$f1o&;WAE׎?̼ɀc)꾧%ϕ OMbg ÏC8y>| P⛸H+oFmj{c;#P0n<"pO]*]fbR [f?oo7Yfь+SA0I,>,&d4N(&L<*Yh3E _Q /;2@ç+N>)f|mrMmzm`K,v4|j2n@8=Z|dl6>EyOJ~j/((r2=S`` '`_VPTާ,4+u)%2*z`,D*VDB"ϣz0FI焝qдavWd4OyqH@qrB~^:q~sh㢎JܾG#FYOJFG䈄Uԩf͘,30v."3g29߉g~y"љfьT i`%0Ls+V4ˠNÎhsQǏ$hvf؅6z\N 5ې@RE|Z b''&rdR2(Ŝch"{,+5MEWm; XR<@!9`pvs] ^pp j"\6D]Ha%O0-@{; G:{UȰxo-ʠf=<41 G givB -.秇?lGýǚ`1 'BkK/T  bJa?lׅɑ?yi\[9?JQae F==v7er&zo uPSx.X"8X#h[l݅WKqd{(sw˿ @J83h'rΟ2xr O#҉Ƴ>) a_3T@z겡(J{,K ,`:Ǯ9A$qG)|>LN|;iTοn[4DI:m'=j #H O QCwueRd?@+87!x2^ϕs:nS/}a9}ɯ ȅ38br$&_#dm^odූY@agO`^X_=m4CJRYQ~MORl~ĂF)+'vjktL Q33@*G4;^e?ݟ784~dcq.ۏFV*XƗC'ipAn(*п\~VnOӯ^fgt0+oc8P)@`FWg<d51uIne+wʭ(BǕ@rF't;rR Ômzw ==AuY|_Yu,`ZVOʵ9ts3 ͬV&s:_) bjǚ @1#𘻕̌nM {"z{*q*x:kיYvξ]PBHA3^aއJ^cX/Jg|W@! eU2`A @Xm @`#bc(np |vΉwN\0c1(Sݍ;'3Z;.tnQ!Q_ҮQXՔ;ͯOc  !dƚzz;!/ɐ}b8*ꆮ.J<9*L65w9[$4T7Oi{vE5rSJ?]G[ƛz')OHޚ*[.,? '6Pwe RUG.tS/69$I BoSk<~˓EFgg{4m-K$}Bsc%(mF9sDtᢈ=wo^9tpP֛?|a38,W@eԐd\C`\M[۽Y~x zS_.21$fMCgO^m2`ˣ8=9a'• [3tfҗϛ$H@h~KJ&0kxj{D{pՅ7 ; ƾku,Wf^wn%U-+3~hV4Ԋ-۲/-Gm/;hֺNfC+7t鑜+Fʎ)?pV lڭC 5W{'|;})M[n%1q^$IL*NZ ))DrIBVXߢ>)F|IjBrsXwvTΩ lvFe<-r O&rħ}~Y4wWHwc!P Φ[)ū k-f>߽+O׵IrVUEnE=K]}s$2-v{ 1LRh/ժ܎mɾ-ZO%~Ŝx^N-Mo <B˭XpO/|iif4޲G?O/m Pa_]cNފ?wO"žܴB+&MCʨ:L#yrW:kY~5\VYZ@2,8oH#fx+? ]M3w8%θy1D؝_5]CMnk49qn؈[ "϶O6h iSڀdgwQɉɍڳM~Z/m* }{^Z~hQmZמث@õ|X%w9GbcM(,r7;C1_q]_!ip~2a7 3<{s7P~pZ:wW ͼ[X{` +gcbIn{ij,U&#RєFe|"(/b#}H7Qʲ$Ċtqw ڥU}Je+m|"I 0\!T}"ew w[_E\z9C0~ss>ʊ{ۺi}9v=(,9qS?73jȌ I]~SfG_YA\[MF PAlB˭(%2GD6U!ASyġϜA0u$bֿږ<^KE:)k:؋zX/DYw}q(78;mfڲU& ϛ*H@hbrPH7aLN.9|l$REm]~%IFڈQw`~Rbj, }e׷_lMEzޫ٥B&É"m]΋]h>Qm(2;v2PvQN̉'Xġ/a+OGhH@ITY8~C+,P$-<O(MGt:eʲPalԾ%ZDfM&4 xdXٳO~5kW*Zqi@Y,l%1=D{fS2wbd,"[׽U Cy#TV.;Z+ʶ&_4ڱE}~#K՜Gةa1hANӌuMa2Wy'vgWĩߜNX s5 Fknd29a3cX}2jZ=f9[+r]nϜ]_\ /vRbE#=v"JgIY^&(,LuVn/,J8ϸƻگ *rsU jINuO/yq{V^佷yQhppsҦ&Xw_NR f::I_EMB˭FLPO~;](Q##|k;_DEpcJ,L[x 9juwƙfh#RSS@wV S&+JW',3QlkCal'YIޓ}$fًR('Oɠp4_F.˗L.|.@Rs nK/8HKb,~R"5f$z"n!9q.{(t;L. o GZ#&Oc*+?_šIiod J݆ U[j;}QXtcƘO=e|oωv'!|2G;-mRbxm;I5='Y{'\)ʗFH~ppҶr85=8kf&jgH5cn: -G& EYOXE,7]egB#R5C# #"B"b*DiVeaBrf IDATXq<'pLrU{W{koOsĊmEU)3,g}=Lĩ/ͥ3?S(٩W~ '$io}ItfogO)X0 Ɋ+Iqfy|pE v.@+d(&25Y#zJ0@@^֝n2IO=Svp-O M/HC2 IFO -7$lr5]9w͙rNܦM>Tʦ/N륎Q $ڕ_TV`7k%̾?3eXPĶL)8` J \jHqNyeNy h_`!@ 6h:X A@ f%nf9 !EʐP @o @p+C:@ @lV!VRIp @l.!oc_՘U9*O(o.-@ B,ZɗH _ZB_Ж f @E+ɰ]yeŽm]4>ҜI;rpxab8@ B@(zkW(1!`F.@  @M ĢMiH{24VHBe @6s+m#SESK^ܞ%Bh@ %bn%GY^QU@(@ 6s+;囉 @ !Kvj@ 6p+7]@ EVbN@ ttU  @  [: @`rU( @P$b "U+X*ژ}} 'E٪Քl1x5p˶iQVC[sǙkFFc)p #'^F|GmM}P䑚 %bJ>i!u|t]u%GK=;_UҪ$^0@[nի'֕sPJ;/[:Jz@?nbpcLDFAIٮ#;#-µ\' <#!q֎3Պ#_w(n~ ) /{SSq~wpsv(s-~ #bnc_՘U9*`\NqƆ1E[bFS[31*g#$׋LKz ;rye,k_Έ\Huwf9ғyszt'KaLX%25g_(&>Eqvt֡>Csf'mZ^21$fMCgO^Dl@}=ZNxS䄝W&l͈/)|Kbql|kJ3Tr'اu =z=q[NXeI)pր%.`p;!4޺gTf#jk eUۛ?qGQ[_\VnԂ$-e >;֬!D!,1&mO$5fw1* D J|Ez?IΪȭHo&ُn-]icG㍲4t"x5Go{1Fۍ=CZl9ūp+5zQJ#ew7d_Mȁ5TEwJڬ#p+7Ͷ59,)H>\V vl-*7ւ1[r4> O&E)^S}3 Uz*{/{qrð 1xUym?}F| axvVkWLj^=C(ӳKj*zKmi p$8ban׭ZavaF۩; ?<6^7[Ш0Kg D< p f bn%_+|G1iGn/>Z;,r7;C1_q]_!iRDvgICſ4V7Tx?fpӱz%u\86٢d:B4MeeiQwAg`qY_./1 Ko\ήOwye/EҸ)ReW !Vӌq\#П7#H<n7ߒm;8.˼S7[i7̞ϭxRVSHiЛ۽esv._;p7P~pZ:w[X}Mb^{hokWU*!k[k@/?\q(X)j>P7$-@GH`ڃTJ2%F dڼk`V&)/%t7[uF~J,e@"R)q<#0pFvatUŏk#|JRL7ejm>%*U{f*>/_:U3  l$sX,\;܅ J]1^H3sJG^Шfڲ reN'yM+Ȱ Ebs.o)/6Y3~oyX&#^PImӸxMٛ-F=ݠw*/CڏN-w <(B7tǷ^(-ʏbp VbϬoؤ9W+Kcfdmmpv3\ xv+G.-Au+[\\P]{m6XQ59G0S?7+kiPwOi+rG;*]HQm(2;v85|6@'=ho=t-N%ĩo\o=~HH@B JȻTwQgEza)c7H,Cy)~5/?m+!<i A)=|xD>JgQail=DObxa(ؿk6rV.R99p2 |DpѨHrO]RYvŠ!Y}xxW1^AEnxg$%Qox`#9naU;~'cck{4"QlNpvE6-6!q|J?&M=NLaֆ+wKH {o{78a)ɐF]{gaV);M(P\jvFLMΑ1,!_SO!D*4yd USH"eys(/_c5d-EBeN_',ݘ1_O9:?>}¡NQ "DqRsq^|=5vB2@<<!VvWk=G jԽIeJ/Yx/Wew4GVhj*oAfo ֭O#ۣ\]\@R/ 7;~Kڮpq3M1(5#!\8H[;tܪ $|:G{Xxdž#*Wx(߽_&DP PfoZxe<~U 8 u4Ť<)))aa//36ffDM?a8 *| @l p+7#H@ _ @`c[1A @ B̭&w@ \Bl1rUd+ @#bJ|E"g:7_Z|@<B,ZIW++m:_9tLڑ[ B},@EoJ2%F d @ X!Y__~I7MsyVf ae7!P  n!VF}䗼=+/K@5 @l!VrUE* (7C@ H JN9@ #Kv6DP @Jh@ @ "@ 6@   @Jh@ @ 66fA#ܲ-7O{oJCm [@X*<~җYCW5bUh1Czkw)H`Iz7P_S ܭd솱^NbK2䲍څ(@2۬gpds0"gK$JtS/69$I[ÈN7NRuseѷt鬜Ĕm{NHu$7Yq&:6N24#Z SoSk汧8\,2:;#>w^lb%E>91J'GfQeiF~(wմ(!DdžF:tsvR_,[t6ZO`1{eE2"{_cgFA1n,}%ɣk&G~l[lJ Een:7PfW\˞B%#s#Aaq=59yRy^vB llͬ*"Gi>yS+zsC6ǃo2OZHll\[.DOs v2/WcCU-[ɋ+yɏ4iCǍ:;l&n\^np͞%mrM3Z.b@L/ǛVr)üe1_3F~%UOYGɅK1=ˍUi[%s/R+wHP!u-mP$Vb{PgeG.7s3⪊"ͱuoX'>;qbsCwR<%FS\;f$R}盕Y\ 탭wK?>Ze$O1;_UҪna()ǔ_Jvi,I 6DED\LW[4lh}ҢG]P*g,˧9_ᡖ7J1rUd+>c֔N|>lDU@,SSnco|Go$\)#ؒkِ[`Nd{ciRLٗ* {pڰEѝ[%._wjSk8|22 @EZ pKWZ;vĕcw_oqʟ?rs469s/D|duБE ['p6L"Xg3٣|_ɘ=5c’xRlEo=6߹]Gbu7vMQ"fMpiDb?b~PqOeglHS%6ӛmc{QRktKRSE R]VnԂ$-eM>;֬!D!, p{rN+fDSuSFWΤhz&"gtXd]V*{~LbmeLuvDTo~,r{s\ueƱq>o^-[@x[Ԣfˢi8w:Dc?ס9frz/ mpբcV_2(21$fMCgO^D55ח&*2;Qk7" 4Bl#Ýf;oIle .j>)o1/_xO*r+āvה>ѯq"q~T¬Յƛw8}X"ĻJn%m]oi{V)(DrIBVXߢ>)_Iy~ɹb.< []He_Z ^w&u۵ "eeƏ_ [&B&iM[mI[Kg DGҶKX-(V#"Hĸ:BH dӵ6-vN~0ҪǏ4w %~NS}3 r^̉ƚz c#qkSܱ袒%C(agS JUEd&Ejۻlk%xyW$LNNHXavcOh3[fN]F俾khQnil[S!ɒIOyU_7l-Ap٬ 1[r4> O&E5᷉zĥyedq@07V%~e͌WH糕*Я,R1dS,Hz0B̭$+wuHs:&{l䢭3^SKcN_ F .''bifhQ E|u}z s>U;Rq{:+E1VoӢ^mUoxր_~LQuSfG_YA\[mi*͋E IDATz45A!2!G6Nw3kٴb8.˼Sk&o_rNKwz2Bܸ0]ƪOQun /H}\khQFe|"(*,m:>9^___!q"iܔFG)jC+iq8٬XǑxCeV_i~gK{ݯW&IE!)Ej"țͳ\Ձ VC JWeoNkvkEPrE <:Mb"$h6`ؓG㹽skä81J<>ŷ Ri:~x]/qP Mx;`bd/j.1͹a5˦-rJw|UUMI[rbz{f?A/}c.|Kw 5;sO[^Qi>H2MT8QTviZZy_ Nm.ێ' <u"\֨KurӌխշG}BZ+u[ߙY3bʉ _8ŢUHey;ys7Olo2]V~k̩gFвx@nokm^{󹲖 T"wOzD}W"[8',Y߬:?5q OtimD(}~P}k]WU Rוwfp= ϯ`59U3 $" W6;]?%Yy-|[S."NꑩXS~HcExul2v#^Ģ2 z<3,}~zQD"Bui${|K'ʞ}Yn;\YVj3ڷDhvҌ)K~G&0|\Yqq^{2gdBܽ5ZQޢd<^YZ܅J5ĩߜNR IJPLG〝0>< v{4ny|Lsg&e_H 6b2]dXɾ\ml;l8Ubfx(gkENq-˟=+-=Ac"h^ptۮSD,i;>؛({UzϠQP/#YvEb_!vxW1^AEnwpJ@g @0QafyEUQjU+O֔v0wC?t֪GHG7#fqu h\(0ma3)ɐF]{gae;0!+55$zj0eBby.DVk5'ғugڬ$ɿ>x$ S2(< }8KDfWeOόwFEmeK5g,}Xx+S_6%u {.ղ)wܢ^|=5`Г o GZ#&/niҢp=8|kq%BeN_',ݘ1_w/qt~|C "k "筈#M:Ng>);M(P/t[ܿ;O;ۍ; Uf2]>$7*˥6? ̒\Ӄýfjv]}3H:}6”?͟kbK[a8AUoaAH?a@Hؚ߮ʗFH~ppҶ9Ǿ/cy܊4ɉs97"3=^O[xD.E l&{9^hvķ+"VECHz0W)׏TN4ז#AǕ;uxzM⡽e# m5^u>_U8#gj5cQjFBfqx֧~V!y9 >*ˬi띦0BYbJG9ɤtYΗ{Io)aPG3bqd[v|tڡDz֌>@UɑT^#OkC* JlniY|Wṱ\Ax, ᱬV0  1HH1@ ;=<> @ur!;@ @ 6D @[I'Mw?5 @ YJp1rUd+tp @l !W$rcQu @ !N ĢdX~宼޶uciNǤ9uO VP @(zkW(1!`F?l,P@ X!Y__~I7MsyVf aj@ C'bnmw(~J~ɋ۳"C' @@afyEUQJ5&d@ ##bn%_~`@ J @ [C@ x[8&@GFG @ Dǩ6 @<2V>2P0@ 'V>N  @rS7 @ B̭&Zbk˵R=waZɇ@ B+;~UcV.V s1N[샙zmyB[}Fe,1bI,NwMZi~XbP~ioꞜʄQbsPQuZcqejUF-H"+ݢ_acԌ KR!,@ F J|Ez?IΪȭH/r,V>Aкv͑ʴ3Ĉ2=KT̮Bi0ljABce"''fi=TsyT68o)Iql[iӵ6->N)ӯ jRY/OvT !O J2,rW^Yqo[:ͱ4cҎܺ'^6Z\4KNO͌!2c9rҀDb%T}"ew ytO]$}egyWsgoI6)Uʬzg-u۴(慯=Wk/F۱v[%68oH#S @ lؼGe de-J@69XA\/6Y3)Dh^7;dȬ𻮶 '_Fa93cE̜(.g'#4NmyMٛ-F;A]ܸzP #AT6\@V"2/6i"=XaIʼn"m]΋]h>Qm(2;v4O}ʕky t{݌ շGi  @?s+m#SESK^ܞ-iMQhppsҦ&$Η\Is/9R f::I ývՄGc^`%E .,Ws>2v*NJ]Y2!Nl7@ B@afyEUQjqaP֔EdhЄSтWM!e}n#Gt(κ}05a֞+(L3ecb pו}IpWQHlKhJ؉$3՛dTzLc @Uf1̍mf{@ L-# @ @l[F @  @Y @ 2(+ @ 6r3@ e!VVl3Kkom#hD[@  ا?}I},kJl^A bk@ 5_x[wH._1wWt׎aqr5"b @ `"~͢Pxi9bRW|"NȺZO"6=@ d9vI4D̠1-4<]FM5'߭ xl-#+LWȷ\2g-Im|M<@Z {FOK4)5udbjGN\Yi4z&ܨ0GܖY}켓NJ.L{yvu`pP) "澼pǫZgԜI I  +J8uZ&$ejivG=C) B`v,]75y{hm:bS'U*?|{ya|U$Fw [=(x|wnSfNjbl˖wnh;"ޒ |& E'@=vf̔R7,%hD$Gsqn̴rLIu߼=E@!\K7ڐA_&s:>k(aYZFP"m]SP^Vc |#_٩[`s~SoRT*_cfcuK+|\)"_.]{],*>-8gzi_++ nC]SW~qyʶ_X֕ܖs׺xٻ&^®=ܚ{USb{q}]ۨhgTVs A}ĉ6.P̳/w}R53H5|$fssO0|Pa㘯6fUd:xۊsrEA*"SM; C_%]${aJ Dd@C Jɲ3 w9),,Q~cN"hs i^QE _ʷg^t8{H$|~fkw28uc&v~cnc/O.k?XbvVr늕ۤvcO t-9|T *=sbE$ݶY\W+E(|{~{M%xJW<iύ.?譂B̂筙qSVיENoPHH$ "Qη(P=ym{E]g[ ƥ'u7z>\֑X_=kѾT'|1X/UuX.JE9Ym36КcCYh!&DԒ׳ލ b_L]^Gu\èށ䉙sD79v{Xrą^$\6WX\8mݞyOaY~ _~-[0\Ʊ7-m">#,xAl3:;~}ԨΪθ*-STUdUz GKNC?s5-e,ڌ@!'be%%+,d@GcGĤD4A9kAX\WG=Gͷ^KU")qmEm^7:_v / .45XsWuŠ: IDATxa bˆe"G0snم\^xn ggegԂ`Ɏ2ꮌ"):/|)/E)B{!E=Q:MSWN0|ż}{h;uj,Z+ﱍ.>KU?+-]xX7mvjkpM*z :5eX4RZ :ht b+Z]|)]8UwgիCe2T &{ 1ޤf1f,!mXbvv'IHABmc5'K[ogFu֍eM-5"\>v"Ux+ՒbZJ#@s㽣79 H]pi;3Q_tn}BpR.WӤsڪ9NF|Pmݳ[fH?Xk&2yJ R"gbt_Qaqj~`]oc% *&#NGOT[Qi l'9/νbv8nu@V˦yE?*r_-*RX'ZC@9Bڼdw!lE^( '8 )+)Q UmH-=q9•39v /[ȡ6\V8!8bo,K/O5ePgy{j^Zܓk/.5om'mdË侢}lhZQW6'UqڼDUĚ^p#dr̽q»1_1BѪ GR+ ھpQ3iѬ v yRq.vg{|5t9n!k%m>S)Ue9OERmP[-9W$w ݧt < Ze[/6h2Ŭ̺Pt#"_YIɢExGGM$Iߟ2s:nl#C&.-*bOqMAhal 72=2]BbBhpEgkxݦ_fW$Dee{krgxG}2Ċ\f~so%.\[xNxqc2+^#VnENAXD2 4y^αsStxkndlZf_ĊIK}_$W޷\j:P(:[) ʷKu֏^TL_-=NS_"TOuCWB<:!)g4(>)|Ib=W[& Q9TY\li[{+R(%75z>P_yD~W)Qؕ:DIՑ>Á@ J{p-5^o:d_\{x7ه~X9~Ja>Zj'Q}gfLݑeMwZ%GBl#V @YUbǻm7;9t˒>(UZmUݯ4yFϵQk@_鹋AqZ;/t6=¹:w_i9޺mCk#І @Zo;}gf!!C.یRfPADGpZ/aמWUnMb ?r[&]gVn1IJ446Xׯg&O(3F>=qxw^- $}}GBO&`YG" oc6,{D۲4awKEzqSEJ 5v@dO;&{/("713?>:hDkI2)oQE{'[fH, KLOWYRf,S4iƋTVGoqZzgmpU@2Lh:%z ӋrjQXI8 [;imeآmJcvU!nm+kkL)qZ @@D>g˫ˆ%$N ݬA!X)ݶya6>6sŽTX̲[ IHlW9g% 9m;(YÎ6n //Kޯ``<]=хQe27_~tA!j3ӡ?+͑`ˉ @delk㜟HSm8o .A $f_IbJB \Xޝ{tVBD$>\7$rB%#P=ݮHijd:ۚïmON <9@Ģ!ecT1bFRQN[Yt8o׃U:t+Kd[ߩ*OjZ2KZQEV%i&@L@DA1Ķӻ`N ~gY,$egiC0Ԟ8vƘ @{?),` Ƴ6x/4ik1۟@i䕞'w{c[Ș3e|Rcuy\18hua#F٧C;ž1`w7C 5_n0*I 2$@|MaL6"(Ƒ ՟nTs[Gb} eѾ9$w8N AFJx 9. 8Y̼"PpZ+ w=tTUOV Z1۫Ee?v\~z39.D}=w?Uoe#Ѯzn\25>얻kD_wsa‘WFk.)7L4,oB^q3q||ż}{h;uj, Tp G|=#ƛ<?v6ac4"ъb/.CFNߨ8mk;w٤,dm8Sm5mƳi^{@u-Q]bL+Ԟs=:DLJ|bZ7hc:W+1o/5I<؄%Eyݠ]PѢ C ]3#GM D k y "l`KQtet징x[?"_x^?l9鵯*N&@@T:X5'& |4dUZ'K[ཧiA " Hy*9ZxR HdR' qF089=SmMFa:B R3gbt We$.q´YIM͙ /ԵYߥ%߽\·\yLZPCB|_6#ҏ,98|uRZ^ҍZ,Ɂ%7N xtVǐe\;[D:??K\D UF&T \qzAJjgk+W29~"×J%]l-M"YHF!d/"aI+"I XxDt_tA Dk^YIGAF0!k c_^P^+Tsz#NwnW)Iˡj)&~ zڤ$}Gw"@L>[&CccL}sXg\s1>(Zx=Y-:\Yt7#6?5Ap[;侼:ܷ /bHgX%PTd߿Xu[Tj422mG ٩saKH/7FYJ_t\F(/λ%Dz`)NvUJe%7ƳK x ՟6$YcAi7B4n70=_cTQI!k1uF`Fp~E0JMADtx"bb65e^͍;Ο玵Ω4u}T_X.<೚d~nXQi*:涋!#++ҲʂM`X]o0Ptjw#KbjNjoٳZ_V{v΍ \BJ>Mh>qc2+^׆#V{|:1Nig}"wg˽ x0_XZBKBa,} "pM;78?-գr*ww7'CAe6'1V2#NVDF  ve65hA-ǐe$~|NwF9; OKPeďǣQ+|ʟ6oo֠Qd 9Œ$P^޵6Oڪ r0uhLj.8"|VgQbӶrm7}^FxqJKjįT=>LO|Rc3|?A2xĹ8tvFܖ+5qE8Ge-MpZcNr)} =f ZGiީ[Dy5\44YC޲R(%75z}C%%.?fݕ&'%w|G[Y QÙχDgJ*b_GMcW0ud!3% ]m Dꄤ$.8%*%I"v6,Cq fYZQ ]WݧځˣM|p5h}ݰ>($ YZ'OմAƳ f n E%*J)L sw ҹ̠n(+(-g%xf$6C.91igd'EeWReqmp}cw0Zo@ p@YyB@ xXY,}/H@ X  ٧?}I},kJxGe!hDL~ @~wMwin;sWuKw' < IDATP^A "6 B @&, ߭8M=o"QLኝO/Sl-Q @l"n%'K[ufQgH?F"Z9@ @@BJ&CccL}eg; RmZNt/ق43  *Bw+C{ya|YYy Wt7W,tN6i穓*uu_ƻgZ +J8uZ&$.SK;:JI@_" 8Fߢ&orsb]'˽M=]Ɏm{KW CMt~{qOJ2욈vC!})[ {aV^*Va#7p&㠔MudbjGN\cN:&~ Enmڰ<{pvf̔RRE߇F|z쓾mkN&*J^®=ܚ;)]SW~qyʶn//!"Hܖs׺xٻirvW]D1soXC% -(ؐt f7B\ߢٺqWMل /TZIYim'H-ڦ m";k8'&+Fl Bȶ873mEbYXbR|B蝑u&ZBz Ӌr#^̪XI8 V(b[v&2'*MM;*vF[܃E+ iiŌIN:w'?~IOf 2yoOXaf6A ˤj\%HaxTxmB9aG|jgf-$k+zqSE Z>ר)bͯ Bhl7 e۷˕+I.2zľˆ%$NCKC?AbJZэ.JQ嫧mӆ! A!%@ CyWbyqaGR 4w[b WuW8l(cvU!nm+kkL)7f "(,K8~矓2+r|AlX6cθl;D%DH.J7uYMy{ڢlu5_۞ ^QByy"clױvS\y2!^f9he*8Նiɣ]<#ƛ<?&^iGd鹙{ -ufQfZs{-Oo-PFKjWoK{|ǺF|/R-9<˟>[fW?m;UB.W^<K: +Tsz#+9TLFY돞ũ&0O?5\\yL Gb Q븒yq20d<^̉ > #ki<O$OԷX"}@ 5epVΓ"8|~J _wf㺉ߣ$1b.xʱrzUSoŷ}y}xRQhvH" ('̈7-Z6W(>t-w,!qn5hhR;[8kjg>+xUc`ډ8bhLEy?9vnjq~ 2e-g/v:P;5{ĐJ|yjkvYoēV]P}$}[\ s-P=_o:Sbűud Ԭ~GՎ dc e l86~%%)[Nq)n16#Js:&Q1546욝[Z <*>O_# K4 - e~ 2/\E^NfMM2eGp3s"\n8H,dEd0к*9NX! z ܫy\{6`+-(o;ޑ]!\C(8'&+FlSGc7]kxJb$F~N"83FTK̴ߙs. _h3'HO,tvځ$?k"e'i?MۗekuI;[Oy+L/$l@ @HrY:4N:<),,QJ,^|p Zc\܋m|q\ߦP؏AV}o>>6J'~vYӣ}Xse'|1Y_'ukWDU[Gb}E}sH M#q\_J3cʹMp~yrpaU H,qP/ؘ_K^z$):6G"[,vċ=]X/͡w2b"O?ƕ~K1!v @ HZSrKv to;vLLኝO IU-%Ts;/Zj˕WO?78øƪ:7Uo_8֩yHF!TnvדxvQ)"R|)⚮=s\?nGHW C[Fk'fxٙ/\yg\I{ߟSRvYzsXg\s1Dn9\SFx8sX뜊MS׷NUo>~ɢOx(:|& Sl}q|Q&=ΩΫli@@A J( ET |#hDHrgxG}2Ċ\f~so%YD{3ەY0<0DR/8ExR}5]wIMSV<[ڡ29>PdJRR1bl `x0_XZBKBF} "$pM;78?йҟcŮV$Ub<@  beŖ-wW% ]m Dꄤ_)J }^}9:PI?(2+us\@ҶV>W۱{]wU]r譽|>sD5i|I_z_GMcW%UGn0f ZGiy0; @.K^}1?w"+PįȸcxjgD񏿱-*_bP/@v {r5m5 w9Pxb jG( U  [hCg?8e+°]=_:l@ -PVVyx`Bۯ!$l=!0(g|V%r+־i+€㲎uOlADb[JLheqwf WL]n3'HO,tvD;y]0#"h2|cl`&Qnn&ilֳ/BLi蟵/F=ƑχqsKsڭeilhD@z؂Dڸt f7tߢ^K`"ĦE!7Pbsb RR\__돦ư#TJJ2Je`ksxյج \WuąX[;hTeUdJYrxц!76τaewt`IqLk 6^zgʠ?6~ Y–lӠ0 eh;I_4 /.I#~6S;O ia `눺@(Pn}z<<5j3JT.kEY]N"L: sqtŗK[fy%Nfz/1ey8<$d+"X򘈍b+),Mx wt;8y@Ofk6lf-/ ҆Ū` ztZ`@&pb\9+{2L18|uRZ^ҍq.F蒛 '޽OF`4O!"O<KeF3Ƚ 5ugkVw7Ԭm'({gq&j %=.@9`K3MyWA]}h3Y/UmJ}.WؼgIX..il.]+/B:LQjz[NG}tᅮo~[ْ>asC;A1Pl>UU½.[w6,$XU,:|5*hQkV - dʷB$Q4zѵWtfCY j sE4Ug1)-^!LQb0 j`Hx@fwwO@fZ{4(mZ,`#*L&으>]+SA_nfB"*_>5JzRQ&j^v[:Di`u)+(f<&@SޝmPV"֙[yOz|JWLMF"+)l߮}JƟT\_:e" x7j>>b.ɴIAEPCf6?e۔u)iRb̭dr**f"골MtHhSp%Noi6(x"܌-Ep #mSgOyJ1\Bӌ{= } *3cN^>/!sMMv|%,hSwNMl@rBΥ.p`W,MfgC;M#F@ZMM{G^gl@?LĔ+ܺP?$( -`y[baEРH^ *^^cMCxǵSL\Yf4a" L4?vÝ@[bT*1Y\**^HHyVAc‡g|V<@-_L tM%3 Ha&xWJKWtMG#X+v7\Q衤^I%SĠ>3N%ovf<XJ`5Pt\ #C59"UL|hʣMy{6ҺQ׀|Á{cޣ_5T|voP?R>? 5-gS$LfT1"p? |J/}5BD H 6!o5Z] A"%6_Q|JϤbs| )D>eM!" [I~x~֗] ]>Xm~pRKwR&Bw, "D@-G %AD@DXBtA6 " "p @DE " " ގSz𘇫C H˽@ȥ͗4 @D@uF \m |ЃZ6V%ާoypD_HO2jD@D'&f qdl\hrݕRn)J eFD@D!JGvˮ?X1KU%(5=ڪw~(5M-&5N3$[ʳxϦ<9 ?d f4 gwŒ&nO 5 FibGICCن cH<<&$ED@NՋ#2lCrTT #fjTpj˂カ6_n6Έw>U_a¹Μn?K&ඹ?9^'4}Q8oC2qJ҉ӹ3_.(@D@*̭d vot\lPX}6>ͻt@c}: m}XnԀܧO|p)#i'ي~pu`e?ʾmbH݅_>b.?wKb8M)IJ%ȢX I7||Щ$y80L_MYHZ >.,F>(SC"" W:s+\PD O=V)"Ќ{W `rnzfә+=_oz$zǵSL\Y89 " h7xt[x1i/fIG}2YGv:pd;7p;{scr)VJ} D@D`%upWwtS122Cs~̘-^NrGS{ϣ!d١%6y1{W-SɣXio쎮XĔ CwþWS3MQ%g saNVRd3aI" "DK ](y~g%|J/JW E4FD@@lBj૥D!" " r+y!D@Dx8E" "!Ri\?"ID@D@3ELP" "  |;uQ~M ʣ~֬ۚ2gFjƱ=JPbD!5ԩ{mvκY-,U?3a\^VN4`Cw:;i[{';?xN֮4ێxF'S_ w_=*7_W͏'!8邢"d&Ξ}ծ[;F~QDϥnyyԤhD~w(^"PY)tBk#p|*{}numEj??;kZ(tlk/V#R.*>߈"'WKt5|/]Wl k\ȏ'w\❦t `CN\?l[:o~6vխ~CpF6.3Fd5=3Oӛ-|uty >aچ+f+j ?>V ?{-&=q8峷4NpT}!H9]q8{FFm$A7kj4i ̸81/ =mF;& BɑmzgN1svfgݽVOr*<`Dg`7"Z{);BS>m|Dep!xP5% y'h]]6>1;& ' /yPU;3yEhB784ɓͭ}a+/ܰp@x麔p“3;swZ:CJ5wo59Mf'cQ5 qdQ:pm3d2} ɒ/UWI'GG&JW~t[n06j&Y%YQ0"` 7;0JN2J7Ŭ @1kg?do@ϰ=4KI ]S+/Ű8gf-wg6|uM LoVɼ &PvNG}ˍ,ؠI*`yD R*;t ߲kB*`%w$hhRUf6w:30kga칬ʥb]Uc|`2_0t :)lݝ_}gl!3'*SiMFU.`̼]L![E} mzdR?S^` *qe@Ib´=znMY,bs( ..I t ;bXD=.[ 0|G$% AaJ;ĭJ4H꫙d-݌gc/vvvK2&IDATX⩾|ey}k;Xzd͎]YoƱr37?>sҨi\W핹/|^k~zy&pJY"Ďfrt+QTd4`هz~x. {JV';y'CysEhv)i/kшm+T<ښso AgVY/KHR MW]Y)M=}#UY)RuqG7piK|}/~<9v韪WD$jP%IǞKclinN /Mv*,-)HzF=o94]Cf\&~"@J*6̯;%ZCDS׶يtE ;ZE`qвDg[]j۬ (45[i6tCpǔzĜ 6'1J13cuyEubFԋQT4.ؑdngf 1w9MI(ʎG63z'57!7Kv|L"O^ֆvgI 30{ Zű0*K6.ЌMߜ.17) *dpoϴLSc"z[8 S.axYHҊ22<Õ9"͑p1pޕspZ)UEn/́_7D0h8|/ #@ԝ]VU#lΞ~vA=ph%mt7O}JOtѺ>fbs K`Q^4)ttBF:%8p~ع a~۰qb>t;"81"ahs~'1;=,!M'4P3f2 J=WfLȥiK,I\ #ųM?_èR3m0W $hfipn~eFAyO7ڀ<7.M C'p#/7!EVD$ES%I 3~b╆[}]u]̩zHl RP! Al.F.0 peH~3l9*Рi/zé-:t(`'-*J1)ŒJrU/@];??5ؾI O0e0(%7UVisi_8 !R~Jyosyd|/RˤjQ}sSiVR(C$QJʍ,!Uxr<ԧÂ1>r oXd{.~PLXMKaΩi{.D/8'tZ#8>>̌d!EEDo/o7$]LYF1%ez4fx ꦤ# HI7-;,E0:]P&>k@~; #[kSlufA_UgF?jpY'qI^)N͇|@:rLr̥OLG TYmbYpG eK i@ O2 Ҷ30;e:_#YH™(e]4zM q _ݓ3@)NF΁<]A)S^<ġr^5e1tlxflp3y59q`r\&^'NKw#--Pm 2MF* p`7,IFUqo}\~cSk\ngw;=dv8./H„>iw%VԎ{ʋ,T0/;qMð H=A]`8kR9C:NtS (h Տ]iy"8릒rmWQAYgnL2fD<?F΁4]{us>$\RŒI !P1eߠ֜>%b`A.~4[d6W$_:?^dt.v.9AelqT@DD!v3!4[%ssB&ֵ,)% ,L'%(. S3Ľ6AF޶XCuFt&OHZLa[!q CsBi̝\WƘ$uOcs}Cza7afg3'9S4#j.HܶoGᐦ) y+;z[">`h6mXs>~)33:t˳p #dv^BB-N5[[#xc3SiZcvKS?Qe`ݛ&3iRe"Y6݃cZ+VI1h@INMrvy0 txaN.Nvj\~N('qxaxW84|&u]Engș4ӨLE^|ZwNM[}voJU\ϊN (Ko]TP<"p4l@4\4w%Ǟ5frkܪ-'ʈ E tUߨRoV;;c[yqh"ΈE -|uFI8%W_ *~r+WC!8ySw\ugο¿npOU9&kX")MUj3c zऔnY8Եr 7^+aܘ]OT D-߇P" " ;E[GD@Dx"x jGD@Dx ah%$#" " 1$ݠIENDB`alot-0.3.5/extra/themes/screenshots/solarized_dark.search.png000066400000000000000000005735611217172710600244220ustar00rootroot00000000000000PNG  IHDRr!kGsBITO IDATx^}|j/K彷o;qlCJ m)P~͟B2%섐8Ӊc;{ʖeY^ O$@޽w= x/^ x2eˀc+S^ x2eˀ/VhvRx2eˀ/^ xѼx} x2eˀ/^f0H(r&,-[lS\F| PaYAa>J0r4^hZТPj,E'Lã:o0[6f&9| aTEBz"AMiNCZ$A0#p{Stʃ`D7˜d4usSc2)AqU8OXj| } sK;*㷰*ώitMpz@@p`E0UP07/_8zOu4OT6KζtVY9PJ-0vp9tyݣv %0MM2!/qU^GqB'toF牂C txgn3Xq&S|Srr1SR7uɿՈ;6f,r0Bd4 C, Z14 _Q >  R8 *-Ȝ7+? :A @a) k )[aԃJ =+XPuF)ii~)kּ CԌ)LԄ *ZɒZ7 ]vФB0hIQuʢ9rS>K(cua2 cJ)ٔú&n[}vaV*g1FG7w&@ĘY">ˬ5BnSY)$,Y*jo` 4[0Vpt_L~r,bowpTԠ܅鋨3@'ESqHDG+Q*V߼ě~<#hjb%z}>໰0PWyB2XA)+3;'M(ܘ 6(O7Ib .L ;/(ǖ{ a9".#(G_0X韲0!9jm÷^4ܧ]Fe=p+cEe\x.2,1pI~}v]gAv0]OPZ?6HUU䖅xzgݐAyYmݱ1%Y Ed"2A1 WAFW7 7f^Ϊ܏0! G![!30.,\ Gp֖FO' f@AvVmhS˞JY8Xi:M1ۭNjNx;w1` W˷GCLj,/)Zp3ĨM,O`AHMi5<`EO_S1N@Dva`PKH$OEO4e~WqX ظjJT wD< {8M˔E}s6}^wibQؼ|+1v~o`Fz)"I]biwר3 $măuߧ^DR%3c#JQz]g?6R1vLZ`U+mlT 폗fGh\$TA"rlnĘJVg=Whw? ח gGE_yYo_}#=23muWtIL YRZޢqQU|pB@mV9]bV9y&W^}O|R}Aa~<I2RfqCt\#*6&А$#  mcX@(h?b ̲aN\B$W)`~!> ̨NT-3 ,&G7id҉;=R@Qv)HK dۆa? Ѯz,zO 0顯ۡsE ALz)4gSH>mGg~l"F6c$BYq![87UEwL8kr:͚5/Zٲm "6XlK ne^=}Cv YATY!4P}Eh_꒻%J[?OvPx~Q*?~=L_ɔy}/0":M5^L_Ķ4훴4p3̽>zںLR{S4l۱۱ЖE iJ "KګGaGeYlxC<06hSYYBW*:eHRT]kls 4npnJT$Su_l6hW}nPk E37/ OAwvw7i=Qf`7;_N(O K3-rpiHcm KL9a> ?W7v 鶙4{Coypݱc{]W/zS 6?>$fpd2& ?hG<_y i>1ˊ b%-0?+(Ѥ:Qѹ52(w {4G]Ij)J-MGBmɫx?F`[F)#ueG688W| GkvRIdUB Bcݵ<ι3& Suk[;EH&8sckq7JC2W9 9Í.u <2JONdi_HG`ƾvb%BBB9 :] '2.&/\73Dѹ-cr*?l ~\vTˁs6WM706;{nolNuʞ%x H[]91KNmpCTd aT怜 S0:} A=ae@y9 ŎbEHJRH:!XM61H`Kf8 Yc< Nw[ 1k%$`Y57 D5Bb:(qsoVC/HBh3@2u5#fa$VZaÂhd&.a=4 Ԉ[_(8oɽ̇n&R7g6ۓnxq#Wq !WY37 HKIHpX|Ê Tqo~ow2^9BvgD)lP(J9 *P@] ֝ʪi9LM5`m<C(>cP7-32Ti(YV.;ISG[Y4o?j;5%Io(t+ڌ|\ 4na;?ƞ_R;Z9lW\#C )xU; Ǝm:xս^*--AcG'lQyQe҈!457zO0K`+1AB#i{3m(9ȋeYČ6;Tv%]u#F3>r[/UE\xe%/9lU'vlQ^z %D*FㆢVl |4P!=*YݍdKϿ8:J.S_e6t~aѡQMX2L]sihNB[Mu6U^…1c8ktn`H]Ro_3ù^ҽAhWyY,)ͼ䢒BNLjmanf$9wZT,>y^vEwxST5G(>}S+(킄uISom 4WG̼19P][qJN[sO hք͉2)mW/Jn蜂kTAh -NlVJp2\r  ɉN Z! ҧKlBlA0 z8}H-tdȂt mupƒ2چ6#K,O\_$$=BzqQhX{dvmhz`/D΅b?t>\#ű 8 ]cga7 =pYH%lgAˇ)S6}6:x_j]{R UCGf)T7rjIքYM߆4\wIFb\~w4i&6I--YHV[Ax1:)W,BO|bjPCB)`5P B7ڎ7;Lj>5(2֢/Ϗ}GϿ/+ % ]*J홏e` |5eJ$K ܵV>qEf}-u/2RV }{cR~P̈;ĊI$ΨF%,hcuhzQJOGsE*3ّա.nkh,/2bn@Wթ KE޶6ƛhtKRi]{OҎQ 0jՎ9IV*&Yl\c\:l7BK. i\3B@eZW]$ aC"̒i|F@١Yց]P@`?00-VGqH$E]/̌Hf9`*(Qa Eeޖ6'&PK`)!\ H.A.B*sk ]ڠBA#"w! ͮTc Eq"& 7{6y DC| ηWBh#E9hCXy#}6Pu .nvٕ:8dd,:t 8.ܬ4 y6$tlPT!SSUUG˜^ xlvDkmLM,wwbfsmg<0aa.oF%81 mԣBy[i93&V:KeG΍{ޯA0M1iQ||V! Xy*zR^Q=`gso0]fHpԹl[?qVe6iS}׀r?LltJ=R:GӠOc A s?9g=s22(HB/0**!ؚyktN76=׭d ~H/llR3rry`aE-+ EO\q_`mW{&0}>|ӽL6vn愚4EqYeکe ͌PN~E|z=v9DƮh !~+T(=m8 i ( .ZjUZnѮ6"Q_. #!nFdfpӔr7FeScGlo hDaf1j4VгhRt(EpJ+#$ x(YE "j}Ĩ44ܚzޚ%aЀgU(lz=7BnF(Ҭ񨠕9+KNb5uYB,00 Oe}"pI0LF{=X80σ2m!YmVm(3RȘsٟf ؾ)8u3 B{ֽulLT+oʔ!2ԡ ,xq`aLf툚F'Uo@L4!j:Mz:l} 7jSҍHɆ?FZ'&$ONT:1MDHcb _UIRB+0ŵצ,;%K if]9RxAnѹfȚ$C!v̎ij>hVllgP8fDRߠ_%cz:ۜIgNp,Ɣ?pU߾߷8,9uM\wOLa 1IS5FK[Kh <}u/)g5_SY7ĝhi`v"tuC>:HlT4H5:ws\pˡ f-GbFEM,Ԝ0Ad9k] 6JeU?)041U|afmN,M*rd Hl@eܭC0;Gcܦb2hZۜNIӨHCT11C"j#,8m;)$ d ۈ.B*+t:x0'EuZMo(C3ttAJ Ǡ)t5@UύHrصH^x+=FZβ@I)BZ(OB ?<(mfq*Y)pN#'vհ` od h6'&q?q6ǵ?}+W0Ͻ:׏5OzZdm I[bGȍ9dEr"FX%y/M]eѹDtb>7&VF$܈3P(.V߽1{W``Utz|yG\lj(IC2W왠g˩~,9'/~#d*lQRމ7ދdJ [!~QƊ]M F\f$ Wc@8#C .78%c'zL[I `R}D C\ҫ4ąa]j*?砽hhѯa`Rz h'(4IVPXHG +1K*؆;]ǥYHLlV;+ IDATe-Ơ+2Z|F/^MBo eːT~Z-06.G"k 4*tc_~gb+U)BBw8E!}GrK7r@GeD ~zk>f֦{(nånf(+6! #t+_usMM})͆dÕ=/_M ֭+OucXV{OKedǥ$'ًS0$Tw.X{ߘXiQ}(nCyyZnnmlI|˾??yW{:2ͬIώ+@ڞ h*a$,=>;Ŋ{7ǯ]w-6ITAruL[jU 7=Agݍ.9_3&깬/UOa\)Mzy{HTx/8%+a$ze&l譹pƘz CiWlKUU=81sCv5zj7  (ΦKqvA"?ժ0ֹ\Y+hs6#8J'a Y1$3&+%5uϜ M*2F,@\5L)Ĺr$rh>C}HT9GAL4mّ hAaw3! 1⩄xH5  ǏYaԝr#Vgp0$ 4ģ`MPHEˉ!hCv($Ґ .V x@WF +<Њ¹ z?4!1OC5;yCu.L_lQ&.0[gbS.@,JfjzCW)+mY,juԁC.ȏ]ZkV _q5x\}B\Q9W>3c@h;C:tĭi^3=& JR2#kMnmy9Q:r \9qfEذJ0Z-8SKε9́^y*[pGISwH' x$M44C\~={eff`zuzqvu%[˲`VA^&IX]Uv;t“,ʋwYQ}f_x[S8C. 2[ܤS0P+JoYݑYeߙP.G{8qa4~jfZ N)J_4^;xmW}nwB ~ Ѓ`]!(jHVyP ug~x0+@ut̎[ڦH]gaquPܺ*`;hܤmo阥w0zs20+(>~(vjnHXUD#hݛs>~sڽ7mr:֟2zb'PrL'3d]wm\ܳ=Lukp/^/7Āj\ѐܭLI‰+Ĺ+rW޿Ga,H NV^e€Eyf}97>|_3hن낵|)]o F}݀dԭKh#.z{203nm%2ZH lEkG7X82e%cXl2P9a|:Sɿ3mGm3{y2eˀ/^ dM̱7;/^ x2eX-mxo x2eˀ/7Xyseˀ/^ x2ɰgͲSibmOL=Kho[vsׇx/,3Q#˅CY2BKȜMnPɘxsz*ΐ( Y;fXH1~켟߽; Wf4-S!M 1n>]N }xqodcޓOz6nIӖ]ؾ^s}{`4샫+# ?1~\}Ens'""Lک?vOXMq3G<;,>=wS2'º<ݕ v9Yp]G | / Ыl5t^j=,P/ēz7Y=Vp=m;߭uKPYopKY;sݰF%7'w$U3 ~fl b̢Xyĩ;Ǜ>B~H|8 a[/©oP$5xzye6Du3BH&ʄ?Ux,ATHtߜ'KAFZ/79c.[@r'{&^O{v=.ޗv4W!'.SdVԜ{x0 o؛ʃ:|"8l}vV=H|I]v4T;4类IS0x ukPH~svأz) )=5.SG='ч=>#;kjuҩ`fP%#{7sי ƒ,{%`_TŤ|Cqs/8(|4M,<+R3mu<} ϒޛΡςѵh0j_zTG_ eMxmґY^«5@ 7T'u3q57 K{yKtZqё8MU*2|05Z$aH<E)/ ^EK$Ȋ>03 uz]>vWADTCCD(.h$`@O. Cb*/C۾ φ" &#V΁'`7xP4FXG*4jz5p!aEDzxpʨ^yivg_+\iƝYsHA6Ƅb@ρy48P z+3`s`o\e))fh_ŕi P_'䀡G4cGnY]\Qz^}=ȧg+z+ewjLj8tSg?3|a >4QХ۳Ӄ ܐ-`1x|{Ӹm>R:rU,0}ޞ|P=~,*J OPIU:yOʚ=~&to Cbfei%0()MzxH# {/,V]SVuld޸gUK%IW=xS)|N\ &eii;5=[}ǍB-/mY4B '-hp2uwTY>&8IGD KӘҹ&΀AE(P 4T$ -? 6X9IYSR ph&8J$uuT 4!, b$DLH+8lWɛa+*S 9 h-,@ wP' ![ǀY<.9g/FM$ur3mb%ѹEg`@ 0t!<5LBI(a0.\BPdo@B/l8$EH`Ł‚ [ hI[a,hGKz+Q^ވѢ=ZFBYφJZ|mXQ&=_ۆ0A?[g|ZLײ=uyKz)woo)}`;{ٚ-?S|cԿAҿ&s LzjR5_މƾ)NR⺾5Zj $8Fe0shqވ4@`SWc c/ZY?$'N\htun/*aCώߊjf"]a4&:Q\>Enr0f|Ɵum{r3uE3 .ܙ2ĵ,!c>4;(sqڐ>L(͍+NJI;4 nLVs-å$>R!N Vm ,IXc 9 Ќ w$ ;YE {ȘP9=vuUa.Hj.5X6־HauqJb\ =Ve&wlzףL`Q,8$G9@Af8@F^|wKY  > F>=6,JġMG$B vB$ ϧq\dZ@<: 4g, &>+0jׅ$ '*)Bk(tKt*9g ;ˇkBΆmf.=xPeg&iad b8YdH, pawD^*3MpEIaLro c- cYe~At9g;6)A7\aIJUk:̡R 7FQ]糊i]ve1HfR8(LV(̙X}8.Y|i,dmQ2X;0_WIp:t c̡=vͅIarq`::+XY?\My-1s2XIR` ,=h4л|f1p7sG&i;N`x5 /C3:mvCI{iE1C4 B;:EA;`{0A_ CnBj|WYlF!([%:#? i&!=+S6ݺI|dwQN ]Dfr:.cMP5P (AÄ۝R!10.>WN1\>}͈FL&\ B/zO&e9YcZtJIYn1ɯV nR+KbkjQ. ! QF3Yܨ O 4;PБyX$bYH5HWOtlSOЪ{4Ou_:i'ĚyӋ\]wu~CH˳I\vg]s_GP4ZNt7sP{_:m]ղ/ // J7.$ISQlty^m} c"LPPsadRv*YiB FF(p3s6u6*1<*'fmYr ׏Eܩ[Lw>$ Prndp @<0D1# :fBANT !2L@|:ٸv ͼz AS!SBUM?"{DDg>_ -Љ <6dBA bb N9˻@X [9I0rksHI 2JXRd #4ǁkS-G+LJ14 ^1oeߨU!>zOmֿqmfjق4F)™E?2::$7*Φ׋(\+>K/N1 30t؝Yեf&|!lqq`QV]旭&[f6fS_4lQݥ?wwyY8Kda-kEp0J^J"6!$0\'&4Gr #QF( Rω ec "l,KMqDB"bvB$!R!Ѿ~LIkvGfd݊$#GޱcgͲIذ_ ʞg.ukC߹escPVgze;ǚ﫺w~U=4mU|vܢU,;{E #.,G i{e1vZ@ybt{e?-1P; 20-[|- Axu b&0tߦv.q٬ӗSVvB8W-b? >nآs /6\ Z9H IDATN' A904`N0U/$ݩpZ[ヘe:SS(>׭YLg ʢ?`Ѷ3X'w`/-{gK8k.V[b]ŏW7h~7tdßm/+{໅"<ډ$97FGZ@:G,޷>8^{"! W$|M ?_\EK53ˏjߞT'/\`l>g߽8 N_N;;6H"# >X~9u˲p^deUIs^d_%IȺkaNc 1WVd]tc@uSM|E͘W02] ʖƳ>X`„y?XnheU^R90g [% օgJ+#mNwkzn<)=ˀ P[H}e>Ç1q ^3O<}5cze癟#[p[YFq vO}؛ag0"_fYKM` pg;OKV>P?原d)z"S 򛣭Uc "044Jr8=0PKVM͉A=Ϭ}:4וALNA5zφ@Oڋb-tE>Q3.5}3n&3kM0?X_]#\#pɪ4V۫ [ I V^i=o pV^5eKSzF5[Y|uhM;|/ -7{To_$i:%_eOϾd?OuMؒ_;cCnؼLq_tN(=,9;EGge8gKb&4~  L垺N 80\" \*J J G=Np8 Y^,(gދ[(heFv$znTx#Ί>03 uz ~̓`G j\ jaב^ PG 2]ٙ)xrƬ73 }ҽIFZ:I+j00va !uG5ŌsŤFX{U٥R2ߩ_[#Mn!ny_t#?lu{+6icy??̈́o3QrO',>AߜOG48'6oÓPL|dNWCKKޔ'7dF X/4>1egfύWj_ H]G3oujîU޿{-cRTPGCj#*/fӚZRYv?"ZU%og#t8"Y$`2yf뱏Pz/ypYGč |ykw0a~;p{A__3z:d"APzwM)Aڐ& ^8<6U%ѩ+,72։ݥK|&1Tt`@[3qZVy*"N 'j^zoB6$k x V 7Ϣq>a> *bYѦQ1 #|Tp/VR%ѪԹێIs7ˎ&5':\׳楒ƫCgsL9q%`\hoE}-Fc~S(t]B},8p2uwTY>xOµqDа?NeN\Ebg"ZƤ0\"FT<Db}"$ZX#!Su4/jDRȏ׮qPnj&s!C l|S)b$p4,,FdWx*m(GDog K`NL~U-a٣o*tlx!hÊ26 :3עkw|s0e_^ѣz˿\ # xFH#{L0"FޱQ =WԹ().;v+ez9oIr?J;߻uiY޾LJ!+w=7 Η^iCN"cxrvW&޳lS'dJ^f 6,:o>"O׽.#sLVp0F9yBAu@{dΨU 1@zGPxEO'Fݼ澥O(`wU=@.^uT`rjߗ_oݬP#YMDž+kE^A/Fe0svIol{?9OEJ51sMmrgޟx!'~]`l$zUgT .HXbV0XʜĎv`ہR[G00H}@[;i]-A^#h> 4uxW7+s+t\@S=_SN c6s^#c@ JշO3]2OX q:&0<|*7q^l/ӡQ*TQ\e>|cܼiڌv AI=¹-y OyWLD| S)Mi$0f5;,s`#$-$!֖;'nM*w\պ^)X4TjA%f?vwG"FN$P D'8q8 p9l ]cfl`z/H{@@Bš+o'K$a7FQ'i݆|уpt:m6?FD9y qˢ'vW)qJ.^RQXO\HJקXW(/@,n097^2ܴ7i! Ơ:>"Z Ox,(/r4P6_XjlE )YEJ$\G08,{42g 7()/(rwE/7e|kߜTYoBqj vn'7*/-)sIf%"1aU\U?F]m:QFg+Dz3B ∜R}ξv3WPcp-b,d)!t/.ЮIzqA;n𠔻֙ n2<P+Ś<>#&F'20vQӨ@\Ϣ>j8qරQ/ķ[1Fx}5zWE'!ҁGHj97Bq,("5Bvً7So/b(fcr_R^;bZYhPa'(~RjMpuz5xgcYؘ_QvC"i '*_'sV,.ϑq1T=hlRm*S3AibՁ_03/M; ̜`}%}c͠$^qu#(i=SFmq TʩptRMya;`Vp9$ă c5ZAn#iQՅ+erbcVBcV=窠(~-J际|`i*x~/+c[M-_9LѣHHIckC VwKO^!|=8lYp(rZEN*)B#k`]].E-]LkGKWR_WN)wp›}o0 >Y̴-g:Ove$moxgn{0k|ñNPD+Y}v ߝCɾ/ک;@? Y 7')b)e2"K;Y94e:\^QÐJ6]'0\,T/t~[' hnv2}޷zᖤA=V;]ezg.Fx0ݦj(dAFmbzީSͅC(B\sY3㷎q_TJ!)k_UJhT2A9P_;|L[T{VCB~zA`Ae}YvWjm"Hp8dK@/=,훹rIN78`J,=7LIJo\=l1"MgQvu}D])`]"Xuܶ=‹K_ӷz34#FZ9J*`|8ww[A||_w:O¢?w2*N^;|N p7Z* cwDz6DEQPq6t:=f0$9U8dxy ?pw5Aj yꡱ1܎'ʹ~qݼ߻E!? ^(X+_EfnmNZf8d;]eayԠYOGdފo{vvJlR 1Two+$upE!31Yd 1Səw-^!˴eYۯ7S(t1 ]jس|hkgg880g'O=pekzL=҄?| ~MC fweLڊ0㵣G \nb;YhO5'qT,'$D0_ذ4ǝ7|zٿԂB8fgɌRr?%$ *Ò6oJU9q խNnX`$8sy& *EF#zk>@VEeQ @>nw @nsDĀx^TmIG'Kو.g* tԅ|`mus dJ bc/<37}H̍XvbGYA|]~oJfS1/cEao-}8Ό>+L{[G_ S{Xn?~U:1Hʣ2UX6?ҋ±C{xq)˚-f9?q E!\,cA(iP^)5P L]4=U` D"7SK5l^+b$ov ϜxbװL"ebQݡ'tQtfՒ8q4vms=9f*>yEE7l1uM\w58DIW=8p޽[~=T1_(?(egK3"K6qJngܵ9sr&85#`%߾}wKNd7zeEI7oͳ=|A$ ;[C oEwJa4{51H&5am_Z }LPP7& 3~S:i>!"e)#$,#g0;O5H(S)4jQ c^.MG̛g, =:#+GlpbCX{\/bk+ Zw#zfDj.WQ -fqaluH.)'pZ\SؾP=pݫ0;bepϯi9-3\@5N)U^)Z @@L,@Q.&=i&=Z׫T0+hTc$j$ަܠ5:1Y[JArjv i.+P'$$$sTм9&?< ܐQXzm\4 <4qc4ZY1{K]Qݹ *7q^l/ӡQ*]z4a7.\-&NS yG7_ Y B8'~bE sZZ6? j[:L,F4{X)2[EnCӁ5a ~O[O թMNxH$RFX?Owƹ\$I,'װ[mбZYJC'[+Ia5c n09,`,t^֗-C*PL;s:Z##a!ځ&S>A;6„! *"c`V 8v J4QFo9vkVY㚬PdxHEE1.ϡ?N̚<%/^ LWPH(@š+o'`$L5TR\7Vq~.>3ti~Lĵtip6ڄ,{VwINGz @:8g[MT9] Ug$8,m:{Y B Uw;LN/O Cp[`iQy;/`V0z"`"ڞm̬,dW'x ╰jY@"eenvzew'Ϛl!I“8qX5fL6 ҭep&G7@P"NDX"b~NG ٵzNi:;o3fM zfSſ>+nbX"epFzRUwP<]'қ՘j95\bccSZĀY`{=!2 g}&(jiဩPuZN GV'߾Rnm n_|o!R_ r n3N^gٽ'jA̓]c֠ƻ#0++wJWf/0OЀ C-_m)h8>\ y-hN8uKX?Ce17홊 w,p& LciҩHs1s-IclDO<Ѥd'@<''yhhkaK'H9L@1/x{sC(.IB&2-)^`"( o(^'~'1uZTfzT˽-Hv囐=tQA^AABm܃-: Dgb',x0H,agOjS]naٸyۣd1]Q9FeAQ}Sx,,fivZq`3tZ ,0_`hwY0lA (L0F^2zxC>QbE_vh*7FA%ϙ"Sjzt;f Ɖ :fbLG*sIL%b¼FE0/>D,AӤ &Iq^L5!ٜ0xl/(n9{7~ %1Lˉ t3T…<\#,I4w&2˨0vBhV Y7Kj\ $ oR6)fQ+vgFE+g6:ǬNSG{UT?B>4UuWa<ˊ%hԐ.oZV,_bmY?i r" D6,o8J9-ڢm'-G^@#qc!2ٹ湼By@PImX&i p Y򖧓;3ٚKϾw^_J кޭ21Uv߄h"V߇h.hhhhhhhniܩή- MʡcFV:# ؼ&!Le|UơS{*i|9-gg܌^wJ2hP'nL޵x/Ț ΝX>ϯ qu?=Wd|M_T|/5"d:_t444 @/+^|zeEZ5sN\/ŧ ,4wF߽֚)%*C׻*pҬOܹt}f9Ʋ\A8+5.<(٫{ƩkyF 4[V ebw$' 9mu}BnT'zRXLf?{dP sC@ʍA <̊) ZԾ0!4X2|r۟dIZ13\^V }Ey1uGJ4koSk'4{\ tQW?bNe|s~q^e޷u@yGҶ7o6jm}sS?d!?l"aAx{~3^'Xn咶~^4* *{陻.Q9F1%Qwgg=uG3c&p7{{(‹X%s**[U,[T=N{GjtU$3,uaoo :0B^ݤü'=nӭ/{4fՙ\ף9ś^-vʦ~#حaݱ /-Qy4#S17t:ʌf0$9U8dxy ?pw57HMU 7иQ/:o8~`б-]ϡN&,p?WP~js%^)ꆛ02. Ūhvֲ/nH@Da<oߧ"7@c_ΎY3Fy/{yZkyJ歘?mg,ZW/$Jk Y-~k0%ih~wxVr"{va϶p'dEs5jOh1g'OkδlM t$]:qULYy^9zP@D$1v5 ~]铑[y`s0㵣G \nb;l4i8*g9!y&j/ ~eb weR ^ %%3JY+q@斈^)!Kڼ)U9N`=|ܻ/ (W:aqY"f;Xp>"';9`f :\* @GYu`:q@T_hPr3C$C -]A#HG'Kو.m tԅ|`mus dJ bc/<37}H7k%*=*4n^Ȉg%mNp??|ݔ @XԣiW$Сe꘹L]mTlkbv5|}WabD͋*k#OD̳+6w=^m襟=Р)bkz``댈[ znJ RcR2KQbigT ,$e :kj0w*(H ҃3=JDL9p|lǏ3LMHE3q22 Ҝ6:3;`̩~XA%eKU'$g|fmŰSa4Wz/51 M2M`b2mʼ?JYe<J%8%3BSڜ99G^ k¼NaC#?N k$&|B-U:^ɁdrA8T83XMimWW,"oׇ<.?}gH,MDr;I~#әUW>!"HyޕŴBpt2#+GlpgVn!,g&5O;N%t_[y8#ꕚɻ\F5T7}Dž=!i\hdsvrLaB)u܋BlQy&D Ҷpm׈j:-Taxj%J+1 W}wGA6P^&=]q8*`4 r5Eo]nӬ̭qJLi\O95J;4fz %kG4k6$P$aҼrh-uFwZXm-pk~ВIJks3Z: 7]ٞN9*RU9M|L_s^v6= XP~{vQxKQaŭ,KxfmٿrƊ=9 16|2|bnznN96ӵ ߏu# V-}!BX;DۦT吡 /#%͝:Fů_!6"'l8XTz$Ƃ[#Zu+m x:ih(pg hhhhhhhhF;)'ty[ ZBC#@#@#@#@#@#@#DV+rawt$ڇ qѐj O(}ozort2AnOYz=R4NO9Z7${ޜtuK/;'WO_/=+1:;PF"7{>|{SHܑA U`&ܶȏ0[NgA01iv?w=f^ې9_m}R u׊Y7sU-??rh\> 8j-A}B>nDp=c}jj i &OrzArw+_;g|߇rk-F>C{r//52|`ba7nР5s;[Ksv+GB$zG:2H.!T4d 1ϛ,) * 쳐 I"qo%Iʓ:e6J';/{eA?cٽOD5]׋ 6=>C'-;l.|Gfƒh)XoV|~J{CxqKzzلїݩok.aÞf>bn;jMtDeSV\W9 Ai٫3u)H^f,>>ڞHzYЯhNZcA(5r;:_ECfѡ)w@s胓o\nAf0s[mnۨF$F=2H.!C/7vOnxFƂaߍI fg-[?r͢0g7V_{Og(}e=w5TdK]/xlΐoLǐڭ[IfvhM{,& jZ?-Nb JYNHK:_ذ4ǝ7|zٿԂB8fgɌRr?%$#qJȒ6oJU9q խNnX`$8sy& *%Ceyɠ!\g9 \L s'Ak .P!՝3|<XyV;)[,IpJo &"贵A ۶X~͎ y̷"0gtՃ._ ؇ӹN6-iz MKpg)1L7-h۬L>T|TËn5(SNJ>;3<חM#E^d?DO*{"''ω踺t0sWϙ +JA:tS*q ^" w{yqZ|m_Iyvb06݂cӱ˶M6It00xR?rF-e=OtJ%1)Hh65P-g 'p20E \(KHE[p 2K d>RH%8rK~eqPlݕihS?^M3Vޛ뇻׾D^󳗤NlWu+"I\ ۦaL {i<׷'2ؿ?>r|.b%›l ʛkpzb/q2~mʊTLE JU_d[+mK۞{Ck$d'g4#dvFh]3'kG"yaƢ<[ւnqQD$[;r g?JYeY-vSE!e¶M'LD,"M;nޚg{•aɷ/d_yREͮ^ʘI^pִjIZ!\ALV-kzg#l-u 絷0BCiDW*I%I>"![ݻ*Ǐ-A|a}$IĻ$o'ęaoJۚlWW,"oׇ<.?+cH,MDr;Ib#әUWgl[eNA\CVWyQig, Yoc9 P]l7k B5U7io 2^Z7l龶j -੺f0GX:$\؝VF6Wh,/r\jq@{,be#<τDږcQMŔ* jPfC@lW q5zA[+t8v4KyG;ف ap{Q.ݦܠ5:1Y[JArjv i.+P'H}N b9&2n(,\I= Tcb.A47oZm6Fqn*:_/W osls,!xfxR28nRlzw6)OnB ӂt)ݒ`nau #؏#H¶ 'I6, TyC(R^C&΋ e:4JEkB\([)էB'@LĀk) vgfnh a4^li$oI%ILc(NIeɦlEX;%kպHxxnoF߿W%>:E$8e>p iK\^7ZYoȠ(5x&DBp D݋OY*U Lp sit&Wp;v D-[~ lˡ<,^BwZDYB,&̋KTFpxS7fRQpE!0APyݭ3^Psk;nǗ=+\;D)SuAU'"|ŀ,4Mz;G5muŚY<{An؝e=+"Y+j[T jI%Id>"7p&yoY#|KCt.SF7&_I׶~cPb.K@Tg{.n9G?XV}sy ~#jrgzSqz5oy3gwͱ3 KC}>_,)>$\Smug2|{.fskP.ΆXVryV6Z2izxX989GXU#ouL;TԏB&i 9 IDAT '*_'sV,.ϑE8&W<Ͽb&֫¾ AQ L0 f@a`f8v@^9D1 ܨ$+kn%`BoAI Ar8;~Cb@N]uQ].KrvN;!=$!G=989 BIB:iw;ս"YVG]ɎSoXSy7-+NYhq%Йo?f_l:QuGƝ3yZ_vRC}>`rP{C}6fdP M#vI/G#tX9TkwmHʙ\}evޜ.EC=x¥D0ɘfxVՆCKw5O7xO!/-໔)WmrW? n'. nUV=mb;_WVqܟddBi+oNMN0Sc۷ZJ `],bwJ䍈« O/𞳉 }fh8zbTDJAթ9?29n{gvYpbX޹ jo؜K7;vH !$Nu_spn?4w/MvUMW.\aM)m.N'@݆1pDCXaYz釿v/caW9uIL:uվwMÚuo~t-V'rOq\ <6\}) wݳfN[++d ,u_g oi |]I" aS`襫YvC>?ZͦNyz' O4>bniמV@6]on )0 6 nIs-OU sܒ`厜NP]U akڶxL`dɷ=5\LZ: wzX+=B(1hiR_I„!vU^ !\,xr딧OQڹwCiA濷,^hm>Kl>iJw3DbBjj00erUvחVS+4Z @O|gկ'27MVHç1h444444479Z9ȖsV<]H>dKIvJliV7ȺXgU} sw`wEM系Se׹^'~Ȁ/ ڡ_SH&lP?Go?hDǢu䟡? =# `aԨg-bƲ's*L{ݒR`a@! \v>4MUVJ0C]mj4W?8ɦW3`k+^Ub L3BESWX$L]辗_ٜ~Wr$AnF+'ڢMՀ77Jh8 XR+& Lpr߹&Ws9SsW7ail! D -a5lPDusH~yI {w^#*69SwDɠA3x]#ڎxuK#kC:P,r?FVٖ"y2VYա9 V>мf[Qybt33ZɎ}Mk?qMwؑs>OS3ԙ1C#%lmm"leyXx(\j%8ީC6mΆ xx?FV~iEw?Yb%lU5+9e‡qK,7\7~}.ꊅuN 7Ițw4žxJlZŔ669`>ԕ[^3/G\z&=aŢ Aiu&8PcLX88 ZorɆi3>=^ g.<>2G NR O'LamxzPdaPCh@ 2`d\F?x/tr qG-3evl6T잀! 'PHX+B-5쨾fL[,-fb勦͊Ʈ-<ib'7w` f; yLGݱ;pQѩmQClټρ]~* 5x?CNm:R'nP0OQIـ ?j3$<XMT xJɊ/%KIcQV6+jE Y1k^p1w³IR9\3u iG ~pIy^aʓ Эf'=Y1GrsO< Ea^4@Ӎ%җkhtg:(u\˔ &u$&L,;|QBEc 7 Kw&'ɋ.Nx09xu$;SiQ˼3q/n6#IcK^p,8am8=hpa4-q[;"+G(<6j#-&R3ÓcJ(0*3ƀ qpȥSBZ ~NcBI}XTf0%R  ;fd"P۫ZM#P,*}6;J炱D 8RFؿ7*O,X[Bܳey{RdCkSނ.yfra&5ݓ?N*᎜h%nq [b?҂:ggNaA#*lD{?{s;_\jMB7T G_R9Q1@; @tZ\s`Q|c )T@;j7Rg|@)uy$gNm6<,f"%琼(pH^J:̙FQ^- Ǝ6e\&co|k]ɢ?AvCN~lIg-zP6|!hɏ# E.^ aZp&$ 3@!˱1>!4hY;[XYoTЙgO[LJLd+y kH_h*o(YUA|cn㟪eqR< ܛG88grCZMX! <Ӳi}0inЖK0i< .Z8Ap$@ iA X m:Pj/> 3`@Rhm:mǒpUTd 4Y`Bd\vMչ 'pKsf#=tE s'."K,$ND"D~ KKa)|69qLI-2kpG|9wRA8MfW-pdֹ/GSUI 0^xbI:CSKQ7y8mӫtH)a SӬDgI8_ vMBaG%:Ma j+N(4ĢlQXάˆ+ҭxVL}OxƤՋ!Fw辜͕J<6o7,f@;Q{ƭm:p@@X8u ՟?W׫1q", p n CY|j ߅I-7iMLD|؉LnKa r|V>hs㴲u†qe+ BT^5-R8 5'9Z= Uu`;~(W$bzd'ph"ZL8#uL0ߗߘ|V.w6m5w_dVc _!'x z)ƀ3[kk7yw(tN$2N¡ߢ/wz_}-~ ke0pqb1֗;mf>/z50gTJdp `ǫG pth|;-z3vfZ0Lj܏NmzB<=IFXS.T$7'7l)$3ylc׳Y'2W*8Vgn GYPqqq"e):&*> [׮Z(V} / u8(Rr50ݕ>6'  &K'A<᷁>MZ>snl&4CA[@+ <n#+_iX̴{_ 8L=sP`ux!b)GI+nM77YqHe0g/)z"/z; ER=OA/Eݸb} 8_Wz;q̒󞛡~an1>(]Q}s \[_,FQ^.a%;MO[jW4G^uj[=ۮc"ՏůvBEuGV4H> ]3c}8w!A,D T3ԫzoP)Je}aIdR6͗6c*xw١sREIQ?5+\:劗Gio:o8,&u]g*ҖhQv!am-r}Jښff`̀H B(ݫޑ2ihS:GiN &lP]" YYص&ź$0"IIvp*,ƀPiP*GR .( `80>]㈀(0$Հ Vגzgk(13b, a g`N ,qNT(`hP&orenslCㅰʪaWeL*f1P@,OxG¬F>1X_9[Da|m}h:z3>!ًOAV_wwHs81h_δkȪ "bX2"XSOqV}r!ɣ* VNN EE^8QcW C>m^G.q<ӓ/0UlKxX7b>tgqm,E(n||0O&syu<:%^xPV'|NGH qPؘX p幺B|~޵`+?( ?|x`|pb%_pƍ 1ׁr0T[HWWKfý@] @#`p*uvpnq8w s\[HlF^Z >~ޚܥFY2(g37}sE0g۶1#%jݶgpfEtx"+.9 Q dL5㶺6*_(`w,۟^AdܮTl O,]ܪY{{i+ʁ_Z>A"//թ9?29ڮ< P aw9ۤY`}.s:U=gT~ӽޫ Y{~D\s䓳=)# %'@ 3m^HsSV="?'͑;^FOf& PdێCxkroLz<_M3@3@X!hHf2~ 8yX%Kk38z S nc݈.M󦃏aj/rLL1tl8I7b&B@r!מ+;K.8rfwh_ a1qNݓڰD`tu5BiiۖAÎ | &dɷ=5\LZ&ߚ:JO lqhIZΆh[Uyk?+%*$W0”EVB)L_[M% Z˛7T }@ :Qi'/7>hk)u;8}UeAV UD~boXNdG iZny%-G, Maڜ㒛ٔiO_j%. 4OiS\-k&bԊ xmRܮo)vlXf[%|cFrꃪ L+1M}P+q":\9[\i5%Q?2F["> ֖)srFr}|B g2|d>H%Rca:yk'/]'R^RՊx+J$Iʋ +sqthe0@5)ͩYQQTzր'4NccY(EԷ_+Q-!m҄d[} CY0H/(E'`ܠh]_(a(gKFoZj#aq/_޼4Ɗ{.uKv l$j %}愎@3@3pUV̂ҐvƸ!WG$/(bkb{ctBwgPɷwyDn\` E8!s.嬨䅔6Q{?DiX[BtcÚ:3`W:Sck|1Wٲ6]rIߢa ~,ICԊȐ!!>)2pA&ڻ6Gg&UFlbKߜSY3-Àv<#KP+%)63!%k`?'hSmo6q"T+Ѻ7/ 9 EVHR77KQV?5ЩSBmO@?U9%B]sD؅3f`K,g{{ą8owp5 ؑE͊/O&W[ondДGָ,::A--K*^lWg Gտh ZTiADbݤ(}#mʐ]J,:Q9M䶲,TGWAZAb:FDE}O~C |.yI}δQ, jGY]r(g%JK&ΰEK=v[XX&$6ѷ#J }ӫ&{;(`%YpGSO*յQ 6e[TLIoGL k}yazȃCތq3.L򍅍uy(_Fݖ:yG[CF`ƢBO]L>s{c!؛9$}8`^V˱;ȏ[ߛ^#CZkRGgLalyh359uUjH-paa݈j_"8!t4P6#/wL;ܬ|{jqUz=Xƍt̨ݡ=snr¿l}{)iY$ >4ˬʪ MJ]_sQF)CL[))( T́$12TLL9j%G ZmTHCٙ²]!pĞVzwH]{0ݲxK.T25a *25{8ҵgi)wW+N|x*g "B-s72'ёK; , ["5Xb`x'dA1NӲv _.]7.*YDV AL &_d!I'8U0I!:^Vً6M!ŭ5:~nY0iGGDj}X8?xXN}T)#%J{'aWc5G06՗ do#1>Y$2*X J0tYQ\1Q:4|8(pBbdM ʁGh:cQ:) 3TRm;k8\R97|"NLѰ J=Rlv_ͅ\Z20j8GuEWuE{4 +IZ al`l7qn @ o#pڰ96A>Xii̒tJ]Ensh*tk^hvɮrtAUpR=0qbc U vquYDO ‘Ѐ3G3@3pK3wk%Q'tHR:$l Mo6t.")umT1Bg&dG<$mQV*kRE.Bd:q3ec3v8II ,& l8z' pnGAе5?GUc*ğޏXe$rUE< -*<5dS<3DеׇT[%fV3\ wp@r 1N [D4qT3yq[ v7LxO#{lo? mƕ/ .nj@>ewҟjLB1F䫏">ʙM< =ZƲ0 sXHO, l@bd 쪉ܜ& 8zaLJKXbk!XkB5ǚ _5 6tu{Y ?D]~ G4 >)\1Qz{M_h gU&a\s;d\:VG;dDV S g`BE 3&+:ӟ&\ҁ;VGI+)e`Y^qBWa3y` /oi #\gD s%t)wyN6w^iL7 |2l|VKPEYFN>ww۔P\2ÌJXegƀDÑ6,ڿX>0i OTTyC uO4u@\]b6V|tDSG`,UFASAnNhD>'u @@&-E6 o-P0^!xY-ك<ե$2b؇Zs4qIQ( we?U q XN;Պ_Z?鐴8A;Ngiͤr#tX\(9{F$O; ܼ NC[Gep*ðZ >ӪObY !g'wQS"@`!ÿlHswLkӤVUp -{S0Lƅ6S8KK|rjq-s,F+Ѷp:v4õ>e)@&ёJks$:Ԃ6j#Q]';҆ 4c$N׶<6ݪ7҅hq %wАɷ$7{K0yZ&V ;yX `i.fa O,:51~baJ%sR+GWЄ/Ff քH!qyJ-Soa3֔ F'Ʊ3lػDƴ*,h  :z#YFnj㳨z9ʧ_ ([h=_d>Wtieר8aOzqaVțtO (cEX^vmD@@*a^j"!H<6pƽ 'O UϘ h1^0*:)AіV#6 [,r>E5)Ǝ&a @:at/V^%@H3@3p0ЮIUh6`5v1QݧئW"u#!d +jCP)0{ms/ .s!.x HS Z4 _#-՟@@.FbPrl^_ZB}0nGn96|*'k?%ޞ#>Qyi%a@`P.#Xb2FLeVX 'rf`ZdoM ]!1ffdڶ\/oޔ軂&LJF] cP:eTkQ򦧉Uf0c"Nh '[ZgKmpMpXr2/˪M.MN=;o,zs;0rRp㩟+ܬ|-L8[&f=Pg": c`>D`FtBKCS@VYPE딁rFd.鉱Q t{ ;H)(?t{ʵ> nՉ1FaNTR^Lo]f^r?S(5A3puuGƩ#0CL x6 SC⟏IXk!2@8 :qU ךSk%J@zZcӣ8|Z+o5r% fJc|G]ihhhhhhhhz0Px7%y]hhhhhhhhZyͨʁ,]/ڎ2.EĤ;C?JQ,Y͗+KF=7 f@(̜ (x<AxY#^|bq\p,j=ݝ*9Px]-=wLja^h44W_W52w=񇡇׾c:aԨg-bƲ' Z0tԕ㒒$L0_>|+v;OhUN/A5Y y]o1!s^C3@*=nrMXhzh_;rfk8̸xYFķ;m?\({V{b%|e+C4Rp.d ^.4@XKk>.#hK_}w)lƛ\ܭB"1y"'>R_{|z\wqG( MY1mԄh1OڎO]eD9:|>  RXw= L^'_lswk8uDTKT4u銹/w)C6ƨ~V;|ռK"L]辗_ٜ?_D=9uN|@a] XDڄڤtMRAE.a`&pLtrY{<9j3 N{Gwjra`貨. t<>֖+ Kke!)SxC}h@.?:%4b#ʳM,$!T~]9lN;dRqCy-hаEUSfnAi5؁W4mzs|nyͶցQ2LJv.0nZyKgG>l '6WT}ja+;CHݬC V+qCۋdhc+ECf1O+OYC29W18{#LˢT ,! _=5Ulg#Y,Ť >ɸdl͂p30jC>R`u?;n$e"oޡ[b_<%6^AdJ^wU JѭHjᣈ?v.a}=2jd1RBYW(uf%L k%hݙ[]׊R#@B(n儚b\[EgPr$qwOlsZUL5Ɠ%a`R86WE>Bq٠RwQ1 ;@}P:L1Ac8W 2 !H l c`* D m hZHR ̭@X@g.opRIJHNX6].9?j)<`/b[A+NWORsp͎+i˰DW.K:"<ʏZXeO"g{QyxA9 N|t㌈M]^v]u1k^p1wB1I#k>r&.NTqj_ck\R GrsO<͕7y% VRgf&#f},٩vL`PCXaISEn c'C:*ٜ_o;c ,.ޙ$/LA : OT3piL-2ojLm܋[͈eؒ&c/Q4m8=hpa' G{[;"+G(<6j#-&R3ÓcJ(0)YZ}1Ja6MW$ֳ( ˬ0_8m^a@N3eTlg3;%:n AVN^|#b2|#_Jĸ`jTkwIN$2=ػ|0a2H׃c.Up0Yݖ]6ت&:*=8 p.;L#n/' o^_ s0ւsS8=)BLQ U+2`mk Z_ت4ognN *cpqhSmM̷֕,d/;y ䷻.Y0c[jҦ#رOKu!hɏ# E.^}97'KbR+>C8N9ao7 Bdkw3-j&䌻o<6K1Za/. ho}!7;,hV@l XyjYoC0{*ޚ sޝʳ6-K (yZ9Q(G*/K'X:'{ jvLJv'+6Vx96k^G Z쌪„MXj!Ep@8e'(X1~,ݒ~> bQ3X.=ӳCieo?{_HtہpYfNbZ_daVO"D,=}3K6mu%;bʫR+ =F@Tz~]1K{n=n2\MMf4=Ӣ [ig cܢ9ٛ6nnBX*hMEaEx %xQy{ɑC\cTVKM֞B@Q8V8_BX.FyuOsUuѳ?6-R\Xj(A1<×nܫ!" ;Z:#ɇ ތ` p ,. 4Sf@IQ2)M1U{ sREIQ?56$it/Үt,ߊqXL@y,"I ֺZUnZ \rM1>䓧$ {D%mM]3u 0Dgfn+,bPw{U;aX& m V2t=+1$p]K^z E.,,Zb$0"I4aglJ;(.䛆X:i$)" x1u =ɩP䋰񏪜R[#P4MOPZcP4Cx`X  @] <{l-#<#2_ K?D>f5`F]v# s pdsR&=YvjJ6OI e"L+ o~_SvͦA5¦.ҲzaOӠ#`sdg?/HgrO_w;"u5ult!pOn0V  jg~8,otUh? ;R<{2#9~=N'-י6}Y5A{"eDLm`DƬVmq #֩>yAhܰ ';??4XҮG\biduv*_q<ӓ/0UlKxXb>tgqm,E(n||0O&syu<:%^xPV'|Nحac#BFE~b gVeeJoBxKwbT;]'ՠq"Q dL5㶺6r?)HB(" gv\4N''Bc a޷mcF+RM#jӢ@H%"E#Ӈ ς3lhT{1K`s,ޠOT;sY{w檇3fo*ʞFД҆?8eh@@'& $_ȊK?t"'zsd;뎗ѓ_”43pHA D9 uG~{rSfK&8傡 [ Pdk#(xs]{+dso}ݏDڛf``(ƾqgXʑ&?q 7lhI X4$w=ٿhX%Kk3@? }*<&{ ȅ)tqā,LbsfIZ @,3_|0^O2P&pcVʁm7+;,`kA){`k=s+]Nڳ1p[[)tb. ~TC8"zns [Ӷ-Ǜ` Cz},6Iנ nk,-m+}S0uo9]%Ю$uȪcnϏRǾDcaP].f-YVthhL8nr2ĮS>}bGi$DbBjJ!0erUvJ2Zy&H3@3@3@3@3@3@3@3((b:MV$Eä|ht44444447 Z9ŖsV<]H>dKIvJ>@}@pN6} 6S?x ƟYBŭrgwl)'~Ȁo sH3@3@3|eݛ*;\})HU:F?#FE͞^ a,CZWhz>Wå%E]=6&s:iY|(8ݚN|U"m/aK jA 2~ jH0iwQ[޳ɦnzRHhI  B+@,|>QD)! =ٔ&lz6w7QO)gܙ3g! dpthV ɯRIPvsxL:x@D7ПF :\AmU9O-HH*).ҍF,fNuM950|_X5ě|7Ϭx<&"9:|BK^ۤ=8fEtĽS|\UQțZj'"E2-5jFGptFͤ ꘰|CNql?*U[So >O(;xK$ >4Er%Gs=VZ,%px?="=fxb*W6ur?Bsv牓4iʍIY/;ƙa1auygңo( Ɇzs Ñc%7oP2|y[[WN~t3O;Qy 3z!ʬ iԒ.whyڪlej>K@Y-h)%F?lYKϽ}n * fϹfnQvu  Eoq޵|{N(P~+=-sOĔNXCmwnu n64 /'*n&}ѶG9_"Lzq$+ݎ`E>vTi ]o7wkXTm{._^B]\6B=<~OAYjj~-7Ԭg~-,MYtي ETJ֥~;(pTgR)ĢT@ӃfڂdDen7 0X]&AâtG0=B7:P2+zx2AF6hf,TPwd7MuJIA-Ax uz1P9E`tBo&60P"}fm#<^gMCY%]F9\dfюlڳ[Ԍ.e\*ymg4Gզ5y8h{ɞiF  Y= ӽ]7_sdW)!{/0V3aL7&EWi=)ՔPox=%鑜;[@"8Y!z}.E/9_t@,䄘8;& TQK[ ~sIttU{Nduch?7|+g2L*xR#bڳDx4[rMhXv'!܀Unw n,Sk)Ba$UVKK z2~|mBΠ*0O$c+SOz4+{+4Ʈu $?!EnNNEAGWjudG&M<}CVs8$Ӭp-ʍ]8|쐰, \T BUVUkV VNSƹ)&+ɝǕMTb> Y᥯G^`t /*vmw|URoWLDۆfSqٰX2 P6< dtG)8Vc)(%5XT 4e@ ~Q:p5\Vd~;qlEQ4MzSP"F(76܎1Bx&$Ͼ\8 g)~`PD,o8WzIGU}mK/3ݢVm32ayڋo~Q!y"C?MԆYPsjQo2H$Yh JQOnq G3tIP{m r`Ag@L52rsJYUӋ^M3͸ofA^47j[VV!ܙ#{tG@E!(<0% ̣} Z0`xU} јF޷]F =ۻwCro,b:R,}-#3a0j=Ag¬ !#g~X# @EE@Z . 2Y1ܔ C&iRh|yrX ᳁P}zu *0Vr0&6%g2 Nvt𠂩4aP5kB~^I;e _[-t*OnS8lUed>[ʰ%O{/A|ށѴ2r̉FakcUG{K djPrnõA#׃߫-hNdRlD]a'/YƇǣ뽟\~}OڡCDz[}ߗH,Ot vyn!4g|ܓ8\P%W"Ɖz{YkO"@nAZP8D3-t`uB}롭_:svg Np:}1N(Ӊ?sKs"( _{>sK7H$iT+\l# | > %w ^ː.'*Tؑj. fY IDATa ջ~Vﺃ%]Sq*a;fذe>0 l.X`)%2k&z;@xX2rl $@ttKcEsyVvE$8;y5MBTt]rQn-iATEt 뤇0( d@Gzdg jo41 (S)XW"buq}CF5m7E-p.OU UzbF),;ײWK^=nrhmlZʠtܽ{!4`,54l]LVÅ9Д6oq_!wzPweӾ!)`ËOš=u4d L[P?\ɷ0jT4q"KCD |D̙Crz$>Ycm̴Qq\Tν $:Bd1SInJ}G_>WRMg|ڹ68T&!2%Ng nGږ B^JY5+I90-ӏamYめ9zFEBQԠذR|}.t,m{@%5/hb& AsGp J@`g OT+\An@\;&9l^ީaQѴl#wl GQ~[V٩erJ`S(dOy+l (O5M%muf~ԂZ36M%Y0'¡>ȆwE$q$>jz첕v*CCE>JB Oz W-do;px_?([U^VKq3d.;N].bfBPH5.78;S`x΢ A-=" lX)5;c Ba`{U jp4# aH!`Ds98^0t 0}HjZX+l`ם  @|Q=m{dwIR/{L-N ˍrf''%;Sl˽W!ÿ1n~i!ʳ?%ePUէ+]ວ(Ns' -U=a\t6;tx` }Wnݪ'/*ѵhxʡ,!c꒔ M!lY?àz[FjA#6]'o-7rRנUm?/Yx͛RPC=yhOl'")p8uN])6n\-︖+Y<%-E Tc[Q1 >4l dNy&txbwP.ګOM| HgO7斚Z~-rO$%@m gZ"8{igzl$ULۺr95"(N i2xٰ@1S~Q9O.VZa;?(z %g;`1C*c[]^jztGi>trKؽqm91*l(BrblONi\DwσZ[Xm]JHZl8U@wV?qnk ~?ϗn9{`+&q"ƭЍLvHƻu0Xn!/BqpQSx "Y+6OV̩wfϊ 70D4H ʑE%       VZfT4sy`9* VZkX`usRAB;] z뙵;^{n>?:4#!p-|t>n>T66a:%0it*ɎCXk!8}fTt^Q?;SC܌϶%h)0VvAVE7{a]6(7&eE:A+;}#EBuvACPZd#cxٰ~ LhJ`v충![N~tpy((>;؇-9kK!}keIɚ-aQfEYKϽ=TG]o֑*nu>}еkj,6PIIoO󍉲(|pӒwn-b0ecVDPq۝EpC0D DD_m{}-¤KBh ^cj>%i:ҏ~4J $UGAM{̽jgCA+ԩLO-Yְ>-!4 f],6M4:otsX Y?Sŧ ,5R5{S;Rft4ge+"k\T,Q)Ynl Kx9(X%1!Y;>ׁ6;B``'<t 33 FT8nZFu plZ$a~pBԿaQdXB*2 o]X⏥QXQ/5~;k]fI@eN[VXŁԂnq YV}G`;%)`!0 *eۥwVJ'=Kp6fL2?&ܾ2dFA~^NZ+Ofa˫/UBcҝW$t✫;N۲r#;v>n 5kkAqV6)\lӕJևsxCU 1eqR[լףI^0{olm8͢mw|URoW\DۆfSqٰX n3 o@a;t~S%02v4u*Pս!(ٓ@ESb@8Y % 6ճEe*˲RVG RbCRNJmE/{)Yi>TuS>mغt<5[c6g&KԳ/0'd|zND0<ፍ/cIl{- )/+/`BxkJ?SB=3rBHv\eSw^cY|}bbӯeNJYv0>x"!K=wO::OQF($|]_r|XΌ8YxIFA0c iW;|f-grPv Ϯ.-(Mzdr#rׄ^kvqAb8َйo;<\Y>4 T𥎒$&͏Nޚg_O,M`#~L__ /Avv)e>}hGkEBnEP9jK*:Yl!khT9zRmjzm'z  +Jg묖# ѯ; g&sXq&D&!x/ mTv[LѕFR\D @M?A=$4< ʁjpP't;0i*xmGZi3ZKet> 2?`soi-: vܸ-ΰ8,Gy$Mk:[Bw_菥_)QcN}LkhL[SLYS%ϰtm](kJK{NAu\,C&ru$;ZD5ul (2mVv!(k4ƅCeN^)hMcp+5P ʲ[lGA$u׃i5i am:%,DLhKdÛ0r=5 dȅTvB[#bQnTgGvN͑6mS^Kgl[D*X"@@Ou@dhdP1`28B0{5<-z3z9,JeqQum#[]9>e*SM"hjFnz5Mt4X>Blώ^47j[VV!ܙ##7R E^\RCh 2 H:Axh1‹<}* oЂ~m78,,.7 G~o\jcnN1XBe9leicA`w@\.9qڨn@HTM n60x.BC[˜8ʀ8AۙRk_$$oh?2lI@EnދEPzMLӡELu≌2XGxxtgZnSz$^ᕖFpkTYT:fRlD]e$#Kz'`p`__vnt%f0urh^0_:V#$#>qNԙҫJ8me ̩CAھi `@[$39L)L1.(Z[YLRa͊2a Jc0wmYoV՘grznZ*SeHZ#$ ,VǭD ջ~Vﺃ%]Sq*a;fذep.`tŬ- .w{Y"靼v F&u]rQ@Gh#180` -з=S4ri5jc\s ?! .Sa]NjA 2ڦ;2y-KIxe㽧!BCO Fpɽi, lPUHYiYqh&pzh/qyE/Fa өpC<9Д6oq_!wzS|C9O"(l />O*N [Gs͞3 I̶͙XON1PʠFQ˽H! #л P<42%3?ZwoɫI|!<x) o! ]Vv啵`mER=n,Gvnnlim c9$D$ùHV۲WOKC]!f4;IR;pԩ)Zzèދ4̟(iT.% Z`J)b@t>g]}C5PQ$tlj0%(k&W&Xڋl^ީaQTn}GF.~PѓiZ/Ǹmuf~Ԃڞv|9*EG\FAfgR fz R)>#H)~ 8 j&$yF8I=0j]]?f.y|°q֘FR,Lrpev4ʴ mו^{PLS AKeo4>;*l>\nlh3Q KhQxsqw%BsZ8H;wn^rȕ_nhugd;6Qs^\EKu{W̜1S^r5S1=J͐5 ɂ9GEz * $H6I^}e+ TV}4v,hg3#j[^2wNP X$s,{^ u!%kz ]C!|ޠOq96̖ϿbӶ,,a%~:Hh5ȴT^RA4nx@K-Zէtb& 7.p ?_7Z"*>Vԗ NNJvP,*5e{c,B&P7gK~ˠVVէ+]ວ(NsJ+l ֕u!əMvKa'퉍]D#|gPQsޢzUOc!u_-mmDSSee,p==**oyQǯ'&|*YxzV=߱_2L|ApE94+{O$Ȇ^`2ƛnG~<$ ȅ[nNJ\*;tȪ︖+Y<%-E Tc[Q1 7u>ɩlMjWs@bM 1 }N)R&r#2Y8.Bu]vEpӌHQޙRurbm3jwMF~w+n6=nd3|] a EeJ=T [":xkvpRtC߫Bu^HϐdwHnU踣K$"pp||8;iD?=4-e$ @r 0MޫNi#dw[6x=i+zeEM/CV.dc:N-Fu>It$NơHgTiXp4͇^n & $y/Nw z 8=LσZ[Wi[ * " Lt[+%5k%or-gCٸ^ v$^Kņ0PxoڎGBˋPB&˭cq;G(.*v/A@9kݦV'x#      K0Vx/ IDAT]PB > V B>@s{/9gߪJI -kua^k>"۬YvccC?m?>y5m@r/+Bm| .w܊o A!3㞻/q}|̊PnWqekO 4礇^vG>A9iōPJ޹b\Z[,jAokJmi.D5J Q1Eổ۟Z0gV<>%/u$>5 ySSCTk{+xF%Fݸ5'y꘰|CNY/8#do?U_ߺzmp*@ӗޚR9sp ~01~Y=IQV`x% ?3yfp0[YV60!9I+-k\4!`Nbcgus=j1}a yzM‹mrQnLʊxى7TRp b]gޙ)7LP_ */M62qp'r;.ϡӝ|";Qy}x2^ jJoqFá%F-˸5'<ʙ$i.Г!ެ#99wUkWйl/x OB(Hm'r>?-izf) /۝%-WN lP+i^NTEFs_Q=W0w;vءaζe*Avޘ-qtM{̽jgC ~V b]9N>fb6 1[`o3 ItD7%Ejc)&{xfLAHZ*o,N )M[|Y:ɳA .*K7v6PਈφRx]bD4P AH4E:S8%Sim00(s5&ѵ55sh$G Li(}:ا%3#vTg!ڳ[Ԍ"^M ѩg^S20R=^XӞ%ƣSOZׇB*2 ce HNc@%oN=}|oyWPq9ɁN\*wω e o~}o6W'sdHUn{15-aY.HԒ0L,)oUF3E4,H=B←7NTA.Fe4p YV}G`ZJdh?AlIһG+{ד삞_8_pC'jE=vM}`߸|֊k1ÙhiS柿䢇j|lP+:թZ+ѭI2>O՗*DN3qB}Β'6gCޝY -=n 5kkAqVu')\lӕJևs U%ĔI}^;oATK_&zSsw_ :U=7a5mw|URoWDw`u"mAA)JrĸlX,5A3\'1sFj sK8t(>di[p AV-+GI"dp2 AM}GE٢2eC) N=wǡvq)c|\ъ"㗽ݴ?v*LY{Ǻ)6lqn\Z%c뗙nQ ʟSu$ߊܙ`u\깣?~Z%yBFWly#uIRon@oHkCBSV2nZ='"asɊpGpl"H`7W?RC~`ǵdžEn(F0<7<"yr:%\aQv- \q _v Ϯ.-(Mzdr#rׄ^kvwAb8َйg2/̝Z@.h/9&RGsS5S5Ͼ,0X6B`Wv0}AZ~-T0mSU0VMOV6^<}Tiڒs5W4}=6w0ZGuVˑ vz[ͳ6X6qd{&D&!x/ mTv[Lѕ@Z;`;Ⱦ9xW#f%@уf A[gol-]u6}Ng;L$@p5G7j]̠jI/,AU0LqJX:nLlRL_qUꚫ؎=_%ʚ2M(qPSWuJ*Z{@+["0X8MHwjWfp0R}?~Fa:Y2k`[pj;4Zq[W]k&K\6pI; (mUp?S :HB'fOeòȣI NU",ЖVɆ{E( i\O8DM3#r"յsӅMОڿsݧ~VLX4~`DM[Ԯ?~(ҙ=[D*աO$"@nC`: 240 h9UƞOGHle+?p>j(EՕ[TÂu.i >xN-O`tTE8MȭKɀΙآ h*UM}E`e9*sFW 00i6UViLY0~& $d'G 0 3jƻ+p#(cҲ{(YVfpY$aN1((wF0J:Z8`$3o 0{K$r "7c"Ycv4PKfN\6 릏Xۨu~-rh N׀A 'ⲁ#NfP5kB~^I;e _[-mqٰ uLt ,s[kTT&QW Ļ@蓗,_O\@Q'С"=-|dK$0l2Rvv!P)UB;zwƼ%.H^ j%K S֝,%Ν̣Q 'es} Z+e> ,&w6[dUƿvxkno7asc:FAfg fz<XB"Y7`Ij:E+QEhnNhctWm(Qf:w%%CkPgJx0 UAM7vٰƼ^RÑàUm?/ C˥1=yhOl'")@(ιy] 8JXNcG.Ix .? rˋ i.z< o Qu}>ylx=h!)XD Y}ǵ\)n9E Tc[Q1,RH>3*[Ǩ2y>Fܪ'=C6Wd}v8|aҬgl49_3";sK-XϭB$ܭ 6XNKzZ"8{igzl$ULۺr9&#|x˻V@yʬ+`| @v ~N^lU|w7Z;t 0\?!8?cHnU$yus8c >4m~ϖn6I!I{)Pje M<ws^v6 XlH}1#} Kܩ1\;P4}xԂivA;H^ ))Ta{=#4J-_9% |hpۄcw'$Nܸ:7ILvHY)MR!TrOwDoJ%r6Y+6nk3B? ;`@@@@@@@@`,jX#"@DW        sݯ!;< CݻRґu~[߿VZizo\PT/47:8) 9k]wј6? |'fP\hChKOt3(;dFʈ (@Ys鋓G椸r- nxZ8>!7*~<'yV\Uc$ Fsh#!@syBqkJ@$ @G"(>9Bw/yXLO[ޣϖ{iIZ4n<'7Fa>оuCYBx3/_Zk?v:?o~/ub=bSug}12}W;Ku[mv A [*wg3Ȯ[AKՖ:4u t8+ԩLO-Yְ>-!4 u/$|Yxmit "B=<~OAYjj~-C7nehb'VD&X(R.@=>JS t-ZKCI-MNߤe`HVR촣t_Zp86Vά32ŐD$`CckZ j;i*VzmNie p tli-8v'!Y;~fni j@z#fx =fvFv~kbL*k+kj u#q~|&4e>]edax\23BhG|6=HJ ,Tz#Tu1(#9!&ǎ9gs$]kUyVs<4HlIsx0 E#N{񇱢yKg1)K,lfCp̴So\!Hv^~ߜ\ ,{th[2>*^]9¹'vӲ_6"hZvoSօ|D w{[Xʜl'P"^M ѩg^S..=K1эG-.h<>6'ÉMGK awA2uWdzz>RkzZ5%_˭~5N#d¼\d1u?5 s6?ӷ4xMi`GYbS(6 Q^T֤i[.՗*DQ}՜k帇ݙ};NYr؄ 5kkAqV 2΅L61]I?d}]n:"U%ĔI}^;oATK_&zSsw_ :U=l4mQWaH]Mrn >OW{$eb)hKuD7=!C ů̢|ޱ}6Y什k[dp=Pـ.-ZPQ;E~q%;#cS ɅhfϨQ$ǠCTfTKbB 7u̝: R@\ >j7TgOL>d)Ee;R PGu%@YGoVgj8r <D1eP-^/SQ]=:Wi8V|n/2~KM )kXw<0؆;L7- <g伅ԛ?*j1젇u1%>i%s"?ظ!nžyXUXW*Xt( T ?X2鈲<-pp$,8p*\b_ ^Dԅ! IDATn9Y(y5'qĨԅ:}.?76܎1Bx&^4q)/+/`%# ֔~X?d>';-q;>ȱ'N+G:ymG9O9Lb7<ܩ 'NSI!c}zB]Xm7 -.1mSU0fM$7V-D\%+v୿}ҷó+Uav`аRtj92}!~+tkέYwUudϽ aΙssf3dbn"ʴ77=LB8^7,n^.y*4pцF1?P# 7{s@C OkttsA!\G7 *Y"o4Yqܢt" n-L!uQ1~^,d/pVu.ZW/c@Q%Y/_(vS󩬚.=vs*v #Z)}Jdj*M:h f*ؔ 篛"MIY*;>@sĮfԶT4;DU?gᣢv8eSيɉ^/pk>` x/έnP{]Ѝ%֖ʠ(_GW2޷5,p'fh25u&#OUxB6ҝ/5ț;,ʦ쐪?Aء암PtdcHZ`Nzg;5= !g;Vc:cq,g7"eџëm$4QPn[ d>g{تv}I$>MFڭv#0E88Ъ15 qVsj`! )/ЃAEUK+6zV7R($,3rS:A98_ 82;f;8Bπ8zhhBNȅ"U0fgp1 );-?r}I-{jG&MW@v(mFrv fa[W 0. jLK@XLTT4jaH 7pRV z%_"" m }-Ngq"(jW0:=qs@E^ЎTPDD-{:-+El;9De IjnRgutjb[ECJSJ>Fʃ#HS%4epp6Z |݈0D { pUQ9uA[[a`4nnsLi _8pk>p݀r[um&9 #Ѯ,\:7w!rm= T4,#xlM5>1L.[sJ0Ak^+eH 9)uSMFJ,aH^ Fd[FL.b Ă>C86‰6%7hhbn*nR ƪ*{[(W-]1I$xzp%<nSwO;F{sp`!x& `g}" l{ [['aÉR$US9jw--o( VEF{`rC'ªerM}"-?-ݙ$mtPvbTGڠu %fѩ%N2GJb0]ά68vΊ!x~Ko5XG`&kի4'& L&F}W"C6pQ/K=7l,̜W+vFmmۺ @$5t&Ѡau8WB':e.ܙC 4Y58 uєBbc?0[*pRY~#GgY 'i#HG62  @/9Р .*b18;C"UGl%[GQxoiW)`(5кpd>clcBb~FA3CBcd#[*\YxRa dAR?-ZM3]LFsmӃ:ؽ1nFpN@xP{]k{ J( i9<@T P7v @а~͠c"

rҰ^ `1OJbD-7=V7XQ|a{LN:`bRS $S]sX1f]<[gIشMDwujRԴxDaMpW}S#RǐQPNpg-,6Nwm s&2sfM35c/s_,eO?9Q9g{N0ј^ å?VZ|⽟o5XSd }M*Fͅ-N` lgtT54mJnvA+?@fB~ܣg# 5X|CHY{86V#pΙ Ân]-R ,fP} 0]dӀs ~nji(}*&&* c#rЙl7|ۙV}rU%W?pgY<۲x1лz%9w6#ϫ,+rw/$xp*o7kkjO&z ֪hYth fLKp3yr_`NESr,Aʚ1(,8  BP[>Yޜw_)<@I֫z!njndUƼ]im#qJYr,r˱kcN7`ݖ="\j>wh1pY[KLo@,:~&wugMUMᮿr\6\q/mS#R`A* 6 8 >h8'QeO/_Ϸ}e]u,={>l5ӴGOUa6牔ͮ/?zZ*;HL"㴶wJ:zG8 VHCɽ_GXOA^Ƽ}/oG|"ƺZjT$M}_zo' 172uon/R855๵DaL|jy66-״aodhæ-XsGy1iS| bQ݂m ^]ZnTVf)ɘ|K\e͌pknCt}u)r1=/ $9 *v&뵋˟]6sO˜1U+llww0Kc6g+߻i5#[(7eE5.pz8lxM q&t֨FTC!QɳyedS·J.2zQP;;DFœ3iDoh|}7G촇Dg+1sR'ꑪ۽)MÄVbSG=zKk(b4O] XdžOU);DPs , BH1^ },8s4{dT؛0X"nu`qd_n_ hC@d @%:p8%y " mؒl)0#y\*mgc7qyjtXE;wEB͛=eVsZ'@OJro> 6]V4 -23Mܑۮޝ2|]XLn{c(h>r+JFz/\A/Hi/rEehHk?lB+hN:cho fmz(;y Aj|IaU~jՎ"a7Dc$d 3,~viACg]+ }n -_T1c{'*qyakiחx 2/ >Fl^Ch Z5qfՔ_Y )]{ZezA%qܬS\)m5̏8aVbª [AʢX}';ðF2!u~wz}2QTdJ ttAx!Ϯiݳu}1Pu"Z(٠D$)koiS_ˌ 洵a@njkO33T*Y=Ň}cve4YLↂ' HdͺA Nsd3#_pؚٟӎMSuZ8(Jcec d{$19T{eK nwE&ygɕfUOxS ,3KWƹeqNJ̶v7!e'c ‚&ɖ1CJ֏:P}ktZZW YO>C_hKuVĴlPc]3& X aS2>,1+ bS@|=44J PH@vGlxpHšϧT]jr,ZzhF2`jC ~"=gA"U]l8{x\m.|8녇:22u97UqlG͈41m_X*0nپ`tGuk6\‚\^aqs!4–M֖ӭ8?.monB3\UQ`;s J"/qV7'V,(wFl̮Jq-ڱ$<oۿ) AMxOv s?@ַ7aqݑ|qLaCYd&Li*i,#%k GfE=SٹR|E f%'j/.S!\}8V#e7zH+^9ٖBy% -n=:ФNACEy;{wDKS{2a&Onw=7/9QUܴK::\%U^݁F7AC-m j[nVD>[TA#gޟ93D^,*ʱ5,C{[ҋM<8sX%j]Vޅf"'l,8r%DEܕi!o mUo8pphW`D09\ iAi vL@c A_gmMz`smVD=OjDAY=.vRMގۍMYejW=rMV.vk"SVkj;jF˦jjvXXD R[ ?$> *߉\ x OFsysgWmĐbm׋<RWw\j戦cv8)%"2^Q&]]Q;<٧>Z-Nxy/7^yoeZ\1<EB`@qN*Y' $sRݟ/o$>ƆV+(ql yq&qB,߸?F16dunkl0z1-uO';1` ?,$q?:HYu/#TPL6=*@qhz7=:BhĂњ$Y/U%s 4DQqQT"`cg%%Ejp`q;)P jwuس{@ "x?ЈAϝv®!,NnZ_). Coh)|F<(5ŤdQ GJP-Z`c(D396CyCm9cW0:=qs@E^NhIoDxn @%F% 5Xkb$܆M&m]f+ђN)vEq)K-5HqK$,AԲ:8RD=qNM_ǭV8= T8K/Ie}{ FdrQJLmș_97Xw'0'iǞ g{N6c˄wIJ5CFEx/ C(u]{yWc o|ʴjC+)@uC[}AQ |oۉCΆ&Dr`F#!(oLaR)r l+ڵ_1oJ[젟oAqC֩c s閆r.ZnmF ECdf%{/]C;K h-Ãp",z=M+uǭF#: r\g.)J7x&6/JyjD́F3INؘ8 DA@q뷂O†P`p@@68eT(sḏsY\g)uVM;*EX )36= qm*2a<W:M6paU@n 0Dm`!/Q 59)LZXa'ln>jb[xp>t p/_uQ2zL DC) N Xa9:&6#0C9PT x9eVJ2$ OQ/i2<@T Z;JbI17:,B)Vd"0FkO1;FL6K̦*?Z/;%—Λ<Ƴ ,bZ)澔d!l zu:>,f> W&'Ep:.N˦mf&V#2kFGO4D6qwrW~\^&G7\N+PHֳXj^#l)PLZ)̮qܛ38RyqS^^0\s4d z ,'J0BuE ??,h$?f4&?^Ѫh[4چb44ݸMѪ m qc@ګG',\zk&8\7q# Tj <7w3eW?Dmb_UO8-5#OwgcIsjK-fuJ|muw`2HncNC޳MAZWcSZ99Ul'pQj x*l7Asr6G0*0L=dyNMs68rf7m.lsrS(e; B!iSpsfZaj7}|4qܣg# 5X|CHY{86Vz>3vSCe l<۲'W Um{:>9trT%j4~~ni.epL<#ߝh@cO/狎hwmMM}D/1JrO|ZS4;dڋ bR+@{kkɡ ͙91,ǥg>f DlVf[ul*&vktlSVfiT(5Ϙr| oq!87yb¾G*7rr3[n]>?Ua冏Ū@Scj:S<3WX]ܶ}GAgrxԆWMfrʼ驠ߕ=L'3@cS]k;6?bM,H_Ʃkq dنs/j!LNvo`{]&%WiRaI[fˉ3B7P1HKug@W{u#V2qo呍kglCPnfs]V{9&/}x^;ncG3 ~|?6GOXqBoިv?ά0/u7ێrԙv0iozYMy76Pđn |jpM0Ih1Յmt%֓00jE^γOox Ÿ|2.,hß>+2rCBC>rR1w-d7{fnwF2\2q8CƄ|uV]HAdu8ҘB(alбؕI _K;UK\?03 W’3G:ܢq睏T_M0ޑko?N_fd?ӯa/<:1_t^fDkCtVSl X&5"LyM90xp!^Si)G5)k2m403FKvgT[w{Z_y4OI +tD6ޙ$]1''7hڥ#RLCgOUnwB~_Qك!,d.ISz㗞Z3{#S"yf,ngF2umqàJ:]!9pz& 0CJ`z>Gr'MaV, r~q2KIU,˅ I[یvsWj Zxǐ wM{ ˷U&n+=-ׯ1sRl4 ZaO S/Ik  sRf P`4;rpu=䝞 5󴊍pM^ /Ɇ!`y2:cho fmz(;%0EwWT@ѣѮ6S uMu=?B4rQchFx 2/ >m^c^1O!hGggv~9Oir|:Z>=7ѫ#@dk./n[q4hB H"S׬PӦd{' ik3zo[SkRf1VYٷH9q IylMOəYpP=FE*IL^RH7_<³J3Gph9<KҕqnyYbO jR>-쵝Mqr|ث'IdūT3AG#j_Ȣy]5NF]s7:$e3 DrS @*s'S38I N $VAML@,vxIUe`u K |k/~.#9 Or,A|2H2Uݣp  G/$vrǓj`^sSVTьHe `g 5w(n͆C㏋kE:N^J12,͞Ed 0B۸2-D㍡*v)3RLTqЦ] H hmF  `p .f+lYhl<7mOj;M,kkōNtEUuMoƦ25]Hs:X|ThEuUE@D~[)tkilP~Myy*C n+ξ&'F6kcU VV6!ܤ(Ta ڢ^: $Z [ǮɽRkk֐bVB:$> *ߣn9_ZBY*4JŲ XRLyS*6*;>@sĮfԶT4wZ/pVu.ZW/c0t;6A-|*+ \dQ %'p(GFTXSg  \~τݓQFX0IXTvk@[M&Rf7yc IDAT׸̷/u)pֲ߻ұ=,>.P^̰(i*Ou.#A2w.+L:#S{QbGK47%enR ^)/j;a#M!j |j^(<3tq~,G6zxvh5- W&'Ep׮t>`F) PBC[t:ŜۨRr@v+7S 1˗uX`2,uڸX)ER,ʚ*1Cw)JE9jÊ4Ÿ 391PE}:%Vyzx)iuܨ7 oRPoyFQWZM8nsA>&@I7q#KOW[7Z+geW?Dmb_U"ޓNnseF֕|3B{Df|۬=z& y+9Q9go95A&Ĕnt\PvJG8#iSpsfZaj7}|4c=!HB$y-7.vʒNSbMTXrU_ .ixe] R% a q=Qփ>d@ ( 0R"Ijb nV]R ,fP} AR`6#'xP lHAgpmgCO>/Tw?9<۲x1лO|?.=6Y)WN_n9v1ѰAD h:t&/&`/>kp \{anϯTIM'w[p!|oY+ $ g{PxAA:ņgʲ9-A8 z nyǟLbQU{Ze5L݁8I6Phjs^LvN;Es- )TU/yQUY[W7)nJԢi#F\ˆOP[DH*rz_Mu!eh܋=% vWHWʃG6h5Ԇ9NrpT%Wcfe]8p!+"C8mA̎{+l\[NbP.fyſ?l6oRO=Dデ7}󂡁 !biջ^w!BF,~4#[VЋrr[_nOI ;9>s(3 \b^#^.i6W xE4eg録Dթ%!ga @ж= Hܤ@w;Pq+@YqB݄nnܵtWVv`pARV" /SCڔMNz]] f.3{iV&Py+[]@8)Ph£A[[3+iwep!B .\ dfog|h0rQq!B .\.rP p!B .\FeV4… .\p!B`ab{\Y{++KxwG<^`A2PT;rܕ?M{dz#Ewg%` =dzK3|'r͠Rz1kɰ/=4חWҾ,hC'/6~[^X4 #pɠ$ǹb+ 4\\8dY^Ktow:_VSXSOdו]>#ך2d`/ްBӑO &y'xrqg@8^i &4dmWcª/7_hd4Lx`awdIR>Нn뗵Dt?d_o~l}+ucm]Yy) í@#翬nzlGh?vČKR5CO{HrJR2˦D~_Q %?nxLݛۋާҊGcUȧQt F+E&lڂ5wRnw0?6U>rv@ A4г}Zki,`vb@1CFœ3ko_.6Z1p}ށIQi,,z[;oXa.)k:ڏU_)GIPtj9nXD}| T.0+4Omg58{c30-Yu!#ݸ|VX$' 4ݧץF]<GYzn-&$Ts!Ҩ\ɍlܬUfݜc6=>?e,W3YݿnheEPE wc&U?2I>SFޝO26vNi dRAz{Ȧ|}!"6 nmjAD,"/Y,]#Ⱥ oeu O\) VemѷX}AfO=u9[\-md#'%xq7kS̀a_= +9!&?}v-23Mܑۮw@^SgL]-͕y[ek/Ò^OmmW<A- *3s՟m?WHǐJ.syՇMv ާ1vj 쬱,~~o ?._83x)mt:؉E"*>4X\4~e}{:: }yiv omJ5 "to}WFē,id@tPA-]Ɲ۷|f.Ϫ{|1 MX\w?_SP10*ƝDF7o^yҲO +zr+;]* #f̚n9gnO ~)M jX ndw|d yF8zdp88]&ޗn)|cWhԦ]Rghshqֱ 7ȯ6,)̓3rD::\Ѧ$Ž?6e<:MǝݴjWwyYEa>=/ZZEi.C'kxg0̪-+|z7n9p)V?t%ܥ8 a/"rN'1YazVaaǬ,7yS3>*7d&R= %$ M(T]WwWVTX"]^iCz2d&gNIf2Iu|>f}vn=yeO7U\;Q[5c"N 6$lF&ovX$bf6aK G*11O=:06"hRZЩ@ѷ•A R`{CYN7py %9J&]4v^|j LЉ3 @J, n~B?"[K.C+fe*Mp|d4/ik%hު*kT}:+A =m骻opnSR@-_䳲o/9v@k$Ig%-t6bU\n08("IZ/dA2Ҳ\ԌZKV乞l8BXJsmYk/sBVZJWq@BMI*-fT\aYIZaQ5!˝iQ245b9Ɓ @fa5̩'reqAn7%9JԌ7TY-QT,tAP^ۛS-֟Pa>kO 3#M{Ќ_|Owg*S?$ Y$5b_T``Dg6`Xbe5tlF6'+mo{0:&}9K^nԆw[9kV=k {'cy3yPMGݪUhq_!g %Hnϳk+mk˽{ 5@ƂYÞ`|r%68 NEVا\ 6.OZ:nX%RC}aXt @sLگ[e^MJ]ϥp0wۉRdG"A8G s>}n׮b ;$Ɋo@G.F 't9';>KWj,k\#.L*Iŏ'͜HoҢW0[lEe.77'J =XtwQ1nˬR}YXJ1OO}1z~Ki3 Ql]Vwi`_=bi,.0#ۏg4hgQ2JL$dCHc@/[۹`H:8s "d͛N EM t QhvR.,#T87:-r]~`U+ ӗe;y]3+MW]UK߾mߪW)ͪm[.Ct )tOvt(-@1bfݒ̪ACme5i5˟mX'ltÉ^(I^O6="&*@K [X}^ @!IVTX@ JlK cl)6q}Gt_vwѥ1y?;kS >M\ -Xٿ::1-Z{t/wmxg$O b4fM 5?P[sMCVm`8a鳽҆As4xT39&W:^Ynn1h|EgROg69~mVժ؉I"—GنXyg)I|+zEFL5k՜D1.HLu4 Nd +/!!|E} }/[4L3 L[4RÊt~ր^r^ z`YZ;&5Z?}&HLƁttO:Ix:A̹4 &0o/G-͂esb*i9@¤SxA1^5!ҍ4o[]83OFEx1lǚ4&uk8TI˘75QȤ />»o7@k )ętZ]$ff=0zCJ09O5rSٯj:FYqaL gI =G)pC/Lp"Ɇc.$Nc׾Yak|KYDI+,j25B7TqSl'aNcΓŦГga\yL3Ld^ɭ:X7߄\ &|f  z3͕>W>{eW↓뜀@yj-3E gq̆ýM C yhLpapOcan:_vw+`eiFt4S=,Ⓧ z)ki:3a='C+6Kq/+Í|2~R%DKK)IaNcw77ŷ^ Y__i:{Ie;")`ysn29F&^rj~hDgb)oNuv^ѵ~pK1/yFFZ!`2B]\9CEg!yA fÙI@EOy抺N?KMڪ0wMH{Y|e GW,ǣFmS'v,".cۮƜo+{{҃onvC@-_\\ tąˍ" bY>X ⚢Ow'D ښq߃ }X^^p\zI2EPh ^+vr;\/׸x;ڐ[鱈7n) #R(O;|dsE5X 0H U\aד 35+Pg__?jlb˱2'h1TjS(C)"EUVWX D;o)% -toeGBE#:y:-l ;c*[ IDATѷoLth/l_6%>B+t|͵^Oh-PnoL|`*UjUZ1gk|1 \L4vV]{aw/t]#'#>s +CQ`w܄l_=C'  g⟺7y-ͮlUͥ2K9t^W?sۇW*h!)2_ߜ|]f`<,?u3!ێ>zwzO%OSwM9*v^}.u/;#s NJGG`_hg51|eiW< y>"JF$"&TFk Mi(c뮝|YHQE-S[wxlJj~u.10\mmAW:2 *0a` FQ[goF6h^)cێb&ܹ{ 4U*s,{/Ui(78c BTt#c pE{grV˨'5< x0aGfeseKVk`^ Rt]5 &̃yq Ͱ g?.NKY9#R_Q$YT 'pgskޡLV[91O\x+ /y =eeZxI+BϝvJQjiiQ\)NFMLaf +ra bTvȡ@27!5L DI)c -wc Ƒ4^!44+r$f>q6` âYï|Wļf\;JywV`F b[|Yh$(6TՁ*+I_(rnEUɳ9Tt80RV7gǤt%\/M*+j%0_t_Sxp ֏(lJI^N1h80fF*Tֈј mq5 3qmzQqHU6(!IS?צFzIX+GIufb4& ,z{_B &w!ʟ=4?F[|5TLiki`>3:´}ՉO)zYг7Ȥ^fPw曝ƣ;ػxZ0gT0lI*wȽ;>у1UW0ºXNd"2zD*[yַ\K(mms>-T[D^$)˜xNo&ޱ2:nPAA|$@5Pً2ƆqPQM*X@E_k Wj #4H퍶D!t 7D _~B:|E ,hјy %9JGK ]4v^|j LЉ3c;lQ,s@fe*Mp|[ehʴrqK(nSiA]]yUmh\,Q ƛQpsH<:a)-k5f-ߧTPIX@`E.^!qL :ZU .ض 6l0W6dNEq:P x"Sͽ$Ҳ\ԌjEC~r.Ѝ/Ȫ cjZAzEN @So2>+DLQPf_<\vFJHY6(PJg6`xX!CkVZeTzƶY 01^uw6Sʉ^Y^SQ0 pCuT _JP\Pw\8XŪUhq_!Z;ua<ي1:8tr4GیX ,%mScIkn0W~ Qu-ts_MM3 B)q~Az7$Ip %p> 0d)Ul^N UT3^xf B pѯ k%LQ ݖE ?E'F4: rLtvg:$_\,@cz­&3"Gw#ޖ$ =Y4K[DW Qaeu -.OZ:nG8^Bz;jȣq<ǮEP4: Gpp@@ƭ}xVhJ6ff3YV =a63ti1m5QVYŒAtc~ޫ= ^=F!B ,ʨZe^MJ]ϥp0wۉRdpS0p;-Ť$>}{ņ  C^zS "/6TG8(@Y]v(2J2ib(uǤz9dkh5ņ*:8ASDZqyD^Ν47UC,%U t[ϨiQ,p(M#cx10w"j{E:׋;Cj{eVTX, q}xIW c=\6IPEz5xJƢ#xSDƠYQ3Y\ar(:+xF3\FO.@il% ``qGc@/A=@Ӎ4Sܖ~?u[LhvS`7#\ܥUjn3kln-DRe53Մi )i=eVK ]Al@I #.Xf'h㰖D)(Bb6F-Xtpq^ FQEνPbZ c9IEz{aҁ1\/zҬ*ܶucLl= }ˁBxJ MiʽD~Hs P~;*w֎.~^ukaop٦g d5i5˟Z![ݡ}k%Shvj y@6hLO TAU*#_lP6QkkYIA@ݟb}zQ$0 `<~o.5slua τ?s~&,iQ7e[:K̓g'gi?sI<%)~+R憳?~Yzc X}n31eyyrЮ=ꏮZ  '`h >&o(doeGn`K}K`FAƙ}g(M1鯯a56~XomTA͹ )sn݃AA1>p?AhZȃEˁ KnT7pQ-ɽbC oC0N6 Rp+(A~D9)β8HJ/2"9 ٢1TjWS(Cg*\ɟ)ףmmU8e(H39$/H"UATM'~:ڿ05\cia־1a秮۹LT 07S=>樜f6_ o*͛> ]_MPq[x[Dͮ{+l~jdRBf<6_7qgfοmzd2@kD_}y0[d!':f`8[Խ)۷lqh*.FrX7(ޱwgWmX1Hc4 K31CMۆph/O A;MG!X$S,\sp=u^}. ̭C#;sH* /;/?>ƶ\<|VtI^ʪ}%<y0@@+U;6-&_+7 K?)\h;:pìplL&qaWCn0}Q_  ]cO]NS7d`Wזzn$v4yeoe=?l$ 0aF2o8Mz!ۉN̤wo,;PÀ< x0a`4p;6{dz0aÀ~ xk{0aÀ< (rD0aÀ< ^#,߆7W|y H v YZDĒK\? abC"8FypK }`= Q%g+SI#AHW coj'~-^#*XVLFǎC sG}oT+:yD{-oқ'ߡ-:-=n`ͺ/w,3c$FJy2Onڂ<0V;/PvZs9 _yV/\77yv0Ph{]MZxǫi^ncb d,jٮӇ|Bn/k(\+5giaMCsNהCAメ(W]> IDATڠ@T>7P&pү p]aN`m)8րH.?.NKY9#R_Qy;YT 'pgskk]}0N̓k74p4 ckHOY<^Rtv$RIw4ajSia(;fIp$Y_eW$NAn7 ʹMQjiiQ\oEfc/]΀:e=>.Zq>te̼Y?1m zJ}6pg__6 [=^kY zj~{NS0oo߽oWމ%H=QpxN Td4Jf\FMyݖµ-7Oa,]l ^f#'s}%ڂÇ X~\;LdB å 9_#U3Ct\Ơ/K;/Xʻf6ӊ+lF}k g.sHCI ̒/1rg{E |8겻BNLP`ScaDnjȫ]gAkH:'@['yl EAx`hi9$ @k\-x$ӋD3L,k(T{/% 8/|\ֳGlJ1ߕT*!\bftan}Wpfˠ3!tҖ[(&3ҹ-kl] rO `V7wAlы7#ٴ|ZGg1^ZYF֦ ͬF0$EO?qkh(fiW=iQ[J^S9{38u1^skC{}O_nsbjK~K:'zx9qH뗦W-PDS$+_Lm8ns N[N$,tuQ=qop脒 _Iy~_*jD:zƔ0Hc93!GS!9UvY+aUH̀EblH50g6_c,H*sHNYz8^۝ "0q^?փt}0"$@ACς$1:$N-Ĥ %e:jd3GK?mTA̾/ꕃbg{A } .YJӫBh8M_hK"- 4SC2;aɟ1^r:3u_ho{vɤKsl |Ͽ?M{q wݱ8|a8S,s쬷x,]J+ڈ=L׾9hϺ5@J[1/n|Tby UzMʝ@RV7gǤt];.N6lb0|o {e˩3qe^^֎vHBXa;lQ,s@fe*Mp|ZJsmMYG 9~B%UbqD\m !U+eϹZ)EU 8OAJ :J jCb9K4nqW6dN}DkB_ds}"W˽'ɢyq d?Serp1|r* ]pE?/D NH$`*S?$ {)S_af̾7=R[VER#E;m(4pf_mVp Ũ'hl{Y 0W1О%YJU gߢ<L7TwC{0^uw6Sʉ^Y^ӽUBQ) 1g5[1&@G΃R 2zs 6lxtcҚռBT] \W3XȪ4AO7MTEg5F=8L.X0o ]ـyk/(mc4Md (lp'(J( saFb{$i}TP0G ;+1gEP4:Gpt !}_ 9\BަqqfE-"=Ꙡ c=%s&E.쇳,׳DY3rl;%zr?YVb&fqGc@/4Sx=lL79Oq[W4h b9ga_ @0iGg2Z/`c*h HSΗYA>@5:ä[wH)|,BbLXT0NkR(0ȹw˾oZ c`Kz{aҁ}BQ "v!h݃A ^X*YUmvoKU]UK{]Q0`l`L.E(wtZNrHդ,_v7K4&Ch_ kh'D2/4?%2)==XBbT/h`@ƭ;B_w_'fvEk宭Ќa^ƬGc8 z-6pK GSyeQق:Dus$F2L\4=3н1E}E,:2w~:4U*vbReQps|ytzJ^$),wf٩|E> /mN<w2a*BxW,Z5'm x̓qх[4LZ3 ,X4RÊtC?{Bk@{Yu/fV\)p%ZKF=A肩>I`8RRlӐ K\@ p <:t !>I.AE#+tDm[GmmE+]B r C\ ځ =Hul>͢B[ 8Ѱr`th`ٜ Դ~Nmxb+R8JUE#pG=DKWP%-cD!3B:1ߨ/ YyʌӜKx'e%Hh%wXVcYEWEg3LJ00嗖rucz-jI#*U, vi>w9fظJ0+X/Tf cͽ4̕>W>{eW,q~.jpb9ӧ!AÎ(u^ \Et . )1yh4_d#Y:ʿة{N^FQptnO40Zw67C{{i2_{\8iKK)IaNۦ{G2?GKlG/5Et).IY__i:{Ie;"aysn29F&^rj~hDce=Msu5vdѿ6M&i} έ}L5V|O L/~:G 5e f}cJæO] ~9}Ľ`I4B DCoro5rZoX8 &ǃjt#g 7M̐{]Zvfś P 9fpM7: :2ß|b̈́+ᶀe-+~cWo:F(i,pt˧iqp}!Ɂ^/HYF§[<;BjbWl9ϟkv87|E|`l|7i xw{^H7_;zE>!8Z•]Bֆ}‘% ݀A(5J 8Bq(C &d@" N+V+V ߲R[SnR1 !kc@(j 9B8^LuCCNdT"8/$;`uWp9vϾQN_S]^h{^~ }8/)B gznm+=u{rO,+o6E<ץDa'FD YtV#sڑPǏ##1gC5o "sJ+g:`p}OmkQ{ů`ɓMtdk5K_cS0b/_{ͼ:Xn cwLIhy=/ek^^"uބ D2X#+]SMhg_B7rs10 @`3+1 -(b.8|H++ ")YA]Ӳ GXś-11l׻ZE{nwjrdf a'JGzލ,ץDFi[ÑyUm*-M8m^C+5_W+JVfj}qmQ{>2=5=)rB7÷޷ٕ[t׎,>~F|36UCW*7.SrY VC׵55+W5]_mÅO,?RWfg QHIDRυ_KcV%g6A_l0򮙍3"  }k ga=9%yO_BprMj =:ɼ1TA#E,]8nMF$-_wqPnȪoGEf/'G R AEKyyB+mDs i@ЖdzFbݬeFS뢿pt20> jv -xL" q!y"imÌp[rs*aRXYΗ.#'S&Bp+TC^ŰV_uZA ĽJaF< A@V[LI>t~@YgCi;D Pns$ $5 : E+pyj@rVphq{ epOnzF|e %t( \X1S֦H IDATnI/5(;gG-{;Cw.xrYgQa,箌փEj{.NVbf7V(UU.ѫ]εj$K5}drTlf3"!jB/JZ]1ǏFy![?@y+/W=r`ծRҶnFK[-o× f%( כlZ>-Գt/CY+$|" IDݱʪj RӢԗ e<#7Sa;6 ! ?]nJW~yS^//eT]-_^@NN|1-8al9;I^a"[L!N(_Yk3!kZEBHGҘi, đyzJ坬wX3Sʟ|n7t`?XL ip(rȢ!%ʒ0z%꙽Jrj~n⿑M 3Ιqm91Mfzy:cR`@:7] DMIt8Z;L0 =z:LIh8T: ]gAr;#3Y{΀i0Kkjڞ< .WXoxR ` X'G ~.F]H΀TlIv<*8xL@8ʇ`v)`.}b`pq86e'demIsΪ-Qg3W5z eHkm0uɪgV^~hEpRTn!7K"_,ֈBIK!־>fPL׾9hϺ%.mmu%je~&Ϝhm ҡd[+~,eR/q4VgYiː wL&yiH4Ufj F 5B< r K,5QQIZJs6غQskMVݍISBTyTtDؗs5Rhu98 bȜ%4+ .Vh&*5 }\lzR Z*6{UA]1nPzkM&jh?S uNjVEE ˔UQ0HȉnHT;!ȏa5ڏElA0񘌗M!Glə 8;svj6Vp i1Iӫ `3½cdØ[h,b{h8/ U}gqESV3oró,cgB悽jZG6g (mwu[V3063H |nb`+`35`gWoN%+*C^)=r,e3zH#Y$Wid{G;$f7"O2j;wwK"ī)3q׈7YZd,Ф@13HBm$So, lA1!:4DlN, 3 Rq mw|WdV@e)aR2ƆםСF"+ 1l[QQۆ8v\1!%DnW9oGiҳ}]8Ź.yi¿/'J?!cgGf%21гB yyEZkBŎacTa ~+vF/?!L2M:r(QPe7WZqm/8sIXA,Q DO8Lt|g_ WPa l@+!Ɗ8@ 7JH< i]@#15hzL(5p;xPWtl2Ea z3q|bN:"8$Z<+&%-}osqoލ4&~`F>V=oQ2#$" M"ڪѦᬪK-%b.{@H]kDbg ľq=i5F3+i`2k>H>.lp^n1EN54YI]Z~ܿn{?kF὎2m<5F'TK0}jt?絿'_bq,eةj=FUӹuzZDŠ,8è ]" B|"Goou:w3ekuf]G}.hՄN}3iЊwvOC+ C꽾_!"N ̪Xeeq,er>z&ZU|I7O9]{-h|b Ӆ0s&$jZ`5 Cp4?=KeJ27_'DI!$uE2snMXl;L!}@HFe:ЉgA.xCMCыd>>5VU:oo]2m8"eLh D,o p2:Wm[H\`u\씭 J`z(iAR"d-5h6i A9Of04Ï%=3Tق&4nB Zdpi94L%&j  Bk1@;ٌѶ*{mF9H}`X@tu. CA<V e+Wo5c* sX6hTVٰHCGss5&Y+`芍y}hU}uT{.| x`h^{u7|um]?rQZz4zDxL)-ӣM_XenMT 1w0<چiVcçێG$z5kݢVPwo; &(gRN ٞyi~*3]atybV\T0rT?p$t<5Դ-lRRRA@jiF/7K|^{2KKSS^*(|Kh54W}c}^1BbwE/om>L}1Vк|a-H3ֽ>~ӖSםOi ^M樏sTNX0#=xg2WGG@ -'̅DRhX 'A .t|g`0 +OCk&Abue)ItAG:t' y= j`rGO,Α=Ԧ۶ ^J̡q~_HcGMΏm KzzxlJ!8:`pv;^BGϑ3AI@߳hwݰDV@G0$ė6&0sY[v '[[Zյa4w{ҵsݙj#'h{X\W6{0v/@vJ{:& ~'vgIb]Ye 8Η9eeM?8GN|̿-d{~p \YcHK :vSRNqչUѿp袾7?;=uŴ}yunO].A70n54=5ݨGܟ"=e\[Qz4٨Q4V+LE0b~"rn]U.(PeoC.2cdRj 8-e@zkG)lǠ":nX~VۿVi6{wET7CnZ+ x9M]~ߦ%+zRג 3̌+dGC,[RVK;_?{*Z锇rK1 mro%#|ëzgUgʶȺg4u3Pࢌ5YCPoà0?};Z¸ g=bNgO Gٺ5/,ސ!9^Q+甃}#`ЧK𵨋7$?sDs\Lh"y!٨BTr#UM~ƅ3.:#RLmj&h+/=ć8Isb#/n0d`WM4wE/+9y`ۢ^S&[=o\0+ّO?DloЅ~]*"'v4F$zKw{;qcxLU)VlݜtLͮ$jYξ/uSߌ@0y ~Հ ?xb ׎7,=lkV^/[?R69$qC,^lbcBsuW jeJݒ`t"z1𳼀Ү1 }&r@gϊG SҬh-~=!8G&nQczdޘLDàE A9^ ! !"pN?B{P'fs`_Au{Ipo` 'A 9Dl,^ K6 ੠L1A@6ñMa{I[cafY}W0!aKZ0FccE#Z[¸kNO b/<8}Z7sᡌ㧞S]_z=O}&;)Ra5ACBV/=?gմ], (;zRXTmʼn\Vf"ӣyLsɗwtwH4i2EypЫZ9z)UFˣgt^j,f_OQ-=qѭUrc'FU8qٖt*S "#0\cԄ0,߾\ioO&0칳W3zw"i2#j ?zsKNJErZez4Ձ:7i)l(6c]p[j+2?1kz3 >{S7U1kƒ?e,IYcgL-=uގw&%+x ݮ9ag^Ls|_gfDdܻiٔ6O3DcYwNjda?]f%XT5T sү_9SJe/Δ{ퟕ8-kW^*RWsWf6nr~l(xS%̃Dng+*HB殍MXJ=0dϼNi pD9cvW0s!\mo(V`#~rLjyL]"n[`Y /\JRҫ̒̎*y f"ld%omtw|+{,sar4fS]z>佸T'Mʺ%v^p22E~q'e-[˽wYV?BWQ1#j k~{%VܵO2T(5Q DI႔i]kLm9ol7 VIg>_3!$;9D89^CKDP.\!Xg ºw Փ7.ҿ2mwEgr(?/ irMF Ǚ,6zȾY'}e IDATGX~SLr7a홪s_:%inlE\B`R/qfPc:H8{xI3|q@oL%4 )lL"ˊPx\X҃PTBfNPp\ܒCZ̈ v u8u+iJQZpWw J#ttŌN_q$ɫShUߖsMɑ`Hbȿ;Wf&Icl]5zs*YQo5%'ݽX(!q@5QS׿+EԿxmc" `| u<>zWi; RW+0bF*r+muEP;JGpr^'M 믖3&BB*IQfp&zHyHF칁.-ʊK^0wljRdPvըMmEye+f3ZV *G_ItkBM/irk1h `-88"7~C3Ȯ@L`{)'nDMdq -\U!ma(pOMcKW9דc{8ܞ=;-'* C<0,(UC#k;DŽv8YnW٢W0o[_9ZIɢgY1L`7P3xB [hmYQbF `6M=7UwbVը8acgDچ#q掳DF- l+/)lڮ-RmzIO˪/:A|EcS#z.1}dl,N:t|4ꏞέC{?/y|F1h+ }笯b-gRCO[U!"'{[V1>Ӑْa{}q闿B*}E+3̙UYx;p뽣}L /oos/+[\;ި)>9K4*pˋNe4̃Z ^xx ~ZŘ~=ӰA ٞyi~L/^^v}41)~šՁ&+i+,uv,|B֞; X\t3,B*[4ͣOӂdxϚ$i*6ik{ `wMJMXAZ_ի熵HXM[N]nB0|:o$V:A}o呭oVɉD`ѹ_~4[[F37܎aL,Dp?'5ߗ+=}{sJzq(! C$_HcG[doH0ݥᗐſwؔ7I>6L_?߸&蕝B.f6y7/#܎t*BN[>Ֆ|е?#|n{2Y9ʃ%[gq-C{^gUbkn2jT-- \Ͱ0DpH0QAec-ZnZKzWDeq>dŬـk~1mZx=DŽV}rcU\)k;>qUqTW;W)}US)?iNr6 7[MzQl 8 S{VNxd'ۛ^J>ko*Dܳ~Feۋgd. (#{M?Y`7hfx7JL6-R[|}>[\d*^)>t}TaiM/j~IP0rA*E]'˝?&^c,򢿼Sw_3Aw;kL ߀@oڥjN%Tz+hx; n T 7';RG1/A9Dž3.:XLmj&h+/Es j/_z]1oJˑ[ \b)o\iH]ݸTڞ2٨QRs3+IA! fc>"yiW\y|6f~͉\E/~w-{.jmyqE]Q*gU^8vdzLۼ|9d[V3g&*,$T=ٺ93.™V9mhF,goFi%iZ~5o2FJ7$&6K}.4WbZR$XdzY^@ c+0W+ab/=o|X2%M/jzssk8Oȴe3- XhHۛ? O jNox8H0 R{.`*$wB&+pgk,G8`:@,vBn=#$@Buȯ]nJ2S \ Z _eZ)\(b"ݒ6L?3s3m/ѶF},|ј>6u/($A9F,a?۷j5[cZIӤPo 7 NTR0$0w$6CnGtXw` Uuτz d!~(3RCTP0 'kl^n;*16LV|{\HIz8>Y󙣥7(wgߗ~ʡfgk^LYَӲVq饯Q'uu;wgf-G\q^cGӂgIfG36W6vG]/J4DxG_OB֟氎^p-k~{%VܵOٳAUJͭw blg^Cw;)kZſ"L]dbݶuwJ۬zyBwZ舢!>0]p Va?;.fTyKUziҢљ6S-__\qc mݕpϝ Ki!-xJ$.)µJnjyc˵eY﷪߯O:ρ_QyҜ}=f8 kWr8^}YUMxI*wx7H}"+xW{&,)zIpj 3># Lj+iշ/sn)fѳ҈i hN"XXR'?|ﮍn:qPkO,6Xi60&|X~SLj\KZ٤QZ\I-AAFzG`v!\ j9_:S0]NƔ 6&eE S.1u܋A bdNب[M V mѷ=3'St|TUSvh_ ginF9mCG? njFD1ŝƐ>Rpq3z KR PJKϝ08s~wKpB>ww`'rʵo}f_ Zf&-F=IVĻ`3NpCUךŴZ31pf)o#sjb,E `$S,lȈ (=0iu3U7![/V;7݋@XI b ؀VS`aCL4lJ@Կms6H#=Sf@djd*x6dAOPjXj'Wh*S(ftUXJtWG[zN"9h:#8zX :=%MgZV4Aح@Pxhj8ןG5OLc!^DG']z` lV lxE4mAeat;arP-qѵ[1!Nn$g/0Ehh@4 j5PAiM]ir,3T8^%s,'Q4l5۟o:f"=:ZZhfMnv,~< .ŽP1֨%uYQbZ &[Fp "`)ts}onz!h _6ُ YwÅXK!>EvqPpoDZ]@010 "rl VJiVؾeHoR#M-T!Xh܎g:یяq0Qe`c!M6 NaV?E ; IDATv%0Ujk,ӧ5YI4Lq^rH[Rp5_޷դCϿx-t{߸=gF$ ހ`8ӧ|[9x9Sw>@>OH1ԡ@UY\d k=`]Ϭj P!aQ)u&_$8l)θk2|+q^ 4` Fq@( ¨噁mWd" Rgl0v.{ u . S0" 0,W$_ipeܢ'u1~fBYu2&z f :~hԳ8FI,:8`.C[Yn=ҡQ `Ip=F 16k@訅7]aC a]wU6C#c|1sB;Ta¢$Y8Ay&Y1L cI'6߭Etj?0Zh2d0њeGOU,~lzTd&QB{ .HR2D抣B8)q4̌?|Ȝ39^bL7.!-dȺUfpsLt],V?! f0P2ܿn{?k;LctοRE6S(ѕCHnQ` IixS?|%2_V5; & !S-ӽ[JD&(ę ;4*_\W46-^+Egd`fkѽVUٗ'=q_^X͉ʄ%+sub[LB01saBۉ {Nr{?/y|F1h+ }h.mvY:K&vms'BZ ٩>  꽾_!"N ̪Xeeq,erd1[GRo; )y>k͵cQvMZ9uzz_ cdo-m!m4?=KJhl|I` '#jQ6$pF%A0sڧ P }B^_\ `xc pOU raj"dO!i3069 ./*sYlѨAЮv{ ܪ1V>ih.{f„^`p֊e+,#F#{.| x`h^zwG7|um]?rQZzC:2ΫG^y۫؞+x:~S4D9hm/<6kֺEw vM0OMƝ4&c22^0jlt۱k)6홙=. .^ޕ & xRo4>ErZDFS,^YgFuӉ $~B@*ړi]MM;xmCHCc_ӢE)! xvv,|€Ӻ; X]Z-[,mSԪ )L;e̾UQXp WCo{qb07Tq=? Y$MNS@|ׄeTy}pE?]s9t|Ck zh Bg{}R-{F*?W2Ņ P3Dסx'҉VfU''=n*~)|b s$qO6imM., Sӟ2aB1V,_s;~F́sR}yc8~V<\26}m`75];Lސ(vey(~ YOOQ{M錫_?KlT:'m$7y]v;ze祧 Za-Tm`}&"/1hZB-~ʴF(I!{vjK ֟>q=M9<l%?vwŊ -r N`-C{bÔ1=kB®a0aÀB:gޚ8_N ̤l>w+O" iK"&RkF#I{W+:=4:YIÀ< x0aeY,ݲ{0aÀ< Z𘕿֜aÀ< xUvFXXaO<@ܸfExlQ8n4|}1OϹ!-& ~'vgIb]Yec'h{X\Ww+bews_b3WX#R9K 3MJ zU JaT6ݧL@3`u!kf"qAPW xʫ6QS2a(LH@?;B%[ٵ$o#*ja!8NvX_͚n~vzi.@O].i7uJ@kH]2fFX0 zT)ⴔ1땸Whռ+7~}rZa3O%7[zJ=ջ"*!s7-f\6.oӒ/&@?Uq%B7sgSZEL`6 Mo3(?p"#9J +3 O_0nYO6ٕ ލR{Cs(n_pQF,3hl]vٚvHt;'WG,:4NSOl\U=1_-0LF ˰ $@(X^ nW]2-M?Ʌ17ZEy/laj &::O7kok_?)#ϻQ g.]6_uZ0$ی&h+/=>ٕ,? v@El]͡gnڬ>o/tmn\{mOaGQ3+IA! fc>"yiW.Mݼx>3Ik Dm=I6;O.2_U]oqR9xݾk?C6+IZ|7#P mhN9Hj/}_vLU$qC,^lbcBsuΟ%8)uKu< l0 }&r@gϊP~gJ^7'(פ- qLiKZT(3.Η̲waIÖ,`ƊmG {ZKy7;ڷ#D8?lŢyQ^<ֳGB6=*47|yWIwOA/=qѭUrc'FU8q|JǎxjÌsQ|sj[cÍ ԝ}kv'5&0칳Ws{uCE6ty "?bI,_'ty*R2]1Qlƺຏ۔LgC<1('M'~25L2@RKzeHB<^{b6wW׮w 3п4"̓jiCE)t5ݜo @j^'LgJ/u6 .>dξ8U)Bpܹf' G/m+T I)}o٥v'mJS+}7K9:y sNfpygwK0̔kϥMDNLQqBjGc򾠐 WnVU"HtniaPrR1Lju˸˯ b9p4h*hIjAπIu=)N.ՁT ̝͐a O X3p|NmVz6%.]fՀyJI)π$*YIIg~(@h3!M'mOGɇ( ,8ҫb{@30}!k*\Mjdg~ܠԳWޝ}_+U"yً3^ge;&NZ.FuU0.\JDݶuwJ۬zA4D*a&r;[PVF"VV7wȀQ~uPjh> Y:o{ 3GwrퟖhXs>5^As;w# /?l SW7ycR `=C^;>=P] ܹ0|ak>佸T'Mʺ%v^ I]C)ű˅h0 #j@j¸^,E_y+[qtөL#9nL'^R, 9VQ}m@pAʴpmҁYdjyc˵eY﷪߯O: %)ع'bl.olS %MUKMPC(B)-& :ㆊȌˌ8Q:09( P@BK6m$mٓ/I۴{I7@'Ç{|y[:^~޹I8MPieKdi5CQVqZi#lsjn _5E``6f2dp"`)1&+a>,a2~#n`xQ= j%$"C5l&4mD%<P @%ZÄ9UV=>M+5:k!1u  dw ds, W`T=&(MHsyM_vD0"gRO#,: rq0UacR Xv㲼ȕ?5C0QU*Kd՞W+*$ag19)ͨ4`Q9]r6g f;zcyY(()I9,*Ofj,ݻ4҆}r2?XOtҿnJU 9vDi2m) ̃6NU/:¡Q ߥ%ǤfV̔Q~MlBc87ow+!Qs5LH5TY8L.5aWu.#:ǰj5z3 KSD{͕.dn?WgkL1&o}Y\x{YΫQU.Ԑ.l{ѐpiLPDQFoJ)maL㚂[v,F#NA[K&Nh))`x(E e[5 pMBem0v<G xEs.6Jz,Q8DzTZqVL!X {X" a f `4W(A`SiB( L"hL8(k  %֠a iJ(r^)BJA 0ϛq]Xh̸P%i =f}YGpjv?jS] sihaEI4WU5 >\xNm˗%Sƈy G}C ҇x'z%c$M8Su)œZmS7}ݑ댬ٓl&$=6<ѐm{^t\E&ӕrc.yi&֒vN\X"#0nИ1Nx03r]Og>܁Dcۺ؛0ilD8MXT{&(\*hoyOR`ܬo4jtgӖ85; NVKe蛆ǃ_u0!)55PwPEf^+C [(3ΞYx;qJl IDATcKQ[9v&?k*JNuEeyr(ЅYN/OjeqM,F6?D)8DLm vYrI誈Rn+yhAҾ<u1%7UaYE`)+UV( f, fAQtW ͆h,9lD#AFfL Z|",Q?9{ Bh'4}kTiQ8e)SadZg_9DZ"E`w|4xp pX^@*{X  +@M! vl=i&II]q+a6>/lwT|æ9;¬}\(l_ ?k7xxhMUPfTT]GSU]]Eo;k)#hrМi2aV%cB3Ml&';m-Ξ~Bǒ+֠x:(x۪M L۹`[m}Sw$\ O?4{C8LSv^zX@HO53Zz0]:ueUWyg $Svj4C/FukRE ajڷtOBެLyfח|1ͪwHnzGic#n GTc,!֮jbVk>[`$`Z]+A$!W/[uώOVc:|w-X˒:{D)43k?(S 4FR- Rᆂ"O9*V)U 1cǀ>|  +oΰԍڧǀ>| 1c0 +o>>| 1cǀ_>_> } 1cǀ7}ZP PW {}a#4ޓ."2qjϻf>_/{NQ-Gz6a7Sv37a;_2k0Q̌'7,8'IeU^+qח{.ˎxk /{q[0cbe/+l̹7zه$3]-K81POW0O1Zıu_9Bł7l+;  Yv߿Cuc]\8nZ溩cQzԿ)7I~(=Uñܨxn,WѪف$t&Y_xޓ.zFڋ+LX pNEnv(Qp'HRKts&gm 8' fDɠ%yTtdj8?/Dsݧbz*x1]umN`ĵ?n/IVr^>25h#施RQ ɜ_>|Vie<{7@|~D[ћ1vТK6u2R_^d͐#Sx\ߡBi_Ҫ, v}<_;{4ۦ,z'J%g-7}Wj~& 7Lf(:U ܨ(u3ZX!AoᗸEO4տpͮM~|}z@qRdUki?Sq!]!=r- ]MWpmܪ)| r[Yo>F֮F) YqPx) `9| FV.w3LJQ ٢EMV?s[ͤ0V|Z'a!0. H ^(\'Wѩ&[7! 0|N؍ͻX>~3-=1%RF,]^x<{CN D}?~Ux}٬]hCPx/;=ⵋlB_t۝=L͋RiVvy$:YZNȎK37>9Xʚ_!{1~̒wqnwQGao^lb]Ug0g)wd,+ SyԷ""&NwϨ-Wj44珟v\vy޽R.nBwNoJ9lOp{`p+. CRl$Kaӥ: {| CSϊ=WHe$ /lX8ʘ\ iC 3rcճȺAUDwfN\(/0 FZj\187e=zsВ?ڻYAl#q`^dC;u[*&4k"*ᜫ_T&nJͮNr]/w .:tōZvb;GY(r|pAĊř8Mg]˛O>&\^5?J<Ӣ10z#+G36fqOX5bvE*s~t@1VԐ`Jtx`»k8^ozfIUG%JfDf;bN}wm tm.0/<}pO/os'WqGv>D,M{ًG`wƵ@g~.Й쾐Y]OuJښXS#B]bJ&hց ~CܾX9B5 $P t@aH@0]R'P'U 08.\Y۠T`P]m`m`7rv7_JAeJgˮlYe,eݮI7~ٖ6m{gYDkrT_A' p4?LGS&k (;jAq᧢4 k\X$Dx00_iC/R%ERH!x/kjbG"J 07),iU7+jMEE/ΓIY&4Hj,?`n2a7Ʉajq$\ CevRJe.rz1MfJV4YLW=Q 6KUĔPk.4%JaW_svymeDX.`&lƖSjC/z],Dz?+ ^2E!EcOLH[p|X ͷ훷l~|s̜gfaܤ3:rv_J*AܷMPL- f Ln48rcV+KJ \ s?%, j9^1N\lٖ+_og D]r 9޽c\VgVy;t:@9dga*]n9\;ͬ׏}/pE&kR=hIKG~pvE_6&^,?11,hYWU߮%hx-DxzG׀bLڻ#$kCAQKjm;/a; DL~1؄JH~{0t֞F EJ|c KUT߾,mᛋ"?gbU{+*zm&4N&Z $1W+z(,<֖Z5?50bVL"dEXNg|OЋې]̄M-Flf3~q d(݆MUz(3k^yR۽2SE =_̨zX z4@ybƪ|; ;qٚb5j} &XpG N좐~uU) .cצ/_f"~a鮀EbARI|?P윪]xJy3lfC{GL&@?ֶO\%6稝uj3h5m]{y1v[G_P*$ mv. ;B$07],o8rOm}搧8~ݔ4js*Jubó]x2pVAXR$ZP F& ;ɩ^&>O!Ljq/G۸hr#)I96[)!oYIpNx W/ayb45&p?zaԹ5Z./9:0X1SRF6= pOoxVLz2 ݇c/]T@1gv D)UkТ%3]z0&׏ajfIzš+/\n0uf+%j2\mO\6b aCU1>/8?`  QAFyog.l{piLikxGJ[0aƸ=j1܎X܆dc[K&NhRB0<E`z 8gzt@޳@Ah2lt9X `` n $@%ƇL!@L-P8@3q&yJtvQ=: c&70nakXeDKfƅ0-I˘(U\Pw?`Yaj.^'zW]=+D ReںuFb6g 0Q#i8fq"1P\}X70*2Ə@zC*:6 9ٶOoU^K]"gvQ#/dkGs +*M)*^)6vh ԑצ sj PhϢOS˄^qFΜ2}l8 9ҌќF -8I7 =*nИ1Nx0݁a'3@^W1mkt0ilD8MXTٵ)=Ҿkov*"a1i)fU~9PcӖ85; NVKZMCA/zB&KGΒ O$;Y++}bm=\>uzMd'fG.0xsVtt@yAEp {a2| *pox|dmq̲u66ƫYL]z.lcgSkR@y4^;[d*]}.ׯ{.2uz}R+kb1j^&Y>!li3 f"x%" JGzP_ BB3iEa0cuPB h#(מԥpھV 9%0-W 'vcAvUI pfuP87Râ2uBh2Ʈ[^Y TXz;M6y,F%[GmZ cdvuRRWBGL6}击*GeӜoa˿_c}vu5/|v}ɆIT|<86FnTM[Ih .˂sqٷ++҂XhI]r?ԤAlՓǁ IDAT?pSx3Eo2@Fe5l3*~Y#oUwX,|u4Ih3mB N4i ŧ;t M"\!M}T&@K4ټF`-eDM(v6iZJ7N}(2qۣ󓳉ƟrN=i D\~IWcqBP_h1⅝=CA֮[hBs5shxd`ӷ_>y3vG:^y(iU׈0s!@FVhYL:ׂ ٸzN``(J\|?ecT&սC;ze2CoQ[7dJVvlˣsU{vlw;7j] 0aMy{l1Z1+I|Ԇ=;w}`_ݵvg[X/*Y _0>u) \ŭ\!>#dLƺPWy;`R|Xm6{30m4nӷg_L9|W~ /=O]07`HB$n a`tp7sֆq NLAt;Owr0*A Nr`Ł^/>Դo ߟ$fRLyfחn!q%_xȾ q"]Z!&عY_C7Uٿ#o7u;Oٹ(|ʀ.{Gɼ7nH5oص(j}o %o36;]-5өnݮ TOWlP-Z?%1N-ݗcǀ>| 1 d`dDL1cǀ>| (7gf@Q1cǀ>| x1cǀ>~ +ƂxomV=0I.Hp8ɓ."2qj>:&$V0@qf ܹnF7GD ex׋rTѢ}F\2ቍJoFEjph<0Q̌'7,8'Ie[vрgDf|aÖF4}\qw6+v];N^xsw<2`Dkypݕ!O:Ws}xObO9Ne}߲?xϋ t󋘼aE[gS*t -c=1~@@H8!\17nwA ㏟wV bp* _yw>:/jFؓ.zFLo 7b)顨7@j!a kh*8y7 f5,M#YOp^zm5>;_~@ߟKnE\_6%9s'|Byvod6x[Kq7Yנ=ϢA4Y$Znq7[77I`3 V-p@X!AoᗸEO4տpͮM~|}z@]SV.\vs=1%G }"uǾڶ7ϭ|~Ox,tp&FA 9 ?LEdX 2<.e(x^ ᢃ&~@fY*ŃV/gf@Zސl:AUuAuC`P+HkZʐsh|&kjG\㭂t].gbD6.$>y㓃̬3,ywg}uSbyTҎi#' EG/;Jԡpp{>nް3jZtb ݕ^r:NB^i*Yl`9?٣rep+. CRl$Kaӥ: {| CSϊ;)x`I^#p1s5kj8f NnguI ()Fà Ηfa> RdqB@BGAj23-P_yݑä-[3 ڠBÀB8UC ۓ>]T骒y4A!^Wh^r0h|&˃]U bҟ]/bYkQ_X4U &~i31&h+ KyB+ǖmz0{kgO&?9z ]cNYH xm4h RD碴dvIUG%JfD=|L!|c2 ep x0% po  .L?L G5? R@0D8z?[m*řc5vʻVݛQ^xXZ# R t9 f?(H5x~ 9vn#x)~R{:* d:uMwWZq>sY){E_G`we`Q}ܢ?f?{UX9=z]lɬWv[whB޶z4bE8ޜƇy_5v9BˉE#bJ/[d'Z_k6-U=T1j*B^|h|&F 8)479]u{}>6m_W #z)<5S4o*?4֡Nx^[#bS"kA~(R v,4iі~RH}h}H6FVr(W: 7vq*W;mxX ,cgƅ__tb~'bqnfz͛n~]/n 2V)W4U4bF;U^mNLulSض@_fi K[ۃ4LZ fmjJ{4R(AI-yޑ 'uBH:HAفJV4 v/eK!RU 08.mPD+]p  \4љ,Bj3A#xzrvT6_J,eJgˮlYe,e7 B4%#r3a3P}~]wԂ8OE5hXע|3,1I`H *5Xpܤ UݬqBJM@ }ɕr,!;Eg歍K N?Ry ? \066ľZ ="9Y&,M :aPPN|y2)KUԁj,[S0Od=#eG8`KQy}I\JUidvRJe.eV76JNhlz Xau7zcL.<'aKzy38BJ]vOQr tt7t}24&$%/h3ɺoőHϰZ XTbޢdPdS+;axA-Gz}(Ɖ\ؚ-rG};nwItt:3)ޝGvxIe$Q|kdI#t5 ?L:<^Z `츙Miק0b0,?׾n^9|axBJ? [Ģا'tl׉5YЯFĘ/Xо.ɜແE)7 ` `_}2E}:ZpG ) Gj%$"a_uvcצ/_f"~H ѢSk ) Ӎ$ f(O&tInR"u BYIpNE W2h5m]{mDDS=07],o8rOm}s"82`ݑd)@>$BRdyfvyx*e0A6!ѡp{l9 '__7*ڜC\tUI֦i,8PQ{/E sE'*Z_M$/xh4~hTյ ’"Y J852IYL.pp&O"E< \\)w>OW_|^ۡ[tF]ˠ41ZތJHz+/\n0u5%#dv*22lQU.;.?<\^+wQ FDH4q vJ+w1cfk n >X,3#G Nɺ= %SqoK JQDA[\(UDa emO7$UzPa|8 ¸Xh4%S 01. 1e=CKb :[5 pU~J!z*; -LfѪ.; Z23.alIZD[0;̂?rRg7!W3Lp4#m4ͪ;r5{rYB4q#)VQ1~,.;Z z5pc dGr4m|X38kS)<1Os8+e0(ڴ/5Jd-8'9+euQ@p\'O4e۞W>ݿ5WFÝ*)dx/Yc&LO /iR,(zJ(C,:!ML tV+54bTֶħ0eY4hkp=.:@12醚f+tiKvĝe}W'+ƥfi-!k=!%٣VgɄ '}{Q>PLr~5]L鬩 & [qs`(U{ #ngy&6^rfRԊqOJ$!c!42fY;m@nV:a;g6|߄UQ?:) 7s!,PM_ʑ4'[X_~  ]_G~c7~~=:j??9l)'_,I?oWVֽ +'Y& ҔF_,5 mFEՏ~T8nwa:N$Hx8V?.@CWq?X?A~PԉCoQ[7dJf:<z;tУsU{vlw=AMf}Cb4;O9rhp$q=SٿWǀ \؟!>#dLƺPWy*UՎ¤bVm/f`4nӷg_L9uB!gmZ) m);lpڡe4 ^8$:e'u eʭQfVM֭)Na"д:N3-Ȁ73(a W!^dl| A[J>| 1cǀ͙u 1cǀ>| I|aHcǀ>| amm_?c {}a#4ޓ."2qj>:&$V0нStv#({NuH]fx׋rT"pbG<۵䅗5k*0Q̌'7,8'IeU=[2"fnx3vV]?>} pˀegXɝ'[ivEѳzo&ˆ,"nỏftsw¸aKV%hcv Y~.V]VKܒFN^|$83 \U}taF=UX=8P + +l|!dxBoTzmd66W`bkzF>diCdE vnI?{XZWVo DvYaIl$ 7HIieMy1cVa]Xٽf2 #R IDATDAZFEq5{ ` C7OnKt'_fׇ&?>=^hz .\vs?Tg ]-s:S׹Wp{ogq^A#?o$`r`C+gZ; ޸![sccntnآ| 3ǤUؘinSBi{gBy:ڈ69U0.oƲtw)7Y!!=t^~ D}?~UIY|ܖB[HN}s񺧖_Wy/'ǀ t].gbD6$>y㓃̬r ,ywg}ud'Ƌ/~PI;Iʳ;,+ SyԷ"x3j;< $tW>K{q[$~UpesG=ojWN]瓇7;7H8˧Ku@?wUEᥙ1 GX+!8W6vqAcTnzY0Jybx< |  'A,@Yq9e@}9U@,1LJbmX =]!0 * )8 h)S5乎 =`t0`k5=(qش|6fQ}*%Je7Į(7˒}3-&`Ff}>YOϔxggטU21gZV4Uoz`ߚ៴1{_\(fw( V{(Jٙ$?(ظ-ݗcϞ J̈]M.niP֟:|v=[.?PoBW{B~P ݖb,s-mLه1V`x[w2EiQvQ aҒK-XDtgv9tS?řeڣWҌfLlQdȒ&f8m/ +[XmQKO6lqDtXL gp P !Su# h/LȘKm |ZRCNُ80e`JD׉Hd ]N:*XpH΄)pnJ,ȶug$ء+ZuoFya'jIhiV~ +>*7g+BBB' ]Qײ6,Zv뺫UuEtEAP@@RH!}gd7idޛa@w!~wn=~ΆkFnxNOXKOqlɋ& y-VK|f>ޝtY0~a cK 7!sRޖFRAo|7WHbݟL֥g ˍ&jUo,[&"W>/VVD-/ OQ=9~ rv$g s;.uU*npC|rDiÅ|oDںkǎ]ܰ-lnyL? MJ H{Ώ=s}= z`S"k_iHnЄ&SwcI '6-hD~P*)´vWv64j_:_vC%NĢni1[*jV@("}}b%qD:l(4 Zۤwx$9R u1a(pY6!M[YJT@- H| uTvkǷ9ݳ?RQ6+ cs@Ɏd짅hdA~|O{m;!HTd4"6+ yaaʀGŢTEsDE~ĸ,r9|X6J PKvve٢~4k1^\'bے4kI)IqSc̋`wq?{WY`-Ev Ml W;x&n7~l&A<֝E)һLY3is 5Ua[.׸ĽKj}Y; :>h)+Du'*@!nv=j3{1󜀁 jE/nR)[8a=cq 489]sdpH&vyFF5úAH]csݰ(|TaqPjxƟr=2u=39?={f?tƃZށ0襵mߝ4{VTΊѧc3j /_7aab٬$ -uiB6&ipp *Z|k*gfGiKorL&Ƹqs)/!i3ՕHyBaKuztVь&ȏo"ѓK"HFr 6 ohLᵰ1 Y"SQO=2l R`c[M0v]f7'"О#2Lm8rQ ^ D߿a&;+r/(Yj-LgK#!:(J48`WH~yas_sҠ}+4y SnRI~B\{(Qj ھŵqNPnrƸ"_L;Hl`E2Ĥ.H3VߦVvȗЙLh\3dq0^՟>~mK6ݮZ~_Ňp gC1 ga]n;UCh\F,2¹.xy00p10:tZj(M1X& +2,1Mrk"HróS|us9|kײ&Ui\ȫC'4TU`$61 بWH+L,غ;C؀s[;sV Ȯ6)Kf#A)w)`IE`4F z u7RI4$[ Qh vhar]8D2DzF@H!j)bR-(d٦<$]>7.a\̴E]" MpPЉXU*Ck).ţ2B uvmA~|FƊ0v Sd\6x(5 &JhxNjOq5R#@HD}tG;[?5H($e]!/DnXi@i`[?ڊ~wɖo>s 8pBdj"roKI!T:Ა`QWcSvL*u  7>?|Ua%X>ڲ'?vˣ_QXAz[;J5=PB'iI.|]Fc l{sׅ+aq:O*%)<lɁ?/O$ vϗO]XJ߮bYRd iʍ>vk^>S;ç)[_-"Oq< a_6Tݽ2~ .\I6z,YSfoVݛ+v_?G݄,Z?T9|sF0ܤr=5AK]p ϐv=d$X;mۃ*8w(-fArd'PU:2)yZ]w?g63\'vľqիVqb_ׇvCG_ Ů9GҌ~D~{@'Qu|N?5yXuLm갇&z4ySimŬo5jw;36±Ԕ:E&M_t"LPފNW;, !&raPayݬ#h %ەsS1\Ps}g)Uv,z\\1 b0V]5p+?Z+،;i:moZL>vC%8 >rЧC;ETXA1L00p#2Ɵޣwݷv3}x/j)1nȦ7$|,+%gN}:r}pwQF,KsW  2 4nʆ]f-ȇskjʊm`MW5˝Wݦ/B]ã]>aݴq"}i+Xo}q{'α\ʻQRq5Q% X`iPMțyH@U5*/_d)^ F5#{S_D\{~w)ѡroT])+\>6EI "fQ~ 20aw6>yoy=9Ɇ b2OU$K嬃{bM Fn{J¬Se20~Tc y~ ?TULG+t< yhIʶϳs /Nfkfԯ5ITOsllz33nJ#8ňǶҎ \zYh!FmCw7@{E)G-3yEgG 9VʼnvB 5tߺE./fJڦcǢk SӥoPY%^J9$2 x}N w)4=epasYlH0&g*R M[Fؠy ~`o%aVQx5;"B_ u._emw GN>%yD$%JRS7);ٟƦ=n7՜$ҙiQm%îAw+06׿6޿,]KFPO7u,*ߜS-q3 W..yf3Oin t{">$yǰј,t7$,䭐!%n]}j.u iaP^>0c"tE.NȯrX I͐'!5=JCe m:h fD0}6ZD3}`,VTHT#s(l8Yޭ 4z p.68xf@`4Q+ojJ *8P6xTns[|p w].>b,Z/VVD-/gNےH)aP4_>V|Ϙ$jRM ϾiIKoK׭Oe-a)6 ]6Iʲ~k @CT4yy+w?X-;uir' ,m'; ArP=Rfk;vq6?۳úAۅGy^Qep13,Gfg Z#}8DS7jK NYxO(mt<{ IDATCN//1#{w[e>Ý:KzRsq{"5 :e8.Y`9܇UZ^oM 2DXN-A1Usl\IpR '!/MS\Ϊsn;a7-U}YV3Y,dnIS/܉.&H\tS6KL0-R^|;8kkU1 fLVzkwniV%rbVV٢v9b 1M2#,S-v̘ ޥrH M`.qVhӃ-~uA8r;.BF9`<-Uo[*4ammҋ|GvQE̘Lv_)3;/7ЮMnj?]߿ /#Oԡe]|ewHD&v-Sє = lQNhLe)P׉ Pv>lB40Oh,p‰!l 9ЀEHgk>ViEئ7ESisYmF]GU{ME M[FJzK܀ ̟iա7)լ@>㍛(,Co8G-S4sעTEsDE\pYԍsl6+Zs ve٢~4NwrXغ*L6CFώPuA h7c 9`2ٱ]OV;%/|e_.]0OeQgR)[8a=cq fMe*=AΛV+v`GQOGbW6ވ}HSx|QvU/&ˠ0w7́F&-BnϜX/Ԏ3 PHۻ]H(Z40hh D_A8 @ `74;SfsUcG ]hz2%nS2YIZ 0}ᑁ)"L8;U&"DgfGiKo!=y0nuJn#j4|ҕyBaTYG3 ?jGO.?ūNFrIAo$vѰkacx"}Eȧ闟{d r͂Qi)Кk!}<jd50i9f cMp&7v&.FN. d5hl8rI(x~Xx"GW1Af‘zL"U3^o"b }$t辵a)DzUG^8m{42EzRx|F9Dr/` W)W'؍ZU[n;YzF}sPKέ9 c留GCy y_"׬]Ž]spB>47QJ-qjhDMCFiu0L20Qf$F/d뻲Mhz] D3ǎv6OW9¦LhpX)ؠdE^.UPh6V RY 16_²i5hZOX,4f.Im*lG۫IróS|us9|k ^IU*j w+amb1Q)WD."xn6`l3jXXY=fqY<$5qBbl5q]0唌T%0;zȀ.HC萂0N&8S ClF0r`/I& @nB Ec6q, a wN/n`2_NN"ܤΎ"5s1j:~qK'Jܮ!#I6u&q gMo.tQp\fC[䫖L % ]8684ߖڼ"u z=cPfu^`+cs21>F'w\s.Unh Xg% p;'5ɕU4e#ᰐFgbkM/%ztuݼ C}p- [<;o2ٴ-+bdȢ(uR#T )iREQ ՋR"M@] 4a1[vŞOr׺JIsYhz2%k/CK;,K][nyk0*K=^'Ljy<荓y`hy[uJtڢ2 .97W*Vɿ~.& YPSs~ vcs{b(`IEE+ 3&AMPsQt4@I+saj$?XGPIY{7F*TwzʁIv*) N;x,HN2w3h׹Oы[rPL7 (]lT49.L5~F:dN.z W,nSQkQ4/rIlTd]lh-=OMn*+llw.K{|0\]i;Z;1ݴbdi'jR;l0)+g U)TJ*E N/j4=MGm&<>񖥩/H^xf5G-SuETy9R v1wݷv3qW!ߐP+ѽHX _W&j$o_Xkm'-HnHJ4i¼0x .!q U ,),K#Pw.kMA J qDwcCOSP`6"Xzڣ W\"*0e]?M.AC7cHUo>í%}07yEyvTDd,&_k>]Q6=ԍݿ7Y_ֵu]Ǟ-O[䖤 |G+v](~ScǢ[lݕ>;Z}^/_~[d,<2~% _0-FitLQQ|m L2MJnЮ-z񵒾p6C4}ĹgmJ .NF =ooz>W7B_t-|vms;JdUv1sՏ0YC& f%Ɓ@^ _BR`*rA>f*C& Ȉvp CJ FkGU^&7ccuGjFUM: w?U޶w3s{*.wwo}-|]>?(u͝~LֿdJޥG1IRui7.@( x}:0aw6>yoy b2OUM?!:U/ ߈ϥ'7jly6򎔘l¢k{zѮ9wd"mGO?n=jQ yhIʶϳs / ekfԯ5ITOsllz332٤8_af`njщ-:/Z![Z?-A_Ŭ2w%%XtX̍yvc6%{$] l?un(Wذ/_zxva:X{JeQdl M@*W..yf3Oin t{">$yGQ+ءOon݋NO 5v씲'pp4'm\̡z=*fm0gЗ%2&N^b^ Gyy8'Ȧ F/$c!p@`jiu3 U@ qRh;gh#eHȀuTRmVԑnUå+4Fnޝv@>g#\Ֆ)B]"a߱af4Epj41y%/o="Roy9sRޖk* OzgsnM/ GK[兦nz%vxJ ,*-/f nΦD }Q2a$QɉP8J-;z<^7fe?^7怎G:'[pF,0  8k/pp\LL2}v|ag53w^:'M_'0-R^v5O/?ƘZd ӫh\vL/Mw.m{(ݿux$_U@ީT;>Qڛ°Ηv+y}yx%zTF*97j^{{%,B=\n@AF/=/.k":rIwwjA΁Q?P\G:k顢 "G9S֓8]+c]РӤ,VИlV@́mGCtAN;蝏Da fқUUNCy=XQUAkz􁍠Z.X0s_kةWA`i+irHRN/z(48L^34=4򼋢6KOm\֟-[>v~Ǭ6+AXԹ aɳ|/!!$l9R&8w`QuESisYmF]GU"R ̟iEȄIYf ݓki;Zb75=0yj j #6hC X<@kzhj'p@%]^yDÄ0aԠP4h[="HNkZQW1\).vOv fJj _,O[j0kk**i+Ejk8O#,rtVь&ȏo"ѓKFm55>KcMR`A=;兦!+B>umLT#xN4inW~֥ Q9*Yna^rk A6?Dâ9:hS0vѨC`Cx(8 <'Gt D&+ɭ@HStn XIYJ+ \DY=2iTvk= %CIF p|\!&L8W/v V;^#LlիX?c:H0c~H]Qf""tHtEmC:nRI~hBF®ؑ߮J>g:.uV:cq=!0UUV'rf B]r0V gsf4([wGyecY N R ~a/O_ēh"̢JU8ٔP`"ח:z҈VN@$A$@t9`AlKU_G6jr#P E7BAFlOGBH %bH:@9N CBntzK h1 G )cˋ2 ]Ũ[:Q7.a\̴EtQpu9ͦx*ϽCH#MW͛01آų#mt΄o'MZ,<_o"犺"SNؒ17*K*?m[sMN ,`jjTKEa%;ŒjkALvmѯl,xUb Q֎}MiP~ %A1~|4C'_w.mc?ߪM_~7k7kw|TV@bSӺ>v)ycY|p 89;>ts}Q\)У#an'd^v$yˎh)pC$d|W̎NK}d]0t a #]mcZ_td1,B]TWȰ.U*G #-zRnХxnɒ] g#@$e |&̢i:} ;W#2+peV8Kљaaaaaaaa1+ob2 0 0 0 0 0 0 `_r0n%ڎBoȿ`<ז;E1@UϠˋL.}7?]+ k, Aj=@Yx|o",!@kH $s\9Ҝizn~yǂ5JНkz7$ܒ_-͝wjN7ću~g0?G`Ҙ9m} f4%T_QIޣl!t5{{?;=./U#{CUeu C'ݹV/mޤ ;xAn jʼ0a[_L㗕5 ^6HY`!Ow%f% BUc˕-兙 qU7ri蠩b:؛@ fA|?5}h8;d`|׌YihFcI8̙3!q< ~7vpSzƐF_QTI`V?~`ڴճ"e=m8{X?GX{JJLئM%! 8/<)fox#'׬Y_9ksӓqENcjer!kdr#-͝wpN7 8{4)llPYI 9sWճWtii~ʳ,[:TjZEcK*,e dr/Yvon*ϴ nyOkhK <3WQuE|[WoNPS ŕYo%f3ZGE5+!SݔۨkliszǑ݄ 1wk̨_.kD? *u\4wV9ث7Q<#sBB#ozqn[Swי0~YQcFDnFY0RQ"p0 RɁ8HTpxCd%P"iM}v0+nhhAx4\G,zWI#ywgcZq?tAT  /S'FxgY:U0oRpd*ڼ62Lo*Ķ@N1bo„g?ᨛ3E~lVXXQ퐾.A#_k<*8zy#Bfڮ*cұB?nMb4 G"^ͽ_KwmLAf ->wD[^(RjD1.X,}Q̡_]&w#E]ʀ殖ѼͳCK;KC+#&g*W˼}K! EGݷZnt?ik|y;ny㿃: CUex)^͎BbvKNx ܰ nz`~y# /Cf&Zwl*K=MIn֞7 LJt l1qOn]lu)O7KgE [e̫ذ/_zxv>:X{JeQdl 2+uEq 3ΙQ4HI=eOcXceJ` }ioHXb[!C޳G׮`>˿n˺vƴ0(SѢ@F@&4tx_]?\ i0 gOABࢊ֗t68|l3`×7NȯrX I͐p`,V.ijc#pm@fL hD:P˜THV2TH*"6fjD)k,i D0; ǁ~,i؞"89ΏĦD ۻUͧ~kn_{w0U}]qao;I3o}x:Ƙ`J檍WMlԋ./Tlϋ.yy1z˙4ڼ'훰\Vy-_w۬ɅzHss~/X~T<ξiIKoK׭OECVxxϘ5F_t-j0UJMZ{.R|459P7"\{1fg '"}5snpC|rDiÅ|g(M+o;Rj枴xYE',v rl܌œN۵Z8 |qp8hrtVUw lK:'Uy2rVb( }%gֹS+0K"Fzot OdL `)RQu֪&c@̘^\ԢkwniV%rbVԳ[g[6n`xix\ djIG.KXY]|՟ݽ~':* * DB 6;q`0v4q6|q)6L I*vҝot'N?়y?3;oߛy=&cAUЬ60h+մ%2N 6  jШ2eT)PZ ˦- 0 )&-yp&5@J$s!lv[@P$iztٵV.gvFL7<տuUkҩԫV3H7)e)/!e^THc\KfCߒ0J$ubjlyךmu-zYppń3,E{TQvZPKO0"a*|I\VƏI%<ϔiϩlP\nC]u (2-Q܊^ڸ+"]\* a2?hbFp:WLLb~t{7(Mgp_RX0ϦjWsfːK[uV8Jmzۃ /+rB.G޻imo(A#=yYcr}XTA#%戾/k~<6RP` @ꋛj1/Rr L?/%$&xpw)$xfRa3 {uraaey#s 7ޛNUD|\tó&]-Ȣ A@tHp`F25N6C 7Mc@B1\%қ[6> A6kZu \ 1Mrf痱<ΗH1SsXZ؂3yX2ם7i5rٸ@"L7sww?S/B }D`$"%~oHY1#ϙ8orҐ=d&]sx"u3E3:1ɍ=wӜL5,V{MA%>=pOPSVxk…!esϦ+(rIJ?5j`,|dcR)RW' wZQPuq#i* _h2fP /Zxb) @3*U68u[P@`bzGi/Z^3 a"1,3<`Ǭp8foo~2ֳU,8H=,M Bp㯽.ݞNҨjw;0R,̵r9>d#>ͭe}T8~ BcowjF}uѸӊkp&]佛g . '݀`KݪiM4V]x"¦1X(L|o6+:ı5?0Ϫ<_NH'AsYP(\-.% Mtv /QjhMӴjBw`8eQ~&jم@ÓR5JKU(-G{# Mi. J1A[$Q>IDAT9]ذ,76mNc'A6'ĘRDG(^ N!]6Zl}jiw1/3g hb1xiyǟ*M~?!F? "e(>zpWzOaEg6p&{W^%IMh4gcOO ׳`,bSڜCGS֬ܲ0a̲^r5^cJx*kKp)Ǧ:ݥ3k[^̭"ÈT}/{D T1QS]"QHՏN+\'۶e;n BK`dM{E{y9 ;ɿra )mBI:j ctuݮ_p -};51AWl:L9'5|mP9 " ΞJ5w$X0/-j8`4H(0āa%+q+=`oUq{&tr;Z8nh{L0V&|<} z7/Upp1KF36 9y`%lOsj\uM_ẘ}ONY1Y1m)쾀t;y .k^U Ru7<"KK~҃K&xo_ [,Bއr~(sS=v|Fw  ghDyD !"D`₟ڟ;\U. [#q֣])Oz(oAc۔vU27rk:A${6@n>b=; 㖝;jU]m"BN7:.bjKl+ܭ[_ УP]ôS27y+QB!@ wNpRC!@ B![n'ܢ"B!@ Hz @ BE`XGvZ81&|~"݁O7h Y?˜ -=Tӧn&"{5ʕW:$a߬J[J]T!@ KA|S+%+>0Ҿ/wBg.}4ͭGݺ94vAI6dn=RQ߬,3yfw|rtJ/O䒭Dop ɎO;ԷSZA^G_/[7c {uT !@ !eIm?oV #KμrXxUWYtE 5C^F1uf I}MiG'+\ؐ&=8oH>߽8DL2m#4RRϗ<`o^ھ #qq%KEQ}#׽]iVm9&1a)ݚۥ#u@ $x*XF nYW8g+^PF u}[?ekFV2B"MeuM3ԡc⺔Be7I \a\~ߪSN@m ,sՒ秪^gRRݢ?Ko~ҹ@ҝYuBcʖS|z1p{wq5iIqʨipj}ʊ ,&1t8r#B= *vwљKk^f |;1<2=n, [KZϾ]O HgkW7uhP6`}37,V (-ը-~xKj; JȻYdJCkMkA2!@ `YY11SYSWt=c^1q& Lϫl~"c]#|"CzlvxɆ=)n JL/yB~.t ‚yVUΩb⑙ԥ:B\ɯ҇':Y.A#)Ԟ[#\y*X DM?eUf3hj:*,K ʴzVxx~P}}JQUΈ 3]w<;+ (FKw4ЕwǧgբӼJG [;{NJQU?Q8v_WE).=N<<ޯCH0UnKGZ|ۯߴ">SFM_iU`u묚ȅ{4kKgӵisOҗ=fQ)Z͖Nm1v(ɪ,,YqE#ʺ-TL*""Xګxˆ8T^,=٘jf⼝N\KH~T1~ `oßod| I |<%zsEIlO)N:u:$Ik_ et*6cr T{ SMF*ȎۺEv[ %"݉(|2=%SSma,`Tҕ[X#v fg~Vuqp)JJA#׺̘(|j]$gѧ ewm_81ɔk/fvQĪK^8HGi~ٵTZeU{Xl,fe44bjEa,uَeP#j%2kkUGUW>G]{wFڢ)lg]ј$z3 OQ~, i0R0?5mLUZ,8X=f,52;8b@)ȻYd{-*DiB!pgEjuǮt-M u~9C\RR/\uZ*i 6])q܎||xƏlڸq~ܓw>L6a|1ס&W*IJtlN!жĬФXl,%Rqt0Mi2Y{6 |+0aF8Ճ# VjZ1LHT&y8G4OdW=0B #Bq@lf=^)l|\BB|LT'ZScCpWԔ E~< rkwwו!IdʪY$2 ߊaxkBTB`Uی.Y8dx^EQKZZ]iuXoZ\rJϧ_\3"_'BIO‚Yy `8Ac؞~u{9Pτ&0{caʯ(I]N7wYbRDݦdUgQ{)IYbb_%aIy[>޻Ӄ]yˆnR12 p3Yhܚ(#2zg'6zUٓi`&n4_sZP3V8luC$W=H LMEo,=!=MxՏE>/}ˆYqۮh;J6q+׆|T/|s4nݓU'' M{*3ӏ G^;LrtB!#`uAH'I{&\WSYquW![=;TF>>QVJz>-O6<˳{솝Cbu9udlj@8r3DM9YyY cuv6mq.~|+0=K6jζdC69h>޶Ǯm ^Ff -E)o%7ƭtS%7n$'B!@  i ;ؙ E /hz˾];Z?(2 )Yk/$egpw-;7{6#B!^!@H1}L;) +PӞ{rq 0H@ @o@S@ p`q`MH @ @݋A;x@ c +{ * @t'7Y {kް'Dx7 feX^zp͇O=ɲu@ pk) Jxy^v0%raBY-韝R^k޸ /TۻwB oVHѯܡUל:{vO9l!\{3s&OTϙZEJ]:<}W>Y2क+kC2N.%O11DWLgӿ<[A%](d_^էi]@ ]4};vD& Mꁵ(K\%ۛn}bEqg?9jWG.:|g3h]:/5Eu{!& EX]+Z0c\É {*59 vƙe*H;}gwAK@ ~Y vYIp8j,ZS8K>sf*8r)W.pL8t DkmYxce\no)z0].II)IxѭeFsVN"3ET}z1LŸY7Hf՝|vGIW OO\,fvZ”R_8vH \ҫ}ye1I+fwk" JMSݱZ)ntbB75Fkv\{ fSnnU]|I2ʵDo!S<#mi56"|җR/~m3ÆnM T[_-wٳMmO W~dz 曫5elFmSVuzS2~DDX Yy(҄A+@% S xKaR9lFR5m:Vkf iʶs)sgMfIa=!xqYGCe)<,CÊGG9!ssfk/:Ԫʅ eդgYBEQ=WTeUì,q$\Jۤѳc {}a!崰)PUoːH=ԃ>~3ZvKzˇT)-Wjʞg8Dh=|Պ3kz,lõ&Ydxq @&z%NT#cio!Bk*&./QZ1Y}:~ /Js#L|a9헕v$7f6U,D%V-lf ,&! tha0@h1w%GSM z-+ͻ5 iޏ )k]"T՝{o'9S{>{/}_/}З- &[-LX!M,aP$D8 L&e3pgL¯~Aaj ]e7U_ɝ`\䜍eA\F)Ō1H$V$Ad{ K9G{iZ>˦-ձc$LކHd\}{k.tY;~w @|[N_w֏Q[Un.q֧Q)@d $ǦEp%)r$/86ZvE)Srq"&`D0Q{()G 5,.qkA1M>iHnp: XB%`e5lHt2V^`ن~\{6b4G_ҵH)2gb6͕?[aPi` Ng{KFAkQy_0I.,k'H$aq0~r_2n;: Dmm32*Z=Fecj\83^xǘ̪'pQؘIq* 6n0uT{a[Ijj(E6⋝%ԋ-# ~h3}dN{}PX\[sݷiʼzOqp/=c "BE6/bdSHqҨJ~D<@ ub dؓU}] j}5Вu{@"@Yw>r&فdiw[Y̴'u[v_Mٝ<@  DUs3P1V}h yM8S&-M0:邅@ %@ &Nb|;%V_Z~r,W{dČ/F\z<wz7ff `^f|>onKCl_l&+{ĩo^3Vkc$,̦/;" Qz.HirqGϨL9C_=[0֔x(#3~5g݋n3j1?훊2JNn8U{///C:}n=S-nlG߽pH!Ӧ-pMqn|Kp f~[VKY,ߑzqg8v+sxg~0p՗O$7k֘) C+٫~Ἆ% N^7nI]˲"Ӗ,=WS OX>v\W߷Is}@,3K˚Y-xzRmY(2݂]b|+72@ p'}JW^7Kv/ CHNy0).Nc6uU{diecW8T=V|(%f/8;`Vʬeq"X׌:V"̜%I[W my$?bCgϬ+^sZ ū(p'o.E3:Ydx?K'~}_eM"+^bZTU8bvTf .zM:Y9?#ZqK7)Mo|3@zF n.!bVjȰ@^BaϊFܐgjx"~ڳ|:җTgn= !|J0aw`Lфch^vf(a:l+1x}$mF;8' 99{7,2XIvH}Z$WG chUXKqJyI k3ߢV#  Y/=f8D{YߊtjeZϮȅEZ5~oAx}'eI_s.z|bHND%Ťl*25}]βd9&ekݤ521|m52@7KWӨXUoQDI/6sIJõ9˔MRPU퓲ͤ3:$4CYVTpf98ya[ Cf`[ܨG4ka>Zu6lҶڴٛ7m6QlZW|oăXlJs6F(8fv-$Bܔu{4[kMe0ޯM:٠[mz]5ÇWϞgYe4EW^Ƌ f6_C{eC/&"c\mϱŃ2Dh+Ut5/R#sݲOwP uz3Z?6Qe6UfkoMRVb9idy@&i?֔Ɨ[7|L%Z hT eTfL bH6rGqp2 ¶H*fl%O^8g kWwl9ym*Ùǖ~#enR^ٕV>fcʘMII~J4W.JEb7YYvӣJS[EG]p%Tfz>IuUc}K,͠,,K2_-SpXFSĖ~Pw 3-]d ,b[O6 dKV c( 1/;R ›[v즅B/|eCo}/s w'4~љa('aƲ>8HÓ^+,[7vke{ ٌq"1wn! in^ ެ3S\XkO>FR(ٽ×zϳ)}ݩ@!1%;&vB)[:|SPVt4-B#j9;h>v2oϖ@?B@ 'F ߺxَi3'nnlX7LW[4% X_',zhpzHy\3cZ7mOMkP zGI1JgqF'.DD;6]-( )J{ ;8S-M1;}&@ 'ϞsW#ԸUvb Ubh 3phv!@YZ;}0o.o`%@Tvm2>;w3Ye%,&@ n_[y:  @4 +oj w2&+YaO?zmpo 2V j1nt ҃k>|OEpah  ʁF., 5P|}f㦆 9:NcKiagkگKjЍ9/QB@)UV?m WNvwh5ΞSdN?A2(܅gs`ekOO`788i^OQC2N.%O11DWLgӿ<[A%](d_^ϵ@nY9}rV5wd&.IE(OsZmgJdS|T(Gqt^*Pf/޼u#-8S˿:r9l޽"S祦OQSz9D؄5!~ kEcZ0c\É {*59 v}ie*H;}gwqxH@ `Td%+UFrK\^ql%EUzVd/̙KbBªȥ\}1Щ/,vgOqYp̋:w-$%avGP&]GQ [:ab\̤{0IS7W]0gQ Uw%s^-d>>As>lXՇ׃o?S6sKA~Y!c.Y ھYH2K#pWp=)=D>߳7CylCR29VP>BIa2oLtb J2sֱp\urJt"Д)tZڤ{|tw0s @[EAxU( ]:u$y|s)睟VZ^ TG,sT {*+X#qy?MNqy%.mMgZN>S,sƤdqoM0q΢UD$5ooTOWv7;՗\vhc Xj)GɈSX^A es큘oOU"UuoC&-6?BxFd` IDATjlD/_bh6|P]ukbf*n!˞mj{J&N7e[]c2Q6  )+ w\t~U)?"D"HIʬ,LhI8Dh=|Պ3kz,lõ_XQoEu @ 46'ThTH4 5sQsCmΗ(ì>UCE+abgH_vL~^0榿ɗGΝrk/+In5 m'YT3K$8{ݬab\$a×- !ܕM^7/?d44YRSlEF_V:p;婻糷rٗҗ }1`r$(d΢V ErHێ dR6Zw&ʤ*z{׮#67h 8v c. ! ^i* h8c$ õ碂`xE.{l&!qg!wP] 9'$W¹#orv(@L kgs)sۊ(΍|hxԞ 9m ']SJ/o/]tK{w|XطCETY|%wڂqA~bszB=pمb$h PV+pҥ£4-^eӖ1&^oC q$2N-SI?\j "foﮡ @En8{m\u‹'iQ K־nLxlZE A;G+b8K6!6,R2H_>qlL_W RC%i#DLdavQ>3*R%>jGY\:ׂb|Ү՛HωtRĄ,",J$1jhِ5e5ꭼ(r'ؗ^V1/Qea]vUSe -wb1JMIH`֟Gǰ\a4bc'O ⳽%5rҨõW!ȡ#ʹf4B.4F ɹlΔ&͝1E^GK}OΝxIG. E_*+F"wwB-H]`y,b_9L_L/iW^lq(/NC녘@ ?l*} x :6 C>eAw;q#=cv~_}??ѕdM^'U(Ҹ{cǾniOfr@``$R̨2XvDoȻ>E+izh% 6/)Zw@bdΔɅDK6|`!@H3V Gq\fR]*ѧn=z~[ #fx9~"Č>zq]uV O<(n6 m 2n,+Җ(^>ˌ>s#t7As5*ZޫCZ<\hOFƐMx푩!nƪ'ﺪdN?A2Gt '7*EW>rS߼g R{Տң{Rn03*@H^:vL q ͎2mڂ >?YA'-~ TYv @$_ (Y.)gU|Gv聺n⒔_2{*TvTP}*N! I]ُ?zbpSpHfnc/n-hr/^<}8V+xL%`)ɩ3AJZS[cr붪}q+T] o~xCG-dIgEmXHg1Y_fxX^p 6>ZX T[_-wٳMmO HC[xC:C%0W6zBTa9eM[M Pg& d$vMV:?^iCPYq,@`@*`u36%5zK숇~?GHS+ L]d9tTrU"9ؕTAWWvץL~vg勃e.8sw5=|ۭ}qNxj4~*LQ֋!q#ʘ痣,kٴ(JVMyfeA9L E)鶀ҪZ)#BvMo0maFA5|RZ @n'=gx6y/jԗ TUXZ˿{\ 'YlnBJWr,V%]/]'K#?|pbDkAu/ao(S#ߌ[@<07LT1r5dX 찹L?[TkQ=~!뇉!}ٽ_Z?Ӱwa,tKgQS_wYvD {IlSHJPji_WďhПԪÂ%L*~ DO&")3p-ssn0Y2e[ fnO|X谴tV4A+vw[Ind?ݿ}j_AN/}O?/Fx\ 4 ~8e)Ӧ#ѐ3XLk2)SC}ᬍKFcRvg7iLޙ>^ IWa(ä r|s@0y|ܬj6xowPکCXa ^աXզ޼ik]7QLingȌd󹔩$k vd.7[(rЬ/uꄲ8+<ܾ2=;>,D"ƊܜY'p~U>/˨4e3\gBg`k{81]bL=ihzW[+ p%As F=|G+w"X\xӛ#C0@Anؑ -cUfSeƾMzVb9n@޵I.{=E2w?|7WZepb҈AAr'uUM{j4{| ".Rr1|\ K;bPnuU׹vdp"TB ~ܩr_]s.eP|!\+VkD řQR׆vEC-5g j74~)/nzgJ3(ʨ<]͘456l)lZxNfAؖIbS/RڲϷWŖ(c6%%=)Ln\t*MT&LR9*E.XD?)sʮ#: \Dv9ʲ˟U:6U_Gboj8wk˜(d| Dkϑ\>WU<랟\8hܐ/ =L ʲkf7-z\s08KpT'\Fyuǖg]st߹ â=~B4 !l &Kgsx0~Uqd('aƲ>8HÓ^+,[7vke{ ٌq"qp^!   ]͊)>3e6 Wh;`Yy6;]HC쌻3mQs;eEgA4<pVЪuK|+ɇet{ߞ-h@  #`҆{Bol 4k~]ma})Pf|ngۗ)p`U,?80fC#D78gƴnڞB@ 0p02)fP,$(1}nT&\8K&T*-LBD45B@VHsSp0t*N\J OyR-ʪn ݰ@ pk3q9v3d5=vy@@ 7jo@ KfD+\z3Q^#;fI[Lbܵje? Z!6&n<Y4Ym-[e*1a,OMg>]@ pGӨ PH/ mzXVR+캅#*ŨTJ.9F*otCt^E#;4%}]Qt -Hv[%a]UfB ,+i\-E3&Ț_PS=fwlaZq{˫e6\B41uPo7A͉Y%B=ualWd @ig7_ $EGV. 5P}F/keL~'J"rGn(FqIk}}.ߐZ5W-~cIլ/u&U_tS4eSkŠMuՔN[z+mou'㵮AK>.Ѷ 3ZkX8NKJlC~L @iOnAr]RΪfu~~PDch"mB6% "LG he5J-M1l,eTLH:-B+$LZQZ֌y!-[NxJTcdr0M9'*&maR,q\f(FvV4ҔQ \`t.%Mҋa SO؝mzqg V%BsryVMDˋ+GQ˃v_goYbvb]EX@AǷy|n\{@YD_ +t.s*ؚ1Z]EB2nIPavb8MQ䡘eB w_h'0ϜO}V]vq #F@7B۬uuq1OU fIvqFNa= g^#>Q%h-xsNJd W^7Kv>A"'ܫME5$qc|W>)0ܑwqR3KU -q_J)ѮY_?蹣.ah[zuzRgn~=n0`_ ΰD̑?8VPJi|z*ډܖ0L1(6lO\P󃖶 u3ۛ<~9'C. g˪`10.L"/&dʙ&+44Zs8yղ=Hٌ  a.:LHب85Frk9W/z_-7 \~E4?ˡ_߈As6QS)iĬɹ9Lz^ߦZFyDAN솼jnjV-OhLѯ5$g^M/q`L&795|mX?ϩH8,&նVe sx}rQ6f+40_+q._NfYNqoLrÒ?x|=>^޺i{n.`d!&\;өFk,X<7kX6b&HKƱe2 v™1×ꘉ1 Z@Jn:Ѽz̰~q"o󾸟Mbp(.槧FTع.țpyEGn.G4RdTN kRjm-kp yhN -YX H7V*ٖ3sJʏYNnhn[a$fL8~n;G Zƚ"V4C\K>LNkhdA LlI:ʠ!t62_sYEA DNt4|=3^!gec_Aaf}%p/   c/n;X~*D mXTuRimlwzdHk( K803O3,],q,7&y}ܐV^Mw0/Si1-uNl9w]rMԮ݈ο'F:O6i Cs~O+E "09, t&QˮD^&P(/ Y&J;rj'Zª/f;2_m! m(t%4j'6^ME IDATSy>aq CM.ND腵2i| ~Hn-O5 ˿LaC֊zh, f-q&,_ezx wHM:(fXQpik'[o1NF^ؖB?W(LhXڎ: HSkMUf^)ѪBbªΫGz-!5Wkg*PKZb5+p*0؈No.|X.VwխB E&g׵p1ޫR$W,`բ@M_0$RgFTOtYR.4a }{ 3ztӨN-°1<AuH`GΗ}."sK;ͣ0:Ydm)ToqZG0Ml >ZfruIkǸoT-LQAq,Opt3g?r ]*+Bbn5p*8D'">(E] -+Tj;ЍO4+򇒀=@g\r=QFK>M*|cR9 Ge >$4~yOq{)e鹑GxA1.Emj2'wP+12!BW^Jc loyJ\˷gĻ.g'j:.y02kJYcuXX}_tfPAegil%P߱`gSOMPfMFmh4~q͸@DGyFVR2V~΢ΰEn=!j֊VI-28F"Dq}fV=e[/]hN>z2c%5aJtvB;mk8 @f0E?rL"yxZ㵛$}^P_K=W<ԸvƗwLG@ӊc~H>1G{;T̶fk”@S( hÞZ8y D)Q47qUV:n0GE`". cP 8ly%,oR 8\kGnt@Vqp&@dߴg0[@7p[l\q/unt h @kBvdҭx@ <Y <@ dČ5/3d-}?B{M:m|'Yoa@ @o ,Y4DW>muY1?-}k! /={ig=k8KW?,\sqV{oƁ aHGwz~v:Ϻ{ j4i@  %0d庤- O\ʤ.Kn(J66a!V%KjOxUb+I',\^G?򨔧&Igqʲ9Nއ|07dYiʘwbw.7?@;-> ޝ-W^7KY=;@!$C<p*=go-"~tfeP1-7wpOUfV,j=OEW~79(Hzj5PN HvcVwlh.Mw%%ͼ7+z& N_)roZ;1ÄLH1@E#[Ҧ<|b!VuDk)cc9[;B@ 0P ИZIpL~zUbGv:.VMyԐ^:?0wgT[՗&X 6A\~u AoZ8ͻζUaiZsS1Va-:ڿ5i:입,6n7H+9l+hS-ٛ~uR:o;b۵.ʔI#9j1ݮ5g @Ų%&_Z?ӰwQĨSbc Ō&sgrXZq(?Ad5:eG7!Oyjiiqj\/ }.{i=&X<n.3^kzm[qHg",&ekW!pѫ昔~F\&F_`togd 7-tp2¶J+3 buǻpn#wMlf EwwW ; X4!@uUT{ zݼg|']ա.0&OCVtٛ1lF›~cxQSЛ-{+J+Ut5S?r\y[0ܤ@L`oOC2w?|7WZepdXTfy g$j(-mppG@ڵ:9y# Zr|tיa5 W~X{]Wg*!p?lz2eiŽiス'X,s92(q>F.J T+5"NHdLikCTj]"!>o$0 @M`=,R ^SX8KpT3i(r}sg[IyeWZcp%+ք1Qɘc K)pUF[vL˄v#j27\`蕥OH5G^1,.TK}˖. b|1-c'O]b޹ڵkf7-zܵh;^TfJ蓏0ccSwD$oc⭛ӎ7uI2d3Ikv @ %c+`7˖>Ww ʊNC.pVЪuK|+GGI2t򿌮}۳%CV o@^Evƿ}Y 8Rӎ c=4B4 4gƴnڞd %( My l-} S Cfn?ij8m3f`(@ ,Y],ՍڈOYՍV@ !0pe;2Y<́ @ O80N@ )@ X~òo%)B',XH{"+xK?Yφ- -YA >>o ܽ]pi8K:dqSÄ [J ?;]~]M}pNKPnJUx~sFfHGvBiV]s=eN.Ǚ 'L".+F,uo~uqp3tݻp"yN= ؕx2ĉII+BXt ΉKNQɾJϻ  =~{oH\Y#;@]7qI.>Fyj;S"GJER=@Q TLU^2yGZ)[7qu䒩~6e^RS_)0GKZt]"lBʚ[޵"Mjq 2>y3Lo?% ni6yR#q%.]Hp8j= 7Yze'g gL\VUeG.ۅ N}aaTh-ش|;btM5[f^Թk%) #<2?;8^/S/-"wr^$$R /oBB ftI-:ݶNv$~$|g|^i4쩙 *!GA 95v1B߮%zKs 4|!)-^%i=v`'lr7M+OVr[.v<>w?  Zx|my՝(~ k R%t=ɒ}Ϲ>(HRT/3,ٙc|~gjȱ2Ԯ5#" p i.M\=k drw_Yz&WETvwkao~J_,˛3/x.a?m# UR/%6]صC 傓ȣS\7Oen;.Q( ifs!bB|dH/lhbU0;+F2|Y T(gbLŻs]1e³i a!Kv ^n%Q3~qx?5u0!BTxBS 0yJbty-B!Kxx]ZwИk(oP:" `$+jaKfID5Uy>=.01 E(DЌ\Yc?*LrAEX:!%v6ʖW_49ƅQb(IH.eݭÞ4S+j"uΆ?VV{f޿|5+f x=;!,EE,*-)|rdW&C!-]}=n P@As`$ PJvelXwtZ+EVqDk D@BEǎe_7M4zgm^ y3\B$>#z;-(( F<ո 4$DکRURȣ3'k|DU1wθ((p`:>>5 KL2zo] i jwCM! ]蠾qmJ0pFAӁ0K:,v0膲A8 heژ %#КPeoVy H52^ R3g 78Q*e&\gT%u2iO/w}1cdz=_Ai" #78˔ƥOpy{g}$HF=j<L X;kjW h%#)NR";h9L2rMK^zULUkluqˬw:E'b,PYa>'b\>+< es3ۚJ8 5#mz)k㝛n%]m7L^~s؄Onʐuo=rJ&^ 4~8|s+o]I bCYeam9Yмt|ZB~MP3ɼj-9q토XZu\1gyP.rŸ*䥎ߡ䷧k*?c_ܳI \E^?5XIW /7t]BFD@zN[o>qNLH/ҠC&x)Y]cv,zu5>}:&_bՃkl$pd]ư,ҋpkX%ɏI&}Fmc."yMɕE H忞f.Ao|tK{H8NkD@zOzay-JTccm>q^Wq LQO=mN #'E5>Ւ>ix {OE91q̂2bN.OY2 GwњFSV-=B#:s5f*Jy)UbniաbsIO.O5d芗;vְD9Brٛ/A /Ȍ[\_ O-1LΖg j *+9iq\( $o>ޅ IDAT^`QiݒXەAAsB3̶ѻ5o]|׷S)%qNz6.SfXeB͂ǚ^s-`:ӪA*@D@CuR]Aٺ?n@pR15 {cnA\#3(.BKnSf%_ۆ= mx-zh\:?wbl/h7j}XDE٢ܧ!11*+7Ջ7o?.5an\Peh +|52M<ީZb*%]rµ(~ ^$X.E5|_ 웏ިG12q2Oj.K#H/ } *{:q-όv1'^VFbTǹE92UQua w݊u eNew#ϻ9B[}*,D@C'<{]&qPS\D )f̞9M=[z :Jpʶ'7-+bU|Xiȩjq0,j|}ch2 C l1!\%p!<35*FcWXҹS(^EHp D v G$|1IҶ_' fo J*ݳGo-bv ԩ]NyZJͭ#Vlih 4.= 9rɸH8x*h!8xgYf I@GTf[~eubZ&\] "zb $ Bhx hƒcnCGlNF렔<ɩ¤-4ĄJ~uIw w_7{XbȂ۟L `BY}`,Y_vUCoXh;lbt}2sg]z+mը\AYj/{&OJ`Qn:]f ߲W{v-ys[F2 '^⯎nG/&mCL!]n)|֊0=RcRNf5tIz?6a.ADOyHͻg4]Uvnno@ 鰳hE?g,W^.TӒ4j@,N;o!L QXL=r9CNjSƪD\P2Rm.0Z)is鞺F 1L1wNL6w[#c'N=O[9n_ R/`#5iF% 1\;#U̫ÑRA,S$8iKlbcE+dY_ŖCG[Ai+.6K9V1gayǯ7ϟ׽3bvKKǢRjjom[_~}zWkFrK?lpזyWRgMUfo[/XʡPjXe>[Ѓ#o_̺mr޺5"\/~y.˄x0nK(Pp3d<5|^A.p6?gZ19#Q@{߾'FZ <'j41y73;epƮJvdMݹAcD@#{a8mwf<>KɺC,n\`mwJ^Gç0T.yvtE^AYAPcLwkFS.]}H^%S0D@ f7%Mn%l^u /AV2(_}i``= _qq{D ?7-DugD@1Vs*`o谖j;؞xT e0,U%@\xC}`NW>)Á!?458eD "I\>*Y|/~ (cEla_\ 0"`=Ʀr Cu""3'@[OԞYR$ ݀aA#" "p,Z[1J@D@D/HViT" " 4ēg}  G-%qQ'?R=?GՂ " <@VIt% 46m>5\d.4>W'jM/g;Y*i?y_>ۏJa7>17QTLo-ĺ7ujv֚W~u[t$sVv\U܈AP*" ";翽|MBx7NuúsjzXՌ`=.MW .4;~G ] Ib>x뒙m(2qߝP79ENk=۷}̽(~ k 2]պ3o}0+mȹd[$D.+ 9E!@\C)Zʾ*wT] K;%G29k|WT"X>yr 5dakN䩰GI.f?C$&P{vLSekRj%\ܣň" "_CVYfGwJLְ:ǮZnqe5STpC"B)h@aW`o"W\)9oژ9 7ꇫGͽC$[Z`V@}.+.1O 6k Bn#SXqH@(d)| Sވs " @'!+C#g$ \Ѐ1B$Ԑ޳z 8oE-PNwU%k }m N0%{vy6EİPIt+];EB.Bmtkn-Qkzc*.鸂s3P2" ")eGrH52^ R3g 7DbHNV*ftE(br٦ #]NγӖ9R8Q*e&tNr{2K/ZJ zeaPC[N[R-t ]##ykBW " @mFSX]E0fy3*oN|Wn;5+nyxʶTg-IL2){ B  f>i*CֽȅOĀ@mGK]={F,YCpt~:o(<{u@(yb\|y z2,,h^:>-!?\ zea Ffg*~kP^n*7N"" L.!m7yeeAZHƷdo͸KD1oN/F oꏞ' #bWM"BKn۰$ZS~#ԃD@"@J,U}PY`}䈜$ZosN1.[Cc'ņ)SU5|CkdSn9N>OqTذI`tfhO0~>h42r_&(`o(rI/6-$?8rÞlƟ G?Xyܩ_@4)s{wB-N@X9!yN3qc!d#鋢䞊=w7x09:,2#~5]bRq/q"q[ &V-YT+ biKӮmKBr&e*0.&q$ Ƕu`?EYKCc`(4D@)fɎ'u$@M=H]aFj۰N7^H*yգ1㧊E#XGI,;h3%6(wi7j_8S"k/q4'k&\ah( n)Wy[R$dI$)<^o_L]xlrSKᰔ(W])9vq6 _}h<&Hݤ;J"5;v T㌝a)7(Δ}RH3jp[!Aѕ-%ET,x:Թq o.]|%XmXw;<6/ π y4paWVs&dwQ6͋?ÄJyZ 0@i1n j<&BxiCq\7[6Q",I1+UԓsU#oM < }´G ^{ JWJrQb ea=JLR/|LYsRz0lxtXND"Хx)&Uc | B-Mea.7a _Wq.leFw60T( @"0hd% -@NPe;X4e{Pc$ '6t6=vQxav.mX gIಡeu:CU|$X <.Ӿ TH:] cz:qS IFI" Mfd?kg/:T"0 ϡ^HLkXO)tD@&9}u(Zxt km" F`̭0nD@D@ND@D@D YQ" " HV> " "D@D@$+QŬ~R#p̣MF+%n>7g;|4 u~i>T " "nH| d~a|Lba'pBG{p+3w;[׿%f!u_u1,Ꝧk.:bH ?N9_'"޻ݦ" zJ%_oeJǃm=%.kwոjHdTY@9@H'`;ek+:+ISQ9-{['b35mua0\3G7np %|Axm7JA"E媾"#5O dl-HT_SFd1`i"%)N<_q+Eh>&. IDATNF|퓞tMr6qKgL`İb=YI4t}/ o IsTwJL*R6I,zvy,FĮ:E$Dž,ƐtcpEF.i,F"mvۣ qK2%J){:Z3sX&L[jubb⨡ tͮ} 4З+cjlLIjjLκY$)S[>Ė{~^Я݆ޙ8XJ#X7GOq*sB)b5KFzU-XRvr_" %+q5PG9Xt _3O' Cyp`=%ųa[+S̼SaZ¢M=NGbRKc/7s Unju[rot?8kHp"q[ &V-YOJ KCϸ\ɄcuM|2 .,M,%

h42ؖݍ?~$>p1L6B(*Aoq' Vw$-z8ͯH'/&'UeWYfVKLj"_b~Ayzo?vԋզ-MQ, ˏX9h)=rv&.+cD t8^I՘lq_{ۄi!k."~Qy*ݞ o(8HR&Eח?Xa{D@:,Y[.f t L& Dcm#$돴.%B%&ƁrwєX|,*'cH0bB{TP11&Xno8ݬlZ}e# DkW}~^$V:|zA6hm<{}@0"<=@o.tX5^aF[_n 1@u| t7*¹,txJ7|#cSoUHgپ]%hNwv "\ŒH(c GB}[roA$gu.D ϡ^HL˧XO"ݓk>,jqܜ,N1 G_2ܜki"<v1⢕MDg/֖- $+gD=qMGswD9?6@>#" "p @hD" " Oʁod" " kCVj^bg<bA `;@*  E@0*v!˟O]P;|~СzDg@ H@`r ;x\ h6AKxKg{]㺨EW+}gٔk>7ì]&3+|ZU+7ivw/yֈZ{ t޵ &>NՔԱ] Ӭk2L!N_.I1hhi9(&hd'ҠlyM#A!WN dl#H . @4`e1u.B^%1p4J%3&rnY85x>lƉD}!{W7=,]>;U2( /\.IaY@!I.Zj=bfqt-Z79cm?) C"MV S]Le+ۭ<FY-oX 0#1ku ʏiS*,@8Piw jNo&b{֩JP[`0`B }d JI^^DjW 7pC55v:dL|?۾pAe+<%E)#9f3tc8-je| \~Ci)kpp8g?lIzq&sLHظ3 ik}NI/H^C_&#Rb `xY 'uѲqR|֛8sq"nv\D064~tʹ: 2˸]5O8a6/ hݐ4Ge}W~ޙ8XJ#X7GOp3;[{pnFƦ/]pv{ަ6G6n$ :_B^_e"E=sT`ij8~㖰ݐ`İb=YI4t}oY.s0Km/X`:Rjvї >e;3y8AQu91:gM5½@4YiP/[AvWyR [ &>췃&+(t0ϝ@ gq~ ΔB0Yz5XO y CٗͯO/A˯AEbt|x"c8D[B:^h_"^ݣ?- ɿe%)S̼SaZ¢MmK JA)[#bDT2 l|Ԓ@ {z2.ǝ?%I D2׹w7 e˴TH"N)|Pp'E~V+D c+1 1D=mF>ܧ+iky3^d)f0|#JAUh^\^6miڵb \ cA6R0282\~z2yrϕGR$gI$be1Z%c 2ƺޑPY{{ W(CE[ QD{˼4,z\Ɠl))`Y,x_3 RpT!lY(gG'C>y!݆s7%OP̙rxoX~<_7M/g]B0 )YVAQnbI{XL%3en=L.Ӎ5NKչlJ+{iFCk,TVlrϓ@E+a+cr +5in ÃN PFA'YKb`&`>]SZWN(VYRw`B ̼(!z\W0>@2HZހ`=&bXa] ]A=9W5քS u`q Z2z,6l.ejjG =)!Mdh)uڎv٪eYLY@%VS({K+0n@ R&$ ;V>lvOPD5r !BKwZO4%O[o ` ixnxy a?J4N\SXԟ}XtہT0)RD=zHeL{TQ{#YqIdb >dbӴqL*)f̈%fE8)FO\'+m&*c@A,ca> bGp̋eM ?YY$i Czv(?¶SeU!F&diQC^p<_NT dMi@hVWx*&l!lea<-K*IN&5eFlGs8L=D*΀;o? U[t $w+f޿\2|f_p M/a~3{xnyh.nj7e!^/I"9WV{DV O@ppu}o@85ݠe; m>PC M4?W߃sXa1lC`^HW?`̯G?0F&y?>߭Ffm ]y,+ k8nPOl'E1 G_2 ԯYw+>>~mt0E>ؽȊVv ` & }WhjfAho+hV2+nͬHeg \ "nZ]a>݈4f}͗J]Y;&r%`zM.;t @XqYic>"u ]Gik PZL5jl?t8ѳ d;k7jJͶ؈տϮÈ _))@D@D 8쨵D@D@#~g0lFD@DQ " "  YEs~nFz+YDjywܕ؟ꈔy~@Æ ^ӯbE){с @>$P[lNpx/Cn=[(c\#UOg^?9l]}zrd,Y!^㦃%7QWt~*߸p$q\?֞yⰐ@^}Z ߫Zwyk@5r9 SBZwCM! boc6e_8fyˇ8;CUjY:Q"D$Wn\qWКVy Ͽl;L735f[ק2uл`ޭFD _I~/rz9ٔcG mpŪǚ^sr\>rˇ#䫋B^AbM=yqJd3\5f(Ԍ:s~lX@\ltBp rO_h?mkcgIzČgdd"2V~i?.OY2 Gw0iճ'ŨEdkU}OPyzk'ϽeFZZ+]W|f\ ad芗;vְD9B#RG K>|VN-;3$;ͩ:5[㶼Z:/{S[z]g.F{C?z|P"0DEF-#mo,sFJpY*,VGk("eMO/xSVfWh[Q%`Xiph_2e @eeGҁ"ȝ.N y uURqL72 ],8Vl.:*~gc]Q[VbBYtfhҀhc=xjz^N$%9kx^gC'ۿKi-x[Au4 /xYrN:0ek6JsUr~>oj ٤$sYɨ[{U8c7)zům]s ~`3erFPĨXg[wfl:N6zῧg̈Ύ[xew%&P;24?ps⪋B~W{4&R%_7#sY\oIpo%:VW[pf9-0}9.& u1xeX(W[(*I?p3l!~C@2 .!+Fw Yi[đi;zw'J,E ͅ-Lp]7^@$$\7oX1߰\jT3:-޻Gb @d-Kn, X*4.:U\c,,PQPh#Ks?F 27z_ I IbDEdua\F8'9|θΌ (˨$"Hfd#{wNNVwV%:x%w9ϹUuܺ]8J/%{yjHA |VI YeJn\$2g9 naҶd+A6B%W>K#EfW^gЉܼ/l}!F{W5hTc۪-Am8CRF$2v18"CZ.R7hkфbk=C0cV*·]F\^a!tJLx?O7eZ %SiƵbN4 ]^Ki{^nҶp6W򝎺Fw(p*aKĚցˎ5žWnSO'> E^*1F2\pL>\2$Iaw?#71qY`(248z$p}q$fov r&[ԄK_Xڥ U#%-Zʾs2BR|J}LiWJ4H:{ged9#)7ixOHZM_^o1'H& 6!,zH\BY p &07nBX"1V+cT]>|Y݆ΝV/xJ?mI1 jG]ύ-JNePKJYW8v1X 'yb[aYb|m2$tjeߕc(3AkS$^Tq@\LtR" )ў~Z_c}i3rv[` GvUտ`.& ߃ \ө">;%m2 {km-Jn yx-}'7ytH", 5 Lڟ1d}idJ `2r$^F=]Pvl;Uz鱨@_5bdO+2T0u.y A3ou˜9[kRV<5t"5FÇ6Y P̑8@yv<?ZAB=j CSE!k&Ԟ6rfHfXU-`\OƇ=M dz,MN\伽ɾ;vd.S!ڄ1!Bu|n.cBz{qr'⻋Pme/ pOpEp}sх6Ƀ3Eloɲ->Ͱ@;{Z7-͐=]jNxd;e  [k !fF 0(y>[6#Rb&^kcV0Љ+7,\a{,k"iZkn̦@e2dXh*B %6?W=m+CUs'bқV~uAPrfGyL+􎉞%'4?cgN P(3|/2Dy3&J+-(+p@GOL9>$TrCQed`AeBvdmhHm=5le(^WE U[:l=9e} G6EnPFJ/~iE9'=i6*ZdvP1cpx Xõ]<1 B~5vƔ45>V,h3uaӱj[?Z+*IR&P-]%+">l3zѣjX#ZJo;},O咿[YV8vrFٞKRzo Y副/eqϩV$"S%yA+![j_[(`3ޤJY4;sĝO?h .|vk ^>Z]W`O>}=mem:,-0.A} A/1iگ祽3gܒ8Y[*{ }CGQaA#t)C\}/oK˲?~{(>~Xo2Uȿa3m>^."/}ye*ʘkO ]wWj"#C݂g=vy֛t}?[mC֑;|Ti.I{'e0oʵ[Ydڞ\wݲ?-Vn0dN}HxZd?&ͩEf,g":J._ZLyVMz󫔖fjwvܦͫsy'ߤ 5U&ބ#[/wj2gTSTJb^A >ev[;[D>k{–AJ36C= ͡MdVȒ䊈܇sl5ִ$+m^Tq/ZXt7 0 ?s`AlBCYůzXl#P;e++(AS?9mVS$mk /X!(Q@@Xi݈P~;D.h @?@ (+Ǩ@m @+] @ (+Ǩ@m @+] @ (+Ǩ@m @ +Y^qQ!v~еH6@ Mpd0M./b @0%R  O/ӆѭ&tU mmxX  @9r~Ļ[3/9xi|KՆ @"oD‸pacٽzB @ 'ba%cir` '}MzX2(@ +pA~ٝ*S t@ "ba=JU(@ p]euif@  @ Z @` @V:B  @P  @8BJGhA[ @ VR1cAq[><iPUwC0S;g&ds8{c$cVi8UcHL}@ 0V`j 5^?MzhϱbIC;ӔF^jjj4gJa^Q?ĝsŕځOPZyOMTP9i!\UKNA/b>b Gڄ0*UƦ&=1/>59qUezvŽk1#[~J=s^דpaļvx1-=2;0U19tZn@?*a͎ڏfuf JTso~x`衈M;6:fE$*\2( 4Y c9MgS&,<˰1F3o5A+%N}c/䕑.)o>+\5wBele6$eV˜ 9v_:ĉ3knKݛ}>pTOEyW΍ vg<=@ ںs{O֪-vKqÌet A  Dj8z{lGqlA.r2p  +Ό3d^cUYY%7,&u} J-o͖[TGElɢ {`Ămؓtϲ-Kц7>0oމe"WM076%-ifYWo,{HneopЃҚL)e-N V_ݷ?mPLOC'{w"v,Oj,ʋIoJ) nFy|x kS[Պc<ׅ}/f]>9/|.<Ś4q+U8A۶'K#E6tK *@E,-Fipacٽշ4mM}an ܄Dbf;IٿN޹ f+ ;; K?Z?w|n썏oYVw*LeOv,2X ˆk'yb[Q9,ڻ WRrSEp}S+ޤo{M ufH 5A AGNK˘9mՠ3 iEL=U*ۋ@t㻋Pmɠ GksP~m+]r.;PԙkoG22M)Tfu5SӤU):CSE!k&v +pA~ٝ*SF3C@ۖ'yrs7-B֛D\!˺FzNZ)bv7~mݾ(՞ohMwRqܦ8 O]jƺ.^A,ަL;2l&GG8P[ʍQ^/,Iʉ(^߯_=pGpE9q~hFcDyɮf1yS='5 vLZjYQmFct\zY=qAee@L- M X<(zz0mZV4 օ@2<NLx[\,4gAٞKRzL Y副/ezS?ʯ2&ZSB.ݕH;$+n d Bs]sL_'wygV_ .߻~p*J95sҊ^d껤35H{`V?ڗ ،>7in~|8Ҁ8i¦NEI(|9j3qY5^zo:Ji7(5&1ܢIyӼO#ʼ)lΡ \\3Sg y~P  V)[x YGS6|Q$Uߺ=֛Lj7/|X zfJ)kɪ ,3w8X"gS-@*! ݯM5jzyi~_Y 6![!FM_u+o ={φGتQWc1ң_~)mp@ G4w8 VWJ6. eȫlF2eϏD=җ) .8Q ~#1@{#o ᤝO1ȕE>)t#O{^7PRv凴y2` @&XwMV@ pa]]!@ `ֹ@)@ C t4@ NJ\ @!V:  @X'ba%+.*ė=uR @!ba`]]x_ts"@ pAZRʭ&=^4UW/lBnJ;[ljz?}G5@ ȥk\1#wf^Jsr% %@ E?ވq1IƂ{+ @ NJUxANj=eP@W$ba%tU;U]@ @EJc{V P@ \q' IDATˎ̀@ (@XI@ !a#-@ +)@1@ # t@  a%(@ pЂ@ \w+)_^9NjN?}Zso[ @u tBASۡ͹LSe rP_GϿq;#GCꎃ6c_#MLфI󗆺}SU"(l0χvL?zq{^ì {'2œ֧_,bU_>Oi/1'P6u,ɹMd ӼEDwe~+Zr|l%pHm(@Ң0)y1mfqg-/k Yư_I~ y?@m=jSoJ*_(B57uޖy#وQ}8_gۆ=jb%K_?3C؈H Yeߤi.lN'͹LS3Lkɕ-6v@kF!ghSWK* 7 Qr%9 %=/iuӼ6QP:B##N?yc1ֺp +Y^qS%u} ܹ^S,HZuW]SwCtu¬3`-_3GB@Yށ^ڢ3/oQ ¶?Iܞ)6nNcj d-w,;}CYGא!@и+y}/Zڧ5*0WLQe m0A釧 %H Fٹڄp_띎po .-`"̙"| '5>saPx,OA[Ԥ8ܜ4&aBUmz&u0nÖ-ȥWcWЫWZОҤvJC2MjҙE[G:ROLj"}j`O}<>"‹FmGNe\U3zIQng; "n;|1ߒ%3.Z[J.ꍕ &V՜q&pewO&Z<.q+~y&'ܙ[ %"˃>{PVUs'YƆ_v+$2L6âEўn~<% SK|2 +)i,N/沥g=T?-^95zuޱ1+%j &.{uy , 󧴁'=j! MgS&,<˰1F-q_S ^rnL;x)֝{Vm%y `SlX!tҋoP <%X)ՠ9+R9OŐNyp8Me;Q}UrCIF33PVVޗ$-D.CfͲ%&B aOW?ntDQUϚ"U=۞a`dԕ(zw2.y1KX]]EQj HA WZ7-{BNZ7[_654#CV_ETUU_| 4\C,ryCn d۵nR.+נԲlYuAeQG:w֧cSٸ{6.cGOw=~*T^ݵMa7!#7>0oމ*)/D"e83Px0n}=3˞u/W?ū(/&y)?˧^'6hz1LQS=V7[Y8Syh95ϋ?~u+u<Fu5LF}I8iUJgNp5Rڞǿ-!ma"bLr9l :!cFG^]@>iå"pwn/{'>xt^]c"F@we.}&ހ2yMykih t{KՎZd_zzST|-p76 %tu52rVQZ:uEMN]g0i[ʕ\Ҡhjm YSQ/ |9r%N"ҋkɱʼ DǕJD}—F{3qE?`J*N_b$"'=O8};;g !sʊx}/乇I]Wjd֖5[*igwte٠a[-(R<33nǶU[}q_+])ƚ\+24"U~yVM(6"h(+5t=U9h2ڮ־GnꬩAk2@g ;Kbɇ怸pacٽ@h8ڋin_$J.": HZ ! (%P.gm)>esc}L9d*ʙ8oS?>/틢\#0n_`Y rهgxhgi+7pWIusT w+5Wkx_FtǾM ikb sc&%3YE pш#䒺`0do iǼghNr أe7ه/') >䪰+={?SLaؤƊs_–pΠSЩ}' |&4˻װ6,btwMx4#%^JQIQx񹱳7>eYIީ2'9Z˃bY \+O"0λ* 0x>|BѪѽb˪&񖿝aH+*, +KW99oms3%7+WXvbIGePL䝽? yS=uuTkvN):w#|^882*Tp6*#F(Oj3@}X^Ǜ7Cu5^,_Vm]rPBRqwKf9Hwa]+ֶQByv<?ZAB=jm!&JiT]?|h ah_q8dl2v+T,g ]jKzߺ4bT6ψ 䧑]B:ОhLam0-sgKl\ѓ6iPr/|n|ᇌ I 0j>J+:Sa_еj91l V>XxVecv% yR]ۍI'ע{1LPVƢ#Ú6LtDLɇ[gJޕ[Q=UΩaK,%NŤ7ǢQ(+pUH\I#ɧ_7in~|8V3nm,F G_v5dE/X\^vB[ 0'kG^u\[ΧtC4>l} oOGR/o u%Ϳ),qMسZ95l%DtȐw}oe*cҮ5?%t]_Chc,Vq#‡ K=<뙵+פUYhR^4oy2oIm])com^e %jՕzŋd:k@)}Bvn|R͹LSf\%E.b c7| =)ˋ+(puPDGQ+#>Ϫi!\T/u~N^JmC֑;|Ti.I{'iI bjwvܦͫ#y'ߤ~ǢH9 2&1ؾ4%к Bm"" 3xDt7>i`{{ `6/8wl4f1*PQi~_qTk'f؄0o15׳/~UioZ6@n(>e++%N@S?Jz:%:QlfL@1iҙ0ѸX_k/@?w-@ 0 @X9G<.rjmmC p͢ %0*1PGU}V %n#tAQɏR^xX8vZ(VVz@aŬk<'8E5& ={ڔxVؤ5ϯ6ua !4uGէF}7 (oF҃yoD.I^j1; -uHn c-[ݭ4>m l@:֋5|`ET [ 4cX91%nMo) [8}f +eP,m:Ry瓏5%i$Ւ?~{(>~Xok7/|Xd*;`\r4=@ p ÒW܇  @"p @ @X> @ 0@X9* @ @X> @ 0@X9* @ @X> @ 0@X9* @ @X> @ 0XX e 4hlj @`j;&=^4FeM]SKGK$M/*DC# @aaE|a0uSbSMg+ES5 @C` 3CwCm/u/V%mGӋʶ\h@ 0zP8 .&:)\XpvovEizT# @&ba%cir` '}Mz[= Z @KJewLaǪ`izT  @"ba=ĖizT %@ p߭t @ pw @Xyw@ G@Xy8@ %a @!a=H0 ww?@{# @]V]cctv=O3m*-9!wd-n]J.5[3.XwahGNMX4i;7UJ-Rۃaz5~6.nY_Qi#y>c}d׋m~"S/aք߽ȰLɂey}?W}<)>e`˨*vUBE^ Aw>-ut A]I.xj8K̔ kE=PjXqOmjbg: &Wr*㪪C:yaw>Ѫ3Τ&P^sc+OO1ޓj2,N/沥g=T68JH!a* *Z塈%Tv,* MrW9Y,tЙL;<* |m9LLYyacӍZR{:cذ07&B褥Baџ YVUs'YƆ_v+n# -y:j2<̍wpReknKݛ}>pTOmyyO¸ [ ^9ZnOFJ37gGG>GU}V %4h(E|O7\{-D.Cf۳%&B aOW?n$X`ף=amJwZQqlҚW^gpƢNVuu=ig]-b"ҨQgMKmIb#<z!u%mB(J*΁2oyiHzp=/E1Y#}^QWkr_jݮm/ޫL6s%Zߚ-k.,JYB},8vI";w;%-&[LBSwX}jw?tВWm;V#5;\q !T _̺3}r^3\tᝊ<5ArR $ 3Ёb5{tlPw%T4^G34]ղ7E}6/4#N?wA: 0 5Rڞǿ-!m`^4UDۀpxްKK,alv = MVGJDLIb_+7[aPqzsn,kV2csP\D`2 (Yt3x[?aa(72~oҍeJBWz؛%-/N;.q^\#2/B)sI/gԐU_TƅO"zàɩ6&mKV+ Tr%4ߛ+Zdm.sܼ/l}!FtnsކvsضjKPۡ/rPN938"CZ.R7hkфb6C0cV*·]F\^\atJ\+ n By:{հ2f\+VISQuұ2ky  $@ihxĚ]6.fH.-q܃ d ! .&:)\XpvovE MhzTYH4b7_2,rQ $-IO^MliUx,Lexn5;k:Uׇ\XVrEr{vH"p=q$ydYZhoIh[|Y|,u.A+SJr&[ԄK߁˖C]oT姿!𞐴tɫ*͍ߴc^OLDE Bk?Uv!~Gr1܂/̍Hl'cALm?ܿ2vHKȘZ3564tn[IB)q?4X64aj]ύ-JNePCJX_63X 'yb[aYGa|U=MPN컘b t3(0umް0nT:zY[hO?9;-0#h.X l2@ږUxANjܸCӋDw[H+늘w/#oNݾ[4&A [6*ۋ@t"T[b~Gpn,J+P& _ؗ/-z J `2$^Fd*:SWƾKs:;{T!pMmiQ5im;VM -c9`2n|ᇌ I 0j>J+uSz,)OZ؅]+ k,_AkU & d|Pĺ@g.ˈ]ªTȰ6aLHP]"cy9tfh @pE_v|@v1ō4hfB"6WdYL`K{*rKV͝IoI\QqN7ɏlI(/Ya3DvЈA졗Eɽaw n[!K]jNxd;e  [*ɎB\Y2ofc#/J202EYiE)S|azWn1.eYVɃv,J4{85;28ĝcb\wLd.i|&4?cgN Pw 1 ,Q^䌉JKЌ2B Wui<|?0!"fޓhT9at~"PtRBœhu>jKCMWwe}Iz eF(OnPFJ/~iE9'kM+B<'([c S'v} LjKuⲌwԲJmF#l:Rmwl]`O QN[& P ~;\l؞U8|^4U4m\w++=qjF*CGQaA#Mʋߝ-|"5VMa@ҊAE̠ˮѷEٞKRz Y副/eI+{^̼ ŭ~p/- }Ropzĥ٩%|I7DS]p̶3nm,F G_S_5_?DxoCUR*N%°ErgnucY=6?~{(>~Xo2UȿagB|1\D^[L]_eLڵf]v+5֠nYLNV]gywpߡm:Ry瓏5%i$\vK F c7<|i4$ؑZޭlaP.ٌF 4n2[\]^\_F:4c9^SA#˲RWP%Ol͐0.A{~c\)(°d|">)Mcb6/8ǘ ..uaFT#f؄0o15׳/~U9u;0M4w. P@S?RKT H?gFQ#%aH Ѐ/XN쌺  @@!@ p1X@;@;@ O1;?n>ͣ'J‚|y]?~,^X5;5<'俞߶58͹'Y}w׺јvA?Hf͟A۪veLdwfq1ej]CcP^܅9LZ[3f~t1Qvp7EP^Rl{\/cжUg\\lԝ5c˻2:zVבf&;"ں(B/o?s:UՍg-^U]z,rQI\,4L>p~m~ɜZ}M?e(an,gN[>L.=>ŧ _.g,D=z9pR[B_pMG\쳇ʔ A  Dj8zۆo]4N re X8QGW+\5wBele6r&R*iC3(?4o+X`F< yjg}5樎jPLu-tq5~`D -wbLoGcP: yJG;شO'/D6)DzE]px|Dꋏʸ3AWyaLHd~BsпiESEӹ^@73C)KWW?n\y#zSkٱl\2 捏۴wQ%0wooJk׉\_^ xӓ6>핣U ){߭jEűIk_mxVPʱMckߍo|c#lɢ {`Ăm7#7y${fŝh +U5ьc%be(F*Pd7*߳n2I} 1^rkCaRj>M: ic5/\n׊{ J_̺3}r^3\ԮUiC3Dw}AaAEO5T)e 5/SG 'SƄd ]TNՋ ?ԗ*QT䝞TLB5Lt6s%Zߚ-k.,JYf6k%|Tm۞ЇƜh KeWKvMFmnͼ/mKg5M/*N2f\+VI;(l˶B I M4 A" yp\?< J9Х4H! !ln^gِw6K Ac!D)P=/׷jY*}wgeџ ]a"-y5Iz*OϡƏrû]ZM~|qpg G6gZSOaV޵BsO%Gxԣ+5+"J wFD?v>YI?nVb@1]Sx"Ak*WQD_QլN;"vH3XK[xρ P U7<5)qfڜ*䲣:B--QhAbDû HŊQf3Ba@E5sg5^d)UNZT1g߬F)IGB=;Ԥ0Zhu0×Rly~s0utMOQ|A||x%+&ROQ *ʼnAr^/Ymne{&U҂Y r!e_,rgP,sνGZl4)T,ԑ6)*g*2|KP҆Qg(*KZG)? q`cEً0cE _ Aaw.4@fh᲻5l_S}!^CO X kN$*T{fSPlQy(tZsH3" 3mX;6Qlǂ𰲒9}^(75M.N "o :JmS"YJR19{mz5ö47Um`a;mf99 vwjzRc4/R 9Ѿ" ]jE΢JCBbO0Uum;n}Lk%Dk/*e\4):A-U3ƣ .= NKz ;r?!)5(,"Z>BgxIDδAhSDA LגmBk{ˈ-9IZTdkZU| 2hr?Bp1D_6 (.dHn?|KfeUh服`1݄ܡzt[ #/m ~>>Q^_8'zd0ʪ$3еq>o$pkRBuE3W5]*/]oQY2g6ٕh7F,2ߤmK'SՂ)m"J:ETTk I |H|fFG'(%2p๓Rt&>4{胂2m3;ICцW1yAjY BR]UxeƜtn?cbQ:XQF{Z(J$e7"ƮPjgdHh*9jHcDh IDATz9V1(y]ӟeR̶;$/Bji@~g>)%yO}s;/z^Zb͍t}CB hk?VƝ[aCq'+ݺ ]5a`%R?kSP@%c:d[桖}|1m+'ݻ>vQ@ q[N}_ƨ7Of?9v;"8Ȁ@ h"8 @ m @'e Am @'e AJ~PA>>h @A.8⑍s@Iȑ} ?~?"}1fOߙ7!Ack^[7==TĬ5 D>1W~g^w_ %0u6/`=tb#9wV<m]!ZKoipHpACg$MbY,9ʼnU?Nb~(du4_9Gv/:cunl^'\۳tKߓw[4'X_TFYY\hI?Aa]~ϳN}΄10} w<'6ln6 p/𰲒:Brů;# iPTe+nNe̅cjH ɵՔ#!bjsww[jOgt\'%Z&^O|`kJ|kŽ]䏘|_gp%$yD+ƾ5<c`\Q\W4$f2h:0 po𰲒0!~XMA޾KwB !r7Pw+4}c[N+rl#reYUƌaF}aWC|֪ms47x?1Rx;Ea/EQWsN-$r~fԡ~bfP7;RGpQ9)I~7?a&1Cjvc]fZZ~I%:CKaw׶VQ&2+hibBےWu 9e{ Z@PY0sKv Gnִay-t;c!S6_Y'/.}[FM36h#Ao bՊov.NaKӿR3zx 3^fM9 4뷏h+=Q7)Y$!#7yG[mcV0"c2MP.q3/ ,dG^ >S_,#)B5r+G,]d(CIo*mgL|7_\W{E.[>S|h続ڐZqiMg4Eo- ,D@:fJҼg=T,-_nX7ay˶>"GQF[ >I}EK 9{kջP7hZܘ%D~6h9XPUU\6#bܯn;ĪkxgljZcgWר}25x Hcٱ]{[S⢽:g+pdjmɢW O82Hkivi5%2_8_i+չ:$ -TBGZC2‚yVe`f'f9e'iinI]رksI:zY"? fNnKsJVWk\d"OMq ^˥mYMW-TzXuz?.l~F(@ GWT5k(Í+JnDkx@}&EZ.\k4Tv+O,4"!9tn-^(;}1弥d"r|\(|d+u< #^P` ”WN([]|X1HOw:6/\83NPttmNE 7`ԉŬ1I*\'g3zB$Cgf<V#㖬h`pTHԝh"MJ}gd54X*+#9>#$?4܇c%0/ PX='y_39OR5m8苚1utAQ`[Py{G,%euF2wnVΒ$WsGd$;:mQb5I56_T. ow1Ӂ:9\(\6,c Bi>(VGl/ze4ڭPމ9_W>5`~ TL2okRjd++%a1}lEir嶝B !B$tMD.zC7暔ж^w,0ii0ѥF 5רVYԅ۷|R7%vh芧_n"ѓ0inP7zw2rwӇڏ/jG-8Ǭ&Xꗍ4,fL=kX\ls?{i!u1;$O(LP2:f(#6Co}Ҡ#uY؛#/r l "umh~E&E'Hr*] qV۽1՚5BtgKg%=)1o 'BipRÁ:MH}ݡk)8|oP|K΋;4ȶ"J;LT_VamzNUiw2gÒ~_jj^q ~'igXCD#iG1i5_eZ wl^v7>p> ¨A!>it %rfѽ썙)O\ɩUޥ1t{7UR*RXڶ᣸-U=~л=t7ZڲKBޏM=:gs|jK[ja߸x"'-V~ܥpG,mIccU]5 R_xh˓i=j딎fICцW1y \˿"mߟNH29ԳQVU'Yw\W_4KpM2/V bfnXc:Ő5 ޷Pb]~ﭤ'2(n"r_}Vϝ=Owo#r]9 xVOYcgg~.ϤiŖ̾$}N6}=_=ؔJ`o{aUyU%"*5$ZA>ʡ6 @K8[.%yO}sOZ5H?gٳ+vd+,0R'{"Lk$/xO~W؀ޓ Di=t 8 @$N2 !FO7dϟW:$@'c-Dz~B + InJ @>++ap @:PVQ}@ }$ee@ t@ H><\ "o/wR IJ~PA}-S C=8  ` ao$Dȸ_=VS/FKZcc|?4vOќD7o?Yt+xr!>\KkUᶃ9EN;9iS9A|H%3W&$E]Ɏ嫝 #NO $޶[}m2mƕDΞ:}35To=r/ (](p^p1Rj:87V_EƅzyI-P1pQ9)I~7?a&1CjA@x.Sߢư.{l}j'i^v dIfx_MP~1:BGKPor,2⑴TUy'(-[_S M_5|JF?Kk6fm?{lYޡ8*3SRc$Ir^6,>Ab/1R-W!q a sBj+8EBQ|`~Įl?YJU>"O X k1L}])(6¿]:\-9>rE>Ҡ0tAZjRBu~4jUTا0ut=@A8꛵>u BJ^@FyQ֢܏4\cZ[ jE]}&榊 },|',}њ*Eh芧_n"ѓ02/-Ur6'k[W ^[tJ5$`F[eyB:樽obCg8Ǭ&ڇ  $VVbY2%]͆B 2H}ݡk^蕬rjB7RU4؊n٬U6-nBRU1XH\;#qsQ1?mh&r &0X٩WW4~ɓ{"zYͭSFt y!ϼxVv*kQ@ Җ]wZQiC{/]LMW N|^7D{}H(*Z T~s.vՂ_6W'$tYsZcP7̍?*kjn7j?H_t1flw-l?Պk;2g6ٕh7F'F}h"JJXQ@ QA%yO}sρzI<~'w:jԸsF W@J,wKV>{xL'טlOPK>~& HQ[N}_Ƭ$UC+wK O@Ys9i@ pEp @~ ee?@&@ @ PVDh @J @~ ae%?(uL 7[p4@ UKH{(Zmi  @on [ cᏭymņ^P=f2ώP,˞{ƖO~~""~eK=}w  =/dwVaȸ(BXCA$텗'rEg)oOE(L~=t TZz~sMK]MBO O}m$P.<k.~ A--}8&Hw3`a] -Ra1B5i0V\sz^;#ҠVp<rNҽچp1HꁹgVVf"dܯ)ۗ[Y'\r=-X-$1b5L3s5~1q K,wWi#=:mKߞ9:SN$ cUA.Ty堌ڝ~ t.z\ÍJ~| o?K[_pB |d毦YUE!u gˏz+G_mw4l}3[A/BŤ6%'.Y;>3tx@/dnp°͛gzӮGfFrr`Cr/w0`JC7s# *-7v0q3#?n'Ef>lovQ!a2ŝidC&ҳ s \FlnQ1#1S1 K_̍rc"q} 6oѪيt;7ɃIcյ^<}pMC[65NRhbn(;wDm% }EYFAk*zy]I-c 𐸌`U"si#;.B{boEb5hڮvl2M7w1@$@kRjd++%&+eXsHrVcM 2BpT#3/x3eYgX8G D P,KOEsu7TZSMtO pPʵ5 cg$x,I^cxV9]SCc\RU6- j&c|E\wh>[}fƆq\>/m ~,Sf4)`  [?e]H"B`H pcO:d'*}tٷvXhntqɤg]lv- ^PܲNx$fy"xoa}I@5DݽPQLZ"W~w/C NOh_'O^A3OpW_M3foc˱s\|ݴ@ `# @"Dž @ B_" ~ @΁@ p6+DжώG >}gf@ <%{kZ:؝{]@ A]~yvUe=-tA\`_[K:H͘4yOF[r.?|⒵zxD[OQ3ym7;3pؙs=VE~2<1=t4E@ ngbյ^<}pMC[65Nkj H Y-$iGn֔,҂v"$W *uq" #Kf)1Eu9B iᢰI $al=]kbY1@ 9 |SgIͩ2Z93]/~휝qGd$;:.WsERU2ZeQn߶zKÄY%\ϛ _ @ mEpSޘSjMiw--no>.tMYө*7#S,{XBUM02VM/; >;t>/ s^v7>p> uKx!ϼWaw @#a-pq/0(*Zpk礱ҿ;{jF>_,֮@7n=^X ,_9L+ 5$"nKO_<9x4(Ni3\8pdx{ m:SX@h"stko @` >%yO}s7zk[67*{]0 xp {xoAZ!|k@ Л @>E># @<;J@ S"xo& @32P@ M(sÇ݅=_|K* >}eRy  ~^)PiGЉ|W$aY!Wx񔅃[(=^.(d-;>. 9覿|~ԥt\6KY4ﺘ6m3{_hrD.pWIϞ9b,`ծ/!BL7/R/ǰ6?a RSGyf/w\0!uh/fj-sv Tw#Ч;726_EN?t^:wcSK+*VMCH_1=/zֱt_ﰧ3'=%[5eά;}I>]oʎES 0 j8~\ izYK#jŞr,rɒonGD.pD00}oXڌ?~VPMr>tC YEk?)x!Ͽ0Av<~<HB#Ib&$1ZZK2yڪ-<{{Z> 4fC/?;V;u ۋG9dmf2]|9k>ޞ킰y3}vܷe%ۤIۖ{he?{={;pQ<' *2P{|iL`vp7:Z GZӆFS*/\ C|8s4<=.>H[u%sV\s&OX-y[O} Ab5Ϸ~{JC>?|QW~pen/ːl?#C~SD^؂korJ Z<ac}ۏt<4:&b=ceF*^'҂x\vsvow-G3S,T|DLH@LR uVxF[o tH(jL["ac&E}ڳ0GrV_-#gje>wV"4&RVbVMY &p?cjou|ǯ.޺n۶%:/~7stަL 4iq~HEs=5YO1WWZmRB&;st]J]e~҉OH7&n̅O>ki=_<]s1{Y "hPyg=#M.<3uUl2ضQa%|ψ-7=qV{6CĮC6]S'g?$)(VCF=b**-5Eql7ZqߔߞoNg2-',ܨ<aˠ`͍N~| OZ\`?+&']_,ߔ27ȌeHaܲk]WWerZptv mܙ;~>ݯ| x <{°97+eM&sBljj7Z>ݎ4ejt8|Eu,q s-Wb (ƳzUv.LH:I}Yڨw$!+k<yeZ~wzѦ&ro#]vF7A-4!%ZU9Bo8'Q@١ HT@QD|2ip ><%D1*WA^'|Y" u$<3(3&A \>Ѫm[QĎZs7#͝G{FmJyր` ܤtλ&d *vqv 6y+ΞX'(zuR 9n7\nBwW?lw=[䜩ucxb٪%]jC^|KG=h,˴^"8:3mG$-:Y#-qn |>y_> Js8W Zj( Lgƶ3 G1WPjr2{ǵ^yuITzwGd! mMK],9Yʫ'S&4w}mqBVN=9"9w9zk2m:[T7(g~y7L{W}0fw=K"Whfld%Md-kߙ>Y%_~_(Pb)òיE:犫aNk۱CٱO/y&M㉋:Eyug5% ,Y2"4 f,do1w,& "tE7P5al.^wy"#@Q5H}"!(]CE{g1 >)e Exq:SPLdi(9ݯf@ p@YyC@ q|_IENDB`alot-0.3.5/extra/themes/screenshots/sup.search.png000066400000000000000000002066551217172710600222310ustar00rootroot00000000000000PNG  IHDR$sBITO IDATx^ ]ӽ5DA"D)""DjlCQCKkK <(W]^_(E 5<1  csrΓ䜽^k?w}k-3tŢ:섊U"E`toO_wr6:X"<~̞1lL̾1Ѿ+j3[ڬO?ϺW>u(\Yt.ۮYޙBuw  7"ި{PW}a'h>8KiCWd˅;</#_.W*^~+[fX5]h< ͶdWfgMqMf+jડfmZ/3^Ļ͎*@ƙsq|~lF|af f 3[6̸;4m 0ByZd6z}PypmqjbGat'oӁGr50F"6>7Ú?5̪Q\ 5J9Έz&M2kzriJX5.0y%.v;AXMb 1;k" " " " Eޱ\~trԬ8\?pav`i{_Mlc#/:!$`bb}\,k%>^6Iβ;4䑛;( N4lK(RrX;\jpξql;pq'S|߽$OU\Ӊ.@|zCvp654 =L#8;]cdbO2;l`u),BO0[91d[/8k涱\i*/:=h1`P6k)v}`RG˿Ea :s@KNz)1OeD"5(RVX@Tkʇ͖ ̻mb;ynXyM<]6Q8)]ƌo" " " " L`٤?ʂsK9<_]v[iH DrYfs͒mOUwI[x}rSeAܧ$\cUl ְF9fBrZA Afuѭ\]/5{)+U_#]aE+< o*d$’bv%B]9%fVf<ᒯ>McY#߂+C)n]G@b5 n?*aeXP#PWWoGqDa#i骑t f,%?alg#L@T-ն*IMb[}b]TˉR\+<_&k3eavُ׶W_0rWϠ6lNEhXW}CD5bJ%5iN4Kbo0Xu $B c." " " "Аt[Vsa d}*?nNxd\3ѱ]r]7^77#=.? ['W")G#8@7nqd8,Tjְuc&nb'ҵ԰.q%hHLwWWǨ1L9v4|X3}xnqonv@h@`An"yܲ햡]$ن2a k~.k6!7 7`?.=RSz-ƭPa%`DV+]ݡ V_\ķ)-5[$;? r`QS&;Ocow&GOsq^@rٻ2S޷rX%[t^ dv^%^q sN *;(3NO߾j]&'@ S9ULY9gE.w'a38CL3|v\Ë8ש`q*alnbaH,xekiy3ܬ~ZEs7}Y (y]߻XHT')Q'L)MG!.s7ЯI; u8a8=hTo$O& W9q*zbi-x!q'x991݈.@|Cvp66GD@D@D@Dp43L2`hvc9:vAC'U.vȭI]\yG'>ʼ99xwXi~ҝ'/+j~FX̞v?$:Rb%+fgqE8ٽg7̕ pD'+ǚ}=_@)ue3~}+._4JU]O"Yʐ,Ix#jAr#VB; !٨0ӿ" " " ݊@& FZ:l<c!c{=^tv[~wySj/7ϕ)$+J:Jˁ~e)yHc\i?XFVK W:1;ŬxTJx ~8 x{d-rPL }([ˁ~e)&}X cN>ɒY!ѭT˶D½L%5l+ bk,!ˆEZWJZq=WII1}&'@J3fu\+94v]d%eY7?VRvuw9K{ټus#*n "}ްrm&QJJˁ~Ffu& @2Ra"r+|".2opJ/)s,^E!s~vbabn{+uI!u.HD@D@D@D[s62mn-\,`}eux#qqLo\[~Vuì Kg|-w>6/0R V{\VA@-`{-C#^St*~nS/n0|P>fd%R<+Bue}C @SKm>D DbplVUf7]W uND@D@D@D{=-RO$"xu燼x4"Aq4tKD@D@D`"Ƣ5[rnF靐" " " M h2ptKD@D@D@D@%tSrX)ltAUD@D@D@D@Dg'nYnC!60e,'T! }pu Wa ё:-kFYw:(kFasqbJ3r-nDP#)Qz&a,s^4}xfǙ>ΜҎs6UU焍?;p݇.,F\[O-/v.rgs^f[d Ug4⚆GħડM+ on'.5M} tQf/8.m0w&`J>pqZm 0B?<ꜜz,T5dqJbGr#h7GPgm9%wV"5j!u\i3_;YO#aK$M2riJc9w0y%.v;AX1r8(3޾F -" " " ".0󋱍Y\#fSk9TٙfsNHw`Ҵf?)ƚG0_tC,ӼIspDC,ff_Vj4Xܸ=Ƹ2 @rZM lpξql;pq'Spn7/Swtb%+ߢMϏj+" " " " 10fy^fͮs'2in$V7EH f9luWg3u6+@ 6Efm.x2;5guXo{Xþ'‘sp`)UoWZ!t[ͲaL)j+ay^cf˄ uw61<7j&Lv3)ͮu cU_7$0lRDnfe9m97{ GRHy2,n5 !q)=%&7ʓ4eB=*l5$Oem'#f]d04)5sa~[nbV8x9.˾Kn yot+i`{>#٠%\l.{@5+ta&.D9{9-"8$˛uYJj3b5 n?*ae;󍨭cGסWoGqDa#i骑`:[IvV:|1Iɦ%IMb[}b]hˉR\+<_&g+6?>Uw4^,_}9[Xg]c)WT oSQ3WWB P<^"׉fbI ]4dR!s~LE@D@D@D@:nGS~.앿mR9t9xqDvu9wiHx܌|//(^ oX?@^΋M Q!L@F$o!0R17K=եZ5,z*|.3dM$\ݚe )NJ\q$Wc&rBr;b>>LL87 _7; s4 `qNDD@D@D@D`i~q74anAtY7gX3?MGTOcAָYRH. nUw,2.t!?p/_BXLEv.lC5 ?[t5[a]_za(0V1"c?.P+/q.[Rnv* 3HP3ԔX!}YE@D@D@D@:+t?N+;NU5.vN klj_&5Ԋ@idHD A`_ܮM$=:NFTX$(H s}$3fԹQQihxF8ʰ -h^gf4EEk[2$ڵkn-VYI|+ЁC*\d]Er몶|aE~^q磃"ֱj91g2:.a@2F5ʼnxoRv ǻMO#wNq},X`js6ҭT N22\rzY k)FWS7 :jAFzm)TW.P;Ep ħડM+Q/n.&p3b{gp6X#>4ؿ@7K7~ Fw rN': 9Yl\Rؑ#-ΰ#h7╉PmS̯Bfu8ýNz j eԠHQ[a ]S+6[&\3kQC`7aq[Nllv[3K`٤>ʂs*K9rnt%LCd!%vh&=$ 0k Tu|W)/!Wa;%!YF}*kK>)&o5>%!Em5,QN |nrZ)H Afuѭ\]/@p_u?h+VzFFd%Xr}_Ns?^=׬Cl/OOXH`,ov[f)7C+ID6s.Sf#jejN_H/Ky\oC^s?;F"" " " 4 f,%galg#L T- ~$i7\"v6VvkO,k}Ԃ}:-CH e0l]lCnn~n]J{v+ww[~ 녡/ LjV|CeT|ǹoYSx[Hy+:$"CpPSrkbe}h ~j ]+Wvmsj \@4~ n7dL*$8Hs@9vm"!Aq4h̤@!Fˮ iM6]1jCD@D@D@:@&FGW**@GD@D@D@D@D 6nJ; 9zD@D@D@D@D@:@o}[]fGeD-Y.ٓO[Ϟ6a[#}=fYһ f%`nCq}ZBWmns6d-RV_*z>F91!fO*ջ/?p4=(Z"" " " Dw"v|:6Ȗw+.~\}Cq6N?ݞs#2%G6’|*5X[q7}gc4; btz+~egiSDwz_2r!V_D߮=6ݴUWwqb*A]X\sMd|>*;:[G.OR_f]K g߶ڪ ?(z*p^IF$70^NBs"#>i>u$[ӞyƎ=6z.';6x%Da. | ^c[n/[E@D@D@DK )f3fyYgehgm\~Ej<ӌwd03gbE{n[M 8l;/GRn۶~Ӗ[4w-_ <7.5p]j5cMUu2H&F@W ~DK-6-\Rw>,R H}-[KܽF>$/O=yn0#;oq?dm)NA="" " " m'p l;S#;h]g=I ?˱/(ZWnC-NV'8]7ܕQmȲO 0hRU S1a4d0" ]V>-L͂Uw~gy$[E)7k%uWD@D@D@J1#}"2Z߷><N|+O~Yms+s{臤BM yY\2% {/Jl`c8">bߚB:ܧQ>G?ʩˁ~R/?d| (Wwƫf3g Y͒T_H18LJHռЎoye^Q" " " u O% ^4}Oj Wy !! =2j {/b᫗#&>rcȘ:h5ٗ8(٢_[F IW ECJˁ~e)yH!0 $/ubgI5]q>W_QqD:+6ħuJJD@D@D@Fx֣ƌr±UY}Z- l|Y$$d=ь;WO̱5IϢ:X|3;Ňx͋GeZ-*V e t9Яl=â}X <4,* @le*f[f[c ^6(ҺR!^{~Q²x$vNҏt-2Selq+wۤs6pOYւF`{a]w,)O{|󖄨o0ioξ(%/ƚ`d!S*).Ub{akoY=H~G *!z`r'/s+FK>$ +uU=!1>"< nΜ譠Ba9h :sB$" " " "Q4w{s6 25F+h΢7 W` _V:W_YxԌ8$Җf0҉>rh:Kۙ_ECr=Q>FA.+P[[猎>`|U^xլT2ˆ`8lL~۪_v'cF6Q2O/[K,ɳN .T4t." " " y0hٟZH'Z8dV;ێ?BoMLL3>ͅ,ԍVJ<]dٲJ_]cO^2Cf*0%_: w9Okl4̭'xykf2;߸.p'kb [ 1fG¡tWD@D@D@D!4#?0~ٗLHj|OWy\5+ ~TQ)I(Xw{uRm0"YR8w0Gi?bWQ]\wCvpmc)hld;˜,4۶r3 _pfTU:ٖ>љr V=0ۙrF |Xlpμ&f3g6Bqģc 'eck:qMD@D@D@Dt g, :1 A|!L[ħ*}ն4;ߙ"|]|3:S]r8'tA"" " " "$=," " " " "PDۚ\vsr++k55>!9N` @6 Wa KgΎhS;w:(rF˅VÌSRWqw6/ph 7Xh"I3w(O;8cgp{V˒ 6</#mX W{kS ĝZ췙3;VpxaBuՙr=۝&WzS4sN5_ѐzq~tC8(6q6ϟkGfֿ8@=Nh9c *3qzP\(qgbG~s8¬ՐGF\6*6>7ÚǤ~lj jl+Wø_]/ci0 _&Iմ^*yp_ qfJ|8(w2yto\ `Le/.6Y9CX.8av`iq~R|)7{5`ܿX.wy'\,XAE yfAeVSO1f>J,R#V@C*t9.OPFo|#n+!Ix#}Uo6ߕ'i*nf7{UN kHVoJrWaV.S2RVXr唚g3p!NRn yQMD@D@D@L+&FuFEd^d~p =o׬ ! |VN,ˑo!t+C)n]G~-rF9ZM"qs5T[%5,Ss {þ} +)^:uR ]QBSyX&rF%]ZK+Z;IMb[}b]TˉRi\+<_&k3eaUk/̯YX25dȇ)*Qz oSQ3WWz P<^9u j2_I!eI;s5tIhDW;q;$Ύ(ލ3ͦg8(Cc`(Ͼwݴr,0]H8]sdvg#wY>`;%9xf9 S8덣uF|qdq=`Yp5_hՔ|;F0iSz;+j7/?#TR; pb2 U-~MfiQXwc3#p/z٦yF^+H~ +6.0pf(ٜX]`'* ^c̎2l쓺"" " " %Krr;c䬜gUgE "cpf5 t& f"+qpw{$Rz2vf?ܢ;|e8wpS;skEh!L3}ɴ2IufZT PT]x["N|xT`q )\:wefens?<܉If+%`2+Oqک a|lg$ol}"L|9qciQT[z >P70^ᶸf,i< |~g6LjٵnPuzXD@D@DLY?A˚^4>gԜB70;O^F>>W2=~HJ!t0#]! fbnV8!g?<t,_9ּTM~|QʫԵAnd| (Uu=ƫf3+CVd䗟׏!/)~lT7R^40}VY'\BV{,dLȾXH7a%6f L>#ݛZ'fd% 9WWS;ƕ%il|xլauxH:!𑷸S>*SjTD@D@D@D E`kDɒ@Ƹsa5ŔV]?sOJx ~C8 x{d-rPL }([ˁ~e)&}X cN>ɒY!ѭTFZ]^ԩB4ob񕔹KTT@w"@3f~n"+luaMw,Jʲfo8 㬤r( IDATy%!v!L{OuFT*m Dp;&ذrmQcTR]dEŰt02 ! Mn{=0^qKIe#euۤ3jK̕emŲo>x tA"" " " m Iw+l0djHeٹzY@`}">AVL7W;3ǔƵgY7ztWjl٭n:ؼH2Xrc|iD POE V_0BVF.%[U\˝ݦ%_wPUWSCNv[hG 4%P]!}YE@D@D@D,O-aO1S&yZYWvy^qaS" " " ݓn}W]DbK5J5m/d'OEڈ@7&F7\u tz3èE@D@D@:@&Hk6a㬊@yPiFd0Ո@gTU9tk- \>t< `2B,[U&[an/ӱ>Nv”ԥj5|r 7"hFz1]D@D@D@D ^VK#HjȞֿ]ɒg =f]1sV[ R4wf[dKնiч8R3ͦ88?I(;z1[o^]nHQX>?ʃܹ=S\kBPs+/sͮ1'2Cf*0_} w9Okcil4̭' q"r!^V?`1;wиDD@D@D@D 4#d`IrYԝMturPt6f ͩ?$ ,\| g۠an=E"KlF <8*&MY/h_E@D@D@D@@88 3,4۶r3 _p鏙TU:5;:S.P! {Dp3zU]`f˄ u"O65wf|HgV&]ֱL U@'hg0|`C+S>IjGr4;ߙSJv9]סNWGEI$" " " " L!M!evs9%[~je[htxX1TEQIiOwrMÒI#~ 8񕔹Ki[[zJD@D@D@D`ho)x^Mog%tW,f7r%f?<6܌UxvL?YPTԖPSJxۦaw%/ovVd9< hf pfV{'a.3)NɾSj6͎G[oe]frefM܎$ۯf'l;L-77ID@D@D@D@_=<5.A]f_&FB}Eh4Fl,Cu$& _(OFM@E6 t˜td3=BD@D@D@I hg}z\D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@g5=[1nFQn=Vcb +ڒ:~͇g_|^ݽ,@<У2=ޱj\JJ2UuI#~sǫ.>e aXh| t:)O*vpmUmxe5kWB<~ڝ֌6zo^ Z$5Яd'm cw9܏ذ챙-TﲖU9puY~4ְ[^og/UD@D@D@ @ivٳn,6ͅ=T9sfmP{ct}m-Xv|͞om%b}{ڸlgS*TFY{d-fu(W76_E'is-tZK"5jU0\=7b_ju6]<ь=5/ lrvD;d|}ukdz/Ş|(l3x14S߷)D$Pc̠ٷW ~_$" " " ݀@9a!UTGkۭEOh_}%D} 赤<6ivvV͙ؑ|_aev-NVo8$L0CAF2АGe=*x{#o#Y3RrkOXo`+_k_;(D6(jƇvsp#zz`Ԫslk%l@ljPY%__el`1)r>}Zq @#hg\r{Nleؑ]ۛsmn~2?;U:ﶎaמ<^c{Ͷ9a=<?Y8$I:zt}d"s hnIJ|d) k8s(д;~sh(PS|h璶@Í|͠{)REm5 tHC'D l3XMѠP[(az9_t i<= YBLB_1KJB_jPB~ }G)=,@CD@D@D`%4GaZi ΰYVM7$0O\VM9ġh׎扙k3Mre[oʜk&]$ilQN?3~=|>=ҞrkM(Wab{m~nƠ}U/N'+i``Pv$q7?vߛQ8ax)iP&WDY)O7FB&A]@+%" " "&I=?SuGX{gۯ6_i^m+,n_8+2KӦz([F"X]RClD~#HdE.ӝg;]x%VI 86-"?⧢r%5(eۯ<~j!^z2r&j ˏr@n ht-)-ܾ@ShgcF#.Q$`6m"1v6;X=9n;`sک6gAQqVs_D_Y%vfQF5SP-^O@+a{͓DknO5Q!W]WY~dc?lgh4KѠmPlfY$N|L[m ?_C@@W޶xuKD@D@D4qv(L6? kJmW#`Q/\=5Z1Z4rf0nw޺^ F􋼈|w5RX 8)~h[ 0R*ְuf5оv5԰.'ޱ3_l J - nAy<4V5@>/fx-5wofgo- E ߨmX~ulbUD8%uKD@D@D)4Aw^([vh34  V6aYum68Ƃ[ggGnNTp|1=7r}h˭$~rփŜdЏ׈Uc*ְuiH,֥GjjXocepn/|5{_/u _;XU;QmxZO7ڱ["ߨm $_pFP#]4Vm([" " " MAd}SEJBSYHb"7%6Iݦ;!6TMG'߭X3:c*7r޻UW6mau[фS8ecB|4Զ2mcRo[VVoFe$z[I؞m: A`=~s3w?X˾-3pAp s˴h70y-hB4{ޛתW/_OGpuāmc7p1.k^cZs]㴐gj*{+b*JV:)ѡE8l픈T4x?ewbFjAyp- SkYWoL§=&\-+aJ[q%y6+XU>¶ibۏj ,>8a=8N;Z/:QIەe۩}~KΆ>gk8{ YhLon9yx{ ̶>Z%l6r8lh 83я׳ob:2x-=b;uHWjV?o~?mtnI#n HjF95mGޖV5sٚ#)=GQqCqr+/j l>9׾ /$]Юxξ54 ;lfD]i~.~mjK-aFF-!ٞ\?]voڧ_'Xd 42-Tv<k K xecdCIl;ru{snKfǚ٪^c?ngnco {wϣ ц5DljwFaYQR,'mmW[.'U s63)W_k`dNZ0񂯢Fb@_hx{ ʱ}!r_łv}@;vSV9vܝH~cZ'hB7,T4+3/5AH".OH:yȣ3Ld@ mӝCHEO>f5K{- XgQ5UYfCe٥ԑ8KpHK. XTm(4qX,iP[Q|nu{}M{L󈚴_(~{쵩Ɩ4P_/}j.)L+<13! )hy8{Jт[r'C|~"[ߛѩ 1DHHD+w+2~e;x#{d u|Ă6nʕT<~ j3WV䕮|mjoUI 1]ϾފOK wYwE@D@D{h#Ǯ+9 W0 Y1oUf~j$xa*<C/]1ٚ))pn/6]њ_Z^d-)Gl 48G/mÕU,Jƴeʼ4xwkCxHQJʀJ?2^=–\N͢4Hy8hw5,R+;Vӷ*ɔvjKb+e6Y -64˘#oG7be;h|t>[g2++%AM EVl1<ب*)0`$/ZJ8vŰ9wSp\}x?cl';Q용ўT~1j5 ޯ̸U]f :zrU%|HB yaSmi[.@#Xi^F2}W)0{X is6Pw2FE=t8 o;u6k:y$8@&?>6mj4b7o2,ҿ6* >ۅO]e_;X;e2/ۨٵWXNzmH$&;GQukvlL.IMtA}cm̊2UUS;rU6wh?b lo. HD@D@D@r 4Vd#WE]Ey_.]o^͸zQfz+F iDN"" " "PD\IӺ." 6|jnZ(!Q_E@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@ 4ܚD3p~3{-Qã3CLɆ(ZR~uQ!$v!|^1Z/Bրv/BW/Gw1#dd3iU[oPt'_T)Ok7*eL+a^=9# e/{EUdu.NN>zz ǐoB+FX(NEjO^!' HQTm2{kQֿ4 >!" 4Gl?Lۑtv@o':]k^fOkNKGs\Z@Gpw|00M#q}49v&0Vujl:ܦgcʈ'96:\H2+ iVK[/jg:t"c3j'e 5ƞmjU+/$f=d_,\[گ wPx^XG%*zygP비M}?ױ?Nl$qJ`ÚMΚC}$R45A)зKzR?kOδ Zn?]kQ"]νUsVgcG#2)3dC۠F:nkϭmÕݯ+w <[" " e 4QiOޤfR_wX;''sC~xHDB MmVwmG\U}-TK}5| Ȱ^lU _'x[ARN*z9W좧Z* ; vv50s3mGbv#wVg#wY> w*\df^~]óXPs {> ZlnPO=*ȳ̭q/ $Ό(2? r<vF6_Ȗ%5l3"J\Y U4(qsy9#ź2x^@́ [ŀ.n cEg*65jvvQ5f* " "P@9M:U6Eb΁ů)om08zLOlӚ {^55r,xZa[FⷼR<+ n?[vѾ5|[mXӸ7/n_ݺ4sփDeKw"RaII;a WȮe h& 6 J~f͋=~警-6?%KT4(|Bvr+:Âj_.Qܿ6 kOJ3 *R>- ӳ"9NyM;Ht*E~ WDD@D@N?ٍjIz\vtIQLL$'oĪˋtcsxOyE;wgK@hF'uԬz$=Um j*TvIz=x_NhS BHReQn[(bFE@D{ΆOZrh{C^hRt~KP9g9#In^t&qߓ8@i%.]WGd_~ο{wXbw}Le^IG|+Z*!:5ty浳_-^{5vص^_~3g?l9ѷr5,Sa0&Wr-]w>lܹE*% nj3rȞ={Ξ=;˭%\ҏcksįY}C7|6bJ6$z/EIWoǣqQlx22d8p;7hHp:^ao"&#us9o*|O;4qk67n`\|wnkz߶jߟSV^\}r/ix:PԠ'x"Im;V.W.?cVQ+]ܯ]vم{yg>YJ;p-|7y@FPO:@Ch,gc뭷Ʀ<쳧Nzc=_|L_51c+wߝʮʺD U?e]m?Y L-F-~z@ L\R>sh_ {sE[גSZ 0V2Qh^ll>qWzL<|_;!>#sJFjj3UL'ՠ_+;F^S6JI0 hϭ0X{*~uA#U?'6ܫ4(-΋gyu1k ` -bИ1c&G%o҆P8%C?-+AI<޸9fsɫE@̿3k&C\ 򨣎N|e4w*kD[dz@vV{g1aLiU#V@C thber?`%3 +2]{2?{մE}Ra!ǯ{Ñr /^LtVa(oPdY5gBfDC-p`=yW7_g^z%wpMb,q&2ISl6kgǀr?v(䆳3^L3ꕔ/MszfU,njm;^C867{RZNCj;1o;h*]ի%gX;Ǔ۬Y3%0x`l#\&N5\avJJ *ݠ&y!\&fV洕R4z7#Dޠ3^b / 8Wz<"LУҷbH`7k1 !!`y.xŽ5J.&ٙVd򉉉.V5FNQvoS,m˔{ꩧ?x@l@Eد_1Umڴ)}:0(8[' eĈ0xaM:_Nԙi$ĕcqUV\4i4̫F[n)c8ܠ/C_rupƜ5d$ i2T^s!j!M-'|tE~>1N܄ qA։{#<<7= M `<óRc/v."ԼH(,V\d9AJ%It(G;sHyX鏠n"!_PC-CCC`\.' <+!κ>V2fSǜ{n/;3YA#xZN(\͠ ͋8Y~^p%0[ X`ܒ- 6Mw҉]/  /lX y'N[øZvFX|TlD Yg7^# d ߒSn`iC0Tވ'[b[&&@j:> WIksRuW#mҤIz?IX#gT"ț^Thl`q&%+IWs2hciB /~ wV"_oYJb` rw q%4g%A;J?3DJXBɭd(2+lgFUJXcטz}-*5t\E.w6ypgs}le`KNj+smabHU)ƇR7' [ø^]h#4.Gd&jo c6,G4e2.N0XwK0!,y1awOĶI{ 5 u8xdp".S8G_UpT8jDΨDᛞD@D`!Pa=l6!!6YJT4(-%\SBT92_)e@&w~3U]%" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "P|RŮ"P9*q 0y|ڏ/n2%԰@!6mکS'޺`>o\>J& P\ƺuN~:knҥVTԤ@ ẳ|u6lpʕ/u%@D@D@D@D@D` 4lѢEݫUOL2eƍfqe@ };vl^JJJRWHG`СM4IWVD@D@D@D@D@D 5*U." " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " EJrI>r+WT;ґG?|y$ZjVZr" ,@_a;]>17R>suR V 6y0^R6ț;R@D@D@Dl]moOQO@X੧zgZgqwBaE7}wR FV+ ѐD@D@D@6l{>EJUN;D_~o<RX{ƍ_|o\~c_>&;CΝ{p4^{5%OEc4dw9UN6lsH2e7c'Nmzue]z?Qڴx~֭[רQc…9{ッEUV?y^|0>S]e{TT />!?|G Fpr\ցwy]vaJ\G\)M4iv[ ̟׬Y3?@#P\ 7p 0`ԩGJt Bejg`Up .OݻʶuO?!W^ r% lӦ }J9 i֏.07nܳgڵk?O?tpv[knɒ%cƌ:#2 0؅MN:z+;/9ҫGתTB.%-_qFW t۶mn;lԬYK;qjzpZ#KËQ}"_~y ͟:tYYµѣG &s[D@D@D"(.gO>0: 0S瞼hp7D?OݻwoժՔ)S\7|.M_aƊˊ:7L6mjs94ryo; nεW/Ǵe{РA+VHVxLl.~Gt+2 Tqk_}e˖yά?3lD`2?Oz'0Õ®Ȏ;ѩS'=Tb%64ǃӦk׮GU:B@We r?˗ӕ"" " "P>LmLvVI}zk7XZV_9s&XzK9~ha^xtPzQ,$cle4We˖(4⪸t|24X䧜rʂ yEj^טx4g"@JXx }6LW aZrb>7̡巛 IDAT(<6l73K6hЀ ~x}ͫ-(ZEl3Df|͚5#@y/!fBdVMb ]H_ hj͘1-积.9.y}’))Ju}1L):/9f 68#TC/>_׫W`ܹ2^){'>< Ds!-gg#RbiRJJJCs_?ըQ#U&e*#" " "PAYhOizć;XK0>¬zxQin·n+ow40&)iDQ&[9-`L]w[o%8`2z(XiԈ/M8ucij_DٳQbD&vc^TM W ;jբn'UspʕA[~lȐK8܈#C90;: Y" " "P%Q_œqmZ~}E[7XW)tGcSo9,xl}6\-Ḷ:HܴiHQӧOǸ?묳8$/M[NF'1m|Ik<͓&M ༵?g"L^5F p%3\) {:,^0'H(;0l ;GpǍwUW 2ErcUU{/66p劀@"P\։{iz8ݺu^:/Glsx7-KSrԨQqAX"XNΩcGD1NqpӬI 7y!Æ <}RPwWͭ0Pp3ǐU8i|&3k~9r5pxnfq{s#~31nJ:wؑ\Zlk0lpy5ۇē0QU9ǡVlxC{eI9=mh2jBp>Q"Vͥ8ŵQ)ND7EmO?x㍼ڍ+|';.%e c, ?_=Mse0xp.SN_ '5\>?}o˺ꪫZhFwe[ok֬_=?n/#5B~m,0[ VV[m~"'L̉KV̨믿9T=묳 2gΜn‘F 駟oWDZ6N:_}U3뮻o+ #N=TY38;xw]MhcAny^Vb-PϷ*/@q9{G.Z(=l?ayϿK{oϴ o6//">N7Ivi;vlBWZ8~V+%\Bs8`x]t+N;lРA . ȥk >V.S:u*rϦ {Ԡ+Rp5;/ _r~0yO8ᄠnF,raW NbW6^˟L˗0٘M69sf&^%Ng}~b;.N;ےu1oz. l:(V1n8ԱfϞM lzo525Q+~fW_v튁1!ŋbHk,'Ñx>cκ&+,o&I0xD>D @HfuGT fk4\@Co$&e/^,O`b@[Jijb a`cI巆""ȥ3أ޽{VLB"" ?0Spq% evZjqűH0!d샍[KL͛x +JB oNEyg0yYF~-S 32M6a>LѦ)*1keQ_~90TАbUiY[ A#n)j%mq׍֭[7&>:mڴm4jmb#0l"+(o+\(.g.}|V 3-RYn%aŶ[bŽ %g3Ñ })|`<.E"Avۊh(I_xlFSܾ}U9ny1&]wmk8r`5F:5s~[t)#jٲ%h,`a7EQAlQ`}hcO?c$Վ" e_a!)S.pԨQ%M#^x$ ¨ {#ǫAqBE`YRa]hbbqeL e$yC=Ԙ;w?oɐe m`={in6 |vrs.(i(2yI'OJ&e ;b8N"^vx5V剰%Gp9aTʠrb3=7lLyQX4S#V^z߃ "! :~{ cE=>`_)mq(~e_a9)Sd~;僯#KI#e+5+`Ip5oFYyța*rP -v/2.;TRxS4CI>nH'PFlYj]NJz)"P\oX¤ Ol8ʹgwYd=CJ? 64R}Vx''0zc):K%`"\ CCFp%oe(sOLC6p !,8,{fYrmۺ3Klm1b]I&{X!Lbd1ȕE,N4l+,'!s9M]F44—oK1cSˈ9VJ˻A:@in8M$p|%IMJ&'XW1c 4t07LfΥ^nN`d[@C ˮ~GiF*oT@H/_+FZR9y ++9/8lHI;Pcq5 ^aD֓^x`~H\s1 ypZ'0<>+ʽE;Q>Wz.6r@7t֏-u D/dlڲg[]x#nS 0NN0Gm.]42Wɢ:ˁ^5Ch59Cg۾}{ፑ42,sq2r\44—oJ=^Dī6lX؋,l$9(v9TE2:WVM3ѱcGZxaaore@"P\aT]j^QmƊf<[lp9N8@n4qKO " " " " "y\T0`s{k׮-*Ť@٘7o^ӦM;u,XRʋ r6֭[7uwЁH-]tժUEBJjE ֭۰aÕ+WΟ??@T$zx5Zhѽ{jժ=SL'mTH};vl^JJJ*d@q:th&MS7i%" " " " "P TRk/E@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@ PY KϮ\ʕ+ +VD@D@D@D@Dت4>*o*|ɍ5*#+" " " " @ql 0yK,Yvmz֪TYgVy[oߑ`U|<{$n6A Fݺu;{?9kݺ?J@(.gc޼yM6ԩSZ,X6#ifuUWoTtk֬٧O }b4iҺuȪS~NԨQwZQdQwYnW^y}'pի.]K7l@#;D`á}#G|1SJ̷{v۱q1ԯ_رZa:w̞FP|A"0cƌW^ݮH0s1bDƍi{F› [5k*&" " " " @$ʺW7n6m[onCÆ S6oh(X}AC9[/?~ԁ=^z駟~"qΜ9Qd˖-׬YĦ Ŗ]N" " " " "PebлEWVmSLIR+8>qG.ZA`@>,:^Ӊ}"%\,'v=L" " " " "P  f͚E;#nZlȴ{tM]veρ 4-XgF1F(~8ݯ_}_q*#" " " " EK lwNDs)eaZlkp"|8~m'T]t?;wn'|ҦMΐp}ƌ q8>dN6*O>}ٲe"\&L8qb.?r6S*H6C-v66GyD8Ž2hР;ٳgǎB_oѥK#8zdr?|F(&|&/DZFVX( ʥ\ h޼%KwzF-#+|@ >ݴiN: QKUD@D@D@D@D@(.gcݺuSN]~}ڵktUV%vƨed%U(.gdumذʕ+ϟ{F-#+pȗ@|+u-Zt޽ZjO<Ĕ)S6nܘEFʈl3v^zQJ/_%E@D@D@D@D@*uvЁ F`V##+[n})lVNb2*>HN+4IY˜\sD?xw[NA^ STlw_WiuUWhg *o =zȺ~i\`z^cF+c=餓(W_oഡoc&V ˶+o+Sʟzخ38;xwO1ne5C2jQbE@D`K#P\; h޼%K֮]~$ZF! ^m۶}7eڵk~߷~w\V׮]WZɻxK.+y~Y.ɡDe(> , ~jS W 1FFaHhoqƱM2d 3`"OZz捶 JNihad]wu}Y߾}GyNa=Y,{5#o} N.07~W_qv`6k֬iӦ n GEaD^xk+Rl~tM'N裏#q9qj$!2.CF]NJ| Jy*nZ=Ӄ pO2݈& xȭZL?m#p U{[yYg3#Eb ,{1"ع=h`Q#P\;\pS=:O7ZF1:ꨏ?f`zbu|ȑ.7s[SrJ<>c07^8C1v3lݲ (g䢋.ZaҤ `3xmK/rhx׼LzpTe Q¦ě=דB`عKs>;i/r%醆ָ_5ea5 ct7fkE 1,,3c}Q0'Χmݵ^ۨQ#Tdu~P>ͧjP[gU{[Y{5]fM}x@9LC2n66C*у{w6D@D@r6>ybWas=iZF1g͚ _3=_==!ea7`U/"`×7|3:.e\lm%*SK9s&PǎK<]2#U@pж6ڲ= hX`ze%z!|=16^YJX]8~guС?g>nQN޽{VX&ih"h(ʟvi0ǀ~sB)nDkm)rQIFܹ85(זr_Ζ;K9b<{SZӨ@^ݺu>:m4zmPcUBGp tC C`2 *P \fMP~7ظiȐAh0(KD@D"OYrit3jYqY,Zu<+zvj''Ύ?)7F(K/s!N 4ub_~eq@Q0+Q+bsϛ7?􅍶 LYW$ 5LJ˖-1XTbB;01bμ| XخV;Zti X^%-10} 4KIZ La^]/nq宻b! V7(aҤdP9nA\L .%!SDfZHVs+r \zFntg'-R<#`&!Dr0,ؒ d5$F-#h3c#7 #F4i1d<83)'Ĉ»YZ\!A5obI7]OR`qdI tR ?8m sm x'ʥs܅P04 ařW=!U/gx} rUA5,蜛G`?Wbf0\W^yſUU9"y\` F30rҝ-,vvnv5@`#`A͠<3 SnJwcW{ᄟQm)i#ц emJGrב{N7iPčU(.g#Ag2"ED6d  :P&2G)QШQ#Cl:餓yѪy@.Z^8U{E m q2A"s2 ^2oH7 Vpq: $:Q kgc͛7z|/6}gZF!?[]F->n~sແM4駟vqGlzehXY(Ê_{U93%tYf|\2|$;q/իWύ#oOg87`2qXܪ[nEYFڵÃ囃| )oP+BTS>nPedQ1C*t93^|V̇킹A14EVX9mld!YUV=r~(Ř6qm5dM湿d\ /AIqjP1Zv\ |_K]ql\~7(v:]&+1Lq NB݈h(7(,7*<@> m3Qڽ裏{)I1q1.mڴ:t)S1>cD@@q}An N:zfQ2 [-lO?w0lnظs 1 U AW:tbJsGDv•2do$ԩS[o7@~G출o{? O._z;^{7mۖo{N_v?SA&H12@YFvEV[n͟|=qbh)'2mL?o,Cy[ Ұqn7(ǡ]y{P⦍V} ^f.83kbkA+>Ua08"/^$ vܠص^uٸ*miq 62˙oDC3%-fY 7Óį]!m]vYE>,1(d'ʗ!~7va劀@,M۷|Y -3ZF!*.l&+~-. 9^m}'ac)Θ1EQ&\fܸq: ӧOgxjt…'Obi"p  Ⲳ12 Ƣ;̈́q)bhh vKia(o ˵%nhtEL$P?+r 5+[-C 4jY'v̸.g5Dz\[d,nǜ3Y\z5cgWU 2uU@D`S(.gϽ4/F-#h"[-C>yU9%e䲖٭[7NKهqϖ:QipGGO]P39llz z9S>M[^gϞ;vDS4o%¶ҥ ˁEr$'m  5Ⲳ12 }Wߏ&b +9(q&]f 8m eF%hkdR[tZv9ʆVUWNwyǕ%tIꫯ 9i;={nV~VJi*el겭C^?s͚5/+bEݻw^r R S(Q~$Qy JqmRQRA7t@y#Mdbz*)" "P8s>|pѢEasJFʻr\E]wݵvڍ7λg}nnv" \EV\_/|7؁bwaNEI?y\ׁȏ?0hDvn {oo͇pKO~gy&ӆ~z/BN[q T<Ǝ>%rAE2ʧ$L1^{DUr1"[hƔ9s/ݯlCeGMr6n .`SN=zw}QJ#9Laq xT|aOK/C_~څl8M0znyox{8㌫9*:t9NZ?|Dvn*'pAMN駟nCHF"]{'?_QƏ mF2a9(薆|6m0V~ŵic $cӠ.+T4y(1mb' #gþȶX[nMЂY #n7n=69me]+יmwߝ5P(͛r7Fv''2Tj۶vm viGsʁNTID@D`)޾}#G>,ed gq[oeUO,'H?C}QcI)93bĈƍs޽QXteh;dqZj_|C=z/ˆ6%۵kysD$op[{[jUzuR`(2gaq?h[0(`H#>%C`d4 Zd3fLN T6lXΝ9,#*ѯC1mzmar5k֌` w3|9P&c9c-+0p FVHM\wpHi(]6ڵk( V ׯOľ8Jq8: Z~`iUiӦ[<{݌ZF!Yrbb}04hPF[lf<. z0V/"8Xp$4.(pEH$e-]A=z`\.ĵ_}̙x50mUgyo8QL>=X`X44̜Qm>ʹzfxbUK_7_H5i;inW0GrmqϞ=J!0% CCr(7FVxk(PE[4bO:vl({L4)#9)kРade3FxkEm(˨-a+eM"Fv'21%^=ܓV8[ZHHJHlӇY8h2r MEJVFSO=udzw ^n#Wݮ4mڔ˗/w-^r%<X!Gq 7O¨Dbh+:/xe2&6+8!syR Q(~bsf:;@PӆߘX,[l7,gxW=8cAJl, 5m.em(˳- ⚺t|OıSg쟰"ֵkW6☨q)]D`"P\w4H)G¨edo1cۀ="k̜+&N 8nܸjȐ!!Ehb0q\~QG+0=n!تC/TVT,;Ddɒlͳjãoh+O?'L0qĒ)`0LPwɗ!*1kx[A Py78v2_̘6=Zz5(fkUv9K<*gAȊlL6F =6h^A~A eV,Z… ig 7 H lkޭ0|[I,Ϋӿs*gug`3.ȵ&kWlpxD\l3g9x% Py_|*S9 CiT4 Ux+or)\sDˁ6^p"_|E9V,<44hY!%ie[l eyE_}U8xEa6fvݙWZUO(WD@D G [B Tyy}᤹HM孄?72`@߷B=x]!T6 gշlF 1^{m9m0xb'WJ"w+*!Fm8OuN{D5J\w#J3W:>y z!" " " " " " " " " " " " " " " " " " "Йxp+jx#LO-" " " ;d#o:ZFVQu\ >&&" " " " @qlUcgjizF-#+N>|I&|uwb|;/IvtռO?Yfb]p<=䛬#O&.;y!o$'ޟA^7,fmP#X}jժ<v'j׮ ۏmnݺ߈ët;\?uѣG0QȊ>01vy 7_"#ߤK/)x:t8s%4}Ⱥq3|zy䑗]v|+jV?ԩs뭷{_29r/ɓ'oGxgXH0*oqݻr|+" " " " `[oKf-?UZFVpn~Gu+|91q|ث{FUǒY^*Up 4ڮ];+Ѥf͚lީS'_8uW^y/Ҙ1cիa:w̞!$E{キUVիWg3?O:4mQn/%e[*&" " " "P6n8m4606lQȊ.]\~We˖k֬ G29ˢJ zqg; , `sD#Vj4Lv9)lj8K/9F9s x)eʔƒJJJ8\OW`x Xx#/$bׂ5jV|>F6mH9ΝdS/{_y>>4I+CWb[@ӧYXOѨedSlŊ?C~k| V'Ci]@u%,Z駟L<.w[d__l1N4at˞={6e8N%JVK裏\*+" " " "P>Ū7o^^7jYy5 7C !4Q>}t~mۖIC.jFQR-uU9gT8zjn N`65|/?&MtE')IIllo͘1Å? &L84 @(.g;>ei(4ex 5t;G)ˁJ|n85N />&tʽȢ0[8os7nݚNSN9k3S8'Iwsb…G+ucKHwѳgώ;"t6Z|yTRD@D@D@D@D }KfQLxGөQNI" " " " "Pkg(m2"Vw78#{66jXD@D@D@D@$P\_Sͼ8o||]w(+6sX@ysrq6TV,(Q 0y|x{ڵVz*)" " " " "@q9M:uk ,H5l2RXnԩSׯ_ߡCv-]tժUV+Q @q9'˗/[nÆ W\9V+pȗ@|+u-Zt޽ZjO<Ĕ)S6nܘlHV́@>}Ǝ۫WV+|ڤI|V@&PJ g!UHCrB_>B*WrJ:KoߍFVT~3hEAGy?UgnO+%H+)nX9sYnR(!'xɓfu'G6;rbFQ2VM@g ?NN:*@9(VD1Mu+͙6MHHDƀ7odɒkצsZOvtM$Q,X?|o;du389weUV^U֍խ[7ޱcGs;Pf͚6m䭷m$hܸq֭õ~c=oo6hYj?,{??(MYYg}=@\r]ڵsƋn݀uȷ 'ݠẜ/^_l } |:!F~!!JW&W1sUW9z0ZWظqc۶mQsk֬Ɍ}1hs#Ry38?pȐ!oe&9(h7m.Ykmڴunϔ4mEg(L7|67"P;,c?!T7[-;VH{[jUzuYӅaS4"]vHF/yFyճg1cưUMS+8p%)"/zÆ qCYsΈ#X6*޽Qjժ+`и[I/`+qjp$2N~~A2f6wa #~\zHׯqQGsti\=m&YSH~mZọAØq깑;Y~x%{c 5"QnbLFyqCԩ9cҍ ɈrCYZ/Cmnm)KD@<kӦM{뭷0@ 2$3gܰa[ l). ~rIoР]䜄Z-[;w?7U|6^^z% I3gq/^̢Z0׍̢5k3‹*]۷/X,el⣏>Oc==&MY2'ѠJF24Wicw0_NZ\~E*щ,LaظZq믿&ec.g p1F6 rT?SO>$?bޚw! ۠h\[F,?~ !1n˨Vu3 -e#P\aTDÇ2eJqV+pT Q,STF _75p,b +/ qYt a8"+mAzp{?C&2'I/9\5VLxS2AՊdhd%+R`ic+Q+_)IS,<,H&~K-bC4g^%o(nc̍8ۯla7||8WA jӖe# :vg̘BbVbu dcFʈM>}|5kaTݶ>7[sE11|b^r*o %F2}^I esXٳR+ Ü+>&(bѠXwj.]w_4e+۴ ^v)i77"bۼ뮻ˤ23$Mf2\nm3'bw17OvFbo.rviWA10ld!}{@A8n67  Pfr~(4e@lyҨdW0aKn #%p`-ΕV+ p>;2.b'},ӧ/[]tEln4, Wƍ.BYbt r-C 'РA:O_`V)[\ sB){T&W^IA#bmd0[~s#E778,vRL<-1n@%+ .d^qs F 卻AØqʇIA&l>7Q6pq<(F[F,')1h ހKA8b̊Vp?\M  K89J Y53j%,Vwf7o ;0ab;Wu֍8 OabҜdbX9K:8g/rg['.5h-C W7oBouJٯ\ꫯr=A`hdeWicQ~E C| /Ej,W `yh7+n=:L@\YL476q4|"6!Y`YwJFV50WA1ڊy@[Qdkx$Arɠ$)A$QQF@ <#¨ u PA2Jv$Hsնt\rέvUW$DL14eKEN' uyt1c]г@`_RVPpfׯOUdlh4G \W" " " " " " " " " " " " " " " " " " " " " "  IDATim{Y+n^HOa" " " " "2"AWƬ7\.K_\9K5kE4DQHӨWHx*\.ʕ+s3oŊї(Q󸉉ߌH0cɒ%5N8Tclr j(,\E2-Pm߾=8D XF׮]~a.a6mڐ!C.W&MRpraժU]Rn]#G:QWYcI. KFK(윎;r)O<ٯ_?ǪaSNj"OpBƍ'TX1`>=zϟmo1M6<_|Ѽys #V]BJH֬Y}Q̆;EQрʗ/9sv 4u ~PbD-BD@D@D@D@D q$֞ pی3.\-[ҥK$"ƍK*j G3b݃}׭[СCsYP!L5jݻ .y-]'L@H,7*P,Y?iҤBݻ7}ٲe uk.SݻqjԨѬY;P 'ٰa؈(ly)R˟7y@E@D@D@D@D $ѥKUΙ37*t:x KTUJ(lq^7ddem6 ~Qݻ:u*2g'.jwtM+Wda$ (%I … 3H(;p4hPm7awEիG &29 3`ԩFO|f e̙6mɓ'ʅ)UժU}G/"" " " " )N 'N %D.zjݺ5{ w)oUŶϳ0eʔl cǺE͝;sE'l' &16pb ;[óg>q{'Z[ƔJ D@D@D@D@D@.mӢ,Qv%HӨEUD@ƥ[UD@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@wJpFˆ#AD@D@D@D@D@H>u\MūHG EURyr駟B&~ȩ&+26?mڴÇ7iҤQF7nܹsU~E$(26 [,Xt;vXbEX" " " " " VYcS zڵ˒%I&={6P" " " " " @.] O̙35MHInʕSR-" " " " "* dȐ!UK?w_ڴiwؑ\ͤ\*S6uLծ]ѣk]g<xH"˙%{1S(pd᤽$r]暴 .]zڵDHHH)Rbzo̙֯3!ovr-^8D^,@wT= c3.Yf{-64vyFMD3eKu?,DKuW<#F$KHHAelm۶^z~5kₒ={Zj]~W^dg%JԬYs~g -[$ٰaƎb0ǏecL ٳ߾}S_" x+V}ړ]ho 2ɓ'_| !-ѳ'-[9Nו oxJbgݮ.UV˖- Yr]v14 "_]<构 ,P@\܏_Jwx4(VN+M4$;uԮ]Νr _jȘ~~pʌws]81B4ٳ+ǭ[̙+J*gώXSӧSBw? 26vӦM2d/KkrwuĉnݺwzȑAL4fb5wQ[kڴ3z=طW]uPm$Ftʋ/hRzveLaۅ#(? i_W:/ロ?< ~(7G 0f(E%qX | A~{1cFWV/(RD6mX&yn|G1Qҕ1Ay o&DzKc}ERcR3`B^ o!rZ8gQؖ{) 2`QF)S_I| o9'q!sQ~rEBo<3hu1r8NJ~IN" "$|Pƛ1c… Q"@L4iR$vh`&/B,5kֱcǂRi-,'$6.(IJ# vu@>kGWH|otǏܹ3TTsq҄be0\WkaFm҉ '.Ŋ@* XF.]Mō km۶kF1A˜,u7uָx㍈ ƫWĚ(Kv *`s1ǩl^ܴi~:,. -bl3/lǏMh}+ սוA@)~ kXT%ϮiѯTar{`[gRuWLj%CHq>[n1F1Y0.֯+-U[]BKWZZ46eE' 'at9 &`So^NS̄8p.kOD@D@D@D HT7 r/;#δE"+LJ~R*QD@D@D@ow%HpjW=s=)UbbdNv J `BD@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D@D hF wOe{ wqu1Q" " " @j{^PBJR$HƨYW2D@D@D@8keK8>0{g̘aÆ+WN.{j9O<˝Y7reo͚5VMō]\GQӧ3G?I'Fn/.'Zx^`1!2dؾ}Svђw9r@/! {`)y2r]^{^`q'~uYh(NC.R?"h3>ѣ^P$Xlt6mڐ!Cj=L7Ԣ蓒[cML2uرzjկ_oD5iҤm۶hNj~ϟ^7*]w=4Qfŋ)wi? op:2GK**TN:={\5jಿ_|є`~xkwc7B?ׯtbp-7Ϝ9ClKxr- u]մiS6#H?S g$v:8auYh!F5˕nٸz\nXQ" " " "@b s5jԈix,wygɒ%&=f̘cǞ:uɂ\x=(ZlY65kּ{{AwBݻ_y2aӧoڴ)$bnݺlٲ}ٌ3>.'I?~|YY`јazjSE ]1֮]io:?X 3b`̞=7g)DYk3Nfd^}\~ 77вeKg~12 ,.FWXy{̽e˖[\D@D@D@D &ڳ̻/\0[llvȑnK(ڽ{VXy̺=&ƍiVZ%a0yd, m43BJ'*>zvZB0~¯\rݺụdɒꫯr,LvYX|9I(\0"FmoBʔ)R 1)g4;,#G.jL, ,.LKO3gB w֍$Ѯ],YҤIyO6x;ݻwo,Tsp,c Mk׮]R;N43BJ).@,!E@X;;wƑ y̙cälE Kb#ASA iC4ɝIt5DGʚ5+QhcB8 o[M#rb2lիcX0a9':lR`+6`8b#-“ lDaB=(KEņ{l?p0\XK'=C`60Ɔ}ó.;Kb T D g- ^li"-Z:zLě*T>%ڷ/?['_po("G o.ӭ{(Oݱۆ یo tr7NOՏi~gٻzzuMQ ,_Th&]ខYQΐ!ر7>wwc[F- H鑉k7jD3gzt钖|.]~w3ܵKS&YWd/vƌzu5bk7O7+!7׬۴OZ*S->j@#o.G`Jƍ)ɻt59r=VXT}KUgݞ Ө"Z[WȔ6e]1~|En]U"X6cnݲh@4XW&e6E7G_ye}իMXST)GŊ90 WMQQ#7{9N`tY2c5fޓXom~/@;u,ʜ3F,_,Sd'rFgB<նmÇOY ~>71c|6iRp̘ HW\*aez̟P0޶m;g`3p-]i!׹pƐ![0 Bgĺ.{6,PzC~Go5v 1HXʑ#=W;TceJx }ђo{oKAR+ Mzd;*@Fc2 P2-MA}vLĨl'Ftc!6?lM]I='" 4?5ᠽo옔c v,J:*,2b W`5?, m۶f7dp8~{WZȻ;4of(w=6yuWCZY;'Ft5z֟@'!,gb bTc:#Gغu=Kzwg͆ ʔF xVmdWi&qs "ɖ(/Sxp.x@9opb7dϞbb^ D@D%ƎS +ˇÝ4Z=&NU/ʮ+ dlI3<_/q`Sc8*~;tq^D_a4GfW &8+$@zU}~s"3=X5 ˔id2L[~F~U@Bfϗ6!W"[qcZB>HF^bl Ft%2\,n01rXl|ȱbȜ9́$!e Rh] WX$xc͙V<H6` ldv7pgde %LFkilsrK>я!xVE~=0V[~vg 9痚d ' -KWZȇZV% ff){.G ]eZc+XyオQZқի6P:/_Y|y匐3 (SaشiHl0- -,[l7bC矏4m:İDm}΋b" "h")?,oE-4tzij͛ےA.&8waöD U/ʢEH{8klEųqpWFdh:y-;kDqh5@{_'AB Z?3m]7sFiLw\*w[ilxw㨰ymQ_C8j 7yz|GRZB2l,Q 暱'O]CV 9t9Xơ.Lko^Gk7QQpbnRٛ#Z9qMƤ_1cJ-Zv\JL 6aP*XvX7' ~?Wqɫ4Q;!v%￿:4NGd+--uYbt:h,`X8Ec2s8[e,N<@Lc-OJtb^*pO\*9mB[l2q~C+'Ѳodgp?-MDQx; ~|L[l֑bXDBW<$҈%Av0)G'S4 R pS ӂ]\nno !ǥ~h|gZZ(Hp{mN }]Nw WÈ5UT7ҋ,1r뭳Xf /,=WD@D@Duqq:@_T .j*k{`GFLIb`,1}T cŌ%/,M" " "panφۙTREbHQ$ReCk/bQ}7qwŊW]Oӧz%qJ]6@?x~oi[IENDB`alot-0.3.5/extra/themes/screenshots/sup.thread.png000066400000000000000000005516621217172710600222340ustar00rootroot00000000000000PNG  IHDR$sBITO IDATx^ Q(yHD1塅HmH;*OSIllŊ2{{oyqi}6 8(WT!yiH~-6~Knǀ'IL̂U#ǫ1Ve*>U)M7ݔO?eĶn1uA>- e[팅l5]~Zlh8`}-ZF4~V>Ԫ+ }d]:pDzjHYKZ!?Q<'a\.p陒wI'mR^TP>{` ﾿o. %PSZ|;pg~ ;C ?cT lPDDjصkG7p#<"`l?c_/ȅd9P>ŁB\BKd90MS@G^Kf{ x",X5R Ō zh4RCկg}!ԁqӥI="ID?կ~cI-rJ!f5 ֢Ԫ`rǠ.}?Z+usԪ Ҕ9<ӭ[T%N2wi@Ct #Q1b aB4W1x \yK. CX)W^.P멧z7I0"׏c%mlkl2N믿V[!8|ߜ3gNF‡~hC 8`G(އevl{׿"50@^QVZdɒ'x NawnsԢwᵯ%(CI5׾5wu CHC]v [>jV\+_a„ܱcG;( 駟.h5 (ye]~8|mvm>^i۶-u6m^|EIܐ;$]Y_ YQ^xg~2>p;nj[d #J\ zdm{ sM6gK :tz}Ao+OCdhQ&yО/=&*CX.nn$!`}޽{vذaH8C__O>$A A9?g5 vӍLNfM#`>e^bw f U%O"oѿw[gm[w2mZ5oZ{Ïc2wqv("w7j`Vw(J?L@{ᅨ|~J۶fM6Gk`?]{300Jvc9k6?I{ SI!,j Rx=nM7R`]̷JJ>b" >U̒%'n'ȯ^a`>:'"]Z/c5R6߾5f4r7bo,`=zt)JmqD:Q {ĉe˷,tF8ž[l84W[8dB8swn hӥKv [lȐ!cƌ}}ԨQHI\U?1WJ zhJ|)|_0DyFL W]ujEH,^r{٤vCo tިơ~뭷C^N:Y:}tȭ>^ 4~ScF;mĹ@KJ\R+UH*!w@ww(%)OTR$ fۿXU66|s~Ƕ !B)H_D!TQ`l @WPlvBI2BMA>Ԗ.xJMܬ-)?p)Nenof(?՜] vo|vL~MZtNo2dy]s3棕ژt& 73#km~/'n桅f'C3ǗIPдv2AlBq(b5mlcCq$9@!K ڥ"C4 IL}@oaHm6QKOs1gIAO4ǥE,#82ČS^ѣ:OCW# 8Tdll3qa AƎ.` J!\v}w?_4dsWS`Rr h .DF?~"/^^vYg͛7o'Yp-̚5wza02|yuMF"q-Z2*zDr_J1Q,崡`P02P|l?2]^wu.&!.oM<PVg/%Ν{Gc^+\vqꢮ#(c9s+"Cs#-5=Z_şYHt(0mx .}Js !̗J-቉oH"ˆRQ" :r5c0D^.B:umJ[DeБ5) W.7i#'C0[T_=5۷/<@1eF bPXJ(-,6Xz۵b3sQ暧uZf[0bL[%=;YQ־f+RpyEV!p<`.e* f`k!L P;s8B /iA3Zߎ=O7,Z Da͟oHD6X&)j&B0}Hʿ3kVwi#*Ml: icz>m*cBф т}ᅆldbCSN1{WwKϔ G襗^S+I¾KP+̥V)\/ m!M7E"_B-'mpfPaI>Aa1bѡ$  d LVdqs eC QjC^(-Tp ,P1FiZ{GL[n. 3#:wLn[ˆJV'CAU"/< YOV*!4tC{JjXz6B$}`gvaXc@?1c| qJP-dΞ=[7mhڴ4K6k#Cۚ:*Q-=2H{u #) #"g=p_#M#Hu zA #@Ϗ-9HQD`eO8o 7̚: Լ6,*#n$+~CsͼyQVCޫz~Bo7[meeX<yyx?|"`dKʒnޒr`BO~YSҥO-FϞ="XVBk wfg*|@T0tۊQzTĺ1DȈmPOTq}T?o?xz^E :d &14M gy&aBӶ|JJz; (H`ĶQ8vcObu.D&LށMa)s3y.收ySbhdD>{+Ŗ^vYJ6i#P1Ģf911J{A,N3X]+fy}+P,~AS=w0qOA-Y/NQ h3t⾍k\NNQ|ıL?d\D G"l0*EC\g7hKa.9ctzDYhh]AV ;q*$Zwhzh4bE;*2.p2z6 ]E9a6 6rN),z{%6t?q(my{X )|a-@*fb1C2% "`'BXB,ÇC,vTԅ2H؛)AUtZ-~J P yQRl)d5i#?n 8D],(T<ꨣ:QqEٰ`{s@4Hdl[cUEaYY"r\5 Xxcu3qR|QT쫿 4D]pDxР(&돬R89"ׇ8(f/hb;٘iyeDDK]vBLyFpq8WIx7 t\j-̭' cWl}l3 ܸbGӧ"1!j=̖' bL,Q(I[|`?񏉁LP+*!쇣)[8&g}Qbߘ2e e8*OPC+C)+RP|aP2J{! @x4ٴȑ~z^ sOQzL!SOŹV}ژ(qnTm^ JVVMVN8h4nF}<%/`ap,v[D,IbH@8o$qb.RPR yfTWRX.7i#&CT]wݕZ.'[nsQBq=-==4t$/^ai ͙/f;oiszp9Yy#sE:֫?7_R>rBD4nUJF 'DR1FHթF Of8ѧOtD-C3e 7E 9}4@$R0 ":. KEao[׊BT ąf KeL1oDy;j{Si"j9BfLqe#~y($iB-NvNcsF8Bo6Rd[;(d GC.}4='-:xI]p9VF\[hĹc JV^L ONգXrE솀T|A*?6O*(˪l)xNbRydC3 44ԫC^e)Uf;m,>?d'*pxSI qqw7[  M X.C͎mͧMfs汤DkF|Y<])JZl l2͹71sROsQX+).-rTǤ9O >Is*9շpIb.ULi8q V$Hr=O(yTmi@GA'A1 SK,+cӯ_:^9cbD@7W^a Ey^m*Zׯ54_{ب %(shc.~ w\>1}le5)k oO O.@F%K 5'Ba[sLa\8Qʯ ]2]vgC[fh Z6@ W-56hDS!RJ<cTU¤p LL :?׿UCdJP%i 5i {פUrbab$kbOy4T 7o!! DwJYe@i@-:(.ƹסJ%' ]v[_ǑCgKV`8&F_e%J ()PR@I%J ()PR@I%J ()PR@I%J ()PR@I%J hQe'W͓޹c.lgc\q^˥7K5[r-C]hԔ)&J݉Q0"inlY{0{u[UxƄg2^zq:<آ>vdⶕXK`&f%^,eiD"$&XUv!#2&Xe؝؝-p3>K <~sQ`s-;U~K.~;BeΫWZ-o9hgfV歏_C^{ob\uj#^haz+WQayw%1rTHF* ý6*LK/n$0AqY!qmݿ&hm;-ݩ8ժaՀI! u)YHjEZ)38W5lk<+~{ y]D-)طo_.]κLYwdb e58}ɋO.Bya~ $&MhdUd]n50jV*'vg-&&v0yy'K,1.r gΜ\i\xf@ ѻxlɒ%< eon8 Vmڴ׼&w0bXU.xI̞&pj Azs^үT* AUjv?+3yx l!j$2~gsW~3.yW^p˅@lQ(C X9L&AQ!(WGh_$]GǎHVHD\3mRxeYdͮ:tCq)M.G"3yڵk'ʂ uY҅ۄxoQ[fXdy*K>)*7Bs#/>(>Ƕʢ|"ysD m_xgxP)L?ʕXR{F"e^~i4j3O⊦!ȿÆ cG!rBn?O* ;u7}Ӭ,]nZndh~|==H^7d^4>e^b3{.ĻK'^3ԇ 3xDZ۶2dڴ{CU2z2 I<pIBVCA_ *qP-%^ͳrs"6mw =ݐG̉{l׋wx/DdÈ<ѪqѠop,.Ybx 1vX!Aek<ӿ|UR5a7Ƭ FoocQ=ztߧO*^G 23lË-rG[\rW' v ,tg}UO4(ҥ V\=fT|3}Z!^e /87ߜPTQFA:I IDAT&R/`M(Kkee2% tKt6֩Ry>[ou0׿f N:Y:}ػ~a=EJ]}PBC,#Oif]CGR U-qtV+k&#&~CP032=xT-{k>ʐs޽{ C}(.Z*5l*T>7|$EW[`|bϱ;mvƥH(??-1QRDnb" Bfm"wۜ-d,B壺7?615j> O[ȥsz% 1t& 73#km~/'n桅f!n et byЧI|NzK\w^+ɷm ´gy/VdEmn,UԷ9l䰨0˨QGʤIsY>l=^"A@(ӥ 65 1cԩ7:5B4$}@u0yw|UɫJ }ر1$ raÑJ$G>k0|`jݺKaĒM >Ξ= 5ꫯ;nN9(.\=~xN !||MCJB!>kʴiH&[Μ\tE2k֬|,jl̙JjPdV WƍC+C ؖG{\VBCyy(V_z:uY̓p*5trvDώy9ϝ;FqEzΈ2 }PBC, dfJ_r%"nv^[as  @$ N4oLN\mHF ^kL᱉1sΔqo|3k`)8BLbS/j*#H!BR1FGŋqhT0*bhh+uPB.'f)LOAC!`(m#kn@P *2H[>eG'}ge dƌ,q\ b>3[mR8&rz!7fkcPfF6-RNf]$Ť|8Ci|On{$r ̛g-3rZȍ_1wAa鑦If7F3 :٫29hrr89Ӈr] 4 7p>SS`0Q=EAy<;RD|DJ!1p pQc ܍DȾ+0+aW DMqE\O8r!4&Xj:t wɖOVbޯPŐ/V+#ͣKJz*5tr|9X\Qa#?O>]HSeU1PR @A +TPI]_DVbVLO8aLIX7a{n.bh|5BK2%QRbsCW$rlrbBC% =3$™Uʒ\JDC.b9Q݆Sb'? zdX5+s+BE.+ǶQ-1۶6"?y&PkW8nP͢8OHxH"PN엥1WeBE%#5+^-5aB0Qb=uw+o~""9YgƏ33 r(Kbp[kl,Ul|wbMne`v[LbhpU?ٛO~ }Y"ZB$},vB(PScF!hSwyJZYY8ط0*D|r=ŨDDfϡaf*S)9 ,FT4r(S[@A& ʲƙBBYSJ-R|ۧ$`thʇbpL!Jۀb`i, 6u9W MakW2 @*wYY憏ү,TR4,@J[h "m#sQ?x쌸5<#dL99rQGIuab9cn`k'mVPAn.||mO^70+ϋrd:1X9Qo5qȭ*0`BՑKϙ+"UIo|lncpR|QTk71y7|/EԐpڏ~dVxo]#KD8ԏZҾ"bp(0c´Д1F/*e3X"sϮϑn'$lz͝LŗFfABϦYP8 UE?y(!DG]JaC/%`FƱNhNQߙKpnCpI(60SL l`,zDV*8ٱFPDMMEK 1WpbPL+F_R:AjrFBn*YӠYdAITdPaHD\/>ʛDae`< Nd#1oi[x+ck.G&{JGyDb-\`2a*.LE#q(:7CW$T4`"5XY'p>V?8u/~EQ!"Ulԓ;a!cq@aa{_.T-[pOg0{w }h|I2I{Ms/T|Qqb]ʹz6@r-́{֝7+sjO3G{+o4=sP#s3(+Ww:\}K:$hZGFq[[[S#GDN nv$ĉQҊdN1Uq:WUW;iG1rbHlBjqe1sWV86J46^1kީE$U*7- d#fF9X&t6fT9 sx2U1pzĖ.WaN!+aiGC -Q05wqΞl(RJEhԋKZ  *V.F ˩YޢFB_.blrdao*L婇vm,C_8 @NXe, Gkd|7ybd"v#G}n] 8iGDfv6ݡ(3i!cH(6PZyT0ġTFH_PITWPK7;s!\u. TM([ah%?~ĊD q4 B۞SO*-LK_ί1VTF?]m7S3eRi"W5#{Xw^sʚv{f-6[obf6o]}{e3'4GFODX+֣v3ǗEX6'pǖiWnCM|DU~\w 0[K/)ԤAs,R9Q628mү_7S;bjt )KŒ!_=&*]uo(;*tw5d,Ͻd5,kxbDGDy 4=5H @IQ^(P=T%/ )^-oD~eMK➍ųl9պAY<Hτ]q!U $F'gX233bmC\S!׊1cݲXI5ǨEk4Ъd9x;'ԲxI&z4ɉ_6e10#: Y3A;@nt,;^Rz sːD8W3a2Ƶz͉Y,)PR@I%J ()PR@I%J ()PR@I%J ()PR@I%J ()PR@I%-D<}Gzjb<\Q+q>/ls%"ȋUx'q0_.ņL!kq!m\ dmy3zs"V[oPҠјMۨxjjxw2Jň{=ܸϋTGbuF*8&`Y?bt$(h\ɂG7qb@[4RR)e$<=s-wKxf "~y&6L$ۗ .;2;s2VCʼ8dy1-y' fG(%SQö dM9J-Z 6( }Y#}aC]UNdQj"z{媬 N5i)Kx]?6=zirlڤاE,,OԦmhMewŹ}w> br]7+=^ر#oZ{<ǣx܏#G?(+oho+>LIƅ^ȋExK*=^j${ ԻaV#y,M*pba{LL`[ KVhP/!`"t -y?($)nrVB>D@ ڵk'o& AeO P $"/s>4ءCu'g*5 !0"#"7e'q<`4o`Y gEp+b[XNnO'Uk2/vfpwaU%OQ;;M޶'ӦU"5ϓIAEo˔ƽN \ZGy //SOereCi;83y5eHy5 !>6mc#HR~sϙ5[sVxo-ZP6(<˓,Wو?QN;44l< #Uӧ,'#a^G3f? F͆y1MLm.]ؙlA3f>otx x|ّKBQFAI\LwzG%_SXq DN/ k=ZR˷z6CuSBӧW~>ECJh(zV, }*9 `;n҉'fbAqYq"}J,AG~~-zgun7d)&j90S$+TP[!|% a +4"u1 aM'$Daa.t}j1l0fɕ21A htDHh(m`n@S,"w=¹ |C&fm*~ǿ|DD 1lD;ۼ,Yf^xhF_.DZt7cfԢvE<5@ĭ?ҷYL/L82Čc>(*s`(J>2=1Q1GarޱEsKlȎc']$[4i|Iuw7e {'v_Wnӱ?/x7ٳ'霔Ȃ7ছnʠAZv= @ رcpHž oAɦ(]>3/&OܩSX)p '@ B? >|8O#asr1_|~#ҹsgL*"aj-u('O~D!%1_+uMZXKRVj)h(ARWJs:{WZne*J[ZhP(_/icUR؊icŸ$4`nz_ ,y!Q"J%u ` hkCL|"u#ƍCwD8? y=aXoE wT RKiK龂!)ʮ%+q\wuXpcuA!c0C;uAJ wg؉7WEO;CSҲ[8|eHOz~~UxoG3Xө8Vm2n!DMަu0R ֝0!Rc7^Թsw#ľɓ+:R7RB*vp9Ĩ<cAޖS jڏm ;[7i5K~q&IC;E*!l*ns@C= 9``? z*V1kdv ey$,m l9I‹Ș\I28@kU } .A4~A4V)ij Z;x \`7 ΡT7AQPt9ur$ Onvvz=C%Bu\.Җ21Ĥ ̑6,oO;wn* /UBK!T 138@f]wm>"Z%RĈwؤ| ySi#"/ h޼yLQܳ.&:@JECiK@y)7bp\`+l߾=?r jCZab HD,|_+o*y}цMK4ӎᆳ9٬!h S)ΐ/EB|ݼqHJlL+^t`*=f1˖92S "x E7ňti]-/ӧb(pa ,E0,_j[@: Scu{  ; Ue"~R8ak lbrɑ{צ!;ܠ/9! '2^.?&;PG-xޯ2Ő/V+CsKg\*dUa)2_y (sCaJ@JQJ2jE@v\nBOйpPE#$cz5#OD> rp z*TUN0R z IDAT8-T&vs=p'&0#y 0/J[}eXba'&m`8[.zdpfϞM~Xz Mt5=w0 d:E%\#-Ѡ4WTPr["'f5X~5rSn]ETA&q#{( G WH,OLmBS+ lDl|X!V気+ =qOsYmI恆bP]ʢi1S3:va\5i,ZV#a` |K',n?fB[9U8,ufRssf%rՀ`I7㍏Mmף4N`CݙQPAqU[??.Gҋll Hryc͞{FB<Ոw2Ah/8 L΍m5=ʆ$})pV"A?r;Rlqzʁe |BN,^zb4D<|@՘qz`GEeofY[ Jlj';M.: ,1$2sB]2e Ǒ9JOEyGS:QjTW[,M[b2h O1>8`PACaJrcPdp/meL/ H-AKsM _>5`b[ JV*h(ae\*fS -Y y 37-m8EEQΘU y(~m 7_PPby'؉&&n8VT R,%׽cJ"@nqq˵qb]ʹS_z-'wlsdJ=,̼9bw tCsA N՟ٯF]) Iɇ%y]H`gū*_gjऒRE!@ ˦ \16,ͩF 4#5>/ ]ƕenI C%(V48 gBB$|DyZ_!% ¦2;Byqm4up9d]Y !݈{hڴi2j9*z Jn>4ty֬Yeb?G§ Ay$!QTS _~6%y^e#bB^qCSڿKzTBYG#uP2CD  sP,&~"`@ $! J$0/l_0s&FAt`*eO Vy=+D4tq,65dab"F&ͅyU ~Cb1یŐea}g"Rb& MVR#Q? D $"$3|o~?;k'šfǶզ[{39X lGf7M3ou@lټetsoc椞ݣ4JW6- 'ۯ_Ajp9z+cpe#ɔbm7| >}>.T75kV0 RHFP fY]qEʌJ(ʶRk^mk͒)כbGep\ȥaܣIbp=2Xٽ@ún99(Y5ǧi:[sK%J 4s 8jޝ;Q1)hRF54K 'x"(xU[R)怓,x*d_;5=E3٨(AjTJ%J rmj©r'9>%uY7~4D_"8IXjD@xrNkyWUsr£kXRkw/XixI%J ()PR@I%J ()PR@I%J ()PR@I%J ()PR@I%J 7hVz3\n{=UWXqǥ[򐅽Wouޒb\3@H|T"d1 b-ޑdڷcC^zq:<K}-٫m%ʋ-nK2.uq&[87e:q=7o%+ҹbX9й)PF0`i]x ֙3#b[r~lx !i7\l,b8.̱kH0_.ņ歏nZ Q-LtT0|Hբt3A]i\6mT<5M5\H#}*L7q)Ãxr9cq/ g??1y"Mxad_SB}B 0w)5&x~G!&N޷o_.ʻL,،C%ih(5^z??l&2#arKJl{[51aVC=JCc47 ^s%x>G$mYևp]»ۤV=-[OxFj8EU wI[]/"usԲoDXx7"@.QYFjNFkQ6xŏ+)y&;@_xK$ ʱ?y}cǎK.+Oa9<;yqſnh/OdZM /Y%"<ɍ %KfCGbm7.Obb'KE>\'{L`k@{"-q:|ذa\-"TTñ7dj4>e^lщhlXL,7Of>086^Kn쿓i*z U|ɀ$uJcw3XHA{s!V[E_o~̙$6)k^mv%S]zmۚ7mDń%-@8vvL-×c/XY?}~1#jϐ~x oE^[<񄩗*j0_{8\4 Q6%ne 'EyM*1'i,lS<̛ TPFVhCujkĸjĉVdz}壀gtJ9J%m=5 enChPCһLEχpts9G{l1;E Ch:Jsd%2XK?\0!8mַ|a>eS8v=W(Ʊ\:y{Y̼J0] nHEZ*9yhok[[C%5W3c+}g( H*oҏ]mEXBZ2czȸ’}n|fذ=ճK! O^i|9*XF4>z:in+KHߐ!f̘5G!駆wѾScK) C]nأl`ÃS?裘^P6x'OGK0zļnX,Ca[#3v je4$}رV>|8&iӦhl]t",xOPWhe?Pli3gά 2+Z%' ~{hq\k…h,Ǐ?`٢ȋ۷hy`.BM#qٿ/cݘ;w.f6|ujTJ%sd(`C[`>\pAϞ=~iA|dQ?ܔ4w\s qh.|%KJU*h("+4 @D:꫑N9 I"YK .S0Ty- pIKT0a6 4ɒ./]͎+]V**PR4ʪ[ضb>%BCeb"`!u]7o<_AgptV/l<È 2VvR<2CO"bUݵb3sQ暧u6[0bL['yg;+ ڷy僺ZDOsۼ89i{xc  CYzHcVć)!3gW^`b,0\c8Ӑ-'o{#WY!jg-"Μt?8W2z2R`= nz628d/sɑ N*8R'R1 W4MtSYH;w>_~e8h(0tfTACp9  ΂e;08/'&5bi¤ L7tSh{ecv='Y @eAi[ ~k>mCh 1FR b bTa6~YgG@;% Jx .X aXeX!ːhL,t|]wQ7PMiK!D(#{WV, Ra J,B rv@وPLbVR0Tsȋu+fIqO<GzVtjC|%)D'G?ϮiPQ2S!I`᫨v[ouºc7g…] 0fˌ3BgabBEF6-ҺkuYᆳ9٬boW* Ri=yfm*Cݏ3{(抐!1ۥH{Ivt^ W͈ )R hPw@Ӡ\^w͞{bX9KƩxFT^&O&0X!H2 k5g1ֹ!t ԩ#*D~rliAG*([Ս#`ӱc)n2+2Bh,+m6$;nI(6?wj aR bB84tf LiGy$1Aqejʡ,$ *@쓅:{[Tfq|U"ʶD &#`@e{q%KJU*h(+Y5މxP6n+"|>XtYNg)Lj aT,@bBCeb"ؖ$ ֊/>qL/a&(`("YZq!!pSL;H qWĂ pl?J;QDʉք!,oguJFsA^qۂ0! 3Dg{ntj"Ǒ CDrԯg(ʑ*t٭+6*D_ͩgbjjep.n&?OP6[ok;u>`S|} 0aovB@ԐEx:dr!&tw 4kSUL'KIT&a:nz.A葐țX]WbyÎwd4̳fyB*i"WI.ͦHR0trjUfBAGm]R#0Ip! *4mV-Rh<\bY(PXiSb|ܮ!Pb]VHНwޙKPhj,N?#n b (.yqQGɟ7QE`Mb1o̙ml/۷607Re>fRssf%rՀ`I7㍏MmNm'oH׮",EgR%(|4ӈCc őy2g=XճLYE(' h $iv#B.@H7r8 V;1 G@m&@P/^uٛC_A!> H:|C?QD BPn 敎Ò$l&lsJ: /L9.`71}Gj5Sxl`!+>TI&V%EVI.SAj"Pq7alFE2Y\Kx )x93v:Nb .r)U r)yqqWׄxƶ%ã>4tx0517GsY b<@5UP7aSRCcs ~gQ6Rk)-6,$ nFh/ABׇy/o~M L@fJAIHڊxy@v^l3",rbm)]%b`PU@dQXJUP0Y\ ,xr[yP6Y嗱]K8le dI)e2V|)>A>rC1ionD Y0,i5tkAI:H2H˗u-A{+իW/ȞP蟝ՌWyqz`eYBwj;YZl]}4kt S@JVNTަ7Znn]NCWm%k6*@ZF\~^SLqQ2\#q6U}9_ |v-64o}^Tj Ox5il IpS H_L< q^CrzÞ?yގ6U녍FE^.ɅNs;XR WdBz+(Y ='y5oyj1O%`?SLNt}r!Ueb韑ՌWyq"=Glǘ)~«)l4-[d-X / V/[Z\*>AuMrJR7G-m s~kG?gp^x>]:+h ;v\t_W.XyGN>d9r7;t~n"#13̙#FY4i^*W,Y»EBޡCh 2_}U]PЯxW`8!bn*+sbj۶-o51 _jqO@A @+^s;3#/+ȴ.RAIv0;릴jOYeBo_Z.El>?(?L>6*p+ӯ9&< ؗǤ Y7}+-6% hZ\wF$ !VIKVJ.#Պc+ULd_rhBhKN^T9BKW ZYKRHR84(W\'c6 ,s*""?@Jb0 ~غe"m^f,3/knx|R/Lm"~C13RjQxJ}bfZhnv_PEZP60#h>裨(“#0E]r%~w}wjE<x#}As .D?~`ر*S2|piӦL\N:5H, )6~ƍt' },zd _s5cڋQ 3,a6mDyRwD0 3gδpD0`4qPJ]lMKP'+^szGJtT L9ނɓ'P\)V7g#LRi1Huk]V͝;^ccKUٔlJ4.+hH-{wt   J̇s~ j^} uY yfξB a\p={|)@B -PRFIOFC8AKX8A` )l`He0!GbN8dE_R*/wmgČ{xtL-̡0w42C0,3cgEaZv4{2|PW in[#ʥIv]ltM`e!QGD','KEF_fi,2l*!\Be IC*bPPAOzM cֲHˢB&"2}'!$Y1b "ͭ%٢zo&Ƭdr#u4"h3t5 %.OH> tMջ.cRCE;)P}v!tÄ.:5$U0/UBPJ,*+ }t 9 Pr),vVvY_P0abɫ/PN^ EslJ^А*(`xVcBRXsWbP0+ԜB^`bҵ]w/PƘPRFoPac"}`%A#vaAF?!89PUr]S(ENP F6-O^ɥ|mfXv Ri=yʍԞ |Hؼ uAt tPu AZL/TPP`FQlm<R&VFA5Cm.&t'LrAVOAs=6As0#|㠪=,-88<*E$p;&D-ҿF ;1m;лT={2RY0D%@ d+~e*uCT&bIF띈#&RyXs*hkK!TSzPA^R&qCC隂&AA-c Ų*hfαŐoJ+ b3*#>0qXpP9`#ePύVm =|F 1eƺydB,o|TwW\q{!NQd6<?=~a2g>Eb߼Ğ;QDʉ$ Ԓe)*M2++G]()sBU\b'.FDL ofKKfIёW p): r[Sh_Hf.ŀӗPB.ZNy n'n5C3ß(uxr7}!L =L[1,cJPtdpu`ZtnY |\bWI9QlR- [W/;1G4ykcEsjQpPT< ᓅ R@jPA^R&qCC靂RKAC%˟J-JЌ9pd"TRȫ,6\tF@RG $Brn(1T< \ C'Vm 9fJ;ꨣI<3aEٰh#aD(BT?["b},/om_a:n5V58H(q>fRssf%rՀ`I7㍏MmIFASxz0:z)p{G"n, -6.- \a'>2\ŋa {񑨹TA,bχ#)LY13AcW"] SL!~`$݀.r"B!^ h]I,'q S{ ìLl)fkN% lHd#泓|BQ0Wp4 eyJ&,VF"X}5Yg#vLa$yha[^k;;88ʅB.WU<خ؇` F:㶥/b5As&]NJZJFB)h(Y @y%KQ _ J- VjVUj)UjfαMPK!،Je_y c\$Crn$n9Hzj*GJ!l7z\fEdPv"MQߙk_^ai ͙/f;oisdJ=,̼9bw t[$glp~5 JE8Mw3{aX9,q݃:}9r4F\MA˔(-#MH04TTM>S\4^Q)mA#BHxXlJȠAP8N:E $(Qy }ZDnFsTeC4hvPjqj!M5޷yd$rpIw.^ds{{W6d؈07H3 <+4( "y˅  T^ V.!Cs^`BE͖YFSY_UGk@qׯhkK! dѐ2Jt4!4*Y)ɫT (c+JV.BaɫT&BT ey! a,UQ6.ef*ʏXS[ qlEI ۝k ׾C͎mͧMfsxH調f7M3oѕϿWjb&fnfշΎnu񏙓z#w*]ٰl춛?Ff7㍤$CD 4T_kؖH~^g9{#Չ6{Z"DCre;KhXXc7@&l-J2k@qZvLhiUx_9&*LONsl}Xl(مK  9d, <9JBbp^gsA!݋*\.ʍcȵ&B&}Y7"./ ij=,+()Գp\^\B(!vcy\8#_{}n}+)PR@I%J ()PR@I%J ()PR@I%J ()PR@I%J ()PRKkk\S-1OmRs|٫f(y\//ˑ\l߅H\g+f,b-*j_@C/n*7Yd*\J5`1DD F'$* JPC1A2n_A3Q򈑠%#* .$BkPѨ(s۷tu] ݏ~ԩSUgsb[ vxG%}y;ʋi;_Uה4Ltљ]+raآ(r!y7z>|z:dyۼӼ(ac;6ͱEoqJLAG46@q)$Сcig]1llڷ1o|hژ;]"YCVZ3q6ж@+nU+4[ X` lնJdymђC2pc`pAb^QH@kBX\ȇL {Z%5/Blxn&MU%Y6'?:9݉~;|ieG !B 2Ty;-}8&?kX!䝋ƫ/ mǔWIy. r-y>˯BfV!C4o`X%3<~HA!ɛO>}[nMM:Hb6v$dB Ns9O>?Gs1m,ͽų͜\{ziʐBS%|W}ʱi ‰fbT9@s۟ym_5 Zh蟒`VJرcy /۵k׉'UnCwE:tId&Uv^%$[f maDB$BkfIDzk#)5YXlwRSyE50ܑ4 $=IC9RG^B IbHd\4 C :Puܸq1Nlfr%\z$:F/Q"$ba۶mXȒ[6iky?X@zɦw-$FK}$Qp $Hh"UrђR23[e9msHhȨ+Vi-6"s!{2&+FpR#jAΝ;q5yФ}Di\0!:˗s 9dȍCfODz}_ i:9b;pؽ;6"4MHFܥKpHehu;8N:FCMhMb A->[' n~ufGfgsbFćl b͂ǀ~i sfpwBlyQw9߼]3hGӶp)mLJ%vZ:>} ֿw}7ΝΞkU@a'? HэH0YWk mQ(}P>" {vp%B yӈ+҈L JG#mrnP>JRFkCH' +,c(:5z,F t^(56Yp5v~ֆ/Tvyp1~=va#ఞ̭ͥGX0~n}pwӯY&) DL)si@:fy˥:s__R{F6hE6?j9bw*s0IHR18xi|pу??޺Z!H쉾f# _jX*~N[98+Ƹg!r#lMg~{S99G0 wc% uyײ@sU;cqf'kgѢE \ku]hc|HV G gl2O IDAT͸ &AmD%xߢ⌓.b* ,+c#ZdY Y_i`YQE @`Eoe/4opV/ˎLg6ċʰs c=r&J)? [p;6)t6!9c,_z%EG$F^xLjx;*F wSa`b GA hX/xz w9uXf_ DLsO,Ll&?SNшr.s(tAHcАO34xl78QPɳG:}j iʅ{t41; ڛ3ofڶ6k7% VB}+lgcȬ?[IWEJѸ MӫD97 к?Gi]g685z%.ѣ ?VF W} `m`x5O7ߴ%QacժzJ>6}a>:N8!ysmߨ??%M߾}qR/'$Qx_%+M w>2i$.Ay0w:h8&qhco|, Q}i[y^Im"Gշ^N(rE&lp1ؗχ vxG$ B2|;pq ‹<׼"̙În 0jp 4dy\AQBya-oi,\O֡kh8wUbĭEpj*-XB(V) -Q&[%J|2pik:;_JR|D(\q|b})R:UZ\že MJ!hk8Z֔n:&kY|G1@(!k# Fvs=3;+AH_^cqZ O?tBn+Ll&s Z!uIɌ;L6i7yAAJ8ĉ':Y7b`SE~2{C>j9*oq:͘%ঊҶXgaN@3+ך9+$r0|>4lT>Dn:M/;W34gZm+pĺBsKݨ0{Ԇ k^L礷7:UpQ3!Bi@gU|3}n6k(P'Hp7쭌2o\r}pd$LyDʰ5K&( t]WTS^͞=Κ>}:1@,QpXq+ k0G5 o7A$#ZSؗX&%z/_j(NU"DqÞD1QFa H0F޾j(1ܹB1_mŰX_ʸVhx@oY|J~ @Ql2r.hP#W1jL!JhۉQ&@H/T*ca+ϖ'Tc}8D ~_+5;6"-:=z#zg2P1AB ,cƂ႕IF}N.y\='57uVc,YD`(2r Ќz7.la\eN\e|{'{;Iƕ9S̻S5h]N"ĥm ܗ͹ ,<@!At.\pVT X@?oN97:9ĚJ M-lrݐ;lE WHh8p6?Hc"Jټ暨8 A6FHS:2D4"J vLw6PACpcm$!+>|8S@  E@C+"l@; M$u4s6G쿿94XՠDGtԨ( =O>y[?#%4h蟒VJɩ7qHdA_H='mo+@bE kN$b0TH$@t'D#E_B7:}!CD#! O5k֐ I,r[#/rr&ZW,$˖/_!;h۶mIK]'0~۷oglzȻj0ma : iEdniW!볜}ɐGۣm;5aKVagH +;w& WF(bwgW_}umof!Ebv4Y\:P2 ޒKO`t7$u9cSdĉV_m%5OG^tsToYE?̚|i]?~sdݮiSCQO2u+%G[nTk dڶ'O\EGe:}\MM>_E)h|Ҽmaexyf ɒ$q|\}a|r l^ѶiC ޽{eQnx5\;~xvyٳ'Lf„ "Wl3F223gF\Ñ3 ')iPAЋ%1 yOqǢ9֤ӧ:H_`x@_[H֭[C NgעQ ҇-^Kf>s΍Qϗ:RdF̾]}P~uqDZJ bv&y0'Xr)B5QF=?4*h8vֻ-;|ߕjL!SV[oO ^a-̂UZ\8мάYg}r$:%az~JLx(|m̍#55Tx' rpXiLɀYנ"p[Ta{{F0&X[7+#gh9"Ø1f|V3gw}腾6lVx5<N5N2P;. #0f)=± CVТ 7pxO(-L _~38cڴiy6Io'6oP^hmS,ZYr߱ JcFE!ti:ʠxEn frP:ԿP2.CY)Ȗx"1"8 /;bU?&DXi yKx[\tE"񥰒wJE[? ׿)S8\QT%*mꭤar)6d/cI&%i{B^tD 8V_vJB tT ЌDG)Q$A4 l@c3i?,(+rJ~) {0m[kQku '0: AyIN؃kud# ;˾kܠA֭_vhPTDKO\4Դw5i׫mmvѣ/i~JXݼ 5>ZnlsPa1 SĀgEߑwV\zꩧ^{Vl8$*D\VÌ -21 _|qT$4 yOĸf͚žeJ w1RuCPy2jժ11^z%fDU 2 Ev!-w LiґRGVװe'x|V5/gp=zGB1f./2-lz]THa7duF$h2.ڦQ yɒ%˖-c EHM/:ih82zt?IJD2+`}) i䶌l]NJ@'؅: 5,I QGrN}[{{fLfm$^%owUT,2!sBxxռaHF׸͚e,lh0w{Ξȣf4hQpYȬ[Wg1jqQe}M13/ˮݣ6=^:y|&QK$5җw\t Ke,5lx,7pkE-D@c /6΄ref m;WBqar6ThV ޝ/`Lz,<Ѿ7Q06 ;yԙN@dCFuj["4iKTzgp|ŀ FCA|}jaEDDS᤟4iw(sx`lag׻3[y/6d/\=JAC!o]u%of_Vʸc#4$oI l ,+5 _ /ŢmPۈbp\WmI''?7R9y&#v GyD[Q> ⸓gԅQ"|dOgݧu(y&?Q}ܭ0зuܫ{ﵷQ"r-^\Ҷ˳j!U ѨrV4 ޱX |Î]5qDX:nuKBE1-0>wqGF͙3pWRpeɓ'{t ;IRpB0'a}F"6#_~7Xd + YI?:eJ+hw++Jeւʖ:9AXP*@C Ej:!B\)b,`EIP`.FސVIBb4+sl+Yٕ 4)PKyUMA^e啲䋡J^%e(s1=caø-..v4>'l0X}Q.v"VT+wڼtƚ5> s?YYa֮&+eVxCfIwN ȕ p.1;A+e足v[8 Mء+͜9CS@1NM\k/s]{Ɯɓw,Bz8e\DLŔA  qtа}P[kX-% uu=zk6-J/G 0/; i;Dbi= m(b5{lIh\PDtmnk Ʊ]>KիpB (`Hkàa+dD0BVyE? 09vRXpEz%W_M D&q#ϗO.a\=p1+wm%1( IDATYl(/a%D_W@ai ;Idؐ]YN"R7rN:L6)\K!7avSf1J_ʫl ,+5 _ Pʻ-#B`ѣ?A &m '(xnޕVR'>ύ=;zW.T\h[n|{'~KIƕ9S̻S ~췓H?̂WsWb+ڴSqŜ8^ݕQ C!N8EٳYgӭSdxqzoPzA1,0OȂ2.ƂI ';[x_M-l9sl7wuW,&N'|2Pp↔ ̢V5;fi ws%(pD6-ti;*0B)ud3* aɍJiÇ^bRP`kboE3uT Tc1A~ՅFd\$=.5"'?IHB )A@barqh,Q  LۢϗM.{ k=2 i޵c(pE x(~j}C̡,?>X$b=V0ma79V^r,' )ICC'odulR$m\:@>Ry>X mã/P.mw4YV^ Fhyr[@&?+8OrA8 H}?0ڙ^az܄\Č Ŭ,} O2c/[a_3۴o/^`#F ͉}͡2!lЀQcٛÆHMԄ~Jdž]DQbvСVf f1-5|m0l +nzTx6ۼzacTUa̪6q2~ؚ";[MDd&8M@"a2<.X8ZlDI1B0[]jۼ&1ewވR),;[h,ݖk5X>_sLZ:I떎h_Mmو_]R(e!\G $YbHJ6LWn^dctT,)PRQYvZѿYϵ-jN|i/ZsƲDCPfeQI1gVrPBR# WF<U9fY ]ʶkg iL\ @I%J ()PR@I%J ()PR@I%J ()PR@I%J ()PR@I%J P(JyدVCDjtDen7GM[h ,i5,OBS e"Τ%12:JB &r~Mzl!@H1etɎD ;n?W/oGyqCD{L~-dh݀ ʱtT%5/O[а[J5h4̆|h" +rBP ^L &:\$yl;"<Рc Kin֭[Ƕ}DCY:?cf捺RiڪukDz-%%U+gm-7I+m>ŇnO򇤀nhT%H8& l -{9'HJS ~J!҈o8#i;}fæ'bMkFȤ&H_LB"2Tyyn6yq`l?fuvN >XJNSVe-yl̓a:,@j(]&!, j!kd_?}J]_ȕɭDw~ Kݣ,j&[Ќ{IU_ l}}iԊ cǚN3dva"}$J_ ^[Xk2bVCm0Y0?oN?ݐXaiـ"Su45rLʓl$Q̀". 7 bN:$a׿5B9qe U _A!r9-388Ջ2d$9)A!OJB(ޝ{%Fžފ~(J+0OuAѣyqQԄĶsYuiY.]ĴPy/2#&CLY!bV\k,,hj[*S[Y9iߗ2)T`m˗/'#/^pc^^BL!g^z)IyaQZ*ZYa^{hbU9$B@6gXh2P5Vh_Yq)p+ `(3maҐđ-jWF}BB|B&JX0S~[ߒnEH(_8&9Iɷ(brÐ4n}/Q}|i 󣯛10?ݼy1=v+}&|A5oOw G6Ͼmwۮ.f.fV͆vlޥm}y)u̙gTwI6| ax;P"3>}m?o.(20q긿<aM4ݻV W6Kw\4^UKHӼ0)w=}9\!%hع%8[2jHt*ŷ9-IƔ+!$d Qr '{frW;D/IT\bk1a4dLʐLFe-Ex:b[3FRVC3g~]Z <2t S U::K:6XoE܅M[z(`Fܹscy@e*M3-0j+^ps'/Ǐ Y_J_#l%VM!ҏ;8 ׄgBKyB@A7N 5p{X1gNTH[PI:KzPJi-L\S3j jKFu"iL A( /^?21yR:}'3x+V Pѽ⭾DӦϥfy˥z_ OϿ0=l m&<ъom~>6zcsUƈ^\ Fe,y!v]D#f*[aEEGҎJ+d㎳̽{~[ט1fd3MA !|BGo7׽1sF=Fiff83h2/W8b,a#վ_S;,57.} wyXvQFhy)< ʃ{\cI/ [$`ђi޼y/W:L2e0dXQ]WrUƅ͐F/<+[)(^D?QKT.Y8rT*IIJ &3Cp>)}9 qnׯ=CGʐի}АWf̘5*+CN{[2|Ռ f\E{[yY7]f RE_ͭaZrbβ,/~~L[ϛg`EC=ĬqKd÷I8(tB%ڭy=fmkc}*RW̾Vvu\9Ɛ[IWU.\r 5׬S\Y$r`_cq=m4!EpK&M\C^8wD{=ȃ:39z˖%J#G{x#P0Q1npr!am;ҐBqp{Nd<2.Lƕ#;Vf+ǖ ocBT$`,XLeʜTеqcEy |ʛrKBCBphsEe w '@V~U1 BN^cgk^ pa @:THX5`T lH:dLĩV jM9޾!4 p㇓4@݊w6 g.W .삃>2.x$y.1#XN%ʍ=;zW6=;w0^ ,ES~˸P45}j^ywyav2?Yu*9y)ָwek \,)<_Ձ~h|֋/A=^)>:/IJܽBvPK&78Jzƒ,:s&xǶaa?$miގ,+؛9~Z؀>|J21)A|\XOhH W'HD^wܿQpT@ܸf"xQ +@W^y%#.cQJt'6;G|JP2d.KA")fÇ2TLaCW W<ʹ0&XEl_7K EVT@d׉2+sRSc-<~\RFC4bŃJU{U 4 8K}/-/5\лqSLXx*# Jz׈R(j6T&sˏGyD{Ee8dG5(< v3^Iy>Lv?7w2W h\Č Ŭ,} ؠy2c/[a_3۴6Ͻc.^P0WZN]hNkVU ij f/'m x|{_B$e=f#MŊ0xqB"d n0T+rC@@B=Kz:^Cp)6ngǮ'7U9 {IW ~()R2Xs7b(+{Mv(CԼp//n, {c&gSNjDe_<NhF 9!8i1t ⸬iԷ^dXoH*v ? JM9ɾmJj}()PR@)êt|UR)\ɒfD:ɉKCk6d_yۼ`X))PR@I%J ()PR@I%J ()PR@I%J ()PR@I%J ()PR@I%6 jFD SC+SDŽ\ o"azK:/63y po*_}D66\Ck |f_ѽ"$⣹tB0֭['p;<0|?ߗ.m*NT6$#LMދ&gm U s@`G>.aSJ#yoa&sELJ/BHRʐN{/QVIu˞I1$Q@c#V0, -:fUmV T?[;jFY:d6$aN=?5&w7i'iL 6u(&$7m ZpB[:e8s>3*'lg$ [$'v͒!CAr~č)A Y$1cD! B@X6̱8/CTdIce+x֖PņFt=͕HW1Lj*^佅(E?9^Po2K]wݕ=3G ɠ$I>} Q .V l6l~O> M${՗ji*-ᆤ!뼨6شj[#j{#F=|`ޜ~!E<5}SH J4wOIJd+SO[lA6n%[{w뭷*͕WN8ƅ %~H+@a*CIv Զm[ zqsTcYT`!bLsIHNJue\'MمaWhѢn^A^}y#*ƍdrȓ Y59z%\z=[Ė2ѿ/eRfLRC.].\JR[H2h4B)aK-uDysێL)ߗɛ*Pgq_ -?ߵk׵kײ(#ґVtҘXz P>×/_[Ñޱi wg\YQIL(ѿJ*xaYR!y)TL޽lhۿ,&̼.Xٴ1| $XMj6/ma n$Iȋ#n/f32( IDAToGf>;쿿9ڵ1v4m,X ܃Y'<ϣ F=/է$LO_o 19ȎאKIeŒpc?r|YƐpcf YIݵYlzf=mVfn^ߊxmm?`ȮBýjʋ4WrrKbz sfDo<|놴\D! I[n`0Tܹs/4('%m*_E#`3@ُ݈O~^+C9~x(SE>Wt+;ɯR*{ E+c; 2 yÎyի P;Y0x jw(l6hi org0mEy,TaYR^0).сDUlFic׷ʴVR{<~ߕjU&zb3Yx>Q,wy'SEcXl3si@:fy˥: ? 3PF+*oPݏVXDD~$BJ>>s6ɪǏcRanj8"̙f@T ĒpË_v3aBoرuvrQ㎳n`ٰsX fq+,{E^FmDQ[pfn~;t㏯GL~!` ]_.Cvy%2ĜuӲz5+X7W8x`+2! ;<s6j("> {?c!v μy^~̆iYÔ)Sg` FNJr]wdDZe\VBb:u,輓:lUڸ6r`p-QIC-Y{A39L:(S'%m*+ :Q.$M;{l'!o̙3Ooڴi1#bEA7ݩ_~sp2?"o_ˌ3~mh~+ȧVibC`[袋/b۬Y w,;6m\UW(["78&P3!P> \_fGE27Wʨ3Jwe^{e0:˫D/&l0.lps.-Ps*yL-͔WOf `rޣuhU3j3ypBj[yY7]f ׵$=[e }n(IqaJV6XvXA2Ŭ^mO4˗[CkEYF̨Qf@Sa,|ze~S3k ʕf ְ}x=߿3Q ]wYSf蓆*^yi]J&ϳfhdKh!9KқoZ=*lX2hXl\JgL ^S]aOرϦ/l຀,v"9~z(. • f߾}qR~+$٣/D> EC[Nd(K_~ڽ{wW?m1qu] bphi,@.hķ5TF5"\:OPo9#؂wް8p0&Mf3׼06.d @``W 03τ^AY ӺFyqӉ +5mei01ʇ4I \5b܍*I+|<"w#E8餓Pa.tX¹Ct@]vY2`b6s\|\ݾ;1p➈PjV,(ϻ:A%A^EdؒbeEz)dʦgr/6FmbV8:i ƒ0 n]xr 1 KA 7*T4̣@4Y4N;4J;-`f6; ;~M ,TCp ~Uk `>;|XU"jZ4m Ò'OUƥtܧ it_Ɯ<UhTbr*Xѣy}X:Tpk{&;\ zxY"Vs! $h?2T`TvD^"9.e*+O o[Bi$vआ;9I^[ B^R^FfދucAψY_aÆqʹ8"mȘ[QL*3 6N[֛Xg }v:=YYa֮&+UlPڇח͒Wm!7wlgb\ov.pOw4 %"Qz? _,dcfk|*lՈs _qGB@ˈislIGVl\J_,sjDf6 ѵ ^^8l?I&,IJ`AW^R5+O{3}j?h#ep]o{)XR!XGh^SP_.qV `Z؀z99As= n <^7G$RB"4'_\@%˹Gr,'HG\ǔO6:m\Ȏe\"gSCDZ~% IP†2.o/RWI/i}'됾h ǘY-sT $^x"ղ#<4w,ǑL}nν0fn~nvdf0 pXbFebfn\q{t ٦ }{97L]hNk͖Uc h¬1|w_SRk\(06\!C-lP^Oƶ Ny0>p[cBxU9]Owhrз͋hB>\hᓢC.ߖ()dOFU#8nfB(u7ۦ)Dv^\s +h#k.XyF)L{of^sLJu"+lz$q5eC#PN$.]4R٣$I^x2Y mQM-?xYu`mj yFС3*ޫ*7=M)qH+3w7MO]WHmP廒%rQ׎pdU9eQ Rq$ݭO$?9w;mޛrm4}{f)N\v/61TkYVR@I%J ()PR@I%J ()PR@I%J ()PR@I%J ()PR@IQԐ"0&,D̠:t [S +y Ù$x#e;;JB]5s~5m4c.]޺u0`npiU(/Q4NB&sE#ͻF޾6y?ؼt` D'g_yU[JG?QYi#5ypĶ_; A_}۶m[q,Ub*$k$ۑ oDF}7̼aZ-9m )x\O)%u[ُZ n-sXmMWtA2:NmChMtV0ohT&$7F#6A"9眓̧A63"%_KX iB&ƭ#%<٣~;|4d'=|l($Z!CH啓s8WN QJ,0$5If. $ S2Mڵk).~I(qyOcIO8r0ŐW1Z&$et:$2.Ո\^+\hQT7 /I-0d/Zf!T7nIćA@jM U/K/KQJ^2ѿbS ̼,ЎP BtuJVa"r4sB%FeG%kjo~=nPHS6yE$SF*lR)52˲L>.*``Ztw_pKΦMCS_{;vXp@tuĉD={7a-zʔ)WYHnYu[Rvm1G5ї0nw7VҠ]yqsToYE!ܚ|i]?~sy6fЎmO E<ɜ~@!r:4G s&"S"53Ci﹧nl+V?!+>2#)M׮fVɾs<d)yYV=(n+Adf[,ֲzf;Ȗb&Msh'7 3Hfq~ɫ4llc>|y﫯m]0,ZToNsQ^xN[SU#+gp|"ŋ!-b'&iДSgϞp Z &Wx92dR{Gõf. И1c$:49sf.icᷛh7W:yit]_w&?E2 Bd]ěd,T,r۴Q mߗ-cّD`9W_FĤFāuf:;K$aח|e+gDo3ᡌVT~kѻ#v727nIP(fdl ]&?&֡~{ٯ#3~|=Og;κ'`ӳGHL^)$}uq0S-1c{?af4L`i",1L-8O*[J <>g23_h_d歰ss6vN4nDGi'Y2Ĝu3O*/7^~xk{ZA?1L}ќRp𹄍!wy򃃄aԨQh|A c .Y ?`aPL̛7_j:B wע`E뮻PEZe\VBb:u%EёwR JF&/ v(!Ֆ,YƱO>2 d}RҦR{5u_fϞ9Ɯ9sMs8rH馛cV>o})T+ ,3f@12?=ǴWh\2֯_?b\4,6?W]u"tIp榍H:_a~r$wgѸk-[l^wuH O;8;BC \y.=Xn|!k7F yl`h8Mغ# M c6^ۙ[)ͯ33̖Gݺ9ff`7(!=G|ݼΜuՌh`^`$=[yڦrZli~ 7y/Gu=~LeO0(g0\I~d_t{,3gN}Mo_k&O6}F_`X)Sl!O<,_n%2`F2 ==uȋ(Ea4M)\߬54ua#oƐeI׬a0Kq:2D2 b3jQ曙70VlUmgҌcȬ?[IW͛Z62v io XW6ZϜ=F"x̧Cl!//W(nêF ?L8SaERoohX$+:5).|qiBmd |)at+bҡ啾17x x5c@A:6xzh}Pq+\e"x WnQg_\%rgatܯ޽ßޘZҺ+|Vi8raW(1}o[#jʩ,jRo  r8> nH}F ʮD`5 n-ƤIEO///ݦz4bMʟ IDAT\C:$wgKy(9ܚa#zYCGTNF4b}0DIB, ?a3 ae lD\pDgfL?+䣈/OQ˨"<͈d†]\nN^(DCmxGGaq4dU`Ʃ܎~vB`] 7 ="3NUjٕ !o4N;4yVvA C2u8Kزkaq]m8&jjuws"}C(i1mc4 uZɓsʸ:<yOJ鴅b:s8r(6V)09}(,p~wqG.CY!ђmxJ15GH'mD`,ܨƥUiP:c0k6l08c'"G,H=Y7 cHhy|&qx*F0ta$*uڼtƚ5>I՜Ϭ\k0k[ϝ0Tҕ.=wQF!@n7~V!F~ï +$OJ&ǂWsH6ܫW֘B9e,Fb}5$x3CEi9 V&f^]hbLդ6JNO$EX+(8+cՂbX0maM1LeINej (:AkLugB. tnR?9Ɓ(MQEʋ/(hA]ѐ 2`:WfWgjiq)C.F^ ',?A:"mc؊5`%Ɔ~9l=PiqD=ʻoP|)\ !԰0f{v602m{.4-7w`DJƕ9S̻S v2I>Yuj̝w( m(. e {;xm`HOm'נ7(mP\Y>:_o_=xH?_5C-.*v!~)T9gJFa,sd~s)ָ~!֦b Rb/W jjaΛF= ¹t'sG ]#Ax8s2$Rj! `(鈋2pFA@q[)*A &ʐD0z59rY$ì1|pL@5SPƕ•d"S!M0ЋJ忂2hTV6r&K&i%B2Fxɦ-2:Bqm:Pt9S?;lYϰnΧاliCk 4 kLN8 +mR^i09GQï_0+vk&W^Q CƋ^ {8S:r'(gzcINrmDyfNf 4b˯XbFebfnVŞd\^44þfimC^F΍>SCwe`ո!V77ߨGbv :y\f߲G>|B,,.1kD{VĊ L6:VI$W pUk'x!Yql Sȃp1`rk@;Z \%P-yp+(7JC,/q"āKkHy!Z&ۖJ pȵoԜnUnD5ǧVo <#|J @H<ʺ%J %0`rUV-_()Sy}#Mu؎_"2Cnzlc/GQR@I%J ()PR@I%J ()PR@I%J ()PR@I%J ()PR@I%jAVRaj;o2ey›p裏&-Q;t@ ɌȌdY Ù x !-VQIhb:-*VBEYc 0*b܎& +y|] vxPVBB^-iS<+y0B]`s%f%A#V9b@7m}K@ DGI]xPi Bȫ B^)4bXGXBt'Wo}cՃA8A( Ki~"ȏX$al;r\%Q^a]VFijhT&0E4M`D V9d(9$_(H$cKR 5qxr45Mf)I,MˠQZec$֭*׈89dRyd,n%zOʼ8p->S1cF43ZxA> fL1UF) ё ʸꫯ /|-ک C,dZ$1dӨ @:nܸ :I X Pp6DOz%\z$'wn'VJWfb}!7o̘1D#ؘkPĈNE^AH,Iڹsgj&uW./.dh1W//@[ʳc?2úuLfi]0 asOK~FپC#;m\Fy6bwo88< 贑KdI/&pIeٳ'3bĈ &QO2y,9]s1 'Yա̙3\/nݚ팎QVO# Yg%/:6YoUl˗/Ǡ\̆(֗ N[nV#/X јh^Q EܹsL>)iSL{;xw.{ Ÿ KqIEIPT[;)9F(iP{Z4qmA*DQ8]4"EBQA%DowY{~%?5kmfM>?ٳ)OC(.Vq)Nwص8T[O~P.9$˗6z ZJ/2D17]r%N~ŁǏ͵JW*@?؅\uf,}98ۗ!-3H* I"` 80K %KA^ACRh $ 4B[P:LR:&SUM ,9-+pggoB{ºyx衇JE~`S@$ .DGm3,pXF%l)<+t@WCm #~U[|/\KXL~@I`tE赂S3-Ztg̝;Pěg|@d4ȁoaVTe(hRdasCȇpk~GTC=3>i$Ȏ͍bÆ ͧT,%:Hԍ$\rF^KE Z!/sRr)Mi:XBP)ʰtǕpPIY{M wU0,ׯQFk9eR#%KA^ACiD/ m)@\? OnoNoCY}bh)۲B A;{*іB"2=',+gꪫ_||衇~_&=㹩/;Uktv˲n?x,{\b' 1[9bqk.?I(S~  vꫯiVg%G}>i9*Rb\W}q Ǭ=/xb /|Z:)@€;TɥDnϞ=~\@hG8^^@2u=C/bJh(%]JK J9Ŋ٤?h '²lܴiZ M4  ja9J e)ȳ 3}GrȔX @C`C0 4̙4hE  JVS-JhKQv@NdDD=d1k[LISe:%`z[ZRfOX|Џ ۽{w~ DaN"P1qS * d[/Gߵ+kݷl`[GPO>sad fzv8qDWmd`pKIL9g \z饑 !$i&Rd \+O><&zjjpt MjK l\&aŋuFs܇oedyMKrHbgsë y,Br? Ucʵ+/rӦ5kyіrh$ʅxn^AhE] ]skrPV0Z VX_Jq VRWPPү$2-E"Sp[n%iJI-=T||[ #*D(fu>Tl|TG Xr@F *n}$= Sa>餓8YpOH"#[ڕf JBOs7~R"i;ׯ8iFa2?D@9r ~6 āwe9pge y0IⰎ?Yzh@Ʊ$*`~)bI XCGKeҲQ&"Yetu)xd]r@]Ao߾P*:(Ə׍D<bla!/ԕxJ!F/ſøӷL>.S-\7D2.\jǸu]JBenwB:y|K JHb.遟c#q R1L8DuOVI#)x,$ y5YD6A.l`C(xjDpeki$ @PfWxR$ȲN0ONyL U"bybz.tY娢Yɒ1&Fn-/ sk)]h,>#0g99 & nM_rS+ݘXZQ7x#<'1>d)MAC utyW+; JR"q!ʠdKO:uC↖-+ m)].J=a#`~7)L }YJĹ_A.:xd]r[[؀& =>H\t!  iq1"0= s qSNÈRLM x&WXw`tG"G;yBl/X0Ohy j+ۄKE!$(†RS3<@')l$D4^@ O F;Ͻ,e3*Xe ev})"Mx2zL vamvm1\,d -^ ڼ( FsW2fdȭan7 8 =#R}U9OCt)b*gIp#APri,yR f/r} nWBMAC%*Ȼ2 W+;^1 A#~g&+|0uCK/-Ԑ&my3Q!T. SJ~xmGY`K,w.>YJ$ lڴiٷxdgqR* Thq -Ib `%_ꪌOC7 cƺp2-~ r'Rq yj])Ve(1[^Q`\)&XUEhgMj.歹Z-=aLe>5jmZD F^ Vϵ7l 9`@ +‡g߰O%_CV2YN*h6i(KT+ T(P#ˮW\WIB'liXp|3 *0sG>G ^W)V_<6kWSi`~6k$T! ̃ҝT^s5^kضf۶ʪ(PQ@E* T(PQ@E* T(PQ@E* T(PQ@E* T(PQ@E@6lgjx8%S sY/ N{,30Dp׋]Wuy6xB"@0o@C2Հ !WAZ,5Խ&*@ fkTXMd6U-8!*4a+<묳d%D&X* :w$bU#湡lSxboРA!w!¤m\ն-LW-a,6+1 RDߟ>Kb)%C(1Du PQAdՉ!$H IN:űR\9t=+1tDo*Q(ݣH%%(O)rq,J0+p6Kx_}nz6=S2o hdnlo|'mo9clΛoD2Հ !WAZ,5Խ&*@ fE-T2wOȃ ZJ}r9G:]SO=,Ć^zM0[%YdBe˖e9!QivI\d &مB\Yf%uCA DdC>/>cAXfx 2bÊ+TvϥZ]SNv<4c,M IDAT|~\x=- $Ow^tRI7C2E,͜g`u|R}ѣ. AJF|*%BA+*Y;6`jWa,9pgg7I;Mc;M6n)^r )D0Իロ,(Z"dd4睇 Y^S6s}F)de7X :1B)Ƒ$T~z(005M̰9.l5b9 b%T|[Mo ̈>f,3. 9s@^=Uj>Xm;zMջv2lc:w2ϿeZSϼiEnjO2y:n;駆h֌S߀0ל(6캫b Sh7"d&E],]jk0Or_Juv5c:w6?oC tP^kM7ĦٳҷL3kArgK-g` 5$ҜwK!o-e3g2^dy7X 1e&U "lݣ\=P)MЍ7ޘG]y'֥KJ98bsFmzx.MbXa0Ͻe~y0?5RhߝS[mb@[̑;kjFĬ|Fskooq<3G 'X+o諓QGi\c1&P"X5y.otovF.˗7%60s^h(E;6svssfwGFy j'GBM`lRAVPd&[/aC'Q=hVirs(p{ ĉṳN! `L~/ Ç@x+ ,[u )(0A $T&PG / P9S8Kw j+:/>]5)CGj6FӐi&`*Frqƌz-}n"eE*0H> zY\aUT&vh9C! … Q `m7o l锂PEeo)!ٹ("RzTy"NP\Q!4h(*̊?n`2s~I]m⊹s\wUWaw8pb$ :q2@C"0sAZRĦ7` o0S˗hh}Mt`ΌXp8fg 3~릵g/3n%j&!~zTL0 츣!=:r /S dh> ׃2G!CL ¬t= ;XnV1F|z T9 5֡h(<(k8\p]kYsGy(+^{AMٯt &!BQ%Fh4=P[АWxPp \ؠ.~LT.vޝ4tR+r.DCTeZĦ{SzsGlOh]:~|ٸVn9Ɛ/ZI%kGr*~a3k{YT<̘1= sSBUv:X/z>3G7huB@;4!TChKz{o@.V¼à$6s(SksiI$uzB  !hk NuĒUoGMJFrPsA]d9Zl$>^пOtjKĒ|# yId~ O9a~A(ÊY6!_ Y Gc wb3f(cA6~usSh466xJ l[d.Ar ~⊐zlh>E6\hxN>QFҊxL%9 vsM67zN*]306 sQf^E-.˭B}n>[ PfOS($ᮘ8eȕP[PR0T<ڤIJȨ 2,G]tw0SnK"13Jd 'o`X/ qO™*'ӯq7|p 6NQhG!rgé!a$ ;r +NM&杕WkXƕ|efJklXd2̀-lih)MW4V?WNo9ڶŵ lP?ǜyMg<DA 0` 9{FʟcAk[cr;<xԉF&hp~$ sӧ{DrB)XV rU$A" 6S6?6yrEp $3΀_|ɧއ4Hs_YFԂQCӓ}-G!nc@61ʖMpv4,1a.*qEr",+O&/ARB[4917w}r:id(0B4sU@:䕬l]J 5ꅲ ,bB9r$?{&-2p-$lDre jJw,(ɝQJ]%(|& -AYgu# ^U*YU&U*wQ.N-χgnĘ熲uOAIA9PY߅^r!T۶0< h07HMlarrr$nD.aWEqT$ ˔^I=)9sPaPE!1݁Qc _Wi\o[{(D7  qnM6-oYħ4`閆l@租~HU!0bnzvq(u`C}*6bМd"ԍod[)\/W_jKï;pnKa# ?%(wdk))EAnK")ꩧW^&L V@Ih \z˖-rB L1-%cldPfJ R*~(}8* .I= Da`VX(|.:u:࡝Aw d l . /Al=%ѢbLȥK}} 4'E,ǜg`u|R}ѣA0 AJm~Ʉ7V yJ1bKP^"Eh̺{ԮVlǨE!ϟWg>e( Ѱ2vmaPʢaethD A-/5ˡ/e8<ˢ ` cjQ׻B4T.jN~LK Q 8cwk%ח)={:urY]b_mf ̈>fZ,i{sfKC\˫惏=ջv2lc:wZSDG7eUOƫAivOm^[Zlկba@QkW>sgvF $P#F6sgiǒ%flﶛ5\z_vܦ#c@[,]jik/a#uha="ET0A`KT"\KYITqہKDKM]~t8e#<\s@"WOƍzڤ08P(d8>I:s`؅.x,רC>՗uO‡]wu~1B=e(93gά3|!={6*T}Z.fRCm(f̘W熘/RVDr +Ɏ#jۏg&|.{  @G\L6.! .D5ļyH5j"^˞$TKs%9ޅ 7p7vYACed{뭷2I9_6 AVh~)m0$2^:%*RnF xΛ:u* @JKL+bܹ4wUWaj vpDv2%l軍8nla6Мfc2O;3l632l\a&oݴe 1d^^mZ$=O3́ vw4챇 &~s…#wͼyf~fo?8 oN?Će{1/lAeYk50&D)S8*|`kZTdF6CSQ3)*x5}ťjv E<ɓ''u: iwŔq?D'J lJkIBX >Wx.&TKyKh7r*# z}u e wJV/ۃb80PK_Pi4w<&\֜9snJ4 MDiSPtYS}9 ,/^Ko1 C‰Li5x騆0܌ Y1cSKQLnt`'AhUCDKi }ٞMMw2y>f80|J|.1o,_},K/|i-F202Je ?^R  5.+.+[=V JbwіNP J%8x'E] ^mM=Xtp mQG))'^m2 Y@,}ٞ qӌ 'k C@,y?q&A%\A9H|#Ém!/ˣ~3I*6/|~XSYh&ԚLx=Xmnt)̂q9!{xXld0)ӧOGoTAqcjI$gZ-&gq wMK=KeqYR F]E}-'H qs;)N$*PCv>!ALKűJh|hP FyE'BJOR^+EKf Z'=8(FN&<>76b!,b8xEޞېpި)$P,wiT]#E1?QDmz[nf^e,1lJ]~|oy#;W9k=mk0|y"M7֗\bݫVM<` =u+`/]2<5Yk|jys[\:|}x*-{) j%*7I{5K6HG=v%_zI<`/xn)AP!*Z Pz.9m4c)} ǹ`CǤs' &8;u1oc]SՋAJx.DңPHU@<@_\ ڔk{+P5QQ`@nm5R* T(PQ@E* T(PQ@E* T(PQ@E* T(PQ@E* T(PQ:*8АCCwZ_ ֠AW 3AXbo{ kk}GZ}#$ky =Jx 5XPJ+,_ŲkGQ9x_~y9 CC~ٖI!ΔTқ/3|i+z`PD-%ݡqњG0ݺ٘u~ [29^>%7ٮdlHVn  3`ˁL,47xcmwZ::vlR"wFDfn/Liobќ5*yF_ixDLdD prguStL@7J#LT!@bX!"In BQ 4pb jxz :xj`"8<@ ,h0n:σTNNDQ2r(m144Eq$z֗|-BuM=mE!\-Ko hN{2Jm\^G?߇S A|HAh#M47VW4AR9瘝viDaۘ-W&I:[  6`ˁL,47(>`g?kHˮRb&>9-ņ &F\]-,蟒AJL^FRRO(uc6`u_BHy%KH1` +Fԛ LYyclBىsb_]C^$ܛDH'ҥK i BZ/¥^lٲ,_+J17$S .C@p&(vmQC"\Ջ'E%riN]Jsq'd . M- wҧ q;wLT NܐFYtR1vUGSʟmLFN NbwgYP( ()Nk{ GyoC-l1[Vқ 6=Z!fmmf͚2}{ mxl#Ur x,]wݕ]Ee޵LCl)"%Tr›w(Id'pG $u/$^6 c6ȶ61s`_;|=ЬW}W GOuzNfmLN6Sk"5Q$ ?є7cb#Xs򂦖T۵Ӝ#9I:zh& z?>ڦ5&eTsO?mt; TT0qHVֳ0V2KZ LF*lur@g,[vmc'_ղ?L! , CM5r;&Pz̐A >_h.?Ӭx4'$ ۗ?5J ђyaPHK*)A & 8-HUD}5n8W߻bhƏ/rGرc%>:]+ko: "JR!xu B,R abti( 2q:@$,= Hz&֢گ[6G!s* V7fΜYghy}Bq > "O\jkƕ Z=Y Q0J^BXApZ\<ɖZ:r%qP rs'2,`Mύ/툵Y?2"(Btyiȑ+-{ik衇JI~0)gAh9f_3($+1r-P0n+94aycY<~鿥 w@3ZjJo`ܵ\S3<'fl_CM1)T͞\@R٥C%[FAs];9ēFB{F̢MspfƏoFhO;ͪ2& 6)) s$kH8q| L9"ɫ a.=D en ` H$@;c@#w͒aH*X…VL~Pu!BKҰ} kЫ`,Q(Z<>ܹsaI|!Q?k1p@?'NʜmGF16c 9Asυ%dE SbދpL"=괈TҮQ]TYM!.rNDSN9^ @ O>#< [o:tz>dOnZ..۸Y#6hk(>Q/R.uaQ" 嗥|hn妋K:C %fg 3~릵g/3n%j3&!~? #4e-Yb8~7VE)m*"[-}GМ{5aֱ̛gM|9˶b= bjug"aԀWAfP`noo#v &ybB\4`Uc{& V-tRCFY kmeuїlE O4BC_`%4Ԯ] 棘%i6Piéëdm=gTXCŒLx~X] K`]tqh e!S8q2 lH0k'O⋳  aH\~X饑woF޽{K1a\)1~"i7'޳gOd"MSpWF\l0 u?"8'ljNw4!i$Sfye-3B)_bR pfYKTj n)YDfTM:/^X.?BG7]4ƌrZ]LHwm ?be&(Ym@XA$ `mFgC֝{9Jl751g:w4tQLy+6YQ·1grkqFNeo6zhw)\0QKC(RM\d/Q[I;_߲[mejCS(8&qG1aËBg¿KTIIZ?ljlO"( Kfi7Vo 4r1n|gaKaź )g5Bd?Y rͩnVrtѨRVAleW K-& -$J. fN>},̅C:s''^E0YQ.l#/d @َ c5$2b!l0 " &xeHYi1~8ՃOxI>EnրvY^Dh$ҘR&<,u &+;b}J-M46=}#*a39ivReeײ*$6.8,s$FXu~[5PḰF ia5dW)X~>Ŋ9Bˏ"<nir?;/QáV Ρٗ $n{ESnu2F턵H"%y>Q'<jKanEbb0tqケ75c!4pB(/ա^4l3ar|L^K5!Nrx<+,-Ry U`1O>=FpŽ 8 ݺ,+4iRy1s?HhN6[U1R %..@vOF(qLD b\`Rqr$'4[ ~OgG!IQ=>rgeF)2<#<=4edPDb?ob¥:+jA9r AMd;*JRV e6KXn aNzefꫯg>ʚ@(e2SWˆYaÆq!yg)o+,l Evmk.\Fp>9R-mxIlPf㳄x#9ط02ѣ:# ㅳ>8S?$(QBÆ-Ip`yo~ӊ>Vw1s+80_nSN}zlСCsQbq{ H:*dV|I_%+ '0R .cWH# G'J"Թb [`!BqU.|.AصTw0c|衇DQ0 Ʃ ƫ>+$0;:)I{z<+x*)$vSV]ܨӄ^F/6< 78e 0+p&n6|WAY,xPPAG\hpZYnYRBNe(Y r^s-sE9L!TppY޵I{$ j)˜zXR1Za+e1AIq>_́0lof,0?|싷 |V~dߕ?2y{ 65I>‚+$78U:VtXWJ~3gڠ ((31b?`C\*P܈ YsRhX7hVr{H T4$ZP ~ز33b_tht[Q\U.n&RM=fMbl,<“apSo"{MrAJ:Ea&H↋jˎt+l`LZK1(1rHxIz`<trؚ8&y{)"á3FoG:{6 )y qԅFޅS~nOlF s}Wq.p{Od>^R7;mxM-fBR41i`.f(܉B*#Kly5t+F//f? Lx nf)P-JȝPz6=/$1fU"H -}k]E~x IDAT[^pbaash.S.V\&6B/2}lvnnoz46cv3nkV2Of*JSR/2oh`tON~ȾÙqn[.aXyA@Nu}d)2%2ȑCXS Lc<˦ds0;$;-Sڀ8:{ ) _)9h}ĥ^|dR݂Hw, ay6T+U,lhh̬Yֺ] { /Llym Ow1{Ŵav[&rX:vuUnQ !fVDEA@LCuw`ߘ9sf*nwL"~ a W K6Knc0ۗ}: (6yR'I7$2 .D(JPr!K7V+soï Æ@ 6h3Y))?g`9(+`?N#r6~9BӆO2 2 fe5CB̖#~4 3~xL+S_7'`;<>bjKEm/ LGڹl^e˔w)s+t#JI Ї{*} +4q֬Yx5&N300'NKA/Ay)Ɇ˖=z!C̘1CQxC% %+D\2uTpH. Bh~&M|60BٳG6:K.:UB@d].]/瞗_~YjSυ)[h$:spx3Λ7""K_z޵,y1T$2~%m 5 %ŵ^{,9wF( ?|6(oᆛoTm)0Ԗ2t4Br exRW INQ  ⊹sr]uUH_~us -1sإs9 )=/dZR~8` o0S˗hh}Mu`ΌXD͛+̸֞!林˫[Doe0BE6V{ 8M=,\h']3oUE``XL1Wbo7&13o0o}-?U﷟ ǑCnrX*ܧk5e2G!CX!LSx̓iJ[R(H5 Q^kX>)C2]#!jssY20b fsLя 7̪>zS91q@G҉f2_%5z-r"By.nJK ҥK.ŋ ;f̘c=+_skЀZBmn ?ez""NQT)0~$!:$tZ/X)KOqA•4;`n"[/CP O2T]FGө/eE6pI85.%*f[B^GC?WUjȥ{3} ޽;? ?{2؈8ѐH݅ Z93R- J쇽751g:w4˚\jq =s>!3_ߣK0T.Ö_I?8޲N/#FX'M_6̳Ztp*Ef\w?]&`RҠ _o-XZj,/-կ(?_KJ1\<\8V$  I(^LެT]t,  }9쳹 cCs"r.#蠪@B `:/Of!e(/.T3 ,)L>}\IWG7%~Iu/ E 7*c6*.#c`:GA#lu]JkD#|$JGRYx[M{F.'( Iܭ> DzN:${7 s ù4Jռ|in?k4c‰KOxmIP hPraoLl엪- 6H8I [+# k`a` !sEVkGJA~sTn[EG:nT#TɷDRg2F Lѯ̈́ QpMj; T3c@e <16(#!9T2YaýUq '|g x*P1EW_}5 o[9PyIW@@.nuUn.PgV b8Q DnqDJe(y;@gNN`)YU_ x=LJTS6=5ȡSCQ,ms8St Ulx)(ՉF2^12/ed[w) J-n_,;i>Æ &aҭَA=ZA|jkobYizufUk\ v{e+aɌ/744no:M>ƜNm߬PGTJ]  .ilptFzUklA:g~ Q !kGkqBdLlt@dڹl-KsY\, S O0T66x Ao߾Sofr\hP`xI"%G"pj|ѢEj/Xn9"0|“Mb87`Em#n~(p %| &Q)ыl4 NBT#) L[0\z|ىg3/u#[Q)N|긹qNnjm{缂FnRܕBo*J|])SU_ XS 7s%ٮx\yG)ܚaxTfcOdvF)ix^xJoiE@4ZݾAa-?(S3Ik9z:̒dq#44 ܸص9d{3c^c- |V~dߕ?2y{`mo_OCXn^^rmŝw㏷>s_XY_\"+V6a1(q>/kƴNBR~1u+=Ѡ rpm1k!sQîj`wQI%gV/۷cۿE=Gzr-w׿?`*^D|nX^xL7Fu84wz働~Ҷw^ t2u뼼@믷whz6nW @G4(*L4F]DGbyCgAhE:j1ù-\4Νc]),݌Ba}~MD*±e9,}k>H‚'G.mB-z;lW^a/ 1_{{ݮ0|bցҜw4y&Hf*bC<@w⇾դ)^"K%hrQr;U@Cx@"ym뭕;:1ձcK :\_ #_ !]{)4TC{rGbmٵO6RRؐ{+w%WqnKa#ۋ.$Qm/mK[\w3Q:~'IKv]z 2Y_ -{ K?ODt ڃ} 5 Ma^d=TwVn! <6JC–B/> ukEh:xU 0|Q6۸# XD D2ơ3f # 1$mj2=F3c |$E<S<PV[u{j0 `H?h" >,! 7_ފ 4t(+1"6<3V/7}׷J/f.ioe0 ^0QM7R84{N"_SOAC]&[_^RD @)L~``DN i*}=VsyGeFK%OgԳwIKH>Ջe[!e}aO>[m0XYM|ԧG>uunq_*&0k..` ~}5fs&_zR66,]4*0/ƴ]f#Q#{7E}?d (vff϶AЉ#MH-{ћ7ϼ}pZ#/hynPs}&nTKR N7 ;~9k5;x1,:(:xKHBWROà)i@<֯&ȞNnEX%%]  ] 7O9yr:SFsk IDATwơp'Ye39DH"黆 Sġt1K^={_HwU 5SG`*$2`ʘ.yr<WxmY;=;h!J- oɊn|G}>/xșDznQ_-`@=_2A3·<6u0MrJG:#?W|q@p 7X }G6_RO6VJq_HgqgKg}Uflz70?fY⅏ /ԯO\ tۗƮ[{rpH7 k[%u3f 0=aq;^.Cc+XGZhl_zi"N,aף- 0甝WoԛvF66xYT8Fwm!_GvJ 989MY ~gO>|2k'\n9&xa|# 0d,Dewə/VtNtA)&I,!zMAQʜE8j$Ԉo!BѺni"6{~GBf쇨Bb0ey(3٫ۢQf"go"*g1;<s{oS3;0-Nqv3 l#ʵtm;/_{lO }I6/,򥧩{tWCw_}2^K}e$"6YC<{a  9l7h~f=SIb@2|IMRf25~hCvr|֪P^nB:뮳 '}=0\}.o0q&@jƒiB.2`_Z  aXzPد I`Q{!H04:dL480dySؤЛ:^$C/`%iY`.Ep?؂qQג?B{sQ!4*ещ3Q6s{8nbd=.rA5ܥ"U%6󁛾懝$|=|`: T ;t967nBu)>E r˒^ wtHa#T+6}mI$-`DZ*p u/CqF3MM:1R# @w.^My_SJEJE[ i(B)Qc(gr ycF C`ȼ3hujB.JBKdg9묳ksNw^޿%$;<(wTp5ÇKN6BJ]nW]%:v'̋6o9/N8VcurAjoN",a6il2SS챪ㅯl( G@*PU$/l% olM¥1|8LD[J8NBO FЋ}?\Я(96BUuH.aU^ ٍHy;sygކ{=`$*DT|5{Ifa+Ӯ}6T`,_1 l3F4V0pP 8J>8{`jǎi'.J\[n8?i,wP-{lO}0`*7F҄oUѲk4qz5]iRnO'n a /"1CI>{R's_/-WKH`L?AyQ@){]c̆L %_8IKh Q 򴧗Nm5 @UCA}q$i_lw9`L86nejȈcPi-5xpe*c+{Ϋ1X_ 'vtlo߸6.c43g۰a"}eD*_+jpL:~H 6§\"ce;Wϝ? 6cqbʇǫWDߢQ{lP2p'֦ϿL~kv34vSgADog6Ss^8zVZB(:́D%Or׋/2tB0fvyQǫTE=ENO~"d]w0X3*E֞=%XnE{^0Z_/`AXE g 0[ v !#:CE1+ƮJ๟!cI\B!md u0¹Pf2r2Gzʄa`H=Sví}a"JNL!m5' q=~8pSm!Cuhڇ:6%}Es(`)i .A'#' ?t<2ǥ9DR!=cA ަl<Ch+f(؍k;.W& |yxiR(s6v]/ +yj^H9C9u|"0@\T̈́P*a" YC4BI`DQkV} ? Ãۤ SZb _Ҟ%3a^rHs T95`FUn 4+CXgIcXD!T a6cصk*NCcA^L 4@ iޒüN FubclF)Y]}jX1Z=A:$L\t(L9r |'1-92%$0@9r 1c @9r 1c @9r 1c @9r 1c @9r f $':N?ڵk{!CPG'v}mٲm۾‘T+0ׯx]9? ; ܹ3p ^z_Y{f |_q5- IsJxd=;9k?#V`ՄܯCp8"m~hqCG/r6O8lG+5g]ROmqz_ZOb9_6uMOROk}Q~LPmåk{ϫHFGߥ<`x^!L襯IR!j6._k׮^ }φ >o~󛯿>8S׿}0?ӎblС%\?ᩧR[|j×tIGyo[6On?lߗ9{0]/ر#'du_) ŗ0iρN쿊$]tWb\K,IܔU?z؈R:*s[[f1|Hw8i?$ڦhտ8lQ<o]Un0$(;mKͬU;CvdD~з0h^9g`_޺ukϞ=Xsg}`~?X#DЮ];ʗ/_^m6Qq|qߗs,*q6lHƍw+$5A^̃f͚ޑ#(۔c~!yohby虵:39iaQjg?ٳ>k^UvN"_ R+\RŜ*v}[vxb],jOI*xYɳe_Nr7oPWWԯ+^@TA_M?^b9+6o#_(Ke2TR쿿qkY3BmP $JqMd^f_[=Eݺ|̕Zxo-)+jix)׏ ?6)'Š lVʙࡾy"( qL@}A~qOTZq7qkCoⶉSd0RsQ z'2 z@;/۷/77 z堃jWHc={~C9ĉg ހ @n{~G۲eKau(fc„ H&_Կ=X--C,7nܸ@31pQGwuv aZٱI͛~cpΆБ۷I>~ n z9ٺBrpW"N"G>"dO~{(Qoql /Pr/<}13%$1BáFOr<ǗXF3g f%ed ?6Sc!ueW|JKU*K qJz=Fqd0ReKq3Gq WkQVyox0i$PKo֚5kT#F@q .>dЦe^zݻw9D^cW_}OVG}4<ȲeЌ!$xcrD8"Ų:P 8p@.3j(Е 4آ@[h|.~ܹ6AsLn`pmWY4$%3F0мysv΄:dh*!AI`@5ԨӝnٷUը *{M_ .Oz1動$\~=gѨQ} Pʁqgĵл+V BZX5!JJn{ P+ʞ)_|p&`sIVN bZ~0!F-Vb}Dr━ D=)L]!hհK7Z*,ёmSs" CHuqq [xW5s, nV@m3 !8%L Kl^K9?eka1[K~v)mn+ɏ*S~FEA*aiaPuC*Mu^h$?)_&A5K/T'o-|):6sV%.'<-w2[J-\'M :@YGzk>S!dP%0PmKF0}gBW.}Sr8\ d/CIc6| zb[鍍Xz 7Dm ]6 lF_8$ˡQeBJPcˋR1Rv?v(ϼTs K=3utW> 'zI,EZ-ĉ(b'fmo |0yy`H>d w6 `ΝwiP ej( KX 0OT ͻcছnBӫ\P2>ߴWx5U3 PS1P 2aۯ!)5;sHr T?P3DSɅU!uC 3Uf2UyPA묳R_;#!mM,)qs 1 Pfa pj1X:U>j9vZ FT8GJbyIMۧ]xMDW[ Gy$<lߗ9{0(lF㢋. ~A9+d^v3W@-JC"D1/Dz#J˳tWPN:<$ǟy R2;B٘iSjoj,tXRT 0JSvhrNYu=U9Vʞc?FɁ/& i+Ov+O ːdds?|߸ h)ӢE & |<]W@TsX 0<2RHRćܵzp\f$[Z Lt:Նu^xU`h3=HO*Xkk% 2iIKEXlx!+ߦMHjK/A;$|>ϖq$߈xZ)0l&kN°:?=QCX#'A'ֹ͛L+r#+iܸi{Z9Ӆ-@L:~~"&;޳p ȩINy*г(+]];1HXY ,YeKnԭ[ x|) w.f#"+^n׷o_g4äٳ'i ElN|F T2zJݦ[)X@R0::uңQBDƍ : H^1@prN`4L+;6ɼyo̟??ىҹ}0ɺǏxA/G9[^6n2|fʕ+QhކFJޔkBZQZjtC*.O9 Lhqz<0oĹE5xG}jL=n駟>M/R@汜mc4xشMr.h˾ZGrI,/mL (Y#n3JagA:{lEmcWa'bV{^6[) ?\քP5ܸQ'!۷\~Ǘ0&ƕ]"eGD!3g2rNq XVmATӦpȄÆNǻBɇ'ɲqbK>{]]ʞcg)͖Wك *-?|WTaCyi-F&HoRl(syf͖.] 5cɘ8Fq!\HGk#bc'x&Mj筷ZSn8b;lkfhSX{`W_ 8d<ٲehPpI-;Puk Y9rHdҏ>c$Ѕž 6 êU?y>/+Vm+F`<֮]{UWIb t=G880<7x#'Vd8[!unQY [0t?z*Z8fp)8l QitAmawh,&<%xd&=Y6fȃDѵ~ ſI Vb\oMV *!Sܽ"FXAP'2K/ Y_$ v&YKI>\=OˏJvQbHѻPG`k!2 ꤓ)Sڵ;lr'>q0n)o7Q0o?Ga*Z NOc^qBp*! p%{d B !A/pjqFܢx:,S}°AN~`Tn-g6 @|r8 T]Y#($([m~cA2lPL{0PreRmϝ;-&70KRBD4$jy7͛7WiCF2dŋd᪄3 Xn} LM I ]H9.Cf6v<7b2g>~ܧxR2n~z|$F1 UJioQ4о{7i ǩ:opJakذ.`\ل̋dtaϷl(o͟ـK~e%VcM$<(CyErv>C56g8DA.>xɿ)f$,[&Vy̆)q`4j${-T V!_5J^&~lA0IHm dUT[lpb"k/)1@E.iAWڜNo =bacU9^ݚ%s ۸xÖ|*ȲgeJ|}T I8v+1T-^L>uuMQYau΀((&M8P*0Ɵz"KmV<3#mV1kn#.701 '0Vj aleݴibPa/1ĉeb#Äd} ϗRyc6 $OtLDn#-m^Cp B7`Ǣ'Ja+ҧm#MH6%D){!gl ۙ={v^^s'+M?;8]f82I<5#. ^ Pua1m†09tn1ղo_d KbNqv0r.YCrzV[D8@ṁޢivj7h 47mզMbd/BƦ .^@(mL2&ŞR#jqUbvnZt'>bY TQ*zC!֕W1c5Dͨ0vU4Ruk 6H'5ͣ0v>gG`}'Od,wVSʃ(:Aj0x\V)\B{ ϠtJ鄼Xd9*rR؆^qAMl؋0d gtϗRU 0hݺ5h7.8mC| "`Sb欦 p0PD Kb(~nqnGOܔ\0fpO34)h+YfጁluhѢQÙR S!"Lz+V6U 5!+TWE ,¸܀F)|="O9a6 ӳ=/Oe+$xPP, 쇧~U*0P (hHWcfN'@ܢm۶0GL NqO ;vYL8W.3JL l+]#^xyFnqZM|Nl.ɼv8@IWbҸ+Q`>[1@j eY9:(k5\}Ww?a1S#T8FY= .%ӧ0_*ŢEe&@QN:v1CZ(QCϺʃ(+wTn@,Z04EB0;GaȊ53&p0PYpT* 0A8dRit6CZy@(G%3#4ncӏ)i!6W" =Jl DY%?#Ǥ[&F,'+TTJ-\'M :@YGzk>S!dP%3:3phTO\dd*e63 "RQE,<!3e{yI'Qe`߿QG%,P" Je؈ң)ezdETULbf\LNft[d%$0ز+n'v^)@bছ${ܖPVubW/6Vݚ-s |1瀒! (Z̮}f DĀ>Ii85{CO:KED~0nT[`KOa20R.U9XW1#: Y60t+.w!/,-)B1E#2?W#쮫k9r TNqpVU1c @9r 1c @9r 1c @9r 1c @9r 1c @k!E$wl Q1'@F%@%VC0:޷Sp^yWh tbҿEf^ИQ;|K!9UZve꣩zfJBBlߋ׏Ҏjn𔪰i[*)6byIՕXէr W@;eRd)R~}zApYǜKۡ9QSv-':mVf;{*2otP&UQT}"1[#RNzG3zQ[HlBMFRjBre2"j~e}O;TlS[}0@*n@bj-B#xGh~w80|{$A Iw>ԅ 5\Y/YԸfO YRnݡC^ZePOBWh< (mӦM8W荌W6̗|K.:-ے` s|ZqOeݯkˤN C$gѳAdCFRH"N6*M3C(IٳgO0jժ_|QGvLD_JV7!N$5u^@X-^[N£XCZ+E[hyfKgm7e'~"]tq^06lصkWVSeM5wg^q+F(%?l>OOiJW\ $iLk@?*}\8=yveu\JT%"k'3crPQ`^+kG9Ήa:C3 D@q`HLc>@©uSԭ+B\وB?MnQ.ܸq7*'=N#k"o/F8 }CLV53Ծ}{n =Ǐȶ9Ep;؀% > 殻ۢ+c̟??_lDF-[?#vn6jxblX:u׏?aU(ѣGÁ'9s_JV Ak3t9uq{fwy֫e˖~0J)EDrpp^\帱<&n,Wzg;O ʞxګ Y,2zdQ~_%N 7[dQ Fƞ{ dW{=2Wܼbiq#(_ywZ/0}Ѫ9?USc=~CH=zlR裏ԥK#nv)҄mb6e.k~gx'1VTnZOl31xd(x1="M8yǍVÑW!]iU"1Lj3ϔl~#%ZY53fgWT66re5GKҟpLQ0Hx<`4m* =76Lo7.z:uD~)I^م#%`^zSiҭ[7tɮy՟$\Z^SN张CE>N9ŏ+u揾}% \q/㠍ۢnԉl6j̘1W]u"!f͚ժU+Cg+U 64?]v$ 93nV{\#G${{laHСi1G\zN&O<"녅 ?Vr"@6op6rSUcy loG0nfvzWKfZn(ɓnO$Ry)H_9m4p|##R7ʳ{=B0oO3%KtW֋ĽaOJ7|32r-0f?ki"|T4iҹ? -))IۡS1hi=f=StS[{cݶ[J,S\_-=ZY'VVQ#Eh6,|}I*$2;Y=p~2E@~Nx]33*:w.Fw) bcD1c%zf dB*A*KߐD4(9Wf IP'ъ4Ր!C/^ bN 9o<˖- NU!p;wE] }mFD&h6Cgm0ÅBw @n Roĉxy U"UfQP7eM+ڴih"yv9J8=.$xze T06Tl6%MF9$H|O')N,)S̙cp;`CU9{btwɜ3ؒOUlQoQ7PRB{y8 Zܼݕv$!{#nP0NCZA, ]AoFjf#ش8LۡI=uhTkۡw7VۥJAqXTapp)o`TYM]JD ZZw >8ITp]8RK.l+)Mɓ3caBY)cuG 0Jrӄ+Tp.NUU3 o[U$>@ }WـWQ( T@˾΀ԋ@ߢTF"gِA0_ W@h Ŕ~hYHTEj7GG3fpC+!ŢEfIV1$:';.tS.X! &1a_iRXml𪢄ubgDJ޼n:s 缲Ϣ<`sL*fG*D2uNXy2gye+Cl' ψ݅q$?Ln 9F$T`)(ĞK9Rx\tm&8T{=4-mZbǕjq^7bbp;exPwqxk˯ⷥqWFGv@ߋ&XU*  @ࢭ\+) Kz1v1Cm.{]H$]xyyR1x<~wl@1'o6r0X:E 3zɔ9p5e  3NsDLY E]=}#gEOlHړ @^1LwC1UgӖ$[Bf#,\Y)" :"GF˧QWؘ 1M O&֊ιaS9LMN3l]z^4fxP(*FS cmUN6~PK?׍Ң.3: 'Ms!ɼl:QঈMiٽW!Sp։W9*|h6`O<񄧹C."r g/nZDwPuTo-|):6sV%cU7-wޅXJǎ+p{71g2rL]"&e@UU +91gC8T@2u8lQ- t #FHl$PTƪ*E,Z/JB,XP$pw!-bY1c@Nhg5(] f‰`8=S +.Jx'ybছnKW|C\wd 96H{oCwc խH(c[8n1T@bvEV~DÊmɪ'v8 uYjkab仌36.%9j@üS(v(9[&GI@*OCo5?&9- !J:*ؑ L5bx80{0k:wf?mCiUœ~6J*-A0D*_~Y@H:<":GeO:-=\Y\vJ! 'ŜlewfeH)Tmӵ]2WY5چ'VdݯkcQYR IRl R8hРiIIFw\סs]ه͚5#à+0޺"EXUV"6ov|m.]!yjq$4iذ!d(9I͟!п CqS9âyJңGTC$չq5,"wE?D& rrhX79vU ({xb9lߵ'N$jnQnӧwg*gnޠ賯_W&{2+ZΙH!sTﵗhNZrd8XU>m T͏W^`9Deqɢ@TȤl6 !Vx hIEKuo%{9E BvpznFdrsJYX He(2ܽR{jwbҨ)-tF\zeAϞ=˶M"j,fAQ662;bb6"O~C6(Q7.怃ㆳ?t 9+효>0h!8i8H Fxjfީ}܂z,DǏ`RQlQcF6+WxG]+*5C̜9$}o1@pȔY߹a͛?~$oOgyZ񊔂To6DmT;︿ϸ}OC[A/G43q&ΫN:2Oq0q`d>gPN-=\< |z{ÏyZ|gr^ypvơ3V1Z/yQh_~-# $?ЊM|&uzְsfP0/=؇l`L :uhn~Jr ?KD?Ç㣔Vl)sT3WO&_*jqJzN9EWF̓ѣ%a̓U̙@ v^'֮>(`@_M?ʩJaմ(zR5l\8ui3fWJATAp,0p6F GHwރ^c6<oMz:)=8,X lʼ(<7̽r-hZnT2D Fh]O0^zÄO3dX4MLyJ8qs<Mzl:Q-ţk=+/_ ,MLZ(ʹzzo>,kJ}ȃwH5d wYjqXp@l>)'*$% o3r[,ܹbd6 $V|<­wT@X@a~(=g)S$cbJHA==7&F`5ó`A͚5ϙ:b{A(&L A vPn 3!|cˆqc͟_߯PAF3iզ~hm6=H{+q&`6C[ցs^vdMZ۷mzH:]\uD/WyIMS'['60 E@[dĈxq| ӹ瞋z̲`&^c_J$jǡͩvܹsPC ">*eMH|[ሜCG A#<Wu`<.d6̅ 'g6B" *1r!Yhٴvqu͐fYp!s2XV@XD_pEӈb >.` _6pB8l#z`ކ Wl,$Uw%aX)ʳ^,}qH'p.+DɆKEU_N*B- % UyG 9Ē` 떉Q;wE(q{& #C^XF5KĐv |Xun |ΡgBQ-)ikZeY8 0mʹB1jT!kDHO hAGK r:U ͕@Z (*%(g[+*8Br8%>8キcзȔC) T8QLs P (b+gE터 a[~0;#Q\:7@Vx4)y4$Kya1 `D5j|Q;ȋ(.F? s iʁ}2cǃ2)y7afϖ狕9Hf*[u&=-s 1c @9r 1c @9r 1c @9r 1c @9r 1c wkp8G)mpB;S zKF%fto u,T6p5aWL[{wxIm=P̶ۍiaw;I5:ۢ[MߪTokvt{Q29o ^Ld$8 }h{^1\(L㚜EWxZrڦkER­~YwI ITW)i"08\iNB?";E5ѹŠ$$Z1r_LEټpN`]d4*F'(a-HG3We]9Yi *'$U= g6ϸ2̨䫐whbK&S,mQXDyJyLo=P̶ۍiav8K/ᐰU&XcV]G%Ô#{^yZLIo`sU1nmQl,CE?޳JTH@젋.*sUHz~ "l0W8,,EǎeyJ^`#j1fZ7w*9e7Ŗ$aÎ "i~C^E#{ updA353ǐ!:iΜ CKHF GU2"#9+q9I4$3#敝YܥK*۫3M6:f^di<[}~5\{d&q#ۿ<ۆqɣקO/ӳEӹC&9+oIRzH% 6ڵkƍII{׭tA=R\"^EKRMǢ! UɅ 9C8=lCK{IWO&G@Ta]yI?:lذ'|R*#}W1J쵻48'N$j;nӧwg_U;y񗓢 >ue*"5xt)Iw]jB]RHO>Y~ŲdJ8DZr.)HP.XE?᩿a{ NT_/]ZIRK/g+2ˆ$Vufd"H!P_"}z.`!ыQԹ KaOQL-NⰡخ\bV^Uqo!Z7',ƆBF۴I.*z5n,cAm4/T800Yk \) PP ؉ zaܔ+!}hB˶t6v.f#"37Huk4ƍ 4j f"N8aڵ6` ! cV=zYΈ3g1 Ƅ :uC%t9yǏ;3bђ Iv!rQq[yߘ?~$owȈf!)-[@ᑽnӕ=[4r'T˖-I{ [_Qs?6|zSz6@#;؀[St|Xuׯg">"99{7nrGB{Aʹ~9 |#riBo6ix2(ע>+Nbc ܴMhgbXGqPj ;ŵ]cђ+ 1s Eֲh/,aB^elB8ZLe]6?ZopIz#ر iR-3]w-v)bܸ B|^ө04&~DA1a0iSQ8{9|%#BlhW'F Y?+J_Nl #(ME5;8"Zp&Ciq<`05Ar>=kzi<`] 4<>̆y>;?aJ~!Ura_ѽ{&-Z~t.W$S…8 1ujr{)Sڵ*쳥TKj\1yM&n|tĵ]CЦȰC5k$leT>JV~#AKEB._qcBfv%(#QαGDwV-b!DdZ@ReĆFS-^~U\"G&y(m$/zQbHѻPMy,1 @ j57/ f!__F:Tl(70T Y`s%ه)-Ƶ\^Pr}C۶mS0vC@Ca\|y(a31 >$` r6prSk˘Auz硊l*ze]Pz Eǟ{@ ZI@r;P5A~H >rvzgŅ o<•}_oC a}ݗh 5j$,ī'3c#ܡsA='D{[nM5%GXk֬AkaPkt}shy\d46i҄F8蠃tX,6=܃9AVF bZ~3',Bw8أ o,Oe7$'# ^c}T*uas1n۶ҞǴیk{g@iЊxzZPJXD\a?D`F˖+HDsdZYג ?_r<>+M]AQ !yY`Pz ~ r6`,Jaf0:̀CPD1j(ؒy6|%b>6e> B7UKv(\ ]-n/lC$gxVP^{qkjJO9DUn0^ ErN!/ &hߢDN $f&d6[q2:,8/k?f"qʂ7M ? ;qĸOX̋F-Cmڴs 1{l$ zNKH-p}=mX?H0BE;h_*c' U1stk.Fw̉~B(ؒO'^lQq 8Vj[ɸŧ +yZJWģi{jኀ('4HcI +'sBx Z}+ $BMLD >oqf"5T1pL]-/6 `}Gp-jVmGLl aor oPdޢ ߰*Ӡgnvؕ}ݺuq+g#;L^RZ G $~_~ʹa!Xm۶qeqWGr5bLO1Јxi$k-=lj(KR[-&>BlX n'lWn(IJ,~[w>5NU͜9_R̐oe@4 xZR L:r?@CU G6Daݹ@T, l'P{xCfP8ΙD\@'H^ӗ9ehST9 :yg- PQP䧞jK)r8c K{V r=9)]&L)xuCc?|)=tѱܥ=<eyR;$bijPīu~yW=?`X \6{W?CXU?@YFb`F V@}dSp䂰׷fVghx 9 kAM_̮H e%!{,]*ۦ|dxZRPcܵ~,]|@=_q̾ǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀǀ@`I? ajHs[oW]=gA[2Զlْp.U0ofٲL0lǃ'3^fY 5YҾ&~kbd$ TGxGy$}<yk |犝(/ v͛>t 6ltr*J⇴@˓|P&Y|{ %w*9 -(}[[峌 r|}?n\}"_&Rg4LBaHP"#8 nT}E粰.9P_Kj2 VSw)`- #pliPuQGl_Ԓ+ إ?v.a.!;Di˺{k`|$D%#<'|2 -qّ)>H=gE2vq'm0CBGR.[,歹JJ$3x0{>`/~&`T|\meS&}Mvr܄{WЬ 'Y'Fgn?^ھ9zE3 ( LPG *>X*m*sYc[B lD ';MYBA):gw,% nn׮e]FU4Ujݺ5<믿m۶*?THHԖhTd48U:2 'uFYf$QSa<$1<aX8ӌ7xBC9^z >s?Ix^{u]Gz-Bٗ d۴ii\&TS(hqa̤cg}嘷j ի-Qw.͌鎃m,hȏa҂UʩTJKAڶmKz؋K9DIs& t0RK !7nhf~˗/鷓W6@l,h֭[LJ'.W{YMis\]Y7T0:bI O(!zS>\6 M(G-3 kUexwQZ},~5q&_{TB$=u5?~}Z|ҼəBmVO4{@KڤQneDڽz6sK_(-H>$(HM@UқlA+!_ɷ@3Eܨ׳Ok$$?{Bg17m2e+Q̢WfտhO HRbRNb˫Wiʺ\`/Dm.3d*yŚzJ^~E'E}={XHR+Rظ/'taPCO0! "y(< >H! ` FӦM#ȉ/ O)٠xldXc8|0o6;:dmi={ve@#:0C^:6㗔Vj*0K0|ڨ\=!_PQ=#7.\m0~׮] \3$'N2ơqKKyu`jQp@Xـ=z4W3 =zVQt**7T2(xDĥUȌ>aBՖRrGg6oo2R}Mھ/Ա%*Ť&9ثn,q0B1B&N 5Ձ_IRݵiWqr%R A0d}mZ󢕹%\ЎEHc{E V-[ P я~?wqH[>j(|9Ȳ ۰|8ny ?_ wxr(!`-ZdMԩS׮] &+5l(f[bI .+WvUh_~y+E-? j 5d;b T={ \!h;BnB3y єKGiQZ T椐5 gΜ G uwpSҩ\e.Q8p_a\n]!RRp h{%])V)J05k U>}m, P=}˭{5AqΜ9j+`Į(X{ل7|3'A_G· TguѮ,Z/H׶H}SڷL}e{˯gy:N~w&220^Mmġm2y1N&3+eA%>7WSn5+̙:$W>:U֮s1lGl"ȧwb2|#AXK2L"Gif"y ܁eO>7fu)RЫ mM.ʫP``li7d:wjz(s%ʎJF_rR%%@咕 A-]V=ÆsA1w*i B2mEyN̗ 7AP }j+(\9s%pQ#4~($}F^R+s)4Bkr} ΢"e.Ne@+J6JYWꀱpr6r?C^J$y$MtdX!TU]Q0ẕĂM6`^~ 䲽ՃA;8[}СK. 7},Nu߱E Wh]lI}d{ݤySٜ9'{|E({d Yp<;vtgp.ѩ% 6COzjl(h"0Z~=66pO 3Md!V+dvqHLh[ۗeE+xh*Xwpievt}Z?!"Kחs' #.CuACǣ/O)n+pئC]a]8 7;᜗q`LH*B&Jt|0F(h9nbY/ 0CdYvwVd0G]&^riӦ@ y'$H7n(TXk(hth;>U$^w$PE' FXs>n8BMjɱJd'믿C2((qbFeW2Wx+**e]JbU P=WSl0R{a!خO:w̏Q0u%9+{o}"9Pf;#_?r,*^Jβ`\t<򷬚 <|q~DJ9|E.a* kߕc:M˧ҿ㮔Q0Pv8} 6Ńu8SOeu, K ~Ge(c.jIy4I'߸#N ](vs.ɾm`Ұa#8o ';Gx\B]}3P$x=Gon X:A^,`bFyr&ĨhPgϚ5+6e|K{vlP!~"BtPNA)Hb<ZQ&!F1[E smk[62`3kjDTa <@S6mEҶ=~G1+^O_8XW1f7M kPx71,9V { $ !q`{SX!b&u.Q> U$n|[2`Ңre]zI+@e]}eo_z%eEcŋ4uQ?EԼƿ>ϙ9"?mdK,KcFq=ܘ{mwTR3o4ԒWe=d!wSvp02ҙ 'c(ET__Ɩ |J4a92zfCuz#nK:U"A8.i5t-& v\΂Af*Cf9ۍ$]Wt9bL^(g] }X`WCdhvn*,9TߠP ‡@z肙% a.?E0d D WiMlh 5M]! k^s=b 1:"<* JCYTMGb[&"AuYa2"(u+2cp2d9o^8xk֯I2$.'P:zߠUn|X}q}g7PA_"؀*? {rvG<{a6i;c2G1ФMb7՝/5ND"- ZlI T"@d* )#Z"SYDu$} "NG#$tH<oNđGwxv9W|犝(/ v[/%i\;|6H#؀z%P/"&kG(k0|}?nV|5c7G/.u1$ Dk<%D%jTU~ xY|NeJUY,3hZ @U]-4d4*ƥJ8a-cGlJÓt0Q@luEۓ+,ᆢ}/!cϞ=PY Vo#HD=ak?+ Ɲ(BXwqCEx2vq'00{'ʵ𐊨KK1vbb*.=_b.Rn!G/xlc> Q΁u 5kbR}ITwWn5eMBGJl߬6Ƌ/ B`/:QhABTUt;W,% ,YPef@zaKýd6HFL]&Cs% $/% 7%㒝WJ+g?ۼysHZ+ 7n$IV}A}yB4oޜ|,ŖL.g8o}ѢE(Xk6:dm#նmۂc.ŀB34k /$cq ^47kH>M缨"F4|29%I F2ClcR Β7Ѽ2fҀP$0f mQիWN-#4()D!Kߞꠃe!)GЮ];`eET6('{4֭[dQCou08h__e'2O0%+*|1ZCK=zd\%_O>igW>/.ûKҪmֳU;a|AN!bI~l@|qU>-#E-Qy34$&R7mZS ĿP8}d%ML ɥmПKZ6yy^zIHvBcp#!M&,R{VV6)-_-AS%9#k'd7Gώa]ɺOtцO0!j=Zƒ3jTƇ_WӠ}P>f)5}~!K.e|d%Fvaک-aȿ ?8tqAcD xnp[Ғmy NPHp+=ܓO?4 CUP*9 .tY狡k׮Ц;'NdcGV}7w\~pC&>ҋ%3tzC9!kNѣ UwzAu, ^' TlFk9+%q$xxbMZ_&L1J*PNތw`jX$ *Y\9<ޅijKdtr h\`?7UFu5.f#-[@!''A a IDATBxMr> :b kVxd}`tM|GǍe9%eTߡ˱~嗷$"xAdF ֻbŊÇ#/ϋmf;BaC3 a!->R^-:)SvtL:dp (|ArIg}ڵ\s qs̹Տ %̙3jy{rR/Z.ɩS:ֈeѪUPڰ2Rzѱ6%+;zo+W[nEWG?z43`DN8Z>{˯gydDMʘä_{Y6 f?%Lo;O׶TA%>7WSUm5Дg%|.484;u]+q#Vh>;sWeKEʘ1ү]z͟/Sդtpj_)}uaB!E~#>:[ /7ΐ% TLFOmrFN&At$ ````-Ki;%KU{FUv3+{3 Z ~R;8SP5mڴA;uu!nذ܏:J$' k!胲Kˎ QҀ:=#` {0LGb&͕Nǎ| Vmy Po#ӥ/9#{#{ݢvCN"hPdsˮ5"gu\:Vv3cر0V2:nӘ٢e.̷\ol^2E6TGw6mwǏ;̮+6>oc|8 Ȇ䋻I95ӾW莲gI2euyfz$s2eKC[)@1>4(wTtL%|C>XƎEmHpSqT[ ̒4`㠼WQ4 4,~䀃|ވ2aڑ4nuք)hɐ(I)3a6=C7`Fs=5Db <Q(Tf9DFy ݛEqE7tP†B. 1p,KǎbS=?c H,CI&l7aMYBESڌcؒ-lpgW4h"~GugSTSp0' 8$-\'U϶zB\w7 (g9όoԃs*B:3iqП޽5Wҫe "M- ^6*%*ڠuV86'#k ދ5#;fǼ|pЀ[V^C D!ŬA۷׀ Q,˼c6My((T#WTW?5;TRιs"FW&A@^(!4LA?W,:RP@ )0(BU:8Ⱦ;0;i0l0 |( aAKķ~ o27.>[6Hڢppqb£H 0U9{,S= [ -X&0Afy^,hXEXQ8.]9$.1l!&j4~^D(bC12{+Wc{͛7o4\`t 詧rcI+*ˎ F꒓ <|4tP;wGԏ)I!Nu`Vwk#䑿ex-۝7J4o_zI]ڂ1%ɧҿ.;N!LbY阍yK2AVSUvʠP3k$!` ؊Tku㎃ :%'aP+,ZY2jn;jB -,Ā$jwWqlDp%Jm=1#`{vyF4J*',ǑW\O`]1 Oj|ɒ%X=!rC~W\,|¯jXR$ru0A@ x@M`Ń? XP6mEҶ5IH1tpI`"Ol]#!)PvEDz_]wU)ݬge&_{nT~&>?.~\ǯO h&GuLzj"5x%: YJ^|%$s yռIF/5$kM+Tmk2EtJ@͖m Cd۴ɤ'MO$#dY^zI'; QI`rF#UUI9 ]uҰYWMw}w&LH5B=a\$-FT&Zn&Zt%d'7%|-wi,I2.YW5,H{뮻B`(MBw .,3/ NU᧟K/=#zSzxN~(tmĪ5A*4$vAL#HEzlETt|դv&B,VƁĿBý?emUlkW5#Fĉ 'цvd+B"gŤ/~=0O09F=װT* /9 kF4Nz)Y .@T7X- OK})Uك%I͉'d&i U6y_eTJ9)IK apdPlʖ-[ Ԑ!DYѧO. o@{0ҥKWZym4:vŋQL<m kfrȑwqǒ%Kd_% 嗷 \*d0Q>́eOC\3g 0ia3_Pe_Mrȡԩv@sQ@U_z8Ӄ.D}5v/Iؘ?_L#Ui: WiRt&E ƍQI3^.l(ߺt1YL-*Se!I0vـ찔e.HXT/vJ!Mz9k`ڃA!9reagƐ>@WvɱSz`7pp}h5`Hߟ̋`ؓ|69sٲlv 9&$v''dx8>X`,{9< PIa̫L ǩ:t(oCݻwG۠wU6la`ر`]JPBS+K, Im T1>Z V锒Ije}ݷM6pkpI48:2wP)hJ3'6sp Ȇ䋻I󦲹D1 N7PVYܷR慛F2/yfըG{=톑@ j%4sD-9s a&{ƐzlNb,qB<وB[ jP6l0ٔǎ5?[FaD=\@4 6=9$>,f͌V4~:Y-Ԅ'YСAw72Bep(6$ljvXJy_J^treV+R?4aRm+ߑ qo[Q2|a \>14gAO27 *͛g l{-TpiS$S(eZR`K$\/PYlI0Dδ|#3b*K\VZA_vѕguȂ.f袋aXÈKthɌ3P4Adc u;iҤ . ah'F착FJCJ;6J+`DOZKccesv* 4ڕФ8(>l*`GoR<&K2aNܓ[A@xUJ$i(ĮA %l@C?TPmܘ6nB=Yh'4޽R>4όF&ӲIS>ÆkJ3@Iv[ĥKkR0naWd*50uuQ/* z*vTW졹tj @4T\y1|,'蘶x]f(b6,¥b fD>ʧnܹYXT|ƌKڇ#('/g>&@?A(gcE9A?#8{gġB4h`c˰{\#4Na2IF(`[ (ZbAZor~ܸqc*ʒz)PXz0,]j:fTѓzg"BYxθWv Ȱa,JQ :YkPaEw a# X)p%mv{'ٌ#l_ۍJ!4xct(m"<;0FS:?KcF6r+0;V^r/4 Bt c<qFBT&ZN5ap`2 챓)m?4$|P3C,w90 >X1ž@7aWb8f| IDATS\ǧjL@WQu*+u"2`{^^y_JKUURXULƀ`$:'KĹ.X߿?{|,$kBijw$P Xedki00_;v|㧯.>l|L\jqѫ , EG#˪NEaJq~[>Xx_N"n?׾+t~&Knj{(oMl1r.;(7 GЅ q7TaXO6JPЪ .44ݪE7< d(b:,93=xp-)j,yD  . &7o\r&&1xtT0xӦBxS`|+F2cQ+}i/%¤1YK^y_JU.˳/>W\!H+E0syc) W-$kBn?g#]7G/d?{IBl>(d\SGKmP ,3ixt5 QDC2ѷ(;MbDri):j䓊uPHȑv]ve)SN5klp&62rh+<(r(mӦMqmƍzɧ|EK*6̅^l2iؑ}{k:Tsj I(`&S,-Z9o޼9B8٨tc& ;-2K( x ThH'r$9ݫW/T d^#}x.jBrĂ+vfc@r@"#')7͛JF좂6-ퟅ1Ti{9Mӷo_T4u0r)ط|z{gn/.ûKҪnֳE7Cv+A$"u5?~}Z|ҼcBmVO4z@᧜"wӌG%͛|~_$Qmemi~G~O?_6\{F~{ 0PN.\3UkDSkPG$yTKI 7BW(=Y MR kE֎Һت(Pal}. "<5Լ}^ I8= {' q0B׮j!'VfLƨ&8M ›$:tҺf}&_,?!#t4ۉd&_,Q㴼>I$R(WxiWh!;N.le.v>ZCQ_[lPC䉣ʊ>}%>&0cƌm IO}G%(O˗7N [-}b ={ ~-Plʀ0.F hѢ^{vA%7ͅ2U%9;!:I ̙3 G)$S{" ԩSё"C*fժUH- PAr(`(R2Y5\MfΜ9\l e]72f+P&U;o^r%[[nꦛnrCvob609m+C4¬mӭ쳇Lj|6gOV-iUsk/ ֦ iġm2y1N&E-UP_)ԪQ>jL^%}Bs"D7N @*b˸IZ0vQEl$oaGb6?\gYkq7o<gU{"X$U 6СC|F8nl>۷o_>IMAt< my1JNJW%"5,ϟQ>i@رcYG,0(Ĝ3;r:$IʕK͆**;EQ^x(c e.6T p}mӦ  DcI&R7CMY^eADv ğщ*uEG% ʺ*ec(yY5c.$)/(gǼ e|[ fݻwi2{./$Su!O3cG׊68_|VSYj0'dWÖ|qvP+53 3zKf/ 6*iH|sg#fI W+E/AQScZ8I޻7 P_Ʋ(VBW oa}=%d)I'm0f ܞi+U?|E=䓄[q6$C;wF2իF1?|$OX`8Y$#ر8}rJ]M9.eC0!0lr`+/eDs΍YN ͭJ9np /*3K(SLeD0u)UfC1c*+ɮKCAE Pv`D_ x[:3Z!CGlKaÆ[>k+"Y7ÆsJ*~۴K.6jOG^,$ȍCRcU}YVl4M.xYo!ݺUG|]-bEt! dގ)@D\hlLekd\EY\.;-ȧtcW3D}MvMؘO}"6TFaյ`> B T9WtֆHt H!÷\_*P+P0%xzb;p2Ac*>ӦMgR2W$ H͎Th}_f=):Pn@tIhA59(KBj^zїP<+؉ db#oBWR;uuajXDFnJ _l2v^ 0u)UfC^;% e{~X0b6@y *r`Aŏu}C;2cD5!rmVwk#䑿ex-۝7M4o_zI]jꥼ+tDӟ~&Kn>D.¬zT>D;<܀yI#FVҥU)| ʐ!!Q[Usxt@3/ xgfevC YRXءlA&$6>L;l =LutXr=&T9դ >.2`ІF(g}X{f6Hp#|0Fqن HƉ,-k e8,1!<\7Z>Dg6VDSF4Yf#AbeUxHT,bEkSO=[.FqGa +Aʺ*P6>rFLL׉hF Pƶ؋b^F,΃ 60?2Z2&~W_}5n<ؽm~:Pڷ*ȼdYLo\!cO7_+b4ԒWe=d!wSvR97L_&]LP`6p睵}ljQ {>o ʹ^D>]ҙ >&gis-iM؂#T(W,}Bqꩆr-]):`{CLF-JT<! ,.1 sްag 7Yh,p{8~3R M Ԑ⭷ުSN8iӦp;3kA-[@IN.fdj5uc2 y-A*YVz(6ۇ4#;R 9g8L]@ҥ;udr?qY5uTv&5o߳v/O^y啖O-(;8Rb"Ae|QwQh7CbѮT+@4CAiiZUA|*.u 4 ;.hl0,%)C,sQ2lP鈪 }8,t $w?B"TpƐ=#nzE9 BžD|MNV嚥 bYnOfn5e)}s}w& 馪Qr؄-$v}J7W[le]6$]l7di3&^EB*Su V),Z$F-G[ÇO T> ) 3! N5i_]WFٳg%+`PE~eOTNE}_[$ Il9 ;\v]veVϯ뮻i`u|d"R[;MHLƃYZhSv~Dq`̤cgPB[غuk/tRr^mے0I~$E.*;})7GlbRvReGtEL IDAT6jf˶QnQۀ"xcVn]žDv{!l0Z,Yz5)`-?ch"|{o6oкc8j**}^?DIGM0u+EG mhdI,!OːewMޖ~8 R :L䓶rrɟ{EHVQK7erD;2aa8\_]wU)߬ge4ޣ";ˇ皎>-{frTi$|6+Zkmdb9Γ2ُoEr~~j/1E yswjjL=ԅF!2϶SWMg5f#:+׭vdfӟj͘Gb6B>7+B?0a„T,4($p 8W|f* U4ѲҝPܾKHnK`@bu'͒("0BcƍC? ={v> a+ "O/X&yGo,\0;:^޵kWdlĈ'NLe,I)^^l]>NpvqPTn8ѣG5uI){ul_1-+U4~}s.* heĂߖJ0Nۀ%[Έ+$T8vSaڭ*%69 7จ%%l`P_%(t_je(1r?Bo .hAR0n `TX8EA~/FǗX%FH9Tp`|;߱N{2O? w; oNuYz!r:v(hXWys\\>2orrwNΜi>B,.'ooMd;}Tϥ W=d)h^?Lw?Sck䶀5i-l/P}c-xyBډ-}MSN1媎"3pA)O 4Ir?aB6WHE4;4Q_H Ԑ }AYѧO֔霆m<A_kCbcŋ(&O3k9Kّ#GBqK,}CB<{n+WZ^ՠ~- a =!G7;ەm]bR1\A}W1؁#1c Z`>< }dB#e^>]R-` 9s&_Pl*؀0zmݺuȀl;*~2 ʺ$,Q؀N*DH7tOVL̥ߢ 2{}2Z$ƪ,䅯{,6 ƸqPCȜ?)Sj/_Na,<(2Vy @vpArw9(P^3: G/ ع *0X7{sΘ1#_<s%r-u7z$$f΄M庠2b@=h50Ar}ڵERB׶H}jBYhh&|{.Kne=dWd<cN~jMdWeaү,X2ۈCdcշLgVJ}o*wL+_sFgc7˫} cyTZ1FңK O46ڵr59sS 506;ʃ ÀbKʇ$$d[叆f=Ўn@(xD0h*Q)pUQN_2B20кm6bm7ljaQ/Sz.,+I6j4d{aٷUG'I^6l4yر|GZՁM/+rn4K%(Urd!5.JjҺ L4ER + OKE(!,|-a>3gNV0aCfС4C_DM’-. N:1NPz*-0)O=zI q;r`fˑJ~'zk O<۴i:~8{6LPs˓tBX]"8hoU]\_mK=i`cݬ? M0'H~|q7iT6WYw}eϒLkULyP,\g8 g6[kGR:VGSk؆ 1RXpg9˖ B W-[ʆ |RZ|ΧqQe~0po>&8Q*wMX.a?}to,"簂 ˩TjɈ0ubSر?S rak * Qg ׁ,8i\c9rYlI0teT Q#@8a%(&Mъ^`ێU9^ ʺDYWͥXe6a.F(*g&Yk9Nj@tuEReWtv Ro+<@@%8Y*-)O=z(s)6uCز:뤖Q܇VW%9 H$4 !b'F?N!!ӆ\_"Iϒ~&^2A@hDG Y@%vI  (78b1U0y @gc#<Pv 0F)H`JY.JKYWzEw^e!8)JEZ sUWA~ʊJ6j|}Q<0}XĆd` q5E06'R^et(WReatRDۧ9 {`ņLe~8`!a ``d)BO/BV`p!$cm2?`YCr}pξ|q|/dy7iq40]E2,X#!-f l)d=Q?fh\߾">7ޗȻ叵%kߕc:̧ҿ㮔k(b\y tFo~}U{? eHbTwQn@tq)aPGcYA~bo z~l@̇ I03m\SURDl%.AGuea岀зvS n[T 9ʤo̘[IhTf4rs>5WiAf# a o^Y(Fv.|fp DB|Pc?U5kV4*="Qe`?džm.){odH8D'u%I7 a^`S } ;7R+ Be]J2UI*<2]\.jc a,k̕oDdEVVoUI{AF68$5" Q 6Ś?RohVnEp?hPѣ{*/%^/ &,ܐaזEhkc?qGv=J-">@Q1GR~o!~~qtwO{,Ҙo\!c7^?1]L+UwrԄ4w29 btf3?t7P xhTX2Kз!WMݒ Y[8K`r;;vEQ5? wyg(*H "^m0JA^mNXh;= Diyvz, ,C}z8xD!A%u"ԩ&|QR ЈYLp*A,"Y|r^[>> 46$P}r#lh*ZavEVVX ꦶx51sc37 ƺJw;L9r}8*2D9ݝs9N_,("*X >X:#NϮADo1P) `bme0W,6; :D"|gkmR*B^Fi$% -h+u 8ɦЋ.N=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<rbIl_o O|rJ'¶3 -y7[lI0]tŪo_$8Mfwxv˶%8WDya(Fw.`_Mi_JE%DT㐜i2Siʫ¥y?cDbPy#eD21ykrtp>EC'*o?j={uֽuuOkf[+K:2^T3gMi% OI֋s;:D,p7Ciս{w⊆W"`m޼9q{@Æ/!@lTkbnV5WiRߋΐ\JxnV3G(e01Id@"yՙsWt(8q_T 6wNu%\3Ɏ}8¬YS%Wwlnsy⧟ntbZ 2~H8jf4* V>E HTG@X:!ZaqNڳ_)\=/M=5`G~D%>yȼq9 HUR~_7rT&:6 Qd賔 wCd*Dï]E!uG "c7 gH(ɀd; "06mB̞=[,|' j +) )G84d7MzZ 9 1c&!"3$͒5g?TvP_Q)EIÀ]\>m9e^VFfhը Wu򽤏xW|-9``al#mȿ_&Ma#7\&M&&6qDӟERFDN"Rx%R o4jک~-\P2('%G[QoӎDi| $}RJ%1^ϙ3t+i\9b%Dh@U/.橍xLÇÅ!͕ywo&Ls+Sıd$u!)Z[q2QiMc,0}o!-9.,X@R0br2KaOWZ%@|9mw3I%|-EI5x[_`AC3B2RvR;o) ]joO)s O=@^r$Tk$^/G)@;r ElHw>0$<6JRleͩAңB^Ѭg% EU'Es>vd{7e<:|Cr"|E#qDR!v36aRAM~0 +WG&W̓psv 4$CyѮYT_ާIӯY27‚߀|h`8Ј4|b8#?HFsxM:Qm HR2?9ڔ7uK)? l^cD3|c@;ry aT&E>88xPN# g;&@s0jkVn 7Z" ^4@V:tdUqƹ Z>HαCE[!!| s`顼Q e q5!? uגI~mzdƦ1Ɩ"V}Lx[ !8'+[nXqW6P0SY[6s  @8XPȺ(+됡m1B1aW\B/&{WkRJu(}][/K*e(y`YP4a+0!! !􆯌 ~N's9cqbGV}qUțT*}^ydCC##_ɆӁ7d*."+ n?^jYM6gx饗<.XN[kN<.;||ʼkxMt%.3wՌ|6434b<Ĭ@?{ VlfDéユLy:٦MP3 ӿ!ks~UfsO?׾?ᄀ?7X) V{ذwR vX 0{A~nyqQs@|i#ɎS1Z,Ҵi{mq L!aHWZ7UC u9\pJDV49n<sĉ˖-C``)yd5j 6AáC!@-Z$wlMחJ& tk 91nv (8;QŶùs6k )UdS s"2 of8:9F ̏+p3n5W_}6D_Ϸ߸Xs# y#LiwV&O|g.uSL!=SWP۷/G!;7 eyc`fE(9Mln<֟+:vB$mKzh$*jK 2*8n+[nqEde@P*}bdôνZjPLE&hF侏A=g̙PŽvWdOCs55+ƈU:SQIۛm2^5Z7/YfQ/,4)>Hu32C6|ɞȓo`K wVV/7MaqU'?1x :x#.0wA=+ x{{#aFA~H0ƙ?~CcԨs HV26qfѢHbUEpߜoa< `i'aDO=gP^JsfnV6ET1 IDATD{N?\ ]vE}&VJ6?D O_zvjTf ‹=d)@۶mkJ(_Vɦ®"7^~eqmqJELx!Cp0#ZA=1by;ԛqqcJF̎;RnRV|;@B@2^uȈ0|'taE>1Dw5)^ 7iÇg磌}xee'G7$Mc{yiDR_NiJŐGN: 2ME^b}hCp{ö֧O6.!U$Z5kk CmͲ/͖ LfEsM]w4w0M+Jƒ1d;7]<8x6cY؊U^>"y??}\ 8 +ˋrI x1yEzk J!o<\(>4q=MW [Y&?*l˩m>l8~FnQŎ"Rl Z'lCـ+mR-/O5}hqd!>.6W<ԑkvR֍X(AfZ+>:bsN| %/Rq>VcǎE܅?C~`+j%6,M|B ױ9Q'iKɱ$/n/I_{2/PꉗWz|jhdbr>%ԱߊG;R-z#} k {ᤩ҄4WVMU*qKL6nң[A@Y 3`c2.rzBIfn;ۗc1Ynh CHiFd v]42H)4r\ظiߥ#nfh@8OMv9bjLJ}շH-DotG6ElkmBW AY_JM7#O,|yXӶ^ lXrgWU"ŊOxdļ̛z'q)ItLYJj'BҸktZ6 !&fذՋ%RFv2"PE~Pmhy(X l\GA1c`GV+F4N EH;?WsK]@Fq(Anَ(E51sro\Hh1~qԩ/nȾ(;K\_KH gzgut!tW (ڄң>j_.5P ֲ}D$Dx[9׊I&jbb5PA67n?ӟ\t=:v%K*s}OO?\\Ƹ5s 8Y*P#3O &08݊6 K~84fATUBo\*VChr=0!&pk!scgU 8nBsE(`LlܔB%I7D>rYh9y ò06` Q`p0`s|c :v>^ȈC+6(/ K86FDt/&C{ff4MAީfEW 0a9C:R7rc-l``s':+r# ָ߸'nߜ7GSe"XjJ^3f̀«/JA9Goݍ;BC3` AHAՒVF} `7Ģ@tQu|&qU*d'as}"N߸J̙Q(N~-e{{< Lkڷ0l0]Z Ԟ5C7w3kכ>49GJ\jl8}{ì rm~ÜW ݱcJæq-sc O=95f4D7l>> =ZZV4T4صA;%l=u|,L n3W_`[UIq)Ha2#:4IS,unKX^iHc_ƒ4]u eV' 1p߇DY{(:zZ= Z.2*`sJHb?L dNܧC1ixd鬳LȄ:u!\DcAҌCd^2[Ӗkw,ĬZŤ=== nWdŰu݂TZe+SfQGX7,j~S f^vRx:Ƚ>}-J1Di#wĉ}z|0^9R *}"U1L$?Z<2UN 9:߁#ah:WÉ!Q/P-(Pv{nKG_ыܝΝz9(\nL((PP@A ((PP@A ((PP@A ((PP@A ((PP@F 4X SCꮬ[b@srS"ڑRKy//XO?t\<ݫυP3;S@>vD%y$馳TJ҄KgjQVf.V)SO0 _wZ%AcXvP#MZ#;ԙT-q.4m)@Ri&J0z t/DO #R=@5j޼9./Ɇn}`b3 5l`>:z *H)ؾ}ey< E؇T\sV+%pdT!'f4* V‡z$#h#`硎4ˋ|yO\ ҟgd :!;2JpjǗ&+6om*e2o nxG*k1 *r#Gl84s7EJ}h-}jòLu]iߐmF\ iڦSG!5 `BТ'mcpI?BI#y3%T&@2!:' pRTfQj0n}ԄQ✲w{h+y#m3j\~yeR<2~{eRs_)KN_j v.G䨔lRiCR뮋jS؈MS~\9L_ҢJe qQ"uSP= o??jҐ7ĽƢKd7e8M%h {t$:} ıaadG# \j"xH>{l727xM7݄iN2ebo.[6$%Sl-Z8PS/$*>ؗ䁎])JCĢstyvڑܮe}\pv"s"{ 'TZv' E[j[RW^ʦT"OlH(nBoR0-P @P 5hMo\4͍(|ᆙ eǕX B^*6)-K#[}a6q0|W KJbI^uUh ar)^؈=K鐉c;c66\M~^|Ň~8d=&kK> LIQѶu#ӯ:WK-: 0r)?YmYɨ0pBSíMaM*`&M$oIaСAA}ME Q`AFBOD "M<Ԅы!2'7 ޮYU1i|$^V[FHoaAyĈQϨQD(~Kyų>ukIXM4& 0Uz6921CQeD;i-tG:tf7n\(/`fףGXg/IyC![SO=}c顼YԹsgHȑ#DAE+wgJȊ2S1}S Eu9CȰaìu) t~VgNjwEqo$Ll+83Pe~-W^yMOǟ~Xqeuh^}a,P>:|eڠ@X(KCi"SN]* o_97R'o-±FW>J촁>㘢Vfm&&)zId/IӦMIbK/?Ay睡IHƂӟߜϺm;\P**Flt! sZW3لVTnw5"s뛁Y#4݋zpՙ8poe*u|/4iŝ÷Ҫ,kp 'fĈJ/,| H(Hi ̇F~JUWXKjH%+zaNo4h0s-\;@IQz/:ضb`7{U HŇQ|ӆ3e˖lޔP0D_lb2$bPw}7H{=6ނ)$lp4A .'Gg`Eȑh"Gۛm2WeBecw757fӺOWQ37ۙK0`yM3~^$??'(n3fM/1HR.(xb]V4h<1za</6_GLʣML+MTdhOE}y?3ge˖& Še:sac}ekglgX]C}2V>?Ñ4{۶mC}wK*+wG˕M4Uï/*^~eq#ͳV1ٲv UO`.g<;V4%yWZ,ԪU+|&9 &9(-!lD>2eJԂG/#2m!UbK}  Y9Wl/R(i/1l:J p;v1Dek&(JQ$‡ [?> w>L@Go2- vr0 J # w޸o 7>9 Du>RAyXv:ض7in*յhrb(]@D }R&"B,qŠR#<2K怇-̊Rw1]&O)K/GɱGcX䕉~.r*rnBM}&q\4TKyP^i|,ؔ0E^*7m+9^?U@b@ X ;uꄟBTV(zY*%qz=Ȁ7mHiFu}h{9z!n1c0;oĉݡ8ڸ|aWrSifh@8OMv9bjշH-Ğ5G6Elktn,)$ -ayllGg[ ?bM+kM 7(2--uEVa>!U z}w qm !Z@f)h7Qأ\Q Fф "G=*rPP!T\V,&$W4oʧiWّOC(k+G?J.ʴ\fs2l#QBCyRIgqI'ao߾2.r-O\VAm*VX!>0b ny{hk|ִ&0krS7VsaOpXDn|{3݉⻴BOEJ\*fًܹ"D%{Wƭ8w"Ɇa.Td+a C>|x03kQoنW[qJcJA|y.FAK.u{=k,HJj$a=mQ E؈EWʖ$"%N? ._hKɖ]"P]>WDGW .h8C P"9+h(EY;Do 9q4YqQhf[ 4is-#`*>"K{72}Q.a= v#ʷG%.آDBRimjcp\[KmcUvrJX䕏6}@cF:u*24s}'?Ŏ2$B-ZR~#и2^n Zٻ?3+d:ڸqNO'3m i#r|ז戎e}ē$Wf惯̉{/֚UF sdGdF3+ꠃAKT6ƍU+{~\pA`܀?x 9ˬYT8}f*6ζ VUiG[6৹׬Y36)hr-\6B}ٚrSBP|CCc\>jLx=W%E6|H0:9h`z-!&yLq0v LDۇM =+|%rie>J4IFmF`5a*mY~D'*u` n\z J V5˛4ǏW@EY1W^R"_TE DD_"hB%Bh-G}4fDR/7rT&.>sQ R"\v$,;$Z&w$jS|&N]5<D']!F.InΤ#>IW`BJ9Sbiuޛ_Y[ŢQ/<^]}U⋃$vs+*e$Biـ"9?p$( 7'IQGD='}݇qd?=YgE<8?~Udi2j QMC,J} SI]@.PI&J&Ňtm @ 9* 'b6h}/eȾ"z"UV$ABCFD"/)ꄽOF9j N;ԡCfY5EG#:.1e^ k Ѡ9Tӧs*(1j@(Br%zi|)u˖-Qw8[.:|}RYEf2U`b9iN@gdb&+Kv(}G) ?.ٯIU@py7k,dad笑]eLi9[{wln>^9݄v5o}f}}&~;F U5{`sv0>ߤ8АS-2/7h׭3-[9mJQ ӡCЗ3a]!U_bh5tkDyC>70' aѢ*rYظ4+I꾡{wYoԦe#Jܐ: [Ĉ#zb4zdIs&j"['w$4ño`.첮$ j)RYA(] c9Ch`Pc&SO=H۝u,$@JG̰ 98GZH <($XJognە=tPV7EqYÑE Y)}@vWT07.>4&\r 8oB¬`µ]*8y袋$u5#5_Ktf淽:_1g355C}>Ǎ3 M4yW\~&~ٯyv;b}=F4z@Br r{_zi\A'-+8d6ahbxO>#l#Z;dؾ9pvۀ<$sPr=T7ꖰK1rʕ0jD#oϞ=}қ3g|f?Oei~ԨQh)`S/_($z@S^kРA<̙3l_!{.^>x㍐RP4sׯ{:'fxvű7x`iӦmI8h ነ?c ֋ʱ|y;zU| 6x5p"r0+V@ Y),< >O4 )^и9vw3x_sh{3mqjO!2fú̈C͌%fj'N5Oi°on /6?/Fcر+l: x6jԃCb3&0 $M} >67ÆUoĢ`(_8Tg!Ob:*[NFwfH(bq&[SACV+jm۶Mc: !)MШy|H0Ca@$F iPlGHC -gjIT*Hi%v[_ʐ"CeÝ8e\[hCxWzaŘW:5|@qрKʔRҠ>4ţ!C˱o@C,K`|Je*Ea{\D[v/Uo-4?餓"p}駟ֿ[=`n;av<|$ 2kn 0@F@ s)[60]w4w0M+KNcwI7*<8~(.FsECeuA뭁sϙ~^=%?Y0V>\X>~jc q=zWΩxLez7x*0Äu84dDbG^ O`jM؀y̺| PQt,.I_Q4|mc$5bH|'T ؄ v))2[Lf9ɈiM} B X^{eGpg ` ,nYDx)E ~|) DX&raOpXk0qWI_WV⻴BOo&9WqY_a. =s){W] / |~p GS/-f~ࠅ$1k]c}CF@0W@M-kAm A# rUnгo,A_¾P:9q&P?&l i6 d沸uCOC*Z%v"6g0ro_LI"ECAQY9PALj E7(QN/xtX Q4 0np6cĤRf]Ke B; F_&ɍ[n҉E Y)!WLQbE4TƅyN*)#w^|DjdR6&@KDMt8YQ-#U [AʂU l>ޚS HĸCԩDijh{ BEwWzq՗J6XhT1ʚ/´+1g wwmih*K"E~jl(ۧ zߚ2'eXkVU"^9c`$ٰz,Kbd,⋁5g݂S?l"@!R2*yvS/vD̛ +F#`g+ G/ǣoHS y0b2YiUia+\fOƞH%nG;Oy% u,, 4lơe$є]pMM,†A~pC$CԯjLŐeáfLDǔ}$PQ4]K1(+>[Ȁu9(_ͷgs(+ֺqkD2dȎ Jijg}V-o\(,qHG\&n _zLpf#b?8ʾBDjd^*T$"L[{/\ˁD@14]zF8" `"?%xy`LaU {rLu齛Y޼9Թ`FJ\jlؼa怹jl椽ِB@Y:>88qU}ˁ<ո3AL{(F┢oK> mu23Ɋ9'0(ɽB/[E N`/> '`īuo[yaM7[ %r]w݅4 g `HE2w$ 8zBoL~J TqOC&0ryaM[6~@_jA0R'sİg7HM(2YX`I}QϽ I@q!Tqe Bx<)P C&5NuJ# ܸKΔld5q_fhET.5lh2yA\.,VD6 /{G oGlPK1Ф?~||}e%yࢋ.%F;˕JD~hHMkݎl(ɀyCBV6pN㛨Yq4L}f"cTNp,4|QJl*>J4 %3*PLT9 VeH>JUU' |i[.e0l"Ŕ`mV֦2/Ma#!q wd"-cߐ e-iHFvyTbׯ:~'51&(?hYA#/ P)=D<6(0wi ]gd\9(@.Çו4d&tȼ<o&rْ6kdtg@BY EB.R"G<>2[!bcq*>ؗyH/iժ d̙c90oiHf+l.(Bz̆R3ƇN( VP @wf%ghs! @Nvdere\oЗ. H6rtHD>+bѕb)|JC()${/9+eb'+Gc*HG~ڵ[b{.F}ϟ?A"ސye}C;}o 9U&RL.Ň27(=P}w?@ϒ\"icTŶqfwfjxKsP;) )mصRY|=fw NԢ9lӼINU0kD3E$7*a_~iA VoFE1"W^bP>}4gtp"x^Q;J2*h9ZӾQB˺vj+foRYA"vC m0 V:t( 47n0Ҕ +s졇rQy睜yQ8Lȧz Cy7ܹ3*hȑ%)ҊeB{$ɱ<}ЮHΊKeRJ{̘@&].a@(J|`yq+PpKSdd(ۗo@@<|r@C@*!#H?M %:e$. 7:9%DW?yJQƍ98NDqYңVʪģiذa t7zqq[aC-O4W&<ZVS^R4BihIq ~"dlڴ)_zC9ąV@zž|i? fg]LvwG†L>u5#5_KӦmkL.E77F4{PGcIn IDATb-G%lDs߬\F !9HڳgO|&hЀ7kL6?QF^|EthR*ڐXo5h x`̙Q/ǐuL.] v%̛7,2<z </FX.GVf(8~VXF9tCP8! <ΝrԥD.59 9m492 mag̘zV9ҝ2Ycƌ?8oHo(:aX LrWEo߾ +4Eڷ!CyP;0ydGGYɇ2:T6lGFK:3(TEB .5@C6rP>oB |ؕ{SWJɓ'sje\Nl _+r(a6p|}Gkp\lWX |}}enзU*W HQxn{ፍ݂Xx+$lǨt¶kaf,1n ._i:lk*~-,Í1i1U)Be ͱCۛiڝ|ʌi֭73# p[y0`yM3~^ &OZ> q$b5'|<&3S[ 2>nE9ԳG{hL(\>QI9}A:kDT ElEZQm۶9t8ް3\{곐 L}*30GCs|5{v$ ސ|\HbLyβeˀf2w8` AUyI'Q 8]tᨦw*eeȌCtȐ!w[هmbgSjEB|h&rP_P4|#2|R(Eȁ?x`W"|ɡ,(Ѿp Ca:GyKQHE@}Bf֭We /(.|UZ L'oMJ24J)W8\1ӘA)lt4p>8! $ 7]<8_1W$ 'ˆ%HG&%n,o"QNbʼn'޻PX6)CA&_PMI}êСmΟ:4+tjɒ%XpAx,p]FhW8!Z[ibX SOSy(6Ґ /".,|qHduBۗ2c"!]y\)i~e4ߴQ7́B|AXPCA)BI'o2h2 -d DLD_B0/X !ȃ&Et̍0U*W y)bj . +ŵ[ ( CH9FVF޽nAyƼap*݈b̦ jmob+ vUZaZA5a *$UX3Pg`^h XnBO+p\rbѐ"G+}7`„ iDd5aq-}p,roQ5a&&uݕťt?`^ Ζf 8rtTFtCarHB(@2.Uً01q_g~푃< `38x^:h4o4T>}[ByKQHW@:308c V.ٝY'|@,Z6cTi%p27?\˼ri]agxx^p#ŃE82ʝ_ {+̤fZ־U{3%7wi8MH15ԂC$Xu!.ptJp=1W  6̟p[ں'j=YĻCi裏roY_nl(%& $^\qs Wu[nֲtEL2ȥӀ,D>(!aFfpCKW^=8"Xpn  @ϐ/rPF{r;˜r!X !ԩS:,mGJ=\ì|9q/Z]9c`[ٰz,K:ۚ6`йlumf6Ba4N.ˢ$98䕔i`Rj"``ja;@ħ o뮻ΆM+}q(l(}# ar'[' IBcq 7b8Z^x5ҠHK8ZecȒxhRgsEE8"x0iVL$m31>L_(a]v9\:A.eV䞇[HAŇ!7ݑe`hJ*Z7dtlb=6MS}oQl l\.});vi=@yh.ƞf9(CQ 5K!TJI b*q)lQiZE W[GhvvP+q1_(X5JKCHR¥Ј4&z `g"D[žI]Fڦ5C֮<g3h\Kv[{moo;fEs^o6ԐvEFEکk(@ _0|ABM*BCZ7JA _[.pr%JXX(rS; xSPnR஻Pa0c[7Q̈́U][YLMS9 x:̥4.'-jڲQOR$l3(nF6. TpvEh 4u]Z/{-eGDŇ0W_G9#0^=wg:>O،|z!l/ZHq1H.>UW]r/VAbePpg 6'ϏP,(PP{LnQt''Db&x5Lbّ,-;2 ((PP@A ((PP@A ((PP@A ((PP@A ~Sa-05$5E|\ 5'3ȝM\Ҝ%"goE {G+$7 5bX:bT!oѨu64}y,SqYƬ^]Auy\;p׌i\"ym^`52YY@#TijvTH%4 n ˣ7 )Űt! ]t598A&4o +j0$'?~13 %ERe`\JQ&mVo|}QS)ߔޑ-S"VgeXk`E7$H?B_OӮtlM[TMICCuM;y|gmVޯ~+XxpWe" V*t!M1UspUkSD;Ma# *7u& 9_`HfsG7/CC2wy$B žү_?d?!uPwHR|@>ȤT'oZ숈 A\N C3:*K@RSۤԱC#_qԓ>šoPO"X4xk|4i2|pZIq1 i^Hr1oƛnɒ+*4Կ>EIĈNuJ)3*6q/09!b#1R(.sz!(MV:9sB؉/X>Z0+ \I@F&dhȱvd_ dCðۗN( ([(y֧~!mś7x*K}eϡ}9$!c;Le~0-I#?mi56[72^Lz<;[]M7OVgl98!xة[41b7 R(Y'M98̓OM* ߰쾻;+i'vRca}|UwH V^$)яO1֠~{V e\"%WVAv͜9UlJz_+jviܼv@A\>t3q6ol4h栃L&aBILC f&#^{_{ͬ\Y+˗Ufox1(*p-a#r7=;seR b{;#=o{UĽ*}0.첮]ڱ+%va\z,XzEZƍs1pZA(v$/Hfm֭#x(IRl 2dNQ; ` !Vn2\6h(nvRM^:ƥꃑ,^ؕR Kj>^e>Ye5:lt! sZW3لVTnw5"scF4RLFU~s3bD%CVny 10C\&>m&"^mB'K"FOg>죭2dijc|2W|Ց A~;3?l0|WJze#mٲ% Z_*!|[2mqHnpws=Hx6#jΚ5mRۛm2WeB%Fcw7572u32fnZ3#53TD<?/Nb(PW0={7`=zauqlܭxA>/J0-zh)a7Dk7f.4wa>,,_)R8ƌ1 ) 8[e0g0ίx|-y_?畕"N֯sL߾GOVG1ɐoˌo*EG03suP$b &g?9tⓐ[T}0s7q¸a59~8q zrDXlrԐ!C88R rⅥ|)>5IPWN۶mdH*zq9BB4!AYF+v$  .F`Ɂ SLJ/0'ˆ%pcbB"6bNn:v'rO7%KQƗ >SңDDltС-hcǎE8 @r͂W^y x\l7iE}8i'z5g))ǥQW(<͸Tdĉ@4 6ШP&WFtC;8t(Jw @pJ[)JSNi-3ӢN|aʹ!6NWPVNBl @=QMqłnև\.]piJtm\ApJHAqݽвa8餓aձpb!PU{5C{‰}*np$<%YmЬb@#!pF;GvJW'<ك[ᰡW_}uCUp}QrL#TJ7)&MBخP}`~IÜنk7.zQ +5q:e|& .LW)JsM$BW( Ք3LWH &dpJQi-!ܰQ:۩,]K*cGOQ>:F&3!p`Z io\Ets:qg -`]o߾\ƳHJ4<䓥97R.BIu6/ךvf?^,\a&-0+&[F惯;EߥE4z*TCD p]!N٫j+E @ \/D Hg.4LEp0ʸ"Q/- 4;0F`9€!5I(0=+nTO x]g.xc7£ڄoO| l. !f{:\GEDa&VR =p܆(BqH*4Fl'⃂J­S"QnĦB@S.VRY2bgY_y__\|DU}'`mcÛo[xI0\#߸P+ Qu%Ӓ{\x{ $HcLNQa 2#|3G`fL/|&b[|Gu7s_KY_ʸJرWZB>a{fl#ҧ(һxxb+:u*s ZG6%xnh:u\)\Gt/M^ e1\݆dt22O6-7wmi\اM׋ƕzzY+s^拵ok%_#; 6Y] RL Dm&N .:rKy%_)e0Hs"K\pG.LJS.LW0A(*AL2.(_bIDAT_J+)O .' UϯX6ìASa7-*@pn\{mp麺_( VQ 츮%PMqKۨ`H"a(,혀T>E]wJ#CㆎAeZѠ=Bp茉 BȀ899ta(aZ7L8.#W\5dpÈ.B]ac\>{-&t&fΜ fn mi[rZftr0U03C>d!N_*})*K6USE:ֲ>kh׵K*qIQbΒҨ8\_A2mKnlޡn-2,h{ܶ mXӾfL\`fxţ|\3d{7vyCs!GJ\jl8}{ì rm~ÜW '(8p59#;=2`@D) #۠2nf#Tl A|q͜)M,F$h"ruЅzAgMS+(:%RC^(a%h/xn{&/<|AB-NKīIcTK_(P|_$~bܻM"B~ p;B j5m٨dfE*@rF4 ]><* Ycݸ4,W-¤SPS Pᔸ۔`%pI#WA V(*(P(,9^v{AIAngA ((PP@Au]j P\&EZA ((PP@AFBب6 ((PP@A )Pg#lkw3|d)͇s?2}&}桑H8FqFڭY<H j;3кuɓ?XtU9h3|OW"BA ((wGzÆ>hO|c~m]8>tٟ\tjC>!&KGއiɐk&Lx烍 0Z$'G5zh_it?7U/*|y睯P1a!<キ{#l`ݑ3 iȟLZR>L$_o #Gvnժg}S}†t3iFC;q\iu ((PP@AqzBq١CS7x9dƪUyӸq'{ q&;4ih [2uj-i}3kv-=Vd?_e]wm?-٧%}-}J^cvڰ͛4icfmnko%nń;5pH==B0dnGϿ@ >{& ".@h_~y锏~JP >BC:c,G}A (( ױS}Bg=w{%\M>m:O߅_t0eݺ>͏ktK1q: `AF u)SرA{1/+~ƹsW v=weu7n;xK\z\+vo6yE#RsE>H_s -ڵڪ™'~a#ͺvƛի?sfaӦ |uÇ۠ Jwx`)?7rN>PuCXCٞοοuݧ 3-*27̱ԹmrʔíK!ʏNmmwi~R2Bԩfwݖ-mL_K^>N~khk>Byi!fȴmvkPQx_P@A @"3AgW.ǡeVqdž?Γ'o1 =%h|jCsPX,&'br luC"?xJq:-,7+mJM)^{Mmh"%L W] [տ۬ Hfvv[㨣^MN/N;m5p`{u6&M+h̘}47gr*ʩğLR)`SRZѵve˖oYryߞ=S=w1q(j2 壨CCi"/qE{)((PP@A%R ~mY>:5q?\dcm7T8JmM7`}vʏ~ Iǥk;A 7o+|eRG~`[80Ѓ+,)E ;w5]^3 2|abuH&^z|Yc[N4Vi8`@p9I5g DO~Rwqǵ"X.Tʟ/W/e߿5zC^rK'ML w~!Uqn DiFF,n$"* "*mż-tL57jͰ$by=ss:g3qz!-#Νy§JflحLI>t-Gl#`;uňE8ކsnG;zt9j؍:miI7I%%R(o8+8B5qmRY09HKwsY6 !ء93L{{qSVL ߖ`l![o{ISMMk>6893c˖Ř'=[LO"ȷ\AAž&.(}|GEoŬ??o|3d|%wG?bOy QGrp*QMMdNNN07̔|}{|q#b~t$=Sjn!=0.13)', 'argparse (>=2.7)', 'urwid (>=1.1.0)', 'twisted (>=10.2.0)', 'magic', 'configobj (>=4.6.0)', 'subprocess (>=2.7)', 'gpgme (>=0.2)'], provides='alot', )