pax_global_header00006660000000000000000000000064147000074240014510gustar00rootroot0000000000000052 comment=de081f422b574d638e62e15661bf833d80fac61a ConsoleTools-1.7.5/000077500000000000000000000000001470000742400141455ustar00rootroot00000000000000ConsoleTools-1.7.5/.github/000077500000000000000000000000001470000742400155055ustar00rootroot00000000000000ConsoleTools-1.7.5/.github/workflows/000077500000000000000000000000001470000742400175425ustar00rootroot00000000000000ConsoleTools-1.7.5/.github/workflows/ci.yml000066400000000000000000000026271470000742400206670ustar00rootroot00000000000000name: CI on: # Run on all pushes and on all pull requests. push: pull_request: # Allow manually triggering the workflow. workflow_dispatch: jobs: test: runs-on: ubuntu-latest strategy: fail-fast: false # Keys: # - experimental: Whether the build is "allowed to fail". matrix: php: ['8.0', '8.1', '8.2', '8.3'] experimental: [false] include: - php: '8.4' experimental: true name: "PHP: ${{ matrix.php }}" continue-on-error: ${{ matrix.experimental }} steps: - name: Checkout code uses: actions/checkout@v2 - name: Install PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} ini-values: error_reporting=-1, display_errors=On, log_errors_max_len=0 coverage: none tools: none # Install dependencies and handle caching in one go. # @link https://github.com/marketplace/actions/install-composer-dependencies - name: "Install Composer dependencies (PHP < 8.1)" if: ${{ matrix.php < '8.1' }} uses: "ramsey/composer-install@v1" - name: "Install Composer dependencies (PHP 8.1)" if: ${{ matrix.php >= '8.1' }} uses: "ramsey/composer-install@v1" with: composer-options: --ignore-platform-reqs - name: Run unit tests run: php vendor/bin/phpunit ConsoleTools-1.7.5/.gitignore000066400000000000000000000001171470000742400161340ustar00rootroot00000000000000vendor run-tests-tmp extract composer.lock composer.phar .phpunit.result.cache ConsoleTools-1.7.5/CREDITS000066400000000000000000000003451470000742400151670ustar00rootroot00000000000000CREDITS ======= eZ Components team ------------------ - Sergey Alexeev - Sebastian Bergmann - Jan Borsodi - Raymond Bosman - Frederik Holljen - Kore Nordmann - Derick Rethans - Vadym Savchuk - Tobias Schlitt - Alexandru Stanoi ConsoleTools-1.7.5/ChangeLog000066400000000000000000000472031470000742400157250ustar00rootroot000000000000001.7.5 - Friday 04 October 2024 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - PHP 8.4 compatibility 1.7.4 - Wednesday 31 January 2024 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixes compatibility with PHP 8.2 and 8.3. (Remi Collet) 1.7.3 - Wednesday 19 January 2022 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixes compatibility with PHP 8.1 - PHP 8.1: fix method signature deprecation warnings (Juliette) - Updates PHP Unit requirement to PHPUnit 8 1.7.2 - Friday 30 October 2020 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixes compatibility with PHP 8.0 1.7.1 - Friday 13 March 2020 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixes #11: Compatibility with PHP 7.4 - Enable Travis. 1.7 - Saturday 27 September 2014 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed: Adjusted source headers of Execution to Apache license. - Updated: Moving copyright information to NOTICE files. - Fixed: Updated copyright dates in NOTICE files. - Remove custom test runner. - Fix autoloading with composer - Fixed issue #16055: Corrected regression in error message for option dependencies. 1.6 - Monday 21 December 2009 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - No changes. 1.6rc1 - Monday 07 December 2009 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - No changes. 1.6beta1 - Monday 23 November 2009 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - No changes. 1.6alpha1 - Tuesday 01 September 2009 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Implemented enhancement #14804: ezcConsoleOptionRule now has a property "ifSet". This one defaults to true, meaning that the rule is validated if the option it is attached to was submitted. If set to false, the rule is validated if the option was not submitted. - Implemented enhansement #14916: Extracted help generation and input validation from ezcConsoleInput. The interfaces still need validation and are not public for now. - Implemented enhancement #15010: The table does not use output formats anymore, if "useFormats" is set to false in the ezcConsoleOutput instance used. 1.5.2 - Monday 22 June 2009 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed issue #15009: Empty tables are now rendered correctly. - Fixed issue #14073: ConsoleTools is not binary safe, presuming UTF-8 input. Note that the component now depends on ext/iconv which is bundled with PHP and active by default. 1.5.1 - Monday 11 May 2009 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed issue #14803: Exclusions and dependencies between options now also work if no short name is provided for the options. 1.5 - Monday 05 January 2009 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - No changes. 1.5rc1 - Monday 15 December 2008 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed issue #14078: ezcConsoleDialog implementations now throw an ezcConsoleDialogAbortException, if the user closes STDIN (-D). 1.5beta1 - Monday 01 December 2008 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Implemented feature #12567: All help generation methods now support grouping of options. - Implemented feature #13240: ezcConsoleProgressbar now allows to specify $minVerbosity and $maxVerbosity options. 1.4 - Monday 16 June 2008 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - No changes. 1.4rc1 - Tuesday 10 June 2008 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed issue #13118: Class::method() replaced by Class->method() for object method calls and attribute accesses where appropriate. 1.4beta1 - Tuesday 27 May 2008 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - No changes. 1.4alpha1 - Monday 05 May 2008 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Implemented issue #10765: ezcConsoleQuestionDialog::YesNoQuestion does not accept "Yes" or "yes" / "no", "No" as valid answer. A new validator (ezcConsoleQuestionMappingValidator) was introduced for this, which extends ezcConsoleQuestionCollectionValidator and is now used for the yes-no-question. - Implemented issue #10791: ezcConsoleTable should implement __toString. - Implemented issue #10838: ezcConsoleOutputFormats should implement Iterator and Countable. - Fixed issue #12561: getSynopsis() bugs when at least 2 options don't have short-names. - Fixed issue #12623: console menu dialog handles unexisting options wrong. - Fixed issue #12624: Wrong text in documentation of eZConsoleDialogViewer. - Fixed issue #12625: console tools lacks documentation on targets. - Fixed issue #12626: Type "ta" in ezconsoleoutput documentation. - Fixed issue #12628: Missing space in ezcConsoleInput::getHelp API doc. - Fixed issue #12629: Method name not replaced with link in ezcConsoleMenuDialog::display() API doc. - Fixed issue #12630: Add link to ezcDialogNoValidResultException in ezcConsoleDialog::getResult() API doc. - Fxied issue #12636: ezcConsoleOutput->outputLine() prints EOL always to STDOUT instead of the defined target. 1.3.2 - Monday 14 January 2008 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed issue #11435: ConsoleInput does not reset $option->value;. The newly added reset() method can in addtion be used to manually reset all option and argument values. - Fixed issue #12364: /ezc/trunk/ConsoleTools/docs/example_table.php base value exception. 1.3.1 - Wednesday 28 November 2007 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed issue #11378: Example code throws Exception. 1.3 - Monday 02 July 2007 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Documentation updates. 1.3beta2 - Thursday 31 May 2007 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed issue #10766: ezcConsoleQuestionDialogTypeValidator with TYPE_FLOAT does not accept numbers like 4E3. - Fixed issue #10767: Class ezcConsoleQuestionDialogRegexValidator described in the design document is missing. - Fixed issue #10782: ConsoleTools: Argument mandatoryness applies always. If help option is set, arguments are not checked anymore and old argument checking is used, if arguments are disallowed (to assure this is checked correctly). - Fixed issue #10864: ezcConsoleTable calculates wrong column length for content including line breaks. - Fixed issue #10873: ezcConsoleArgument default value not working. - Fixed issue #10874: ezcConsoleArgument default value not contained in generated help text. 1.3beta1 - Monday 07 May 2007 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed issue #9857: Multi-option and default value. Arrays are now allowed in default value, if multiple-option is set to true. - Fixed issue #9902: Problems on Windows. - Fixed issue #10594: ezcConsole*Options throw incorrect exceptions. - Fixed issue #10595: ezcConsole*Options perform improper string checks. - Fixed issue #10596: ezcConsoleTable* classes do not perform proper property checks. - Fixed issue #10604: ezcConsoleInput exception not thrown on invalid option string. - Fixed issue #10608: ezcConsoleInput->registerAlias( ) does not work. - Fixed issue #10617: ezcConsoleInput->process() bails out incorrectly on option exclusion. - Fixed issue #10619: ezcConsoleOutput __set() broken. - Fixed issue #10620: ezcConsoleProgressMonitor->__set() issues warning. - Fixed issue #10624: ezcConsoleStatusbar set access to property $options broken. - Fixed issue #10625: ezcConsoleStatusbar isset access to property $options broken. - Fixed issue #10626: ezcConsoleStatusbar does not trigger warning on invalid status. - Fixed issue #10710: ezcConsoleTable reports fatal error on example $table[][0]->content = "foo". - Implemented issue #8461: Argument help text. A completely new argument handling was invented. - Implemented issue #8471: Disable borders for ezcConsoleTable. Borders can be disabled by setting the line and corner properties to null. - Implemented issue #9216: ezcConsoleInput::getOptionValues I want long names. ezcConsoleInput->getOptionsValues() can now be configured to index the returning array with option long names. 1.2 - Monday 18 December 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed issue #9493: Convert < and > in exception messages to ' and ' for Cache, ConsoleTools, ImageAnalysis, ImageConversion, PersistentObject, PersistentObjectDatabaseSchemaTiein. 1.2beta2 - Monday 20 November 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed issue #8466: Properties are not documented in ezcConsoleTable, ezcConsoleStatusbar, etc. - Fixed issue #9241: Progressbar does not work correctly on Mac OS X. 1.2beta1 - Tuesday 24 October 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Added the 'target' property to the ezcConsoleOutputFormat struct. This allows you to print to a different output target (e.g. stderr). 1.1.3 - Monday 09 October 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed bug #9046: An argument should not be treated as an invalid option value for an option that doesn't take any value. - Fixed bug #9052: An exception was thrown because of an invalid property in ezcConsoleOption. 1.1.2 - Monday 28 August 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed bug #8738: Unexpected behaviour with options->colPadding. - Fixed bug #8478: ezcConsoleOutput format (foreground) color 'black' not available. (The fore-/background colors black and gray are now the same and available for fore- and background). - PHP 5.2 compatibility. 1.1.1 - Monday 07 August 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed bug #8525: ConsoleInput::getHelpTable() exits with a fatal error. - Fixed bug #8645: Default values not set correctly in ezcConsoleInput. 1.1 - Monday 12 June 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed bug #8460: ConsoleOption is a class and should have properties instead of public variables. - Fixed bug #8472: Update documentation for ezcConsoleTableOptions. 1.1rc1 - Monday 29 May 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Implemented feature request #7792: Possibility to omit the short options. - Refactored to use the new ezcBaseOptions class. 1.1beta2 - Tuesday 09 May 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed bug #8173: ezcConsoleInput::getHelpText() does return empty synopsis. 1.1beta1 - Wednesday 19 April 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Added getHelpTable() and getHelpText() methods to ezcConsoleInput for more convenient help output generation. - Added ezcConsoleOption::$isHelpOption flag to indicate, that an option is to request help. If this flag is set and the option is submitted by the user, rule checking (dependencies/exclusions/madatoryness) is disabled. - Added ezcConsoleInput::helpOptionSet() method which returns if an option that was defined to be a help option was submitted. - Added the class ezcConsoleProgressMonitor that is capable of printing status information and percentage progress to the console. 1.0.1 - Monday 06 March 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed unreported bug: ezcConsoleOutput::toPos() has a typo in a parameter. - Fixed unreported bug: ezcConsoleOption::__construct() still threw old exception style. Now throws correct ezcConsoleInvalidOptionNameException on an invalid option name. - Fixed bug #7897: Access to 'step' property does not work through overloading in ezcConsoleProgressbar. - Fixed bug #7923: ezcConsoleInput::getSynopsis() does not work when supplying parameters to show. ezcConsoleProgressbar --------------------- - Added support for a non-integer maximum. - Added an optional parameter to the advance() method that advances the progress bar with a different step size than 1. - Added formatting options for %max% and %act%. 1.0 - Monday 30 January 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed unreported bug: ezcConsoleTableOptions struct caried deprecated options and missed 2 in its constructor. - Fixed unreported bug: Option values not properly checked for ezcConsoleTable, ezcConsoleOutput. - Fixed unreported bug: ezcConsoleProgressbarOptions disallowed option read access. - Fixed unreported bug: ezcConsoleTable "colWrap" option not recognized correctly. - Fixed unreported bug: ezcConsoleTable "defaultFormat" and "defaultBorderFormat" not recognized. - Fixed notice in ezcConsoleOptionExclusionViolationException. 1.0rc1 - Monday 16 January 2006 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Added redrawFrequency option to ezcConsoleProgressbar to allow less redraws than advance() is called. This allows the progress measuring of huge numbers without redrawing the bar each time advance() is called. - Added setOptions() method to ezcConsoleOutput, ezcConsoleTable, ezcConsoleProgressbar and ezcConsoleStatusbar. - Changed exception behavior. All errors will now throw a different exception class. - Changed default progressbar size to 78 characters (standard console width). - Changed submission of options to ezcConsoleOutput, ezcConsoleTable, ezcConsoleProgressbar and ezcConsoleStatusbar to be an array. - Changed value "step" to be an option in ezcConsoleProgressbar with default value = 1 (was a setting before). - Changed "successChar" and "failureChar" to be options in ezcConsoleStatusbar (were properties before). - Changed getSuccesses() to getSuccessCount() and getFailures() to getFailureCount() in ezcConsoleStatusbar. - Fixed unreported bug regarding rounding problems when calculating different measures. 1.0beta2 - Friday 23 December 2005 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Added convenience method ezcConsoleOutput::outputLine(). This one adds a line break after outputting the desired text and works also without any text to print a blank line (similar to Pascals writeln()). - Added option filter parameter to ezcConsoleParameter::getHelp(). This allows you to filter which parameters the information will return information about. - Added a new class ezcConsoleOption. This represents a single option. Before this was named parameter and an array was used to structure option properties. Therefore changed / removed the following methods in ezcConsoleParameter (now ezcConsoleInput): * getParamDef() -> Deprecated & removed, since getOption() now returns the complete option object, including settings and value. * getDefaults() -> Deprecated & removed, since getOption() now returns the complete option object including the default. - Added new setting "mandatory" to ezcConsoleOption. This indicates that an option should always have submitted. Normally all options are optional. - Added method getSynopsis(). This generates a synopsis string for the program autonmatically. - Added cell, row and table based format and alignment settings to ezcConsoleTable. This allows you to let rows and cells inherit their format and alignment settings. Rows inherit the format, align and (new) borderFormat value that were set globally, but only if they have no explicit value set. Cells inherit their row's format and alignment settings if they are not explicitly set. - Added property to rows. This allows a more flexible way to specify the format that a border of a row should have, instead of just saying: "this is a headline row". - Added new alignment constant ALIGN_DEFAULT to ezcConsoleTable. This is the default alignment used in ezcConsoleTable objects (cell, row) to indicate, that the alignment setting should be inherited from the parent. - Refactored the whole package to fit into the unified layout and provide a much more convenient API. - Renamed the class ezcConsoleParameter to ezcConsoleInput. Reflects it's purpose much better and avoids conflicts with the acronym "option", which is now used in ezcConsoleOption. Renamed the cooperating types and methods: * ezcConsoleParameter::registerParam() to ezcConsoleInput::registerOption(). * ezcConsoleParameter::unregisterParam() to ezcConsoleInput::unregisterOption(). * ezcConsoleParameter::getParam() to ezcConsoleInput::getOption(). * ezcConsoleParameter::getParams() to ezcConsoleInput::getOptions(). - Changed to the use of structs instead of complex array structures: * ezcConsoleOptionRule (replaces old handling of dependencies and exclusions between options - former parameters). * ezcConsoleOutputFormat (to define format options). * ezcConsoleOutputOptions (stores the options for ezcConsoleOutput). * ezcConsoleProgressbarOptions (stores the options for ezcConsoleProgressbar). * ezcConsoleTableOptions (stores the options for ezcConsoleTable). - Changed to the use of "extended structs" where it made sense to avoid complex arrays: * ezcConsoleOutputFormats (iteration and on-the-fly creation of format definitions in ezcConsoleOutput through $out->formats->{formatname}->{formatoption} = ...;) - Changed handling of parameter names in ezcConsoleInput. Short and long parameters are now also handled without their prefixes ("-" for short names and "--" for long names) everywhere. - Changed to use overloading property access - __get(),__set(),__isset(). To unify access to object properties and in different other places, which are explicitly mentioned above (see e.g. ezcConsoleTable) where possible. - Changed handling of default values for ezcConsoleOption. Default values are now set if the parameter is not submitted at all. If an option that expects a value is submitted without, this results in an exception. - Changed "colAlign" option to "defaultAlign". - Changed handling of table structure to classes ezcConsoleTableRow and ezcConsoleTableCell. These allow a convenient way to create and customize console based tables: * $table[0][]; // creates a new cell * $table[2][]->format = 'important'; // creates a new cell and set's it's format to 'important', * $table[2]->align = ezcConsoleTable::ALIGN_CENTER; // sets the default align - Removed $cols setting from ezcConsoleTable. The table now knows on its own how many columns it has, when it is rendered. - Renamed ezcConsoleParameter to ezcConsoleInput. - Renamed methods (consistency): * ezcConsoleOutput::styleText() to ezcConsoleOutput::formatText(). * ezcConsoleInput::fromString() to ezcConsoleInput::registerOptionString(). * ezcConsoleInput::getValues() to ezcConsoleInput::getOptionsValues(). - Fixed bug with argument handling and non-value parameters in ezcConsoleInput. It was not possible to sumit the name of a parameter as an argument after the argument seperator "-- ". - Fixed bug with submitting multiple parameters of the same name without value. 1.0beta1 - Friday 25 November 2005 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Initial release of this package. ConsoleTools-1.7.5/DESCRIPTION000066400000000000000000000002731470000742400156550ustar00rootroot00000000000000A set of classes to do different actions with the console, also called shell. It can render a progress bar, tables and a status bar and contains a class for parsing command line options. ConsoleTools-1.7.5/LICENSE.txt000066400000000000000000000267071470000742400160040ustar00rootroot00000000000000Zeta Components Copyright 2011-2015 The Zeta Components development group Apache Zeta Components Copyright 2010-2011 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). Originally developed by eZ Systems as eZ Components Copyright 2005-2010 eZ Systems AS (http://ez.no) ----- Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ConsoleTools-1.7.5/README.rst000066400000000000000000000012001470000742400156250ustar00rootroot00000000000000Zeta Components - ConsoleTools ============================== Introduction ------------ The ConsoleTools component provides several useful tools to build applications that run on a computer console (sometimes also called shell or command line). For example, eZ Publish includes several shell scripts that perform tasks like clearing caches. Documentation ------------- `eZ Components - ConsoleTools Online documentation `_ Changelog --------- * See the `ChangeLog File `_. License ------- This solution is licensed under the `MIT `_. ConsoleTools-1.7.5/TODO000066400000000000000000000001541470000742400146350ustar00rootroot00000000000000- Fix issue with Gnome terminal not recognizing position restore sequence. - Add generation of help tables. ConsoleTools-1.7.5/composer.json000066400000000000000000000024111470000742400166650ustar00rootroot00000000000000{ "authors": [ { "name": "Sergey Alexeev" }, { "name": "Sebastian Bergmann" }, { "name": "Jan Borsodi" }, { "name": "Raymond Bosman" }, { "name": "Frederik Holljen" }, { "name": "Kore Nordmann" }, { "name": "Derick Rethans" }, { "name": "Vadym Savchuk" }, { "name": "Tobias Schlitt" }, { "name": "Alexandru Stanoi" } ], "autoload": { "classmap": [ "src" ] }, "autoload-dev": { "classmap": [ "tests" ] }, "description": "A set of classes to do different actions with the console (also called shell). It can render a progress bar, tables and a status bar and contains a class for parsing command line options.", "homepage": "https://github.com/zetacomponents", "license": "Apache-2.0", "name": "zetacomponents/console-tools", "type": "library", "require": { "zetacomponents/base": "~1.8" }, "require-dev": { "phpunit/phpunit": "~9.0", "zetacomponents/unit-test": "*" } } ConsoleTools-1.7.5/design/000077500000000000000000000000001470000742400154165ustar00rootroot00000000000000ConsoleTools-1.7.5/design/class_diagram.png000066400000000000000000032452161470000742400207320ustar00rootroot00000000000000PNG  IHDR \ R)bKGD IDATx{t/pPk lEAvUUQ[x*HD+:]֭V;:UP@B1(83 iZ-D(JՊ `@JƐp(׵VߝIeeeeִ 46 .6`# .6`# .6`# .6`# .6Ҽ |^̞=;~{fN .e˖eʔ)NަLlD _>k&MC: hTt؈ dٹ &@YlYLR4j2eO:\40Ahl&MC: ht؈ ([oU5\> /ر\Pٳs{iӦMZl+oB4i$M4⋹ꪫrae]wMӡCq1bD^{gu]޽{]_˥oΠ1=;Y4/t4˗/裏֚{L߾}_*۷/@]k׮͕W^1cdÆ 5̛7/-ܒ.,FJ-\pv[jL|_p@QGG}4:uʭޚ^x!WG}^z)?Ү]<#ׯ_֯__?3֮]O>9wyg5koyDz|[.nf̘Ç2zkodݺuN`*++!( Ir9dɒ%W^x!W^yez֭[(p@Fwm2k֬3if\~5kVvg}6sON:| _Hww93a„<ԩSf̘N`@.] . ]tI˖-S\\Cf…u7id?+++ˠA/|!EEEڵk暬ZFO?G}4_2un̻gϞ曓$wuW^z)g}v:wt9Çŋ7{]I>_Һut!˼ym}.]4vXve|߬31cdO-K[.7pCk{0aB]6?O|%;vLͳFfΜYgnrl[> 5[`4iR~r)j͵j*>`[c|K_HoݺuV^]}|=׊=COg]vI\x;vlFniG}/zm 5k:g]vC=?;wn铵k֘/**̙3sQGU??dʕu>dܹ]r]}O~*?O̹.]tQ~_k-浩ͽw6V\˖-SN^3mJçy_O$Iɓ'טuc=_:uj='OW\2}:oҤIMV}t峐t{ 80+W_ŋg͚5ygry祢"C ɛoYZ?Fׯj.|ǹ袋ꫯ"O=ToJyyyN=|9[oչ瞛={v>̜93]vMEEEF_+WŋSQQիW'L=kon}Ҷ<ߪ[Q6V~/_{9#3wܬ]6/B뗏>(Æ _K}}x,X +Vȿ˿Ԛ/~ &oSN9%93ceYre-[N:)IrXotA={vV^kfܹ9ꨣRYYYfK`)~_fksmeKVrG殻ʥ^+Vfי;wn$ 2zn̘1裏2xkgh"{l~_%IK.Mt};ȪUrqeԩ9蠃RTT:(SNq=zt+_ԩSsGM6W+IF$ư~Eiݺu$o~'m[xqG$\><0O>#FgϞi߾}:w\k~>|xvuEK^~sI۶m^{?yd5֘8qb{G֭[e˖9#r&Iϟ`)~&I9y%IO5VX3<3ׯI'G5:"\|ŵ-))Ieee^}5k$Iڶm[+iƌI:MM~GK.ؗ$|Pc|J7'NUtAormy~U[\ڷo$Yre4F֭[=>~]cqܭ[1&^{TVVW^$y,[Op$IqqqJ?]r^~_Wi֬Y_~9tHؒ|$6V5^oZcKdÆ 5Ư$3`)Iwyk׮gիWoL~6۵k$IyyV}g$I_K׿|&Ms޼yuֈ^ts9'ƍˢE'IfΜ m;ҬY3f?ǧiӦ9sk5m_zu^y啭Ϋ.;wnaWރ0I2f̘⋹馛r_~5:lذkogz .vO>9߾x~Z󥥥iҤI[c3`1b{wqIq՚+//O&M>T}_ 'z+U^ziڴiN:)I֗7lؐ[o5ɦ5>&M^5׻wZc3my~_fڵ9SVV=͛}"wTСCuƏu d6e5[nIvi;,ڑm/'I:Zsv[5kԘgz .v/8k̙O>9ڵkO /Yg͛gԨQL6-//}){ouw-[f„ kogy'Y >9cǎG}4={/~,Y$ׯϚ5kR^^')));3#Fv|'ڵ˓O>d…… 3`̜93mڴwO}<$ɠA2cƌ|Y~}3dȐ$Ssۖo޽{sw;d>|x>ӧOsL .ٳvڼ++rwn2 SNSQQQ+GK/")ٳgkuh\kGU.]^x! Bw߭qަW}? kRYYYY$>&MCa7>Gy$WywS}\RR?[{?>w^uQy'ҪU0`@/^=5k뮻.7xc~8 G}Tke˖EiժUu /0=\Io~33lذy)..η*EEEO^z)\sMJJJҾ}4k,[8∌1"/~:-̙3'KRTT#8"?yO0!'|rve4o<Z1 ̙3SRR͛g},XVEC-v{p[{r!e˖i߾}?{n馌92I2f̘m~mg::\l'bN?BPÔ)S 4z .H.]2hРBPMe .1c9i@@4iRZ46)@:&Mzi@ 4)԰n:M @c@/_I_=%%%۷o3k<-Z(.--W\+";0+vN`gU^^$9蠃jٳ`95&W^yef͚Fs=ѣGOeeenݺCypt(M\Tf͚^fMF8:uʀ2bĈYfg @CRpT{IcǦ$'ONq))))dl„ |qcgyf/_ &03NxswfΜ9YjUg|N߾}kĭ\2cǎO`3|t-&Mj<{|}yw^~\o~3Ijժ?c,YR[oeҤI7o^Ə6mX /̊+$k׮̓> 6K=G[. ,Ȃ fС[nʕ+k>l?իWw޵zye9SB'3*//Oٳv=wI=2uZcOڵk'I6lP=vee˖;wn~ӟf?~|֮]W8^w]{$IvjUU٧`'ԤIOu' (6֢Es'G_>8k׮͝wޙ}ܪ7ݢuEEEOsU6mZګ>}PpeرI={,wqGW^)++iH_$/f:u$ykWo۷O~2nܸ{9nۦ >걺mk.;vLX\XU } .v$+xG8裓$zkz뭬]6?SRRo}[q}I5*3G?J{SOMIIIx^:u떤˖-:tHOdٲe9skuҥ83jԨ|͵"zUCy2`}ԩS,?ϒ${]G}tf͚y& I l^xбcL0!'xbڵk-Z{[ҷove„ 9ӳ{Yfc=rgNrLQQQҵk ><7xcuUW]Ν;Yf욗_~y]w5EEEݻwF]'tRڴi~ֺO?tڶmN8ƩIeeee<4iRϾ1cdܸqy衇ҹs$o /yW a}[l>;{g&NX=6qgذa yn%%% nYYY3hݺun\z9묳$ӦMߞVZ8;'PvX.zIK.$z*pVlo .)(!CdȐ!NԴ 46 .6~d IDAT`# ݔ)S @ ~/)h`~zSI@2dH R4m̞=;~{ؤBVB'ylٲL2i2e` th`}]l .vB'N믿N:eyg2v(w}YpazI&gMiii9̛7/ӦMKUSVVV}d˗$I^2bĈ?S3f$IFN:%Iwp ߿y{-Z(I{ךg}j@c`'t$ɡZcW^b-[$ׯ_k˛Kk׮\XU 4 .v;WTuhȎRYYYI&[تU6;_fӦMkUյ/ ^{W^y%ӟ/zOS,]4?pWǎ[oeŊرc+VT@cRχw '$I~<ׯς Vq$K/?λロ)))E]T[թbٲecp@dҥ֮B СCc^7;|̜93ϰaj̵j*_~yq{e)..?>:fJyyyzU$ɱ}. . ,eee;t֭[gرׯ_vm4o<ݺuM7ݔ$)**m۶mƏ!C8͚5KӧO}5S\uUܹs5k$I'6m駟O?mVwB 믯1o&Iv}۷W\+bkx9k.2dHƍ7x#;wNysM6r``'|%%%%ydÆ yWs7&I=g=3'N8qb3lذl:\4R%%% nYYYx 7|sn:t\]lݺun\z9묳$ӦMߞVZm׽`{PpӥK@]6W4N$;3䩧5\$w}e…ٳg&Mg}69C2o޼L6-tبz]u\tEձU*-[V=v$I.]Z+תh:\>;F7ߜo\^7|̜93ϰajĵj*_~yq{e)..?>:fJyyyux`RTT]f׶mی?>C Iqqq5kۧO>ktꪫҹs4k,IN:)mڴO?]+ק~:m۶ 'Ѐw; <~mܭk߾}\q;s'e]2dȐ7.oF:w$y7 /L6ml+.h>瞙8qbĉS\\aÆ03v:\4R%%% n]-֭[nȥ^:+I2mڴ~iժUNa/8zkK.I^ ;gEC2dH R44-t (HB'yw:LRSp@tA: Zn-Sp@9s1N .ȤI2tBIN- )0iҤ=zt6m}^:{5׻w̛7/ӧOi>M[SNMqygSOkIqw_.\={fҤIygSZZC9$˴iӒE9s$Iw^+hLtJJJjE [6mjժL81K,I>}2p 8F܌3$FJN$on?5b1Qp{<A@Ek 6H AM*UbAxCjb``bʉ\D:p6ZP% DaYa{gYkX̳_8 ]uUKfҥ\~婪J֭}VJ :tXbY~}CMj =М8?dy ?y+2eJfΜ+2_W$gƍ\/++KhѢZC;fС:th^{3cƌL48pQQQ+W'HEE?sYvm6lؐΝ;7Z۰aC!!KPȣ>|3$iժU3L?>znݚwy'{o BƎ[m8bժUZ޽$+Wl͉.CGĉsw;h֯_K/4?ꫯfԨQZnxݩS_>ÇOEEEx≜viy|F&˓$ o>6'\XMMMjjj3[o5#8"GqD;\z]k߾}MTTT<;vgz]w]wlٲ%Ir9m۶Ypa .L3x`905t :t};v5\kf}C ɐ!CڵkL:5WNݓ$W/1cƤm۶{&`?qѣӵk̚5X5kV***2jԨ&s@3U(˾555eߝiӦM&Lۿ%\$?~ᄡnf)q;L\yׯ_ShI[UUUJ>؎ ,uCܹsK?` \g#F(u`ORUUU^0p,Z(w_cԜ9sJpZjUΝ[M̝;' '\g>Ahn3rRf 1p7H޽w6gΜ,]4Iҿ1@Fbks=&k/R&OGy$ sǧo߾%H ]RsiӦr- RQQnݺe曳iӦ + ) M>=֭E]T]|Yn]O^d{-K]o)Sdٸqcz왯}kҗԨ6,N>O欳W\;jjjݿ}zٙ4iR^}y9sUWM6E̞=;zjtRڵk BfϞK.$:tW&ا \4cׯѣok+Vȭޚ[sMlܸ1_o֮],[,ӦMK۶m*Ø1caÆ$͛8۶mwzϓO>TVV6Y̲eSO+_^eEs3foN8!???I2mڴb?7|3ݻwϴi /dԩ֭[VXzh3{챙7o^/^/}4ٯ͑ P(4U}ѢEI믿>=zH۶m3~6 @?>'tRZj}s馛$?: 7ܐ=z#ȑ#$.y7~Xc{={l͑ f쭷J{}WN|skT?7;;&Im۶{֯_$СCZC4G-KpSSS4IRVV[li_V8OC_M?qONh>O'IVX˾nݺ%I^~F늊ba+vܹsdÆ Mj =hN;$ɝwޙkfwB?wg$I&NW_}5~a^}~IA{;u$yDziӦ曹;:cǪU޽{'IV\٤͑ f.KΝꫯ_r9s$I/7z#W_/[o=z//6 gs=4hPFO|{ac9s˛7p%VSSu9ӧOϐ!CҡCj*}w|K_*u!ӧOψ#ҥKK.Ccǎޫ:\pA:q-ܲt=ٲeKsI۶mp& .L3x~&oep(ȑ#k7>`N{,ݻwO^:Æ ˘1co| ./F]f֬YڬYRQQQF0잁 6md„ ?~֬Y5kd0aBZn]xK-KC)q;L\yׯ_S`JUUUciQ͍ NR8ԕ: \'̜9sJ -Jq~hѢ91v2pf6ѢiY~x{ksҥK$ψ#d4Hbks=&k/R&OGy$ sǧo߾%H EpxyvXߴiSn\pHn2||ٴiN @  R(ӧgݺu袋/8֭K Ñ fmoNsEezv6}e:묌;v$ߟCf9s3eʔlٲeZbEƍ_bn ;ʒ$={vN=tҥӵk ̞=;{-=ֲعgy+V[o֭[sվ&MJmmmշlْc^+Y&ӦM3yhƌ 6$I6ZI# IDAToޜٶm[ƏdRYYd2˖-SO=|+{~`O91cF~p 7o^IiӦzŋsm>s̼k9S]]_|1sI߾}lٲ̟?{l͛ŋ/O}4ٯ.B555YhQO=$ǏiŨQrGO'I&Nnݺ%PƄ 2lذ,X Æ k 7P:rL6-.I~?={6@3[o%IzOmb]}ժUICŊMjw\uǎ$۶me'I:td uדl/޳e˖tw76jժokѢEڞ~/0pЌ}ӟdŊ9vWVV{i߾}>***r<LΝvlذ!;wnaÆb(M?fNKyYvm6oޜ~) 9}:uJ}|'_җ}W_}u.uQ9#RYY[ncgk߾}MTTT<;vgzhNҽ{g˖-Is9'm۶… /\0۷?!9rd~ 5fԩyҽ{$ի3lذ3&7J~^9faڵkf͚U͚5+5jT p82p@ЦML0!Ϛ5kf͚̟??&LH֭KLRrJƍ;3IrW_~%NJUUUJ\Rhn \lvZ:nĈܹsK=I=կ~1 v~2p 81``?ȑ#K`K-ٜ9sJ|xnlڴ`3pPbB!B1>}z֭[.X⋳nݺL>0pp{rYg{)rgС0`@=L2%[lI3lذl۶~VJPyW={vN=tҥ׵k ̞=;@8}ݙ9sf9\qI-[dر1cFz|YfMM*۶mKr??34O~I'|2uuul婧JAbҤI5kVz?m۶M̜93ZNڪU$C+V(5jTyL>=YdInݚe yI:4٫*\PUUU͛͛7̤Ik|ƍ>|+_Iuuu/_ &I T)++KhѢ^ 8T8\s5'?/?|^{p I\2OOOӼg?O|޽{'IV\$CCU.v^Ș1cruו:J<7tS /W^I\zٳg^}Ռ5*_r9?aZn^_R__$4hP./_來x뭷|'}Ieee^x|3ٯϬIMMG=餓r'S.ڷoiӦ*)//Oǎsg䡇iڵ+>s9i۶m.\侅 }= .LEEE.\z饥D \+2?pwyO~s9'奎d \>:v[F.]:p3p2vhܹЈK{./76lX#F: \ֆ 2sL>=/2`jࢪ*UUU@R__{.?2o޼$Ʌ^I&eРA%Ng-Z1vjΜ9͖ Y2dHy|=ܓ /0GuTcUVeܹWZ(̝;3p4+73iҤ|-u}'MuuuFYЬ>Ңƍ?7pC.j~G}4|Aկf۶mi5woFzõ9sdҥIgĈ2w.}nݺu>}zz衼۷onTUU裏.u<>k6=\jjjK}z֭[.X⋋s1N:G1VXqeo=6mj[WW?C̀rfʔ)ٲeKg) Y`A{ӟP(ol4 ={vN=tҥӵk ̞=; v_^zRGHȘ1cҷol۶q+cƌɲe˲ylذ!?3iҤ-[2v̘1#oV>Y&ӦMUW]UӥK|ڵk$k׮wܑ.]䦛ni'|2uuulVYYM5z`ײ84ח:!/s,Y)Sd޼y)//ψ#0`@IJ G 7ܐ=z$IFiӦw-?I'[nI^ze„ 6lX,XaÆ%IN=TUUe֬Y׿_W~x$^Ϟ=C?xVX{'UUUi߾}#֎;;&ImVZ*I2t޿bŊFW^ye^|WJ^77Ͱ~$?%c{ 8ص(uMeee)++krooұc}ѹ[$N.袴k.:uѣ}z6me]v裏%\5k6ƍs7W^9#ӳgό?>7oa͹7կ~]v'?{Wt۶m[ӻwyСC ^|T,]4W\qa}IҪU?`wlܸchW e.8:w^zeʔ)$I]]]~h"ӧOorC=T9sf-Z_ԩyy晍}ʕ8qb-['|mνy֙gwy>[6zn[lҥKtҼkvzo~;'w޲_'L***r<m<+VNkɓ'MΝvlذ!;wnaÆb plٲ+ٴiSn$ɸqR^^_=uuuַ$y'OO?ŋӳg7]wݵgu]K3dڴi,CgV|ݮ]?dȐFLۿN$իWʊ_'*<O,>裓$۶mI;3mڴSO=k6g3q5O~ʜtIy'rueժUy2tД9555X{o>ӦMKUUU***R^^;3C=TRVV$iu;6mJ֭}^ڷo6m uyw<~>ɹ?wy2u,^8O%8qbƎ$??_z<^9rdjC>SKݓ$Wΰa2f̘|(qB8~^ */B>IҧOӳg$o~7j:u.,-ʲeo~3r5{2k֬uY-'F]f֬YڬYRQQQF0[.8\qyd…œ.s'/|Yvm嬳gdz%_X;RVVk6>}sI#8bر6md„ ?~֬Y5kd0aBg`ײ`O|ӟN BzxyDzdɒr)ڶmkԧ>u֥w9cdɒxy73iҤL4Qs)dܸq;$W^yeWTo9??GZjݻ~]~QG%K䪫ʱ-[裏ΰaòtҜ|,xֽޛ??Nyyy֮]=zdɒ%2dHN8ᄴn:[Ή'o13fgv*wuW]0+'\_zo;wO~=zvt)w}w=wOY[￟ 4(W\qE# 6mܹs3u,\0sL>ݻ`( I9st$I3bĈ}\.`ԩSS]] 6, ,Ȑ!CҢaoKϧm۶;qf޼yYxq.$ɳ>[\իWcɌ3 /{^~_7kI>}4Yk5f￟ٳggg>Sk_Z~_ggQ BՓdԨQ9#O?tdĉ9S^^^ze„ I $I񔋚$ɋ/w}7w^6oޜE%3///opm؂AfO?mIق`6믿?hp^x! ̙3'Iv}92 Ȍ3>,uv^˘1cҽ{7gə6mZ_zצMV7kB`4eʔ\uUرc~_;/&Mg}> \sMr'w}S^^?I [NK렃J\z3fLjkk3a„\r% o>I76lXޜp k޼y:th~Neee=vaEGeNȰa2v۷޵G?{sΩIӦM$oM74of:t'e,}fС?׍x [gܹskOc=:, '\™9sf~e-~;7xcz 0 :u*:",,=Zʭޚ޽{g6*Z+{wn'CpIv1ČբEtMիW]wݴh";s}2cϿo1rK|l&Iz+={I'O<`9䐢Av}EKGy'欳7lEǂ&aӧO1T*, J~rEe=HYYYqI_tz|Ф)\TWth>#3w\ Ei3gN뮻(9zFn-sq h$UUUI^{1"Iҵks1p@> <8̘1cҡCuY9Fhž\~Rc=6nmկ 93iҤ+]w]w^t$`9X-ȬYrW^u嬣:*~޴lٲ1b@!ʋ@n;8p*UUUEXl{w;nF{ 8d.vmVZӥKuQ ~GK.y7$C';]v͡+"|BXP>O޽{?1}s=ҥKڴiS7*sOO$MBEXvFn-׿?8blRUUQFeرԩSݵc歷ʮv7MnzO<9sO>?]=Ooy뭷Ɔ 3g6׿5|O޽{FG}4_Iq@3o/OǎSUU<{wJRр4#$իWNs9yg2| 0 ISO-~wqGz뭴k. ʳ>n)/r`~kZjc=67|suVt4si|ĈYoc3ȪR'/K^~5*I2cƌE5lذ$ɹ瞛;&I:w~zs&I6|lH$wyg8∴lٲDҪG橧J=2|L6-*$y('Nlno&I:uTo|wn0?L ?VD MEE}\szl$9#3hРOJ=?9ٳgݜ_8qbڵk+[mU:vc9fr/g|$Ys`E'>|x8E?gΜ7 ([@k׮yg2~~s_$ɴi\?6`9{dzn'Ȱaþpѣsꩧf7΁y-@=}YN8|ի=O3iҤu"sA%I.Ҍ3&0aB.s۷o$y\?6.x zh铪\} {ꪫҩSTUUYg#F8VR{G݉ڵKuuu{w'?Iw}楗^>iҤEڧo߾i߾}&Ok׮ׯ_:t`nd_c=i_hD`9;rGrwǼ+usN;l馹ӭ[ ><'Ng6۬TSS^///OϞ=$GuT]?3se˖iѢE:v옟߿w"hѢEn+뮻nZh?w^x`V_}}dǰx㍹[fM6I[ٳgN:餜x'fe{% eK.)ueN3u$I޽ӧOe `Я_lᆹ뮻lF۷o`)\,#R)g}v~ng`ηTTTo. !iٲe.}z9 2d[or-ٳrN`={C=%b~bQ]s=veEGhtNfw2dH6d 80nΝ^{-sNN>:#F(:j䡇*:RSSSt e  =#9CӦM=:ni9^zi;#C- e}Ks.w}w?L>=EhTNfnKy ,[$)g9O:5?fE|8ӦM7E_7.Xcn:w3s̺9 K=:~3f4{ƌ۷o\sʹm6w2y/9lYmղ[ٳ^Xu%?QtF hڵk7|3&LoM6-ݻwϋ/ڎ;gy&kf_|~;M7ݴFT\Y`o~q衇OSy[neƌ^{fϞ=#555 o׿5 T*uֹ袋rꩧQt:|+_Y~__̖[nÇgΜ9y駳[dܸq˖8SK/O>y睗$y/mJW_}un'|ٳg'Hl?CfΜ9>|xr˼k++LMMMvu<;wn&Lw=?xn%~fhrSNEGhtNf2sɌ3k,}o}&N{,_><0:tĉ, GNΝ$|AZn|g_^Tѣ /dر &,ͫw#<}ku>ӱcnj?~N;vl^{zW_}5[ouk 6lA:jƏ;.pNT^ne˖={v>䓴lٲn|֬YY}ճjeIp1gΜhѢZyyzN;찌=z.IW_=fZIz>Xuy*/:w$2dB92뮻nv$ ?IbA9s,tEK_SOѣ[O3a„UTT,/z$6m"e h$կ~{y'?IJR?BW$O?]o[lEr\76nܸ{$_$ÇW\N8^cܹ5aÆ%If1T*53\.:^{N׮]s=dԩO3rzyG}-wٳgOγ>sf9䓓$zh6m$In̜93&L_x饗 2$gΘ1c$IG[Os=O?4Æ I'$իB=#${Θ1cR[[ɓ'3LYYYzJ ;Xo2~^?#swfW:ujvuzf?޶mۺ86|_7y }ƿ/9蠃ݻw\o;gy&-[\ӦMK׮]3iҤ믾y駳;/4<9mfȑ/ꬹYkҫWM(\|(\|fΜYtp;l0dΜ9_x̝;wYO?KE(_ѣG2dH~_}}ǏO=2}eXBm]jjjT.B92J.z7tS=^:tO?:d:*2v̞=(KL($/B͛W7޶m$g{wy$oݻ祗^ZIեK̝;7?.>~{y7SO%IfϞ)Sv<  6H6mrM6޽{F<[ou]w݌5(KLXgɹկ~^{-~r!9R[[5va;ݻg}?<XTUU(KLX]v%۷O޽s}eܹyW$[neꪼT_ IDAT˹Ǝ;gW<>}䨣ʍ7޸<X9KLX̙?{Ns1裏裏_oVZemɏ~\xᅙ2eJu~)JYeUrM7'?I .HTZޏ]E;wnQHYɧ rW^y7'IclfYgu2nܸ|;vlz{z꩙7o^k6,Ǐooy뮻w]64[ r6z7޺u#SLiPHk6/Bnz;cƍ6tt1/b7C+ `,^z)?sa`(\M\)S[nܹsr|I'޻nlĈm2hРw̕W^+"NQdM2%#G!r)'f_E1lذ>v`Xye OgϞO{K/4-Zg4TUU5(EhS[[žwu]A'XEѪUta=(dmi۶G?<'|rfϞEU]] `p4iyg믿>OEEE#.쒚J,2 I[otMs+\@3uԼEGXd @뮻_ɓ,N:3jVtEp4i]tIYYYFYt` UVV旿e6|,2 0-[9묳N+:ƍ#8"g.: RVJeee0`@ƍ믿8RXmgرEGR  a̙9 /4ښm]>y7m]`f:9rdQPuY'oik?y'5ù瞛M6$wyg 4TVV=(_ѿ{V3<_QK.y衇p0v腋$UUUeҤI1cFQ¨k)SXB]tɼyRSSSt/p0SVV{(j׮]6pÌ=(_HXazj2jԨKaȐ!9*8v#FX ;SB RYY;ҭ[l|/)+JC4%J֭cHrV^t"7tS ;8@pvZL81[.:)\,ӳve}͠A,GEh\s\wu;2tТ@S[[O? p<~wٳgYfeu#hQpP7<]v۾}âKK.裏+ `T]]$NN;UW]5F*: @  i 6Ȗ[n4`VYYN:( (\+1Ν[\5kV1PVXՅ}#<v!3f(dh.7):@  k׮Ywu3eʔn?^xrX.Vn3lr߻M6/~k&cƌYVYT*`ET*>d9rdVYe# K, Ȅ r5hD K}9ss_/:HJR+9sdw+8@#PhoF6x㬲*EGO?TUU tM3p$Iʋ~ <RҥKFYt :  &}ԩS,.]dҤI1cFQ(\ԩS.첥^:R)FjT@2o޼  lb-sXBڵˆnp4 @Ν(TUUeȑVc:th^~c K.Ms`pO>iuu#F4Za޼y9묳rI'T*VUUU=zt1(\D׮]3eʔ+EGIgy'3hР C 'y@h:w-Z4S.r)3Ȕ)SM. . ~(PBeee.tС(g?K˖-(bP38#UUUEǨg5_;3?xqETV*JEh:ꨌ7.ƍKeeeq/ Kn2s̢  Q{'gyf1ФL2%zkJRQЬ̛7/G}t{챢K:~a^}բ+1 Y)//ϋ/'x(7w@C;S***2jԨ+1 0;S׭s=6aÆewΌ3MJeee:upJ(ZkLڵkF>lX:v7|3~QɩJMMM1t53f㋎6`\qB)|Nuuuƌ @p4;;CZj{(__~s=ӿ̜93/bQ쬲*ȑ#r7fĉ꫋MFǎ3rtС(JJh3bĈc,m6w^._*:4 ҥKV]uբ+) Pd6l2Y{Y8묳?iQ$EVn3g\&mvh"=P{u5JB8TT, ,E% h/v"e)ұ,JWt@Ltp2KHHr'㜜 a 4Hl6ΤΆ PpZffQpֆ tQTk:~e{naaaJHH*C=>}hȑ4JujɓB0ϯR ѣGR)Uw}W:u(T 0t B*c j۶V?뛎Sf7p֯_o:. Pծ][ c*blC8:\\ PpP㏺;f: .''GVMG. *oԞ={c: fo߾ڸq(Pp\ƩS4m4RnիiӦi֬Y7 VZ)!!t,6f:@eHIIQ``6lؠ]Snl6v]rww7 Y(pWPPmf:JX,={ۧzt*TXXo߮ϛ9 .1=zPdddJݳ2k„ 4i>l:j*##C{1Ts\Ү];%&&Q!ոqc7t*LHH|}}s<(.m۶:y^xA'N0r3~x-^t PMQp&MZjiѦPnvܩkך) .>|^~e1*fΜ%KhP.”h:(.+??_|{z4rHeffU ӡCtiQ@5DpY۷WRRΟ?o:JywTN/p$.BPpT=oZdc*[``ԼysQj^{5jxQ@5DpY!!!W\\(p*em۶\P)!!t P Qp@5fXdX\n׶mۮh;*8 (_mȑ#ȸ^z)44R+[ot P *f3=:s|}}/9oՕ8:\ybСCzGj: N 1"R8//OS;w7dl6;Vofs4m439gjȐ!Uxx8p wEիV^]mgA \P||~'eee饗^$9Rnnnڻw233Mvb [N999UPP=iӦiӔviǎݻթS'}7 8&gYj֬߯LHV\)1m6䫯RNN4k,IK|,եKEEE)//t g*իMGZXI.bccաCIRZZjժ%I駟ԪU+IRFF_|zm믿}ݧ͛kϞ=nݺvܩÇo=t萂uwjӦM咳,{%%%)44Tt)իWO5jޏ$x_u}W^ԸqK=%Njժ&Mcǚ@eee[v?8 :\ڴicf͚[liϯw_N:I$$5nX,IڵkW,^!!!֭+I*(((~$iԩц snkKsVrbbb4i$>|t@5W㼼<=3U֭'J _~S@@ "}}}!COuo]6OzzƍƍKAAAQvveYvڥ]Oӈ#a1-((ĉլY3yyyf͚j߾ϟTc%ut8++Ky]&|||VNNNYܔ_.9˲WVV֭ۧ[_/̙3Zj,XXIR@@^{5 > *77WmڴQ&MfqXI?k sh"͚5K EEY?dX m߾]VΝX,ޫ׫F咳,{]s͖u) IDAT5:uTO=Ν[࢐fرco;'IӦMӘ1cC Pj۶m+rK7o^⚠ IѣGeي}@j*YRwА!Ce^Й3g===Giɦ\T||~'eee饗^$9Rnnnڻw233Mvb׬Yu)''G ѣG5mڴ6mծ];رCyyyڽ{:uoF .,e٫Yfڿ233#IZrŠH|WQZZf͚%I>}z .@ 6L?yf{'G$EDDhWJJeXtw[|6-okҤ,{=uv?-44T-Z0fΜVZ[FG'9sYfQtt$ I< OOOu^xbŊ\l$O>џ'Ez%Iq,^ӧO-"=ZtG%Iof̘'NhĈl:r%?XlFa@\|\Ҽ˭رbcc̵Zڼy=wõwb9}}}eiӦ\ru@=Zcǎ-v믿^'N$5jHG՜9s&ٳV^(L/77ˮ'5o<,S999*++ĬuֵM\mβ앓c/lQ{wKjС|}}QF׷իս{wyxxAƍԮ][[nըQԨQ#KS\\<8ګnݺի%j߾{wոqc߬ (44TVHHƍ>WraÅ.,b۷9.((9%9w\,^v*,~_… ձcGeff*&&F7xfϞ}E\:Lȑ#k.m޼\wL~[(T{Չ]"u֩[n URRõzۧGQF]J~GKHHoG.<<\)))c: yf >\Կ?#m߾]JIIQtt,rV{\߿>֤IY,{JKKS֭պukIE;f\"==]uҥKէOqJzzRSSu 7&*Eǎj͛ps{-[M6咳"u IRFtQ͙3^lr={jtӿ 0܊m(WV (::Z7n[8RvmmݺUFRFuW^@wUƍIRTT,XPy{{[!!!7n>r3:\cJ:hks=`gX$I  .P*zݻk.y{{@a:3f(??_w6RRR|r1@G Z~~M5k֨W^:sj׮m:ppͦI&)..tEmV6M* X,}'Zv( ׯ Pp oߞTaaaaJHH0TazSIR߾} ' BO/Lǫ4Ee¼yԼys1ULΝեK1(3 . I:q℺uΝ;kڻwcǎiѣwTqQ gϚerqEFFѣ* j߾֭[nASNݻݻW/j֬/R={T~~T*f:(ڵkkٲe3T1\@C ÇuwjzԢE S͚5Ӌ/[N:4{lӱ4ӦMSNLeVZ)!!tPPp`/aÆaÆR``"##g-e.}k4iq)==ȼ͛7kݺukb թS!!!%)_nѢEjѢ<<O<ۦMm޼Y~~~(͝;W&LK/t999JHHP۶mi/Էo_eee[UV{ux_qqq뮻]伧6mڤt=(--a /l|v5*_nʔ);vl^^^ԣG+QK/+.YD:znf}w;LǩΟ?Tp1Ә1ctY:\TӧOO>JKKѣueeei۶mz'oElb_9rDQQQ:#G(77Wc$uK:u*Rl~)++K?d=c=:t|AmٲEڴi4i\+E=Ziiizꩧt*33S~ZhGjҤIBe}.4+<<\qqqݻճgOh:ydr\pEAAA_LGH1.j*==]3T!\T9sĉ?~z-[7o}Y;wN3gμu4qDIRttt gVNNq{ :wO>DG/͛OtuE+VP˖-驖-[jŊҥΜ93f8\whŊر|}}uwj޼y[$𐏏.-[Lzrvz/t뭷jƍj׮ԢE \R]tѹs4{R&OFqEڷoOU1b+-]txQ@B@%/$IO?O>$iÆ %^ܹs߿u_/ro$9Z*ͦ#GDz$IkIرcUFїjԨ^xAn:k۶$)55M7$I֭/^?s-[fӯZn.Tϟ# M0A>>>E.¼Ѯ];%$$QuYO%)_^z h ,3+,0 ڶmZjEխ[WoΝXq.$$$D7T!\T7={V6į[?gb-Z\r+}}Ν%Iׯ5\ Ν;pٳgdZ<򈒒/h…:t6m*I:tFU!ٮ%=DžTNm6[ n:ӧեKQw}6lL! .*Qa7[j]vvLM4I={,qn&M$IWt~I{={7KYrRSSeXp?Pd^yiذ  h߾}Zv$iӦM_EIJJr8'Ijݺu5x9033ؚCs:LG6fΜ[8 .*< IzW_edXԭ["?vܩ޽{_`b璓eXdk۶v=ߌ_رc:ujԨ>~KL"k 4uTI=sW"((HEw.vC3[Y2mڴbcϟo!IzG d…%SX\sߩ4in[8 .*ȑ#UvmP||***J^{55kh̙jժ>"%1bh"?^ǏWff6nhf1x"kΝziݺu ̙3ua+++Kz7eZ_,=aĈY[[{Qnn٣޽{kӦMO?}Տa%I}_ +99YWQU/R?ݫ\ܹS=zЖ-[t7kСEwI4l0mٲE:tƌYf$iŊWnnna: YblCdXxb/>KGYYYϚ5K#F[V%%%]̳pB=rOqFy{{ONNV޽upssSLL&MTڵkշo_;奥KW^E GJzTvAUF*$[iK],e3g9_~}mذAv[DuY;w p.]hӦMEƪˊK,Qdd.>:pPɺuQCM7$ծ][]vՆ YСCcСN:ԭު_~Y~mb I Ѯ]4w\= մiSEEEiǎ-$顇5`];v+h(Zji۶m?hB>>>rwwWpp{9رHEyg+WM6j]7VttvUBx'66V={T@@<==ծ];}zꩧJgѢEz'wwwxy[PLGU.1Z댁Pr-zg׿tpP Z~Ν;g: (ƍ+11t PPpP zߛRiҤI6PmV c* R[6m8QgjԩzWMGTc~߯TQڷoOE馛K/iʔ)MTSaaa*((PRR(QpT16M6t pi኏MGv׿*$$DQQQT (00 pY\c,Y:u꘎QjJOO CpssӼyuV͟?t@5x1Zl)___TKzg /v)33t (F;wtRswwjնmLG&M$3t@54~x}c'n:"##j*1O?tUO<ϛQmk޼y:q(Ep}5!?tj7(Ncٲe#H8b:Hl6 8^z)""t .jɒ%ZjW}5ˣd[Tt4LGT?MN Pe8qBVU{$رcZ֭dc߾}Ne4f3F˖-Tyi::u&Nh:pR\*#99YԲe"!!!29{؛/;4c EGG?VrB*λᆱwyt kʕZf(j ,,LIIIc'E2J*( 7ܠ޽{_TVVV%b9rD.k׮O?tqUjÇYVׇ~(I;wV.]*I>}Vɘ(Et >ֿ8qB-2 ~I~[zMGTqmڴ;].C\\Ǐo^_~/KKKӴiԽ{wGz뭷}sa!5rHuIs^" 4{l[۷wܡ!ChժUEuu/~5|bse*}wZZ.]0]wu9׷̤]jW~~MGq]wL3fZhxQr7R @~EDDQFZ|5x`5mTK,O֠A<'z衇$Iz'ta-YDZp|}}\;**JΝ$e8; IDATggk*((PLL$iƌ]vi׮]Pdde[JKK+RP땙:;סCkÆ zGK g+>>^wq8.cС裏4l0ݝMXX.CtUBrr$)$$qE→EZb{=# }:|4h j۶mZ`n}mۦ^ƍ$"윘X쓞7nXK~%+Ij޼y/B^|XUmV?.bhΜ9t@fZQT -[tx\Ѷl"IVÆ 뫘%&&jʕy111jժ<<<Ժuk?^qb;v6l(///{TŽ/ֲetGZf0`<==+ٷo? J.TdU]vg:i֬w֭['u֬Yԭ[7-_/f[w5U8v+΢}JJJRMG儂 5w\IRHH5}tIRhh_믗$L[ԴiSIc5$IVRVV>ɓ'ݬY3I/R캅cst%bȐ!믵sNѣȹzAqFСCkذa+1h k}vT4a„2߷~jŮyfk׮~<.%??_sQ^^(*blCdXxbEDD8J֬Y65kҢ'hĈڰa8*ԩS5k&O̿Y$:\~9rNS/BCCMBlڴIuɓ'MGqi3g?6PE)//O;w48 :\jN+""BcPZj%ͦ8=C㸴:(**Jcƌу>kt$ V:;?>D%@󐘉xT$5S%nV^bv2ZyCCРǙҜSo"!~\TPdCytk 9]jҤq@S>>>3: $M4I%J[oet@1`2dXsN" ԬY3 .GyD3f̐QńbQ||1@A@jڴvܩL@Rn4j(cƍСCrQ@dt-]T%J0:ҥKQpKJzap+C _JJʒd2:)QQQ 2:Ttt<233;CY7: s0:P`mܸ(" c:ѣG5d5o\_5i$n L͞=[]vUfԲeKO+WuW^L=*Yyeff.)))6m{9mr$iX,۷oeXql:ubц 򵆒t%?^_1cd9ŋոqcyyy}Q{QPJJ=ϡYf3:OVU;w6: g rNF*I:|TjU-_\ ۷|||m'hΜ98w/^d?>۹ӧ+%%իWSN$&&j}/SLъ+ǟ~*VK4}rpp;#IJOOנA}$ٳTBBf͚%쟫1p@%%%\|233v|fͲ""Iׯ_~]zUsYYY/$I;vGŋfd2ym\o:~ܹsV||"##նm[͘1CTjj\]]%^Pi&mVte9sFfY͛7:}sHW_}T8xmذAݺuZ\Ge EEbG}dt ` vBBB$7cEIFXm۶M%I[n1n5k/ĈҩSTBEFFj튈ȶ{âEm6'O/_X 0@eu .ԁhQ UVՊ+KI+bۊAfΜ۷k˖-5j$iɒ%߱cΜ9#ժUK#9s&y`?~\+V9sTB=zT͓ի7nhϞ= }rppPllk-IM4k>ӧUre͟?=pȑccc%Iuqfx85mT?@aXt)?( \b}ݺus=ٲevء'xB֭Ӈ~{O(*$$Df~l+J SUD k̘1}FRZd6$I5zhUZUݻ$w7J&N(9::F;v$iڵ99rT"gggwg(w.՚vl¦sΒUV}Anۺw%ɾ~Prr:w4}ѺukI[oV4zhխ[=9r:tH.VZ>SU|ySFd2wbXO2ESLzw%ŋ5|pQ${{Ĵi4mڴc}7rJ.}9mN>-In:9sFO>?a9;;kȐ!/i&=JHHZl))kx)IRsr˗/Kʔ)㜭'Ojժںu7ont" @۷o7:'իWOgϞ5: 0;\?)S\BBB;(&&ofgLێqssvvε?ɭPfQD ;eYvn:OiiiաC-_owO鏝;֯_y"///)11Q/_ַ~MC 6T8p@ 4895u^p@1w^.@fZk. 0@7oo߮^{M0cddaǏgddMʕu1ݻWM4ݻ7G_ɤ,]zվCnM/ooo8qBk֬w3777uI:uҁyf͟?_ӧOWn$IW$͘1C9ضmիW{yB 矵g5m޾gIvڵӞ={4|]zi͛7W||ʕ+jݺaʕuqڵ+%$$[lY;wNIII*[lsIII>[Ν7n8ȣ/B>,_3x(lz۷+==]3f=99smV4i$%$$(##CפIrt\R׮]5y|Y ”7nҥKeX4hР|iuɓΝ;bhٲeJMMUZ#;f9rDTլY\n֬Vȑ#yӪU+Iĉo>eddh߾}uoѢ_W988wݰ\<䓒EeS>3,vRFF~GM0!GڵkKN8㜭[~1O?[n5kQy)=d@󉉉w+88XժU/}* @/r۹Eٳ}wӷo_UVMSHH6m>1m"]vUǎ%I}$M}TJ>}ZW@@3gΨJ*߿oٲeﯴ4ծ][^^^URJ?m'aHHj׮si P>}m+m ["x^F1c@Á PZ`?f;[ovکdɒ2ͪ[&MH,YrǹJ,uIrrrƏ/I2ÇW#l6+00Po-J.H[rssSV4o޼{A7TŊtI_llV͚5շo_7Nׯ_׺u{?*S֭[ׯ5O2e4w\S^^^rtt^z%͛7OnnnkNRbn-lfY޽<<}裏***%ooo w6mڨw:t(d\ܤCɓ'M6jҤz!* 0(jJ,cj:{Ξ=իWkرrqq1:y>EִikFGڴi.]dtPȜP9RUTѺuO?)==]_ԬY3(5j!ChԩW_}U\r>}㕕%dt$@!3(&&F]v5:(D\AAAA 2: owrJ}/|ݻzmt AJ.ڵkjRpCggg}_qƲZF BдiSL&SFsNcBF@!㍎qƺ|;ftPp`hڴ~c|󓓓vܩ5k .@ctI& Sff؈dɒՎ;ԳgOBOad2_P())I4: ӬY4tPc Qhh ;\"OPTdee\]]cխ[8ʕСCգGh8Bꫯ2 .@ cxHEGG+88Ĝt!UT(Oz҂ 4p@޽[FG0WRP(uI{FG<\897jժ)<<\&MÇ(`+(6.\:]6mҩSԥKyyyiڵFǻCv=Ζ-[rJ%$$(99YeʔQzԵkW5o޼sZ,Ij̙3Zpt9(QB*USO=^zg0BAqLL$Iٳgdw7bEGGkРAڼyL&ё Pl$$$H֭װLyoᄏwk=и IDATj˖-ڲeڶmql6^>|X^joKOOסCt!YFs̑Wg+vޭYfiɒ%ҋ/(5hh/׮]3:pO@XBݺu3:Pp\؎ǘ0a6oެҥK+44Tmڴ_%%%)..NgƍUD ?rVŴitU5mTjԨ #- #'''aÆ8q$72b4w\]pAzKpΝk`2ȟ,/mڴu0mذS_sy}NJՕ+WTZ5С@#""Bo|||4{lIRjj̙7_Ur*44Tf9\7oҥK4+WNZҐ!Cꚭ޶A^Yj$_VjrgOOOhƌU~lsjڸqf7oC\rݚ̒}׺tʕ+6mh*]tq/^ӧk߾}rvvO?aÆe}SϟŋުTb?%%E?<<>>>teG_~~7eddѣ ך5kr?}tXOOOנA4|9sFׯ_ٳgaÆ)33~'|Zr222t9-^X'O}g{JZly17o.Iڽ{ws֬Y4%''kڵ߿8歮\oZhΟ?oh*5555T||Ҕ˗kJ 3gjݺ~zʔ)_|QNҦM۲et򲞳fҧ~'OJMMSQQQ]^W_)55U9 Tjj* EXZZjԨa/Ňbю; XHHH$zl3|]xQO/&N(777͟?_z) Y,FG؝`%)S4eᆱw}W;39sFTv Ht}޽[TzfM $I{n;W~ԩSGn[p I9 z-٬%Kh۶mrppГO>nnnԩ̙ O>JNN)/ו-[Vz[hzT|lET^u5JgϞ5:5nE($QplO:UΝSZZL"Ţ^xsnZDݸqC.]҇~(ŢAfJOOWbbƌcoONN]NUV$I'NԾ}}iҤIran^z%I:zumٲEoV5)_:w+99YՓ{翗{zvYE˖-Sjjjժ%IwݸU^]fEam߾x@Ǝ+www 6(6d͞=s2:_~ڸqۧ>۹W^ysW~ۧl\\\4|pqVvZ+<<3-YDK,ѧjժ:th_~YK.toҽS^ֳO>8q&Oɓ'g.]h 58a`~lz1]&M<gϞOzGeoBBB Lnɓ'رc@1dt Zli㣎;8%KرcۋNV^3ftf .Pxyy)))IEaaartt4:a5j!ChԩW_}U⏂ ?1jtbڵ[T3((HAAAFRVVL&1@0oft{ү_?(QBFGv_}"##| . m6M4 7n(55(p2:͂rJcxH-]R?~~W/_8($111JHH㍎G5RFFݫf͚0vEF=ԣGcx4mTdZ N”'*>>({TF yzzjǎFG;\"#&&c=8=FA!ӧ,XjɉQFڵk1Om@a2,#xhF@!2LO_z7EV2:x(EJ.]dt h\R SffBRvm=ZcǎUUZ5#b(22R2F PC=z0:) PjU >>jРIRЫW[j 6}c///5jHݻwWFԨQ#;vL[V͚5 L rmv~mu]ޒ]j̘1ZtJ,Y1ײeKIUr}֯_ҥKG-x9 ,,1s… >^҅ 4w\Pfg2d2r_~]pIҥKԫW/*UJӧs\5OJzg5ϕ+W4j(ըQCΪV”k{y/s߿_m۶URT\9 l }*S[`~lz1{g%00P>>>ഴ4h"׿?ꫯgي'ʖ-K.e+("""[MVVF?LӦMӈ# /ڷo .j1۹=zH.]jD4P@P\zU:uꔽ-11^lqt 4H={VJHHЬY櫯Rjjs T||6lؠnݺ]PbbbSf{۴idZմiS͞=[:|M6)22RƩ]͛J*i„ 0a>sEDD(++^tqsdDD$ijݺgiȐ!9s& Ň~+""Bvȑ#zg J ySNB ?:uh3f.\… ujѪYN8'*>>^WV.]dZsݭ#66VTN9lm\%ۧ=yw [QբETzuIO\&L˗8gҥӧT.]4x<kСzճgO-[~رcY@AG)%n7|#I SUD k̘19nܸQ4qDQ5jرc%Ik׮\$UZ5ǹjժe%G$ըQC&z$Iq$)33L:U%KԆ o^z8qRSS V O;wNW\@A N8!Ijذav}O<)IԩS.]H=zǹl[Llmw_lcūxknIII9fd!zӧ'|R SJ4{<M̙c߽5j02(NF 6,nm/ YYYَs{ >rtpY>@QctyԳgϻV:9 ~iǎZ|{=1B zs^z%͞=[mۦcǎE*U2:O\Aʕu1ݻWM4ݻ7G_ooo8qBk֬w*[Ν;$-[69'@qգG#(@/&O ͜9Sŋ5m4M:U[_1M&taHj֬Ǐ?VHH(PL&f̘!Ţ ȑ#Y&@ΏI@m۶$M4I 5iҤ}[n-I SbbnܸK.?bi/NiӦj߾>3h]cǎ'|Rcl[[- #h۶m6lV*'''yzzK.SÆ =~5jI$i3g"jԨQ?~Aɻᆱׯ/TZ رZZ:gɒ%N:]NNN%If޷tҊTPP(777jJ˶;śo+Q钤U[n͑c֭*]} P{+S\]]ʕ+{/WhL&}#ZxXF)<<\III2gaxWC|~g]~].]ze뗕{cǎ:{ڹs]k׮wsww/)[J<== N  OOOgk=U\lnnn1bFq1۵kvek+U4g>}Z+V$>}Z{z… ۷֭[ܩSt)-_\:tТEf@JG7n(m IDATo]vi׮]Zt# O М9sԸqcyвeKY,-[LO?qI6lXG>mQQQVHHHj֭֬[ *hԩ]zwUL]V:uRFFѱlRe˖ʕ+{N~]_d|}}e6Ureo+Rh{Hܹsiٲe2Lj׮]c>|XڱcG;v(ɤe˖׽@qg*.bd2)w5] "QU4Ek[DbZmͭtPmR%%$Bj֥DAsyܳ>f?{EE)000cxآ$M=yھ}^u]v͢A˗/Wڳ+L ,?Oݾ}"~ھ}%ITxx<\oܸQ/M}vt1Zj^}l+..N-[TFFYnݪƍ~G+JII6?wwwũB ۽0?^9͝-[j9믿|_l1QV(P(si{-_\rwwӧr{UBݸqC.]YСC5sLG7~ʕ+ŋŋrtt4;~$a Pd\|YJIIш#t k0`233ϛ3 ɓ'̙czi 8Po֠Atieff<==uM2cI?\C%ԬY3b .( @ԩu %&&SN~uk;vںuUL}Gf#FPJJx 8qBJKKӖ-[TV-9sF&M*[!!!ڿj׮;v(--MSƍe08~Ĉjݺۧ7n())I;wVzzz*x 8qB~#I 2urtt233~zy\ HǥK7lؠ [ Pd̞=[/^￯>LrppPF7hذaz &HƌcYfƍ޽kN͛7ŋ%I-2'''Kj֬ UjjZj+VvڲWڵb jJ²ߢE XBM6o$ڵ,v߾}SSvvvrttT˖-#IZzuv~9$-\PM6J( j…Ç8~Z~4h {{{լYS˗/+˗/kم~666/yyywղeK?l\^S@@$)66loFׯ_Wǎs޽? xQpN+H\zU=zЭ[Ԯ];}'f6m$ 2b N>mjKOO$,Y2gbnƍwyG}]Il:Ԣ_$]vͬg$uAQQQ_L}kז`йs ,3g`0A}=^7n/nfcc1cH藺I-Z7|7?ש]v*S֭[۷oKn߾09::?keee׮]RJ]vyG,[LvvvNjٲeNx%%%Iwĉ|M9sFZx+fII /\]]uTRS"{ۍӢYL7ׯvޭݻwKS+cǎzeoo_`ݭ _n:uJG޽{g}D$G(RVZUVY; `%֐׸9s(&&F...Zzʔ)c*);Vxzz*>>^GUÆ s5֭[f;)HR...ׯgnӷo_*UJ}/I_4|͟?_ժUӆ Q ^Xt}:u*c]]]۞9 mllO+""BO?$???ر#\͝;Wj֬>sIR.]$I]vU||XIRsu}pa0xūHXq׆+Wc' 6L666TZA7o.IZ~}?e˪gϞ6W^v̕+Wr:w})99Y߿$I?^( ЩS+88XsիuϱtU-ݫlsZj *h͚5ڿv-{{{uQԭ[7IҦM?+66VO=^}\;vE/k<<==~ڵKZedd( @iiicljtA%&&iӦ; @3f_#Fn|ʕ+u5ؘo>ݻW۷gS\AR~I֮];j֭[~_n}ǒYfif}ϟ7ʒs:-ZXڵKTNPشiS :TO秗_~,&?שXbW_}M6Iڴic] 6Ԟ={'NhO"vEƲe˴l2k !7ǺuELLlllԡCѣGFV$IsεKLLMm/ڴi .{JKKq_UӦMFmjo׮$iԩ2iӦI^yё#G,4ibVɓ'%IzLӳ?m4nݺO>D[cTXGMYYY6lENNo߽{wI… ͎ V"10ǏJ(adx .\0\~?j׮mdm6ӆr${9CXX6ܼyӐfHHH0L:PB$Ä /$CN G1ܸqpCN NNN3gΘtC; PfMÆ  7o ڵ3H2tڵPry Aa .nݺeHLL44F!999ʔ)cѣѣ7noxW UT1\vA`0"##Mqf}NYYYg}PX1$ʗ/ouV;'pqEEE)000cxآdz//__̜֭9S6{{{k߾}"""4`loܸ6o,Dĉ9QX17N&M[fv7nX(QBK,YEv5j~)1+WΝ;UjB-/%I||lkɒ%:qℾ[ubHbO?^ 4U9]K-Zhzgt1999s;;L>]m۶-7o;vh9sf;'[tA_>TT)iF6l׶mktҲ??P[l(:u(!!Aj߾dgg'yyyi:p@ԱcG߿_={/.777[Fpuuݻ{VZrttTQFf[^_^Z`ի%JU֮]>@Yfe;>((H111U3h:tE񫯾R믿*$$Ĭ/) @mp;\y-Ir&w;vL5jȶ/&&FqqqF[n35@ x=J@nЁT~}kT$@ތ5J۶m˶ ҥKe0Խ{wyyy^zVGV^mPkZ;GA|||b۶mٶ+88XrssSJ+==!g gO-oookQ̛7O/^T@@Gx͛gxPp ydeei֬YSƍբE OV28IJKKShh|||ԸqcuQ3gTffY唒%K襗^RM1+V,Y<xؘ855UcǎJ(!www7Nsh۶m*[Znmoպuk.]ZZ  >\UT19r^@v0-\t|M%$$(!!Aׯ_WPPP2335h %%%bϟ?%&&*,,L9?;cJKKS&M,4i{jÆ ҥ_>w$jժMmɚ#g/ԟi{]zUM65ٳg5c mڴI;w}b IRhhvޭ;v$iҥyTRRԩhٳG111WݫXI2#>>tsNIR͚5-r5c%`x<}tǫaÆ:pn޼#GYfڴi""",UҴh"I.I9sҔ-[H֬YsN$yxxh׮]qv!www%$$($$a Py{{bTEEEԩSjٲ7J&OJ*I<<<4qDjݺu1cǎIVjnr/&&Fxb=s4̟?_Ԁƌ=Z㔔 ۷OQQQ:|6o,rJIٳոqcIRӦM5{lkN+V|@PpÇפI8}߿nhmٳg%I>>>ٮwseIErĉ<!; m5j0;>:v}Y޽EvN>-Nݚ7on@Pp{w0l0wW۶mըQ#mݺUW֡C4sLEFFjСҥKtsEjj}mll$I}6c ȽgCŽK[ڷo<==թS'թSG/j׮sbw *())I?,X>TH8777%''k͚5rsss.e˖Յ tU-[֬xǽ]ǎә3gTj|ͱ~zIҮ]OK9bysѣڱcڴicj߱c)?P8(ȇN:ܹs;v:tի_~$$rתU+͟?_ƍ;Cիʕ+ԢEYfIsc`ٳgUJIR5t%''[V$''b(*5kk 6lhP}qyyyI:w*44TuեK4}tM6MZ͛8$}ڵԿSgBBB4h EFF[? $Iz lO>hB7ָq$I 4s\߾}ÇgϞjذڵkEA#F0Ŗ)SF秎;J6m*IJLLּy9yS'jԨa9bرzk.⋲M&'''M>vIԻwo9::_ԡCLqw}W^^^:uꔚ4i"{{{5mTgΜQMg.#'>>)OŋWLLN:%IzgռysSE^J,͙3G[lŋ hРA4Ŏ=Z_}.\LIRvkݻY۷oWɒ%զMB<ȫ;v(((iȣ(Z; (4_|ƍgƍRJi׮]rJ;wNjѢ>o{༡y֮]۷onݺ5j~'M8Qj߾}.]ZvĉrJoXt & `0X; d1acͬY4w\ZJ+W$;wN8p ` /::ZAAA2cwO[k'ѧOUXQQQQ(gϞV GOqk'PP eB9::jĉ6lz%I՗_~)+g ȋ/!ChڴiCAV x:tҥKkӦM_kݺuNH8vjԨQ֎Q\\$QF֭L Cxl<*;Q@Z; DIRڵ͎ԩcQFi۶mV~k8p@aaaZt w.///իW _ekSIm۶"vzz/777UTI~~~?~ru\"Zp$)<<\Zd$)44TL97o.^S[=tE͛7ϊ?ŭ@Qoi̙ڹsRSS޽{Cfq))) ͛\rjݺ|M,Yg,Yd>3>|X%JЫÇQpmذAϟUN:lǛއkҬYb];SRRdK*_)bŊ^z%o+)AAA v 7?~\Z/_DW^^^.<._>}ҥK'Oj„ }:v(IJMM?O:uwEGGk޽իW%IZ|4n8IRXXiwIy٥. IDATׯ>L)))fyY~ԤI&Mh޽ڰat钧& .@c@(ISNDžmtjժ>Hʕӌ3rJEDD .\SNrUfM%%%)88X'O5dV'B ;w"""eSŊ+$I͛7n:hҥ 2]+$C/i;wJj֬i1mΝ\)Nnڵkg{\v!I3fT"'''7NZr)n˖-qƩnݺ /_yfyURE%J0Vq5SqG(?{bwv;&NAɽb((:}٣+Vh.@ѭ[7k$`'qSjԩ?P~wv(($I5jԸoܹs$I/Y{[jL]]]%IYYYÇkҤIS\\k_rppUyg˒$>c1deeĉt.]]ҥKҥK0߼ys}wV<(EJTT'Ttt\666i:m۶UFuV^Z̙3CK.\?- iLrƱ xڪs駟$)NPʕ+rMO?mj;|{=+Zvm rVpIR:uPIR oz *HNc1yw%IǏիWuqڵK'|#Gw޺u&M=z?QpDIw .;~6m*I6m.\ M:Uܹ)e˖ɓ'úu>O>DԼy<ݩS'y{{oUZZW.///IqYLIҪUSN)$$$5jԐ$%''[kl3p?=zг>k*ITTt颾}jѢEf?' Vtرbbb4rH9R1111+(\Oe˖Ç믫Yfo)O>RΝ;QF߿J*߿ӧ$)$$D-ZPƍ5n8Iwv02QcǎX矫y֭J.nmczؖ^o̙lcf͚޽{kȑ9s+O .dԨQ ʶ Ә1c4zh9C{ ,9bv0-[VS۶m";;;լYSSLQLq...7ou˫Xb*_z5ki„ zeoo/{{{UVM}դILqGVʕUX1effw#F_J4i|wvkkNNNNھ}ż۷oWɒ%զM<'˗u hΜ9SLѐ!Chʔ) t%$$ O&EEE)000ǝ$)>>ެ===]]vU۶moKf̘~A˖-CxRDGG+((H?{ z͚5KsժUTreIҹs뫁jVՓ;µsN͞=[˖-VT.#T87o.^S[=tE͛7ϊO>XLmQQQrssSϞ=;}Zh$}:wVXdXBACէ~ Jŕ+W4zhyyyiΝ8p`m0Mxv,kÆ :TZ5uIfiii;w6nܨ]ʕk^ F"%%EK,K/˛b*V(ooo-YDzC8k@A$dg͵ Ka_޻rQ'N԰aԫW/IRllK988X9;@Qs)))I5j0yzzcǎ2eVZoV˖-SΝ t[n)<<\Mmz l;wo֬Y  o޼%$$ $effjРAJJJ2Ş?^JLLTXXlmsttJKKS&M,4i{jÆ ҥK@x5dM6M4tP5hYw[ >\ZR||6nܨW_}@5j(>>6E'O:/_$Xی1Glll,o޼zKzꩧ4aI9;;L2ӧ]f1>==]z)Kϟ`>;v<<xdffe˖zꩧ4~xGO?^{M4n8;V%K W&Mի 988ZpPpK񑏏?hLnnnJNN֚5k5ʖ- .ի*[YիWM1޽{u!yyyO?Մ 4d :zT)ShҤIZfNԺuk+((HgΜGe j֬YS?jӦMЀ $OZ~~~ҭ[^qqqVZzP;7b/jժUo tkftM#Gڵk_gDXԩ*--MիW$תU+IwyQݾ}[ y{{kРAX (gϞ5ըQCҝf<+խ[W>|$)==]gV51cH3gTeoo&MvXbEkH/^zxUiӸV6mRv@ѵkWURi(<==T$yyy~SzԸqcY̛7OIttZn+*V֭[+::@.rO>+!!4h"<̙3 ͛7駟Z;<+_6oެ nZݷ [+Wn:͚5ˢ/00P ҕ+WTt[>>vzAijذF3g(99Y͚53۵ ,^X:tE:tŋ_~.(X\1}w}(;(gϞVPԼ曊T AIzΝ;+$$D Uݺuu%M>]ӦMSVyϸƍڵkΟ?'NEN_ԹsgOݻlSj֭ѣ|MӿTݻK-ZT]pA7nTnݴlٲlcqF]pA+V,@<6 eB7'8q ^zI$/R5@WB5jȬ[׿r3vXZJvҋ/hӧX~ƵΟ?_`s(zBBBd=zTeʔv:gS)0ԩBCCMmݺuSJԫW/zK.tg'tR 6@ ҋ/!Chڴi6m X;-@zj*W1ch-+UvڥÇjժ*^z)*..N/k 6رc9hȑ9ro'FSU^*+::ZŊ3ko֬9R76l ٫_~z,)hc x;Q@Z; @1 =o{ٲev<-Ieʔь34cƌswj֬Sp7n(&&F;vٳNIIIy(km۲mOOOWppJ*OǏWzzCȿmVTff:uJyԟ+pqqQjjyD(m4 ռytEz衋/j޼yV Ȼ3g̙36mShoիվ}{U^]/od)>q)tD=.] &ujܸz 6d[PqرfΜi=GJJ,Y^zI˗7TXQZdRRR l`}:yX#GTΝe0̙3 S{b999)--i<*nUVY; Oe˖Y; Sxl]~] Яjj;z}L 4HIII+""B mϿZ~ԤI&Mh޽ڰat xxF={ҥKv:[o} N2E3fSk׮Y; Gp]k׮NC$)**J*U޽[f;PEFF*))IuQtt٣իWO{Ull$)>>4&>>tsNIR͚5-6cGV\FY;UZ'bΜ9zTdIk-WWW]~iDu'X0>n8=zTo/B4h)xCٳgMm5jԐt 2c4h`4]QQQJNN֤I(6m5k;\ұcr133S۷׵k^9G>܊Q\\$QF֭[A$)((HΜ9}7o߾ںu>={988hĈ2e󓛛֬YMj۶mJLL711QԼy91Ο?ݻkΜ9v:iw k[zS$+WNݻwvEV2e`ksp FRPPP[nյk$I׮]֭[Rf}qi̘1=zBCCuP+>>pt*^HYQeɒ%@XbruuU˖-5|"ѣGr*V233%IڵonUdIӎ駟ֵk4h I7nPTTٮG(z(ȅm۶ݷ_][9=9>Hzz/777UTI~~~?~ (KzJ&LЦMhկ__ҝ'U#GԚ5kgm޼YӦMY\۶mzjٳGׯ$9;;+00PԹsLΝճgO999x ׶m۴pBkX͛7[oMYeʔQ>}LEwOOOW~쬧zJzOjjƎ+(QB7n222;ygiF*WׯbL4aըQC%J7n9s<ٳg5n8URE}{wVUq 䀙e9\gMRoNhh9\s@s( +fbU -5(S8%ipj" 0E IDAT?|8yd<)5dw.xTp!"""""""'''aϏ?H 2e O<5:#g$$$пSۀHHH`&GIqrr/ڵkdffrYfϞ `*(.rڂpttdРA:Zhѣ4i.]2:ccرܹsYf ݺucƍ$''sU֭[;35k֐A`` mڴ?sTٳ'=z 33re.gggvErr2/_fL0!8uTΝ˩SHKK:t~ ;6++;wSV-1bgϞeGGrr2o?QDDDDDDDTp!"""""""K.ӱcGZn͠A ˵P#99___\]]iݺ5=zߟ4Sv233֭tڕ\w?ӱcGFUy/##>ӱcG/_@bb"ӦMm۶8;;Vu-}_ڶmwϟoy"kd帹ѺukڷoСCٲeIIIlذ^zJ*+W6l )))}GS%;vEۗ*T#Z5k!!!GHHfήX1'L4(p=JJJ s+++N8Arr2|9o۶;vpM8@͚59w ,s A˖-9r?m۶ڵbYׯϩSHNNf|WlYYY+V믿͛$%%o"66 йsg.]D`` ̝;g}6qbaeooϦM5: .DDDDDDDr ۹v8qiӦ蛖ƨQXv-;s] NYYYG롐 d*(L裏&99k׮rJBCC?IXX$%%m6|||LrFIxx8$&&_uW???V\IOO'99H͛g7|Crr2mڴq6mڐLXX=)[&Oĉiذ!vvvXZZ舛AAATZl޼9Ø1ch֬Y#b4/^̺uؽ{q K.iӦ1~xRRRO_>{\|9xv튍 mڴ1l޼99y(U5b͚5[΢Kݺu7lqʕ<lٲr~~~$$$0zh8{lUGe߾}|s}m۶%11((<J@DDDDDDD8qT¼yhذ!̘1DGGӸqcMFڵӓpBBBի#""?S^=֭ @zWN"""^z)GZj`zЦ0 +**?5jn:/_/rʬ]+VܮF̚5gyUVݻ>}z1A-___HOOgxyyqFhذakn;p{%"";N.N^bsƈ9EЧOw;ѣGM!KxMߗ+Ws=gL2yܹmp̙<ǜ>}'7Ŗ(s5nO> pׂ{ FXXaaa̜9ӧ3~xJ.8KKKϟ%oG͍ݻ`t{V|yn޼it )p!"""""""<'''ڳWɝ>}:M6ښf͚1cƌݹs'ԫW+++jժŬYؾ}]ngϞlݺ5q 2[մ0 kԩԭ[C7odڴiԨQ[[[|MCr&OLձ5J\v-|'A>}0_'O:TfM>""""`[t)6lP}`ookUƗ*e_vaB~VNEYn,Vŋ _&99ӧSJg$)^+]ty<2^ADDDDDDD A;\#%660_Yf9?WW\믿;WRR} ,`9޽ׯSlY.뛾}ڵkᦢܟϙ?~""""`V~Mwml5kɓ;w.ׂt?PCeȐ!,[_:uy袘[nԨQ͛7<ʗ/OFF1DDDDDDDÅ<"""̾e:0sׯ{믿&--->7o믿ў"ɝ[rnirMtΝ;c}yRSSŅ/2r˝x*""""1b&==}1j( _www~g222‚;[+sũSLmk‚~ѠAƌc:vgܛd.۷o|'*^7n0:vGJj8s GEGHll,8::z,YB6mr?x ƍ#$$>}[n qQz WWW\]]oeڵ,\޽{PbEILLbŊfM}DDi&#H>yZjeرc3uTli޼ٱҥK;`Q\O?4 ԯ_5jCjՈ!88/ W^LOLL 駟6:}uK *plB޽˫w,o?u}ٳ'/^dԩPn]~7|njOlllŠXSyFG|lݺFJpwwg̙9|z-۷eҩS{bϒ%K!"4n8ڶm["~: 6,%++UEDDDDDDDD0Yy-!"""""""R,,, 2n}G=R !!!~.)˗/gժUlٲ0ŋի#Gd'g}MZD.kp2)M6Gݻ9vvvvrN)]vuֱl2n޼ɩS4:<@ÅǏ籗nBhҤ vb\p+WiiiFG+p!"""""""ɓ'SzuvٳgIKKgu >J*1Wkn#4o|||3f ͚538cDzn:F޽{K|W y8pk:u- t:t+رqDDDDDDDD yXZZQ am+)INNNDDD䖓'OR~\s!Zjoq?;+++>SZhU>|ёmt._ KyFɗ .DDDDDDDDDDDDP.{͵ȑ#qFׯ^0 Hi֬?2e M ,##R+Yrr2?˗7:}元FG""""""""""""x`vػwo)))̜9>}H*Upsscƌ"oܹTVhC ::1cPjU.]dtßɀaǎ}oΝxzzRJփf͢W^l߾^zɓԨQ#DZ5kyAq@, V%=Q9‹/ht "ӼH!>|M6CDJM8|1DDDD1i&}C!㱵СC,Z~@jjj?WWWL_Eky\@rrn#"dO?GD+WLc7zYfF'B;AAAFb6p@#c$((wwwcc>`ݹsEsG;uܙVZgnѣG'001cлwB۪ׯ_x׹ݖGQ &`yH1z*ëjt$y̕Ç'11ٺuDDDDDDD .DDDDDDDDDDDDWWW\]]oeڵ,\THQ~cTXxXٱ8ly۳~&Lg}ftGǎϏ> +++z-ƌC &ceeetv{yBJ̒%Kؿ1Dڶm˸q㌎!""""""""b={rEN u~ںYf ӧOgԭ[Wqrrb*8<իW~"66GQSlY|}}quuetHRDQQQ<4hoooz-ʕ+gt,Giiiٓot"%*{iiiL8kRT)Ǝ˜9s|2cƌ!44\]]l|rr2ckkkG}tׯ3oc}<<,~!~!_}+V +++n!믿ٙ4֯_*ݻ޽{Ph)))̜9>}H*UpsscƌODDՕ@<ٸq#cǎ\rEĉ |w[tܙ[raʔ); /^4x"  tyKD(UVӓ itCjj*WfFGˋ+Vn::vhttR6mǏn}O_>{]nO׮]M6ƒ͛79gpp09/JQF2-gQnݺۛvrJ O.\ =,Ξ=X#BDDDDDDD:NNN899ぷzj߿m$$$zjȽ}m67+Ǐ3i$7oN)U*Te˖̘1s~) 6XI]΍7>|8۷oڵks S5jk׮'==8?~]6(ۛPRSSv۷ogذa\vo!996mߦM +kyxxxハcƌYfFo4iB̤ytܙWaΝ;nj3TlhҤ +W(~-[0i${<PfM2YYY9222-B :Δ)S?8qb!""""""R\ÅH1KHH`Ȑ!ر#DZ .pK\\\qpp0 +55w}˗xի7ǏkkbĈv.)AAA\p*U0o<6lHdd$3f !!o`` 4nܘiӦQvmbcc$<<zEDDD;[fΙd^z%?Ό3  Fvہݻw>>kߗ|٫Hsrr2ʯ}L>MbmmMf͘1cFܹOOOիjb֬Yl߾د8:u*mڴښM2}tk8y$5j1>{>""""""E1d[3-ȝ.]Ddd$FGy$lݺmrIT;3g.1˗̙3/@3gN<\׿>}:ϟ͛9kkk.]Jtt4uԡ{L2خGDDDDDDh y2bW- 8;YXXNۗ~j׮ԩS~Y}c~i6oޜv7 O?4'N0x`V UVeȐ!>}:ر#TPWWWswiFE:u'y̞=W:SXe˲sNݩyl۶ kkk,YBLLիiԨ<׏<:;ݯSDDDDQauv t{nٯ8ǜJ _?ɾ۲evODDDDDO?1l0Uƈ#] ^bELjոp}QEr-ZĹsHOO|W9 #gqտΜ9Czz:qqqaÆO?BJJ ̟??߱E;\Ck߾}tޝk׮ؼy3_~%...:k?7aaa۷2eQx':א!CxhѢYm۷/)))k_e^}\ya:t@jj*7߰gZnm?$S[ZZ?3?3k֬СC<3ŒN}V^Mff&Fo[x6l| V??6m[ Vk f֭|t޽@s~"""""jժq=jѣ9:::Khh(%_qy139`ybŊǓHŊ'&&M"R222|7n޼ɗ_~/iӦ_bsZjժFGTp!+WЧO0aٺu+oT\99 Fnxwy?3g7o۶m30aIIIL<5jÇ3f ̞=bv{W^'|®]r-6mZbż 9s#$$Aqi~i;ٿT(jNtԉ3g0|ΝK 8~8ٙ5k0}t&OLݺuz*_'''/_<^zfμ|ᇦk.]ԯ_xbccsVƚm۶CD e˖FGȗ8p $%Kɕ+WxW^1:<@M9}tDDDDDDDDu)<>6m .N:ѪU+>SyYti9t>c˗/͛׏e˖QfMi׮9ׯ7~XaÆE.____37oƆ{͛7_۷o͛y)]4 ~)4O?GQNC_rnݺزݮ(ӧhԨQ!(<:u* 4ᆪe˖ҨQ#+ILLCN杻eԬY~!CЪU+o!CY&ǎcРAlْ.]~z0ao pssGEW9O&>>Fѯ_?S_~ڵkWy;~e˖QtiFٳgٸq-Čx{{BDDDDDDDKBJ+;6ÇgѢE1o޼\$&&2`222ҥKNwZ NNNN9v8HIIlٲܹɓ'XuҒ)S{nvؑu3&G[-vY{j8}4...3SZ5{wnW/;=HJJ̙3spٶm3g,Ԝ)""""򸉈󘽽=+VϏׯSV- ᅬoٲe `ʕ޽ʔ)Cf5ju14iK.%>>"g/̜yYp!|'k׮ >ںtŋٷoo}QlY:uTWgFB 2[AӴiS&LyשRёxÇ7:<7n;{^zѤIӈHaBJ8::/{W܌1sQN>s̎_;$܍W^%))Ew~NC9eʔ 33Ӭ}ڴi :~~U=zн{wu5.]ׯ^zȻ&U^z%N8Qt?O1Ov4SO=eĉ8qbܹ3;w6k˯#>3/_ŋ;L2j*.^HժUx" #Gtw/""R\ED\FG(>/wyM6pqqqwȝ; Ƹq:uq 4AFB{OAWϫʕ+ \rlݺ *su;Vd?_322^gWAArqF p߮];?u+V4{%\d֐^OaL gΜa%(_N.5f\=yOjSDDDDDnqqqˋ;ҢE KDD*T`Ĉ'5k!!!GHHfR<ҺuF3f=-z* .^zkdddi&Wnt4yHdddлwo7oN@@CHIQ3|~[fԩfm5k4=\<gf>,AAAۗ[uV㶶QF<uV?NΝsZ*qd+`ccömۘ2e Y5k:%88#Gl^ren݊Y{ٳg}5e7s~<`$$$[ |^Ӊn Hh yh/0l0U5˗SN1zhgΜ)Æ c޽t֍'x4hܹsٽ{w7nLdd$+Vk׮8::bmmcȑ9r$GE=z?3h )U[9r^z:r?ѨQ#)Uuwȑ#9 3[a߿l666,Z'N0uTpppʊ'x-[2c N>ŋ-[;|ٳ'''J*EZxI&9^]RLJ*ų>)"""""""""'33˗F֭i߾=Ce˖-9&''닫+[G펛]4}뤤$6lK/DJL}*W6l ))TDt^͛c= cȐ!FBPWV*P?Hsk׎vj-o6ovlԨׯ/pvX鉧! ݩN:}ЧO~;wӓ*UPV-f͚E^ؾ};zs'OPFj֬iGDGRRL:eٳgҥ ڵcÆ kkkc# 0#FBDDDDDDD.Tp!"""""""7x""E5~xfϞ͡C8t˖-cذacgggwy\]]s=ϯ>D*Vhv,11GDY~=#FWj*|IZ)11*Tm(]ёK*ry\2IDDDDDDDDDDD1ֳgO{>)yإ,m(qgy"_AEMER;JAIS+%4( Ö,H(D?k=.9 g `{s^sFܿ9Mlܸlz!Zn @5,z)8vW^?୷d2kk>"--֦MRSSe0HR VÄ\l7 IDAT0om6֭kt{N)Ǐ[=h2HHH(){oc$˝QTTk׮ 6)1cưpB jLJ={UZj1ydzꑑ;NNNн{wbccINN? гg^ڵߟ@iڴi}U>BBBؽ{7mڴaŌ={{RG$x &N<`tJG]"""""""R:BDDDDDDDN1*Cә6m+W$11X""b;.+)wwwfϞ? 666lΝkgggGhh(899QZ5ݻ7k׮*Ԝ6mM4Zjзo_j׮M\\\ qqqϖE*7n+RL&<<<]6}Geĉ*CթSVZsqqaF R"""|1D4:]%66%r󃩥uDya֬Y :'''DGGckkkpB  ĠAn)S0eʔ"fVN<==Yf OI&>}DƏOڵo"RZjVΛ7vѲeRWDDDDDDDDDʟN2at  >X'ȽORf0:&OOO]DDDDD/^dر:uv1Kōrssȑ#3gJrr2!!!TZƜ/&&&K.m6Yn%ft"##9<ͳ@\\pb˖-|7cggwkޱct-8ݺu#>>;w2dȐSDDnֹsgXt)'NT"rrJ6nHF^dQqqq~FGTp!eFz`[etΩSpvv7ޠm۶>|@Ξ=kwݺu9r:0sLZlIjj* .$>>[2x`<٢$;lw΢ݻ+VХK=J`` +֭ϯY֯_38p UTÌ?RXqǏұcG=ʬY8}4}&L5۷mܶo>\HDQ<==4:/+Vbȑ*RJJ nnn1+VGDDDDDDDBP)<<PvmIa'VFEEEDDDDDDDIbgҿ>>sN:iذ!۶m3:]i殛;**6lXyFJCE:\,PJ[YA'<ܩҘzu_\v U8ϒ%K8z(M6'e˖o߾׻}4ZjQ5/h-涂>K2i$~gnjt[ݝtot$)BZZFP&99[Сa&SN%66ȇСCa>|8[*"""""bM?cYڿ|}HMM%&&v-9N8t>`2ԇR^=NǧȾ>>>ٳ$ժUɓ'[^׫W qrr"&&dԯ_?ڵkKwl۶ٳg3{|׏;Fnݬy6mU%Ys݉%99WWWzYݫG"R \p>9BϞ=(KҶm[fΜIHHH9)={rĉ"O;.ߘ3g}k׮1m۶뗕ſ/{9vʀXl.\g20LG=xgXh׮]cժUOЫW/^xlb5w}O?m>e*ɺ ;++HtBÆ -}7nd"22"qғPh-WfРAw}T^֭[3o<lll,} '''U{fڵV')L6&MPZ5rsso;{I,̲eѣիWaÆ=UVYOо}{-ZĸqذaU3gҷo_lll_>ÇgԬYTܷo_j׮M\\\qⰳb"""T/}0_/0ydxNOCK˗/sa>ŋlٲedeeY> cv55ܹ֭!C(HY_~Μ9\pFL2)S9nnnVm9>ŝ֭o9wqƲg…^3]| J}DƏEDet)aÆD~Wl̙3y饗ptt,G>`ܸq$$$P*""""""""R跂bd:tPp9ڵkoA x7 |ᇜj׮M@@ |ǖ~w Gy5j裏2sL|cB͚5-U?rݼ gxx8QQQ :nZ%.(ɺ rqzA͚7onGDDDDDH 88~{ Wթ rw%(([rΜ9֭[ VZF{[nݚQ"R*zӽDDDDDDD1:Bʕ%Kd3|J˯ @6mwi}QvnqnԲeK\vk1w\8xwy,C#* }k6s# oO?Dnn.5'`ر4lR ;wϏK0qD\]] N%""r{?^e8p]veذaMt5FŨQt>gg2_V233oӧQbt )\HTJ;ըQyڵ+{O>waݺuL8!CrN˳̙|oժ1*"""""RV'FG)ue9ݨ~""RL::t6l@^^Çutр"w?5k;?ƃ>ht,2;?#Ǐf͚FGT?9-RXz5:t !!+WJBB,5j"w,JLLj7vrr4hk֬?d̘1?e˖xB/Z Z[q]GGG:73u0k,θHNNN9%%%1n8xϟπ8v}=z0:Hz*Ço%&&F""""""""PdzEACXt)\t%K`2x-z … IJJʕ+$%%h"zY`2ظq#vH-[FRR5k֤O>kZxVVt҅ Z4nDdd$F޾&{Vݺuoy-FT^(~i㈈TZ*rws?`<8::FHHҥKj yK?{{{xسg<3888xnwwwWNTT'OiӦٓ1cXM6~trsssTZ]vdb֬Y]}e1zhqⰳg-!"""""r3///lbt  "%1c8w-%%ٳgsU x饗,HOO'""xBCC]me?~<\tM6q5 gǎdggqNn݈gΝ 22T4m۶5:+W"%<<3gontJMRmf?7$3 .eunA1hР"sssͭXs2c f̘qYwAsשSOOO֬YӧiҤ O&11$EDDDDD<<< "=$ro8wڵ7ޠA|DŽZ .>CNm۶ȑ#̚5֮]meh֬AAA4jԈ5kݻ  !!3m߾}* lڴ ___[ZZ&M"##_8R\|y3a㈈Tz*b3f ~)p68TQQQFG n՞޽{>}:...;]bݖk<>(3g'_~m\["44yǁ7k޼U(==իWj*Ξ=K/dtiѢ̛7Zhat$@jԨ_M"e`޽XBBZQNvJܚ-AAA꫌5 [b jժep:Tbt "믿ЦM">}^dqN:Ys;Zlik׮yOFF}D*rJ6n܈cǎe„ {?ׯgĉl߾8R8::AHZZFPHΝcҥL8WWWSHE2x`<==!"TDD[l1:Hv9y LN5*˳[h5j8_ժU]3U*Ci&\]]Yj#G7sޣW^DFF2b#HPܵtS?H"RI]|Y"F/Bv ?Lbb"?=11'''K[*UŋRIOO'333.晙>"SL[nFG)5=z`رkۗzIDDDDDDDDDJYDDDDDDDDDDDDݻw`ҥs%,Yd矷ݻ7 .$))+WĢEٳ-[ɓ' *,mmڴ 555_sHE1` UlaxbEDDDDDDDDDʀN)Ǐ[=h2Oi,NVɛߧEEEqvʰaJ}~<^xvERR /[3f _~%iiiV\\\z͛Y|9˗/իGFF899C݉%99WWWɀuܽիʕ+駟"k鉍QDDDDDDDD:BDDDDDDDN1zEO"$$ӧ3m4V\Ibbb9'{MBBB憽=5jԠm۶,^[ưahذ!ժUaÆ9k`;ydJݺu[n̚5O6&MPZ5rss۷/k&..._8xgo{N)_Æ _7:cf͚رc-""""""""rw R0k,mٙ ޲_ݺu߿~̘13fX37fVN<==Yf OI&>}DƏOڵo&7;8""""""""R\H|2Æ 3:T0U< ',,g2bKȑ#Y~=aaaNDD3O?pKHxx8NNNx{{NDD֭[yW b„ FǑr֣G!"""""""% )3ᄇCDDDDDD*sαс IDATb KNN-[d̜9.f͚5ڵ Я_?Ǝ 7~'ř Gpp0vƆ=z0i$4hPfyرcͣ{?3ӧy deeI.]hذOƍ1LDFF2j(o+Ƚ֖ ^}UF\wŊԪUt""R<ȑ#7ns1:cH Bʌ7\xcr)K۱c,7ח#GXΜ9Chh(ɄPjRWs.^.]ĶmHLLdݺu888I3fp!,Y+7&==`6l̙3 .nc֭[kݺu#>>;w2dȐeu???.] ĉquu58H>}XrqDDDDDDDDJ|8[cǎ$}Y~G-Zy9z(-bڵC=_źu裏0LZ ~C^^iiivY Xva.\@߾}47|CDD;vdժU~zzI.]ߧ6mڐNjjjŠTK1TT-Z`Μ9̙3ÇCIDDDDDDDDDJH"(66f͚СC-Htt4SDDDDDB>ǧȾ>>>ٳ$ժUɓ'[^׫W qrr"&&dԯ_?ڵk,OVVAAAԮ]yQzu9r$saÆ >uޝXquu#99={8<==4:He>L^^DDDDDDB g2 < ٳ16rH֯_OXX~~~^+.\@-fXݻ9{,u___Zje;m4~mͽ%0˖-cΜ98pcZo1w\pvvߟ9ŝ |ˉcVaggdz>[""roۻw/+V0:ФIѣ15jޣ{ڵ 777#I/g\e.)ZDD[l1:2\Hu9VX޽{ɡe˖=3gdggfvN ׯ,‘OHH ++HtBÆ -}7nd"22QFaoo_ׯٳΜ9@ 2e SL)rL77|GgwƯ[.˗/y,X ?s|wwwdɒrC?KE/_VH .^رc9uꔥرcbˑ#G,mgΜ!44dBBBZjsرluZn݈gΝ 2W%""""""z";;3f憝,^vkcƌO?%<<qrrt""b(#H1UR,[TlNtܙuQZ5#H)))q ,<ڶmolsܹO?c= ԫWqr%(([rΜ9֭[ VZF-ߟzyflll$""""""""H'\H3Lj޽y\]] dҤIV}w… qvvE1x`mVǏYf5oܪ9\\\ؾ};?4jԈ'xcXl/stR&NDDDDDDnOnn.߿uRڻw/|{%55WҰaCq^x{9# )Dyyyw""""""""""R؞ìY:t(NNN8;;N`` 999RDJΝ;rb c Sp1^[od׼GZZ|{jjjm /"""""""""RٙL&IɕYXXgϞeĈ#GrY L&"ȑ#ߟ>("Œʸqpqqf͚899ő#G _J[~,!!~lllhٲ%3f… Vؾ};?7o+4wwǎc̘14i4i'N(r]| O?4ԫWAY߉'UVԬYΝ;3w\)i+K.oҫW/^: 4cϞ=Æ t6mK/t׭QDDDDDD*7)}?3>>>EaϞ=$%%muVZL<^zddd111tޝXquu?99={DDDDDD*hjԨat  "%o;o>.\@=z41l$$$X{sի6m[fժUdggfvN ׯcǎ7d2ɨQ/wTDCve 0 IPqqq 0[Ӊ`lڴnqZx饗zɓ,^;wG:uXnԯ_sо}{{1O?~W>6nȖ-[ӧOcu_-[VeF\nWyeՄW_qZh7g϶#޽gRN\]]-M6~trss۷/˗/'..ѣG[刋rↈ-[زe1DDD1c8w-%%ٳgsUx[.[,ↂsss9̙3LHHUZ6>~x233gnڴk׮Ph;vMn]ܹ֭!CzE .d˖-L:U']]+##Cɓ 115k'Gr_^^^-Z̙3x-?ǏիӤI8/ȡCXx1`޽s8@DD:uȷSL!&&_tRb 777><Ȏ;S$&&riKӧO7k.Պ/}{|sh׮7o櫯 44mѢ?55(bq\ .SOyfڷo ۷gCw-5k? ));uĎ;puuƆm۲i&y222x _NJW^dgg3c ܰ#55ŋX /J˘1cO ''''Ku.{ٰaÌ "`yIDʟd*V{BBeӧ@@@@EIȅڵ K3p(#((m6l5%?<ǏkܬyV}D2d /=j2:_~+PN#+I ? >vX|MvoQ`LFɕ+W۷/-^ɔ%'';;-NZ*:wfkĉ{1|?כ6mʉ'߿?'NW^4m[ߝfQi|~E y$ խ[R ӧO/O?%00 eF\]?ݩƍ &88zz7n\ikkKPP*F`֭XB8Att4'!7iӦT51ܪ=-- A?%%%_[˖--;88pڵ"ddd`ooGD=o6ڵcѢE̝;8rl_|Q-9SN8Qq?ӪU+֯_Ojլ^>_EVVu-=5I^rVZk3q3g /~\/xg8p  *l7*ϯN<?@||<$11ȓauV`{.]8z--5H夂 ;m߾~\5jO<رciذaٹsgXt)p}WWRGDDDDD^1D"ZBBk<7K"//rOnnn :ݢ pB5jX/n~sw=6m-E\rwz*#Gɓ|FG2ĭNaU(OW^>柗=UV$$$p1bs -04^x%TLJuow/vZ֮]K˖-ٹs'-Z(l7ϯ86l̙39yd5֞}1c""""""RBZTZOOrLj9EDDDDDDDDD*F/Bv WJxa }+NNNs]#dffhu-33GD]& 9_~eTu_%>MѣTRuN:deeq…bѳgOرcG .ХKȺuE ԯ_?=%[ixyIKK/`޽ݻ?㏼klݺԳW7?xxxмys4h@۶miݺux}2CYQDDDDDD$VH""""""""""""ݻtRӹtK,d2[w޲e 999""r,XիY~}OP&_ut#F ;;s2hРBlb=b}]iE9iP9 VJ /k֬gϞ2v_q-XUVqQ͛Nj/ȠAN,vZvz"""""""*)Ǐzbʔ)ѵkW}YL޽{dnɄd*#**)S0em^ 8::Āѣ/l׻wo/_NϞ=6lXvL7oNRR<ۗ>Zj1y޽GEY )jxI3MTH7vL#byfxɼĔ4o%(̂a 505sT/ZZgwr_XO߾}7ֵk … ؾ};/HnSO'_pTR>}O<={ό3JXmїI*UhܸgϞ=zA_>̼y󰱱aɦ^zo[\tyO{Y4nܘ*Uעˢfv_Yw 6lE_DD낂bϟ7oEEEfm.\`Μ9e7X("""""" 2xW h￙:u*!!!l߾SNqF IDAToĉ?IaaM޽(^}U&OIOOZ>""rwIKK#-->WWWbccٙjժѲeKΝk6nҤIPfMb̙7111b0ooo/^|]NL<777lmmM? GGGvacٳ ߏ@ժUٰa/S%SfMRRRxꩧصkΝ㫯bر 6Uf:gڵ|iӆ%K,Q_|{{{bcc>}:;lٲŴȑ#YhuaÆ nݚ>Cq wӓ'N0c ڶmk63_}deeQXXHVVlݺGGG^z~ HNN毿… dffhfv=_y4lŋM!'O,;wā(,,d>СCCzz:s%99Gc=ƫj?\1FEf͘3g| 4i҄ ^}U-iݺ5{eɒ%Z N8- 6{?~1?9sؼy3ǏnݺٓiӦѲerGq\\\o;w.k֬_~4nܘ~1uT֭[a+^x{{{}]ۇ=='N駟Yf > ҿ󃂂cO{ۗ3g}=BDDDDDDD8'N 22;wRPP;ÇgyF~~>$''sqԩC޽=z4vvv <:--ʹ͊-TvmJdd$xINNΎ.]0aԩS9sEeN|x}Æ 3R n8GB*Ldd$ NCDS`` NCDDDD/FÇMm7[\qƑej%&&L)q uVjN]t!22ݻw[͝;up9HOO'..o gϞСCG]윱cǒg>ҥK ]7n$??///񼼼صk6m*vJ}Q7oǏYHE_/"""""""""S 00":tfG``|\x>L oaѢEf;4őE֭IHHoe<#ڵkH?~PjNwǎ۹s'|7РA~7JjK,СC;:: pA/^lqNFXj)))5 ԟP.c1FDDD*G`` ͐!CT^z֭[3vXJ_U_ʯ_|#GX;BDDDDDDDnyfGiƂPڴiCj`ƌMNN ,,͛ckkK&M={6IIIUXX@ժo"jU$iӦEjhӦiw+ ʢn۶ig-[X3e6l=AAA>}ra7nl#"""""7-1_5NRTTĨQXt)[XDDDDDDDDn3kά"6''__buRǪWҰa+Fǎ?#>|ԱKm۶f~8 .]2:u gggsm<<]D*Usgϖ߮];rssټy3=\q7o6_1667gc+ (VZ5=?ƶr1w̙c۽{w/^Lhh(SLYf駟… ˓AɡaÆmۖ~3zhƌO<+'N/?zz˔޽{ W^ƾ7/&,,7xVZe뙔բE =JvvEaEvv)FDDzt҅˗[; )+{j*:udTn[[la3iӦY;MBθTq+رc <6d.]Jll,VNDDDDD_|ȑ#K9r$[neϞ= :ԬzL4VZ:u  ֭ 44/""",޽;3g,6ڵk[٪U+ tͱ4blBNNF2kذE[Ytܙm۶Y_ff&p}"""""R~>So<3̟?کmDrWҥK,ZM6Kjpww_~+k@~~>$''sqԩC޽=z4vvvfy\ә3gHLL}ԫWS~}<==ILLdذa8;;W$"""""r+hE9{,M4aС̚5 ɉW_q1jԨƍiӦɓ'pQ Mvvv}v>sӧY&?0~~~t֭|#""׿Ejj*k橧2XWrvv&66>[rI\]]y'xpqq)s{^z{cnַcٳg+""sNS||>>%w֭ŠԬY{cWtRfML”)SJ^I+WרQ@9rnnn9rtƎcc\KQQS2RS$$$`4DDDDDDDDX*VZ$%%βeLe#++֭[3}t&,,]vvZHKKm %%-[ZjlKIIQH1uF~~>ӦM''';w.ڵr#F~zM +"""""bE&LiTp!X`pV۬={x:7\W\rr2aaa4h&M0{lHJJ*QF}76s}aʕnWV-ƌcnO̞=_~aÆvZ"##^8*&NkFjj*,XQFh6yq999;KԩS8;;[ی1""""""bnʔ)4lؐ 6/PXXȽKN=z4ճvG}`͛rV"""peZ|9tؑZ%{8p-ZX; H\)M tWǗC LJ;uV֬YCFF~!qqq?+Z@9{lUTƢfs666hTJevxNEDD*ݻbٲe1h 7o#MDDD ֭#--صkWggl3ƈH*((`̙`0hР̘1k'"wGyg}`Ν;gtn L: .D~ʕ+ϧYf4ojժ;{~.^ɓ'ydܸqXN9996۴= <==M;%*bcc9v6 2cǎkDnΛoiTn˖-5S/Z;CB1bt֍N: GFIƍٳgCCՋO?ի3i$SlZo߾tL\m]v97/"""""""""ryѣ-??KN۷/~!9s???.]dv<==ׯ)611S^=S\$113gToYYYNj݋? ,,H;p@}˗/'$$/_^YȝJbuiiiY5f͚Ń>vvv3rH^{r999C`` [[[\\\ffSL<777lmmM׫W/ٱcE;vɉ={V3""""""b=AAATRE:rYǠ܀w}8~ƌ@aa!ƍO>~ĉt 4Ç_]st 7^^^iӦ S M67"kSJLL UTvJR^yg߽{7QQQL<瓞^ȝ <|}}esqq1R|||jԨA`` 9r7779Bzz:cǎ9n\hҤ 7 5..,Znqww';;0vڵk#((D/^l7[cqᔔZli-%%WGc?o*ݻ7Z ;;;k$l۶mŶ0sL0 f̘+ppp4EDDDDDD.DnQ#F~Ǜ1 :ԊTOOOZb >\\\L}ѼyslmmiҤ g )) 5k?_ 3gȠFxxxp5jd{ƍbDD*&L(v;M˖-IJJf͚NU{&66cǎ1x`Sې!C8vVLDDDDDDDnwBnYGv9880{l^~e ڵkzVNDDDDDb 8)PTTdDu5sM_:e˖_5/^SNTzTN:ŵm;`kkk4*}ڵkĉDFFsN pwwgL>0|INNԩS޽{3zh%Wbqi߾=3ԯ_OOO6lX""""""""ע [أ>Jpp0`UDDDDDDTZ; K%$$d4DjW/sʒj*Ν;… 0]ٳk׮MIHH 33ӴR|׮]M1UTrqc1FD2mrk믿=z46߿TlqBƍGVV-77233*}hƍeŮ]شi .uDQ5HDDDDDDDDD!!!ԩSHoNVVZ`0ͺu0 ׼ְaXr%˖-믿ƆΝ;]]]9z(yyygsa4hoI˖-ٻw/3fرcfqqqdeeѺukO;لk.֮]iiibвeKo ڶmȑ#ٺu+{aСfT^I&Y\kX]uܙm۶i񾗙Y9"""wcbvڋ̘o:x`c:u ggg>c1FDDDDDDDÅH+YL:oΩSp۷ogĉ]QZjE˖-9~8;w,5o%77&MV{nxW}ر#&))ڴicˡW^p ԩcs)oNy駭˱c& 2KKppʔOAA1Æ +6nĈ_xL@||<CVZ""wϓO>YǏӻwo4iҥKvJRF={fΜ9<۷9sXvޝŋʔ)Sh֬'qqq|xzzpBNEEEҢE =JvvEaEvv)FDDDDDDDzB*̄ 0aP;w$((im&>>^;U'NΝ;)((ݝÇVP#??h9~8uԡwތ=;;;W~ƚ5k>|Yŕj׮СC?7\Bxx8ѥK&L` U9sEeNEEE:tQC*_ѣIJJӜ?-TXXȸqO8<0qD.]TXtݻw[͝;uq9N>MRRFeeΞ=?ҥK߹pG%!!ѣGoqرcٵkΝ#//>SH]z5,XSٳgM9$&&^)Ny^(g޽Ǜ6nH~~>^^^yyyϦMn8w=ԫW{{{:w{WJ>(̛7y1~xI""Ldd$K,a֭N ''' 6Pn]kt[KKK"Xhs=TZ͛;99C`` [[[\\\ffSL<777lmm),,.Ȏ;,رcNNNٳXDDDDDDDTB*Tj&1A""""r;4hЀ7|-[w^f̘cbʢuL>www c׮]]??w8~8Ԝ>nIoߞ}1c ~7.q-YCoA˖-b̙>)Sd0 :%"""""""w\D;wNA"b!>>^+Wlr򆇇ElNN^U^=rss-ȑ#u#<ÇK1ڶmkn|~7sM_p%'|lٲӧOӧټy3NNN{ &Mľ},*ؤzM8^{TRSSY`F"00իN:mUÆ ={6 2͛[;UR7|iJZFѧOV\Ixx8fjb̘17uLfϞ/̰aXv-fEDDDDDDDK"7сhѢE}˗/'55חN:ѷo_>C M1ƢgΜ!11S^=SL$113gTΝ;yhٲ%s뭝^Ri%e0ӧdddAhh(}`0ܔ+Z*jբ]vL<_~׸Yȝ+--}-Z/sUVyٙb!00-...x{{xb &Of '""]r=`ccCZ֭;oDD]tjժԫWÇpBvvv3rH^{5S\^pttdǎر'''Ӯ)""rs%$$;:n#!!?>?NjjS)vRTTK,1=><{e޽_PXXȸq2Cff&QQQؔ\qF]ˋ]vi&NDDD|;ȑ#ٰaEÇ9|0}}aҥ7?buzv;,PNvBBB )>>>߭[7uV\k֬iաikkƍK3aaa.iӦ1mڴkS __RcjԨA`` 9r7779Bzz:cǎc[|S28pS;Daa!:t8`ŊNADDDDDD* .d6|<==9}Xr%ᄇժUlٳy6lk׮%22ի[9;B>}HKKYf2?V\Ɇ x+e;Մ 0aX;k8q"2h bbb8wY\NNxzzcKԩS8;;[ی1"""rwf̘14l{{{ AAAdee_JkWKKKcԭ[;;;ݙ6mgϞ5۱c6lnݺZʢJ[6MZ-ψ#pss777FO?T}|wjժ/v*~qѴiS{xGy?=Q>}X~}vcz璾-sÃ#Grk^ܹst WWWVJ:uxٺukǿ, BzÃu֕Xה)S e˖T^ f裏̼y7oǏi-t/_NHH!!!,_):/Vx|GУGJNTСrh tW\a٢"w;vd֭Y >C?~<\=ij?X6ǎ;xg8}ѣ$$$j*>kNO>/:ܹsٴi;vF0n8j׮}ͱFC=c=f־~z @AA~O>aʕ^][MΝcݺulܸ[ҩS'Sw}O<3gLm,^TޛnёZ_ի:F/\lllfѬ]cϜ97?Yɓ'IJJbÆ ]gyLc_Drr23;g׏%Ki\\666hTJUln5+l۶ݻwŲe(**bРA4oޜGy jժ,\Ν;СC+lO|P|UF||.[zLrrqq___ؼy3|  @vv6֭`0{ WWW=J^^f}yyy:uΜ9äI#==hbbb d߾}fwW:gOS/رcx"ƍcԩʨQؽ{7s_`Ν:D^pttә4i| oV .{?$00tNXXM6/j*""""m۶^PP̙3 0nό3XbEѼȭcǎ;W^y>}iaѼysY&0` v"?^""""""w eEӓ+WOfh޼9pyݻx"'Oӓqƙb;UZh@vvE6c>#;ӉiӦT^;/|PuRSS5k* .fРA,XƍSZ5vҥKOMGZly5|Ξ=KYj=vvvck~MRZ5fYvwMnt<ϕ..Q K,s888`ooOLKٳLc^̉'ѣVUVѮ]; Zn}=/""""i)V˱c~R߉G===e!BdXٳ'}eZX~={ 27qrřc><(;#w2Hjj*tڕ (u5j> Ϭv|wxxx( ,*Z+W 燽=.B!B!jz@r ===q_^d yPReN8Arr25kԔӧӲeKܹsٰaIII vZΜ IDAT9Ä 4b8s ׯX!q]VྩSrMZlΝ;RX:uTwԮ]XyJf͚deeiϛ7I&qyΟ?|۷/C O>b˭,޿f͚Ѿ}{Μ9Cjj*j"%%`Zj(KrÇq+\pu|i~'ԉ]v{x7n{uBWCB´iHIIѣG޽,e O|HLLDR+WёpV\)5")) WWW4hyd9BZZZ %00Ç?+ nl۶͛7ӳgO]#D/9uTqssc׮]dgg3zhZnMΝuB!B!*"I, ,,,gddhRӿ5kbmm 4i"""6m5j(I!QU eUSV@Whٲ%aaa\|nݺ'Oazvւ6jժԫĉ]6| /^?`֭lݺiѢEĖ۳jo%((+++>̓'O ]ݢ,/u./Vԟ k.͛$KVi~'״Đ^{bF8wHSfpvvaÆxxxR8qℒpELL :t`޼y ߿KKKjժŋc…l޼ECV\aaaʌݹ m۶Z˂%BQyyyXW ]LWSN[΂ 1b2щߟիgB!B!$B֖퍃cccBQ^j֬w^Wߘ5k*UˋvGjj*<(Vz",,#G+իtڕ!C<~=RRR[1Sb+ Æ cذas1Ξ=ٳgz*ׯ_g߿c{/7uJyy_RTTIѣGeɓ'WPaܸq[9͛S~}ڶmK֭gi~'ܻwziEY???] BS'3UAN6m 䬖R4pqqqhggg,-- Yooo>S;vl]\$ռys:BQQ7^.,*W\ш_OOO3fR6vXv܉'vvv:N!B!$\ jϪH^:̚5~֭[W!gB!ѲeK.^ȹs4hP{cƌ!--%KB DGGӣG"3f }6mbΜ9&&&/2p@c.\+KM6eҤIL4 ~!C(ul}ٳ'FFF:tl9B͚5ʥ\2YYYi%\~) 2к˖-`L6Mc_bb8swC={_~EkE;I>B+ٺAQ?éW'R SjlϘ1 .E-ϋݻe:B29s&իWgպEP$, $11}}}LLL:tXziiixxxM4hSL@#1K׮]iРRQFT!B!/?It;;;VZܬ355qTB!(Oŋ,[~QϏѣG3h s,e_>}VEtt4o6͚5͛@#((ѣG5j[Xj+WfܹJ_Xr%x=++Kӷoߢ/R7oN\\Ѵo^cV_^zzz׏ݻwCrr2C jժqթS;wJ>}4Tgn믴lْO>к˹YFy^j%6lgϞeO# ;;[9F-KKKu޽{uIř[___y_gQIT<(IR_"LK/;ٳgC۷oW333"***+^FFӧO'&&FJ"::77B'8riii~hnnNhh( >]!B!/6I6S<0$Bذa 8:ve֭~6l@ǎپ}{fƍxzzҨQ#f͚EZ /`ĉǸӵkW:D/4hM6%33ׯs!V^۷Yp!:us8q+++\\\hٲ%̛7'ORF fΜpر2rH{ͩZ*/_V@rZP+ٽ{7 .T˳ٳ'cԩxzzbffFBB6l`ӦM%nɓ'߄F`` xxxh%մiSbcc;w.-~\|+Vcޝ;wL(g}ƚ5k dȑ,]-[r5ϟK|=DFȑ#u) BYÆ Ү];bbbpss޾cիGRR)))ԫWOc_JJRG!^V6mɉ7Jx5p֯_ʮ]D"&&:0owwwNZE%c"cqpp`ԩlڴI+cr5~g6mZh[Uvb޽ݻwkR>>tܙ͛7cggΝ;ի]vr>fgg|mӦ IIIi%V)ue~>sp^ys5jq{fĈ1TUU7n @-pvvҒ,-- ʕ+~7ؼys:B!B!xB!L <&Oo>kצ_~j݌~z~%K`ḢwGDDD0a1c9WCa[>}{{{Oׯ={mffVC4燽=:!YyDFFbccCn0`;vZj̙3TQK,AOOO^hԩS+++ |n_]7Y!^ǎ#44.\`رL:Uk!fϞMժU cѨT*=zTzXXX(c333廝B{.3F^2u!B!Bd !B!Q֭(VT}իTZ>Tk׎;vzU]6...$/755Z8kc„ L0!Ǐg%-[d۶mZ(cc'^L_~%NҚ,\cǎi?>}ӧOӯ_?/^A]фƮ]fѴnݺVBj*T*l‰'HNNf͚2}tZl +/Vfnܸ1,ZWWW-[ܹsٰaIIIddd0`֮]˙3g>9sCCC"o%!!/͛ 2>(BW޿d}])ՓGGGTŋ ,,,رcl۶5k(ŭgllL\\(D#))ի/%%E#B!B! dԨQC!B!J[n}uVpppUVܻw///v؁7orV&>>MЦMJS!B!BH…xnuB!BQjWo߾ӷo_*UՕ޽{pPofݺu={tLLL0a45 ((3h Lܯطo&LHȭnݺذn:~G%BNpp0a``@Ϟ=/_~}t;ǏΝ;ԯ_?SbhhՎ/k֬!22UG1{le5, $11}}}LLL:tƊҵkW4h7j333|}}?~dʔ)ܺuK)|l[FFӧO'&&F)KLLDR+W.K.SϞ=YnZVXxDDD兑Q'˃OqRT*jԨq̴iHIIQ޽{7YYY899ەDEEÇ7nG!-- sss %00Ç|BU.]cժU̘1CAN!x?ЫW/|}}5VWBOO???eoЫW/lmmK\JŖ-[8qԬYSSSON˖-seÆ $%%Xv-gΜa„ 9sCCCܮB!B!^p! .ȑ#u5eB!x|wC7nݢq|7mۖ(ϟOrrF]///bbbСĄ8\\\ eXZZ q1Z={uѵkW~7ϟϟvvv۹qM4aҥmۖ,X@ll,[nNf͚LÆ @Rq %bϞ=_333233 Օ]v) mV+.uYpp$\!xeX[[cmm0Lу)S`ooytZXX`aaQŭgdd=߿?(Y&xxx@&MHHH ""iӦiMZ B!BW$\ooo]!(!CSB!BTꄃ8qNNNtYdZBƍhѢXZZeqg++l訬 ѱcG1c'NJ(L~ݩS'̙͛39~V{4mT*߿Q<ۛ7nлwoF#4ڹr W5!B!^LSe,$ċ֖퍃31666:N!B!$\!B!;CQnjjU7>>3ccc A$&&$.'!!AW5;w [ >:uQ1&&&k###ٳgxbBBB aӦML<kkkUԻ{.jC]#B!x߿se Ua3V$իWٙYf1~x w}ݺuc^!B!B$B!B!^އ5G$*UTd|ƱŬ.˯}!B!Dwqƌĉ[Lu;;;VZ3A!B!.I/|8xzzҫW/ZlUիl߾KKKcp|Ҹqcn޼IDDݺuS#""Ju~򅅅p1mƚ5kzꑔDJJ 8>%%E#BGOBQqEEEaeeE~X~eE}غu+NNN888ЪU+ݻ;v̌͛79+4dggOӦMԩ1dʔ)L:}R^=n߾ѣG?8p`e˖)qFEE:W޽ٺu+...,]vw^J|-JBB ͛78qbu'Nɓ'Fc_j՘3g]N޽8p'''?~ѣGYf k֬O>,X ֭gv=zt}fkkljg6mUVꊫ>SSSu=8u@>Bd$WJJ N:ٳ5j:$!B!B!tB.Ι/֒UV孷֖Wr$991c(ecǎeΝxzzbggB Ξ=˺ut/ٳx6^:qi}(߿Oڵy/05kְh"BBB[}/ IDAT.dʔ)qwnjӓ~'OrիG߾}022*ֵ ===qozV7`֮]˙3g0aFgΜА~!/7? bͫaȑ??ڵkY?++ʕ+CdB!B!B/Iޱcb+=zÇ,Iܹ3׮]S222>}:111JYbb"*hܴfYbѣG2 iiڝ6m)))Jw&++ ''_ݻw1,\O2dFJѣ>}@yȑ#ann92|pB!DYuB B(?>iii8::ҿ cŊ/ԃ3/[[[<퍱1666:N!DE&'!^2yu :Dqrr9ET>,--ٷo_&}aÆR7n5j`֭m.zжm[8e:B!Bpuu?weȑQNN_NիWٙHbb"ٙjժ:SL޳p9Rq!r:!B!ρ$\rNa7}V\oo| ۷׺19͞SqEz-> {uW'%T\W^8o%ve~e[.ÇLJhΜ9@^}eO!x^:tYLɗ_~ɩStra}Ʈ]fѴnZIB!Bϟ| P1OqE2b9s&ҥKiѢ7nTVOKKcT\OOOnݪٳ\xQc"2֭[W_pBɷpqq!44#Gh)m=O>^W>}s͛yFy9::*/`mmNcBaܸqC!B!D~*Y D=Jb 2Dc'Ox74n`̭$u{V38p0P"##5x7ݢz<6l:F^Ǐ]vq9*WL= %%EXuB+%\_uNp}`F17ʊ瓞^Q !B!{M6ʼy -/kEy5OIXC Qqr%Yd vvvTR˗/E͚5{?yŅ/~W-[VIDGG3{l]F&M0`O>899qe>}ʝ;w133cZm.[ho6_}\\V?2u!<)?۷ornԚ ܜU%*OOO3fR6vXf~B!B=x,'133rZ.JNPBT6lcǎTVٳg9IQ?ueذa(>qFJ={ Sĉt===ڵkU8۴iCꫯ`ΝkjՊի3g޽[fHNN7+B!B!^|z@ݛ.\… _|ssslll8vW\aԩh=|_tCȈ+VM8'OFjժ)_Vn]&NQ֮];FL$y?i$O>gi՟0aKc_=8uјjS!U_qFy͛7g„ CNd}Fe͚5DFFRjU>#fϞMݝ@ĄCbiiVaaakwwwΝK֭ټyF+/ ;55___vJ :5RfƏOZJF! 1BA(k׮#oyq1jiiixxxM4hSLQ7 wGaɒ%cÇ3|>#\]]xqYqXyu|MѣG5ͭ={y5\g6+qW-Wur},̪U||xʭ$u b``1ǏaÆ QTlٲ'NL͚5155elR5kְh"BBB[.ԸWvs.Žϣz鉛ϟѣGlْ'ҷo_P5 0kr&L̙3*7BsUi֬w&::'Һuk|||{w֖۷o+e,\O2d睟#G>sssBCC d%IQ{w^]!B@i/UqLFFӧO'&&FJ"::777*W. gʕҨQ#puuA̛7Y1z"lllqΝ_WiJV'&TRcJ^PT8 맴}VRBW3f  `޽xxxVťT+/9uTqssc׮]dgg3zhZnMΝuB!B!^&p!*Zjܫ1@΃8::j[If1Y{{bկ]6k׮-v Yqy]t f͚iXTfM !!&M@DDӦMBz%Lzym۶qmڵk7|CQTJ۹qM4aҥmۖ,X@ll,[nNf͚LÆ @Rq %bϞ=_333233 Օ]v1n8eТE T*ƍ:m۶Z˂a!!PvvCB! $㗂>T^q 111tЁyabbB\\...~,--@+->VZ,^;;;.\͛Yh>dʕ{ˊ{-d %ċѣ?~ȉΟ?O>}ӧO?ּys\͛7i֬Y\S&MbҤI޽+V`oo/ ԩS`F1VVV̟?zB!B!C./4?Əo=[[[<7x{{cllM+BG`Ѿ}|g95m'''%)Bĉʾ;ЩS'̙͛39~V…qPTܿ__F S;v,3ϳ+c(!^ ;w[nE֛:u*^^^$;c +֬_;rmV^ͪUӧǏ/x}UTl^J֭011ƍlܸ:uDll,@҅^t$''3flرܹOORB!B!IB }u֭^:̚5KIؿ?֭ZjBWއV\ʕ+KtR?hӦMr{w4DyM5++K)={6/&$$6mɓ.h9%UݻwdR!ʆ?#Gq$BW!!ċ4*8&>> |Q?[%Θ1 .E- ,+c(!^ {WÆ ޽F3g,GGG˹sҥƾ5jz\}_"996mЬY3n޼ӧON>e;$&& CJDLKKà oׯϠA2eԒ; $ҵkW4hiԨfff2~|6 !B!B$\!^ 4 %%333RJut邝VrnwE^ӿwɓ'ٷo.]bƍxyy1c ^dY[}lʕ˞ !y{{cmm0(ƍ0xM:(ͬ܅_By3Re\Vce %e߾}H f&iծ]sαd~G022s|JB\Y(%Ko'''y1ӦMC__ 6@˖-OSfqsscvff&QQQDEEC3wFFӧOW-@bb"*hv077gnnNhh(Q!B!"?3!(aaa/e) `{i bmmիYzuK[ !!,, www:t@XXׯT>>\p???:wLhh(4/s @۶mbP!B!B$\!BQC=XjIIInӦ qqqZuB!y_Ԋ;ӧNNN\|Or{̘>}R$cйsgT*ܹ'{T֭ݻW4~*jԨb?È# S([J*hggg gR\rf͚ik޼F!B!BuB!B *BŤI "22O>Dcg}3ydzM6*+[[[\\\puuJ055U^שSwbee1(޽{g֮]ڵk077תWί=zp)5sʕ|?"$$$y3f FFF:S?,4@5jT&'""H"##n?d„ ݻ ļƍ{o޼9/_ggglB^طoYYYdeeЧO{}{"""""""Q…7K.%11a2 $$$X; :_$00ŋdׯ_gҤI;wTvIS튋y뭷8~… ƒETTYbDE&M `ĈݛN: kFll,_~%^^^зo_k&OLAA7n`ƍܼyP]oAvv隼<9t4iĬυ eS)))#QQQ^tMII dffruƌS᳨u۷oOOO><==9t;v`ȑ#"""uP"?f…YX;*{nkHf,>>>4lؐu֙۷O>g}ƞ={ϧiӦtڕz'|v|Q\\ 裏HMMef1booϠAjEDDDDDDa `[T)tEe00 CD!!!sѶm[͛;o6>>ǏөS'f̘+999DDDp!6o9|iF#FiӦ,_UVj*lmmҥ gᦓ2ƌCRR+W0O<f͢u˞={L W&;;v1w\9~83gӬ\@>8ҥK֭'N ,,oji&-[%%%Irrrs^ZhU,--M """0͡DjOBBRx{{6mӦM\iӦ bbbͥ]v撞ɓ-6"h<<<^UٳP:w ]v%,,̢ߝ;w 4cǎ̚5 jΚ5kxw ++Ea0h֬||W\zcǎv]pp0...4jȴpʕ+K.<*56$$OOOlllܹ)y`Zu駟l޼PN:J()CfmDDDDDr~-'N&$$X?mڴ10!!gggƎkDDDDDDDa.DDDDDDD䡒 /`V^>ٳy;&L+W_-ZرcINN&..OOO+~gzIÆoոvtt͛\tbvϟ?oc=̾8wlԩ̞=rAVXĉ1 4nܸu/_cT͍M61dk"74j_;;;f͚Ŕ)S7n7ofҥUaEDDDDDDDC 3QX7*j[75._޺u8x pkϨQje\y𕟷Tg>SVVf}z~My׮]O>Y-DGGB-Z`ȑ$&&Ejjlll2'eW>ض~=ˋ=zw^ ;ˉ'((#GVwu3cjr"""""W^y!X݋/H`` / ((DDDDDDDDjJ "R+=JTTɔkfIUDDaE""Ӿ}{Μ9ñcok*?vE[gggrrrزe 5ݝ#GqF~mzi ƍdž HNN櫯~իmۖtwn*OOO22268z(9::퍷7Ǐg׮]Z%KTpQZlI^^lSy4h$""b}f00 CDDDDDDD2J G{***b̙뱳8DDDjC޽Yn:r 43gΰ`Ν3<É'X`ErJBCC 橧7Y>bв2Ξ= ~~~9rX]ƈ#pqq#Gƹݯ~+ Ɔ (++K.,Ǐ7kEf̟?\233`6Æ #77 SO=Q^u{#''"˜OyȃE'7r?l2hР1Q…XqW͇1An#??ѣGXv-qqqZ1:y،3m۶w1a„*N0{رc7n{gy\|ٲe gܸqYd-x'xw-Ǐ끊#ߟݻwsY&NhVbQТE kAdd$fmvZe<չW^۷,;&"""""i޼96mbŊ;GDDDDDDDDD RڵsInܸAVׯ4iҥK|۷" 3*&mk"44۷3g b*߶maaaK'?˗/'--k׮ѡCƏov]y%/y&ر .`cc+Æ cUwUٽ{7.]UV 0-MJJbɒ%dddШQ#^z%Nj:ի$%%ѭ[7L׷i7n}"""uځ3f:&!!`0*U%MTTڵktؑc=|gٳ|6mJ׮]y뭷x'MmOL^^Ŧ)Sн{w6l?.]DÆ СljԴiS}@\\~){ҥKlْohq͒%KiѢ/2&M2{.>>>4lؐu֑ @ӧSG}Djj*Ǐ7#55{{{ tGDDD?<4l:vh0g}>PFAv VXALLYY^^III\r9sO8\Sm۶qu>kҶBBB8z(-k׮iӆ<"##qrr2%u\|.^hӄ?3tл#**իW/))!33L_^釘׮]70-[;11CkHhp 6n͛7 `i1'bǎ9YDD)++v"RGԫW!3ׯ_gѢEEWZŋyg7oZ+߈넋M6l2<<<())!%%H+MXz5ٴk׎sǙ9s&Ofʕ]O0k,ZnMLL ٳǔpxƲ4%\=ӷo_ {{{rrrXp!/U+,,/ƍj6nV|r{wN{O R*K\(_~aOYY'N`֭on`w^f̘s=@׮]ӟĔ)SI;ѭ[7  dffo[S@hh)In5i҄k׮@vv6__*۳g)Ν;ХKf̘;ݻ-.M0fbccr助ԩSČ:t`FDDDDD^2d6l 22H͛`n۷k77NP1k,LbJ:ټy3K.qVNDDDDnؾ};'N#où\wޡ4m!#N R]x{'NX~sLIFZZ3bQAAA|dffұcG?՟?~}ԩS={6X'b0\ J㾝k 7o4]|uGy2cyp:uҿm֭[ѣFDdL ߺu+׿(..uٓI&d(((ÃP4h`x_$00ŋ]vrT""0/ ;SiPȑ#|?RVV3={d̘1mH_ëJDDé5_qww'""[;:mҥ$&&Z; ) """"""S…Ժ Ɠ-*JhXh'N}wՕ{߸h񚴽_իwWח(..Gݻ/cǎ|r bȑ5lllL_Wt_Ʋ[yu۷¿=JTTɔk?oHEQQ~} ڡT(%%*d#TDDƑ#GL_x{Sii)g˖-ל={g_`sODӧO3tPzMll#YJ֭Y~=:uv(@BB 111cX; ,W@!ݴbcc:u*C5/--}fdeeYYw?⣏>Ӣ?DFFZ$dtڵٽ{7gϞeĉfu...eѫW/GVVSE$CDDDŋYt)WWWƏoڽæİsN~GZj1i$F2~m())nݺdjӦM<<֭pkRY͛7fǎ\p\]]6lÇ7nO7n ???:uT}N///찵cDdlmmח͛ckkˠA &mk*22~`ڶm @۶m &??ߔвeKYp!C {___5k-̜9Ӣ<3bkk+&L`ٕ@\\Fɉ 䄟+Wxò:L&MHMMKMMޞAո_ׯ3i$RRRr %%%x*uؑ9sзo{6n""rw~֭ٳsuN:Ett4cƌ!??y?=z(>OβeHOOvX"""""""RM:B4"""*t MBHHYyE5i[]Mϟ-_y^ymVzomggWQ͚5#88_[QyӦM1 ĐKv%==ɓ'8lZsh۶-ݝL>tԉ3fJNN:t͛73|p`o; -- b9r]y@[!ddggӮ];Ν;Ǐg̙>}+WPM6l2<<<())!%%HM :vt:tPMDD}u~!ׯ_{L2;RZZʉ'9y$QQQ̙3cEEE̜9___[i  IDAT~zj%s:BD;ڴic[BBΌ;֊/{ 44Ν;cccC׮] hsN"""pssAtؑYfRXN'3."2]MOstb:owq֭[O?/fͦvw;2BQ^=\h&" .Ex~nmzFÆ iܸ1/icX3_G~~>G6O\\# "r1k,L¸qؼy3K.qVNDDё /`V޵kWgϞĬӧOW9˗['g,3ݨQeeeAD!77.]=ϟ㾧Nٳ9x dŊL8`Ο8<=;vQ<233ԩSIWJJJѣ X;&Oz*޽K.ѪU+ @@@fhт~/cչRQ٘Ư>}:nnn|'qOJJbɒ%dddШQ#^z%NjvŋYt)WWWƏoJ^֭NNN۴iIII7"""""""Rw(BDjŋ/H`` / ((ŝ"""Gԗ@6_1޽{өS'lllj<%KzYCE&OLAA7n`ƍܼyP_ΤI8w隓'O-n}v СCر7MJZc0hJDDĊڷoϙ3g8voMǎhLNN[lٹclْ< hٲY""""""5նm[;޽<==3sttooo?ή]XjK,1-[laՋ{v%Wh&dСK?<~mV^[oEjjO=ypdggӮ];Ν;Ǐg̙>}+Whj?b6mYjV֖.]пmcuؑXS<̚5֭[Cll,{1%\$$$p9ڶm˼ypww'330JKKbcYZZ.DDDDDDD8-DDDDD4h ,X@VVdff` 44'O?s%?xxx[o?>{駟 ''ǢocȝׯdddPZZJFFiӧO;{ذaxxxa yꩧpss0YnOoƍ,]SNQ\\Lqq1׿X~=ƍ3^u;w!CбcG֮]K ~|||gN0{RymٳgpVΝK.!vmq9k֬wÃ,-Z`cǎd `\\\hԨ+WTy]v%,,̢SN8СYt…#b̘1l۶ &Tv„ ݻ ƎkV׸qc{=͛7̖-[ի#++]]'Temٿ?9YYY\r~C޽SN-~{^nG5V)ߟݻwsY&NhVbQVӾ#""$22ҬyΟ>6lHrr2WfԾ}{BBBؚԾ #[lI&°aprrvHRtŬ^^;&L+W_-Zc~kGGGn޼i*3&$c6*{888XˌmDDDDDDD """QzK{z>|7= 6͍9sfooOll,ggg4h#cʕf|ONvhР <&MjGjj*7DD&ȴiؿ?/_~3uT?~^މߴ˦5=z(>OβeHOOZ<""j@\\Fɉ 䄟+W4->>>3`kk-L0ٳg AL>^}U~_ӨQ#ׯ#"" 6н{{6n"~'oί~+k@={6M6߷v(uwgߡ[ F}Ã|:[[[LnXUtYY5կo,X0-"""""" r_%&&RRRb0D֯_`vAm0DX7ofΝѢE .\@V6mӦMO///ʚ6m` &&\ڵk.==ɓ'kGy(͟?]vaooϤI0`['sNlllL o5o>Z(STT̙35,,cggW+qܮYfm+YYw׹ݫ5w]˗/HMMv8,{{{x)((YfIm۶|w%Luő#Gظq#oE_Ɠ&*Jfڷoϙ3g8voMǎh۲eK(((e˖fu6"""""""R)Bݻ+Bꫯ-wgȐ! 2aY`pT #???9Bll,׮]cĈP\\̑#G߿ck ̙3,Xs3p =駟&// cQHݤ o}]}]k!"""@1. >̐!CذaDFF7oޜ{:fbʔ)7ubҥKiܸ=KD"DFFw^7n/LPPق]v~zN<ɍ7hժ#00д͛7fǎ\p\]]6lÇ7_0~xd۵hтcDztR.?҈dΝһwo}])D=餢WݻtZb`oooORRK,!##FK/1uTөW^%))nݺdM6xxxĸqpppXn"AJlQ5oޜ˗/ン3[lߟݻwsY&NhŬ75k֐LrrO<y3m۶w1a„*Ջ}E׮]겲$p!͘1cHLLv"r  jDgΜv`\\\غu+׿(..uٓI&-W^|EYx1AAA./!!!|WDJKKM;7XkHJJʕ+̙3(V^mjSRRBff&\~1cp1։BUݻ7K.ѣu .4-q)))cڵkdgg[bb""66֔Pb4yd Lcoܸ7o ),,b}:1yyy@\\~){ҥKlْoZ{LBٰa?t 6C 8???mEckvvvDGGv;vdرckkkj;x`>#RSS?~Y?3hР;JF E@cr 4oook!Rgm޼;wZ; ~֯_`ocTcO[ggg>cسg)bڵČCR^=233;w{l.]~?WWW׎|2ˌmDDDDDeΝԔ,RWUݺZې!CذaDFF5oޜ2;;;f͚Ŕ)S7npK """""""u.u)~iTUVу &жm V.Z۱[75V+??μֶ>-Zĉ'h߾=puu瞳xyyѣG_|cX|91rHV2Å صkzڵԾcׯ_T ؘWEXe]'""""(0`پ}ydddm6)"tȭ[bZnMϞ=4iR+xbڵkm."""""""wH Hzٷo_sy6mĶmۈsεԆGErr2eeekvua6mÇe˖<ҿkX+;?9sU^ hڴ)?8;x}-Jױ4olS}ii)ooo9~8vbժU,YĔp1rHRRRO>f'l}^Ç[gddmpQ~_ڶmw}Gzz:ݻw7\ZlI^^lҬFDD4Eaa!Ck9y$!!!xzzһwok@ =hׯ``0;FDDDDDDD{=f߾}U>|JKK#>>=zPXXGj)GoEEE̜9___i۶->>>X6.]bĉk$''snܸAII yyyl۶I&op5kP矹r Ǐ>c|KŅW_}aHꫯb0 Mqq1'O$,,T~ 6lz 777T.]+PXXȤIHLL~ ;;;lmmyX`$''Cxx8< ʄ ={YDFFdc=Fi޼9}eɒ%/QFƴdzMÆ qrrb|'ښL>vѠAJ6ppp ..QFD prrϏ+WX5x`4iBjjE]jj* 4ȣK'z0_X$::kҽ{wkHر#aaa̝;lk#""""""""p!uי4iΝ3˟ɧ~z* 2wy&M}7۷-Z|*幹LXXɌ?"+VW_q%ZlI^ Cי3g`…=zFѭ[7ҥu}}iii\pƍK/Ą *VU|@ZZ?غu+Æ 3;?իWqpp' 3:}4˖-㫯ʕ+1uTXauY;#t:^,|5&]y{{]K߾}ިYf|GUˬ{n֬W_uWӦM1 ĐKv[3yd'EDDK'7{fϞm4Jo$$$֭[}HII`ADDDDDD%\Hsh۶-ݝL7kԩ3fՕ"""8t7of>|tzBMwCmѢk<8ҥK֭'N ,,O``O0k,ZnMLL ٳǔfrssi߾=͍L-~j*.^ȳ>˼yhժ_Flll 5eӦM,[ JJ8G0 IDATJHII!22dSEZZҔp!oaҤI\vT?yfoׯ_lܸ6m"$$T駟Jrridc=ٻ!R229C2ia!aH9<3!bTÖ$ dZdC'9wxo7^뺞{u=оN-r%B3m4ӕ}q1lBnl3""+++?~ d%:ugϲ{n:vԍZ-ɓ'\vk׮w^Kݺu$^5_}NRK)%%1cp53g;})R%GϟgҤIjDGG3i$V^]~_#GtB!Dȑ#(>>>bmmBd$x5jԈh~mM"Uez쉿?FtH/L.]$B D~B!BW$\rJv(ҥKHv011a…̘1C~PPY;99aaaѣGs%jr*)IWvVh׮888$\yall رcÇ3g`}] ~~W̜9S灬U'5jՋ#F0b6oܸd%Z]UG@JJ Ǐښ M`` ̚5'NP^=弼vؼy2}ŊJ#Ϟ=ʊI&ѠA/f̞֭=_RZ޽t ڹ~߽{3fN~;w.M4֭[^'O2}t?Λo/? &ЦM"##?>o eHIIaӰaC>}ʕ+WXx1111_oLbˮ_qmTTTlµkhѢ˗/wEKKh-[Fxx86m*REIÇ5kr_|Aƍ?VXEJtB!tttprrb̙p!֯_+W!Bׅ$[T<ݻwg͚5{党1cFgB!B!lI…V0$n߾ v111޽{Ԯ]TСgm֬򵾾>ϟ?WTWTr_@Vl3?ʼne֬Y,YPBCCټy3ǏJmYfTe:BݻwsQ;vHǎooo9vZ&N}KFFXtR޹sg;,,,8ppm޼ykΝbff͛El޼BCCٵkWڥKVXرc|Z]U"—_~ R ]t͍rI%ᢴeWV_A rXf\Y5k֬O>\~H}?ӦMoΨQ/ŋ2S]^7n{A@@@N|Iy&:ubԩJCt!"JLLdժU6mZ/B~c!.ȡ$$$PjUz->CF, !^]uƎB䐙B!B!I.LJ*Z'䢆/wyTf!ZvV|]=vx^TyEqb177̌3gpA\¦MÇŒJ%GJpNFӓs;aѣG̚5gϞѳgO .\:׹^2ӁcJJͤIr}iii1i$BCC9sLוP%9dhhȭ[?~<666JB-r]_icˮ,޿TV ={6co0%GNʕ+3eԿx}焄;`xx8nnnɨQhٲ* WTVVVXYYi: !.\jZ~c7o2qDRRR nܸ78|0%9S_BjZ*>>>CAgtB!B!ʘ$\ QFo\r_)rJqaemYz䉲BL^Joddh(+}grE\OFtU)W ݋~ЦM"WX2dC !::SNc\]] xj?x@#DyQԒsnݺ1GGGO7n̺u\qծA[.RLzzz<|ja-x d/EpM\•+WRI͛7 -7Zf )))t҅3gҴiS>}ʯʚ5k~:nnnΝeѧ0rHF0d) !B!x^] X(>}իWYbE{&_~gϞw})'OVv.}v1<~(f͚ѰaC P~U/_\Oxx8n0ٙ˗/S]s}w իW@zz:~- 6,СC155eϞ=ҢE Zl ѪU+ Ԫ2U!CQwɯ?GAWWwwwjժNjj*P+T9w(ȳg^Ve/aC͛7*eݻSbnnQfeW(Tׯ_кGW^ {{{6n~,=R]c~!ŊUfjj)xyyѣ1cƐ#B!BhJYThii]`yyQ%|,_-[RJWNǎv,+2x2228xB!B!B3BThcǎرcܺu ;;q"##V;VzufϞ]6CCC>V?w\Rm~qUSNiӦ F*UԩSܸq{{{|^D~)AAADFF8qb'[[[qqqEnUջuFHHQQQV[WѣG]Crr2M"&&KRR%\]]i޼y}Z>:wիW9w:t(}]]]>|ȣGx7rC%`nn9?.]ҥKܾ}K^汕+/U"?R~.6k֌A[oQvm7oN߾}gIQay&:u*?OOO|||?~<?q.B5! **Hz뜈^Y222bĈ1ӧO3qDKYV={ݻ Mظq#˖-c̘1j?VlْK.q<T+v͍;w*S/aaaxzzyϜk,0a/_=Æ {mVmd%DZ`lKKK+D rxx8nnnɨQhٲeB!B!( I^:uXxZY||<uU+gΜ9̙36U+TPGAگJZXn]_y2|ϟ_hѿn8xȐ! 2:XYY;w$w!""I&A%r56nHݩ\#G0c >C<==+Vpu C}M@@@7o2h 6lٳgw>}:7nATBB=ZZZL0A)ٳ'QQQlݺݻ=9=Liٓ;wp1Zhv,N6e[I߿ruuhܸ1})jǢ+mj}s$ܜK.Ap233ٶm[}o>6l؀)O<ѣL*j=oooi۶- ,YfLXXBm.\uֹbU]pA&9rCB/;wKÆ Yt)[&::Eۙ:u*@ck,0l0tuuٴi;v`ǎhkkӾ}{zڂ=2l޼+Wk.Y%ҡC;pD1YƢE1b@l .$00PkB!BQ&$BTh={$55cnnqqq\;j8B2ȑ#0o<|||044Zщ׍5v/vvv̝;wyp>޽{\2sU9y$vwaj2vXٳgo&vvv%Kr=D_|90h >3zꅑO<ۄm6O~ررPL_|AƍcժU\xlllJ}̖-[6m-Ąjժob '2yIII\z]vq9TKdcŊ̘1ڵk֭[ٿR-4$hxxxp9MٳiҤ nb͚5T\ +/ihIԨQd|||W^j^PPдiSѣy&ܸq5&&MB4B!48`i׮Ă 6mOV.B*~W.^˗ _~! ͛7oI2h:qvvV7ޠGL>}M~ /;///=zR6fvލWB!B!N.DGg\\\pqqQ;Vvm5x̙3‡bT^]щMYnOXZZ7ЦM ~:^mo@o͛7yfz;vdjeoL2[ng+Wf̚5KA]iӦqIN>>jǦM?[T&d$HWW 6`ggDZc8v9+W/,4qDYfdBCC e͌?+++¢ֻ}6@;MYfc2U!ī$R!(/AQw,S-_ ԺukZnl߾wwwVZŷ~[&}K>}1c`ookpruuwaŸj: fӦM\pd4i¸q裏=zwwwN>MRRu֥O>۫%\WWW"##Vc֬Y%\f?ѣGѹsgիiРaccgB!B!DqH…͛1?#;ԯ_> &LԩSNe888`bbW^lݺs]ttth߾='N{jU֋cȑ4i҄͛7NZZ :'뜖-[?o>?׹>+W333lllhժU}ۗÇyf~'000[nL<͛:򢧧Ǟ={غu+AAA۷3yd~=ޱcݣM6,[uvZߏpsNbcciذ!K.uDGGh"bbbؾ};SN-Q 7ɉၧ'8::rҥ}Λ7cccUرcxaܸqJȩI&juB!B!JCvB!e}\xӧ`ڷoOj?7Dh-θڝĤ8s X[[ի^:gV^׮]cii![n.@TTPRBG^+_V6Ϟ=ˁÇԬY{KKKw^8oܸ69E܋_x{{JBBUV孷?d뗸ו]@@'!/[[[N>۷?~1cc\emcn޼ĉIIIQ222q7nxxxP^b]u憿?5-[ҡCR]B϶5k֭[9s IIIзo_&NX ̜9.]g]FRRUTI&ۗ1cƨ//!( 2gΜW2"ggAJĈL{9e9iii:*+ !B!B$\,00 M!(@F0/w}m۶q9ODa!5hЀ j: 񒲵ȑ#(|||044s!뫠9+=>4iD9VN>#=z4GeĈkNs@dd$XoPJ:v숳3?L"11ѣG+ecƌaxyy1u2O!xK,wfРABnu놟Wٙ7x@Fؿ?zb8;;tRڴiCtt4+VGJk8pÇKcT%pܾ}cccZjEBBqqq+:B!B!% Ga!BTT0!@GG'''fΜ bT^] !^6pWqppZjju<<< ݻԭ[A1am[… 6ݻwgƌԭ[W{Aƍl]:uf߿_I(j???'䕘#9}4IIIԭ[>}`oo<???\]]Zj׏Yf QNoݻkqCE]ףGsԫWOӠALMMƆ5kyB!/! Y XYYi:!4O?%((H>cc'NTܾ}ǫ366V+իcݺu[]8FU%>|nݺBTT&&&j$!B!B ¬_WB.!uԉSzjr=4B?>?5חO*+8gdd0yds$** 777 cʕ>|t=JDDpzY`[ݻwgHNN>#66V)KHHח0<==Q9&MJ{8::*u&L˱gذatޝmRj<(?~8yNڵ+aaa8qÇ!)ӧ?~<_~$[W^A兛?34o;;;ԫY&^^^lݺ3gΐ}eĉjcٳgEPPiiih" TΝƍIHH ##n:Ν;Ǹq;w===_B!B! \x@FP(!/˲ŋfee%?!7nϏ`%ۛhڶm˂ h֬qqq8;;ơC(ϳ~z:w̯… 믿fԩܽ{CCz뭷HLL,v?fΝ;aÆ,]֭[͢EaJ*7ɉၧ'j Æ CWWM6cv؁6۷wXXXR{u]p֭[FUم $B!(իWdȑ8;;k:!4ȨH jb޼yʸ:?:::̟?竕7~YnnnZ.VVVxxxp6l;w`ҤIB!B!( I/>>>CQLcǎtB!BQM/^wȚR^=㉏Wr~I) U۷W+ر#Ws5k|ǜZnM֭###۷ΪUoPf\Te:B!Bܽ{AѠAۧ ׯÃׯ+ ɉ3gbccCX~=իWptB!B!xUH…BQ6lؠB!^Jwl<&O嵓DvɅƕs U"RֱcG9u~im:uJ_~JU*_o=HKKѣEݝP^G^ׯ*B!DN?*UɓJR-^ݻw石sNM#SNL:իWB!B!īD.kƍjJL5a0M)LT@@2̌O>D !e#00P!!(!qqq>|CC6 VʆѣGGj;oܼyScaaQ~Ȉ[nA.]֭݇[se˔)Srԓ#J{ HHH{RG!AOB駟2rH4ذaѧOM$ +++M!B!BW$\NHHHK>x݄憿?5-[ҡCM& 3}tM K㣏>t"IBQݛ۷ȼyhѢ/ڵ SSSlR`˗/gҥ;\zggg i߾=8r&Lޞ}b``{8y$=iii 801QQTܾ}cc<ի۷oٙKҦMYb[Eq3˗/Irr2Æ ؘ ._̚5kR{ujՊr%V|HZxgϞgܹlܸ222lf͚xyyuVΜ9CRRۗ'_Ļn̙t҅={p5R M4o߾35j(sn:Ν;Ǹqb9wzzz߿D"Io'''3f -[tHB!B!BC$BTxc?45kƸqX`>&55{.ueРAjPT ٿ.DGGG?7|G};v ү_?\\\ٴi.\ 99&M0n8r/ϟ?ݝ'NOժUi֬C¢=z;O&))uҧOնV燫}r~5kEQcy~~~tܙz) 4???lllYfBQܾ}@FPD``oW>saΜ9%jVZ[ȱٳĊsssVZ̛7y^q#fff]Q}^ץܹs p"""4iZBIOB|d|#^'3gŋ>>bmmBxy,㛋/\BG*U^Ϩ'O(B!B!ef bdd'?3j"x{{M۶mŋСC8tK\4*5kdɒ%xb?_MJJ NNNcݻG6mطogϞeذaxzz۷ 6?sYϟܹX6l[aiܸ1… ?brZnUB駟bgg/ׯ_tXB!BtttprrСCϡCprrzO!BhHLL t(Bp>>>dff?'*?!B!BI _tIHv011a…̘1C~PPдiSѣenΝLJ>W2f}/066VG+DiըQd|||W^1#Fx^^},XiӦqiNvμyk;v,<|رܸqJȩI&ju2Ut6l؀9o&cd Yfo޷jeW!Fxx8;vtB!DשS'NիpppDQ !BMgРA4mڔ;vh:!^+"ǫ|N!B!H…rM8k۷2dHm߼vȏ/^ի4mڔӧ믿hժU,YfdBCC e͌?++'!߹sJNuUqgL (;x<رܿ%$'U+Y8p磏>R>#=ʁ=zYXXp*ƍ@hh(fff7!xg22Ja!xI$&&j*x VVVL6-!JQAESd(*0`>L54/ŋ dȑEj:!B!/$\rPQpZɺ:9%''52Wő븹9fff9sr6mڄ7 >XeOȩjժy]WQcQ UYiݻw/ct邽}z97?N`` ?wޥJ*ӻwo}ςxfΜIzpss#%%GҶm[ԩԫ[..Gef2-^UtrḹOff&Fe˖tA !B!,\˗/+ݻW`yY+h q222$11+; :Ga!___Ǝ0B!Bܳ@5j+Ws044V-tRaaa/$ƍ7C6mMxK8HIIQkC__!CΝ;Ç۾jתY\EZm*'U".\Hpp0>O͛7ٺu+?C9sǏٳgYhQIl~~~r5RSSyd۶m2h oeCՃzU2ޅ HOO|HOO租~*/NHHHiii,Z#F`hh,\rR!B!DFFyfX`Ae-1DƁd%((͛k:$!B!B!+D.DֿVXATTO>իX"W޽{yIII|w2yd*ۥ_~ח:鉉JnXz5 bjjʰam[zHKK#66\){!55-ZвeK@}Gz3<}Hѣx7Ն,Ye4hggg|}}qrr{*'O`|}}ٹs'&Lرc_|Yl2իGbbb*G`„ x{{ビ㏩^:P;WuSN@֪M6Ofj֬dS,YBv011aڵ@.-3f̠m۶tؑsJi֬޽:u`ccC|||$''3|6mJjhҤ իT*Uɓ'L>}}}ԩŋHJJbRvm%0;UfΜVWҿ*WLժU2e`}'U}ڝfŴjՊjժQfM>mV///=zR6f`dB!B8Ci=}TiSKK mmE8PhJ|||X~a.]ģGprrF|7TR'''ƌ_?5kO?%((H>cv&Nozb߾}[uеk\lmmqvv%WBIrin߾Վ*+ҭ[7BBBcTTP!^f|I{ڴiĉLHˮzꤦrO}177ϳ %Zl=˖-#,,Lyv;w$33XΝ;G\\򽝚 :t:t@JJ ׯ_GGG6m(Td*[_ cccCؿ?ZZZ|j;99)?x{{sy._$'攞N޽S +ٙ0?zipwwW^/]Mi&%255;wLIOOg_wγ޽{d%RlٲgϞYj:##PBCCy!s?;'N >>UҬY3ZT<<< ݻԭ[A1aeQߙT_~XB IDATOsԫWOӠALMMƆ5k)n!'00r!x5=z;O&))uҧOSetW^^cPi422jժѯ_?f͚ @z022_D)Q.ׯORRCbqF (;pF!B!$BT=P777Ξ=Krr2M6ښŋ\OOOOOmFpp0bbbɓն;w.7n$!!͒%KIFFF̛7/_^^^?Nͱo߾?{l "-- SSS-ZĠAYZZRJQF[[|ۯY&^^^lݺ3gΐ}eĉ5n:Ν;Ǹq8wzzz&Boe֭[zqشiW\ʕ+3l0PZ\۷ow !!!>V\.33Sz;vӡC"""x9:trҸJ`` T*jԨXm?1ԓ6mro$'';vs ]d TZ 6"+=zЧOۛ`5jmڴQB!B!(L.Dgll… 5RRR011(722bܹN:,n5&|reE ĠA47n{z8;;\j666ؔ}ڵk3o<͛Ws$5kbgg/4idΜ9ôi!/4 N$XZZҡC~g>ŋ ~`̘1ZN(Jի4jԈ.]дiSZj\vm޽{|2 ;wV'՟L]GTL/Vu&NȪUaٲelˋ[%K?}||&44؄ Ͷmxׁ]`6mDVJXn:t`,\{*}g,^Xٝ`_@N{ (kҺukhР)))}444$##OOObkkˌ31cF`aaÃ`e? o4v Vc%؂DGGE'ˢeHB!(ɖ-[z*M4aҥ$$$`ٴiŎa+^c'Ѽyshذ!T*"##  !ףG&NȜ9s߿c-b&GY~=,X#055?>۷oWvB!B!(H.D3Ċ+xwtx\ݻ%# SSSu[F~:ϟXqe:+++r 111޽͛7k%\\tv)/\hN(/ٳ,[LytuGD!CpǙ3glSn"33:uhN%[V^dףG<޽;W\)ς:8jժ.V ST_SLQVxnn2a(+WdҤI8p`\]]={vkԨAVV\z={2bFQ/""wwwe'-[憭-ŋ?hBB& !*GqN"22WWW%Ņ3gra%ᢼI̛7f͚0vXT*wy @!ī櫯bϞ=pJ^T~~~1zhl̘1l۶ ??A!B!/gOW^|ᇜ:uu2uTG(Wqss#,,RRR ]'DpCBB="11Rϝ9s&#Gмys塷zwA~-/_ѣG?^[nu!nݺL8'?cVXܹs5Nښ]rvٳgY~=8991|pk)NĩAOO;[]8'BWG@@vvvCR='Н«o?Ϊܥ%ȗCծ]tҊU4%%EWq$##UV1"y7n~~~|'8qBNJJ *x~L6MY&'';v<1KKKbcc9pQ!B!BMvvvvlݺǏl+/;;;kkƌp0BBB )򘾾>/###dڴiۧOf̘믿>4m[[[tԉʕ+cll -x_ĉ}}}Zjٳ5Ynݺa``//B5|8"^...$%%$;_;_ÇgQR%zS(}ݗzʥK2333*U7|Cff&ozkժUվ666esJSSSéSbccK^z@;|uB!I4ni {q=1T˖-(xdd$Z @egg+ N B*UƍټyCy"7o֭[ʱcǔ*mٲ%,Yrh߾=OбcGbcc jy愆ͤI,= L]G!B! YE!xU^ɓ'3ydIE%mXYYaeeU~ qvv.N>j֬ٳN(H;@ƍy4U^AJZZlƅ 9r$eb211a@~b̙3=ٙ]vq PjԨ_svvf˖-ܸqU~}h۶-͛7g޼yL>GGG5en{С$''iݺ5*6mՕyѺukoٺu+lذǵkh֬m۶%55$ 5mۖ{#B!ٓM6ҥK177'!!/=zS>]]]K-,\,p:]Ilٲ%nnnFSb;v,*;wYDQ"hBB!B!DAp!B!''mɇʱc~ׯ[B7|||7n 4i;;;,XPbMڵ9qK,aΝ$''cddϒ%Kx뭷Z|k͍SҼys100 ++k͚5r5M>֭#!!_t|enwww<<<8֩S' 8rΝ^sQ׭[t ){[n=zxv'$Ʉ@׮]9r#B!ڵk}f͚i1%22֬YÚ5kb5\2k֬!44}q144}1B^ų၏S(SEQS+UTsԿREw/V-++K.Me_]OOOO똺 !B!B$BP377g0f͟W%MJu(υMdddܹs;wn(Y&vvvL&MHNN̙3L65jsf̘Pxu֍ V\;uˋ6mΝ;]ٵkÇ'%%'ʊM6ʼyhݺ57lݺ r?OP'\vMyնm[RSSIJJJHJJR!B!B& B!m2p@HKKuh yOH^:nnn|'70000x!^=h<<cDEE1~xQQQҷo'!B!B$B4o\cjjuųfggB?]t̟?7nFݖ-[R]&lիWiҤ K.7$!! ȦM66o̭[077gٲej*v܉JR.IHH}`ffFRRJ ڵkApp7{YEGGoj}eђp!B'&@!ī͍Ν;2c ]S&7ݽ:իWggg3Y}O###ΝܹsKm[[[cmmQVfM%99&M̙3g6mB!B!DAp!B35kчo@www7n 'zakkKxxpQF իٓ#F0b6ZŋIj-ZШ#B![̙3lmm155uHspp`޽0o<055^ !B!{ !2p@]!D$?'qqqk׎-Zk.-[ƵkXxqEff擄,ڵ;pud ?ɢwy7n(em۶-1k׮`ccNHLLTΞ=jժêU5j*6Z/==Zjiť.SB!Bd-ZD:uJݱA<_իW͍0RRRHII!,, 777 tB!B!*(BThJKbY]PժU111k׮L0AYe !&&ȟ\Uxr!tmڵA?^{'N`iiI͚5|xc,ee!B!<\]]}GF[A1a׹sgYr%NNNtIQ !B!" !ܸqPۇ7:tuX;}4ǨQhӦCqʕrշX[ IDATޞD?ѣG `ɒ%TR zA=޽;uI&@O3B& !;wN#륞۸qc~wΜ9)gΜwHRkذ!ۦ)IIIٳGacc :t͛7$R^zHMM%##ziB!tAOB\\\{' 3p@FqqqT^]!;;;]!B!B$\Q?ox{{Nhh ݻǂ 1b2qiذa̟?۷˿P틍 #""oժZb„ $''űcgʕTToTwNHH#x/˗tRx pww(ʢ={dӦMtRIHH/GJnݺʕ+qwwN:xyyBӦMٹs'VVVlڴ WWW͛G֭g֭XXXaJrr2 8֭[]zm۶%55$Ċ$B&LŅcǎi$\dggKDD7oĄ0ydVS'DGGADDUV{̚5 Ay ӓsQZ5ٳ5vz}6֭ѣܻw^zR?dDGGE-?~<$33Sn]BCC5nD_vaÆѤIvMff&AAAt҅ (5jAAA7Zj_W!ˮ^z :Cɉ'[Si;%xy`gg0^*eIdѢE`llL0ũU~~~|9r۷oS^=z͔)S022R֫W???9y$999nK{VRظq#QfM:uiժRwذaTR^ @ӦMѣׯ_?V^MTTǏ׸֨( ۷o!i/'O3S#bd !B!B$\3'N'C͛o;qNjj*AAAܹs%K͖-[:< ..8޽[{;vJ>}: JYJJ *xnX={Cxx8gΜ_8N6 ;v+?tҤI$''+۷wj]?==nݺ… yC aԨQT*])E%ī-00@]!B!BҒ B믿 ԔuDdd2e۶m@~bƐ!CTqqqL6(P֮]< <<˕pall @qqq̌$݉%,,Lk'ǏE.]8<ƍ6o7776l/*H%a֭$''ӴiS-[F6mc…Z,߼y3nܜe˖abbªUعs'*!C0vXشiS =zw x7bWEGGK…ꫯ8uFBI߿75jPvhJ_M^^~)-Z7AB!ϖ{% @O`jjB!d:tmۨ\7k,gtB!B!KsgaaUZ9_|A˖-^:SLcǎ?cnnΏ?Ț5kXbYYYJ=j_1bN:EXXXѣGT򿼥iӦ +We˖Վ3ӡC%!!22ڝ7o͚5ZjJ"ɝ;wGŅvڡON/z?~>f͚QF \]]9u;wW1b.]Rv8ٳԬYN:pE ?ɣ0:B╴3QSv(gTuvZ_> 4O>xyyB!xWaaaBXXnnn:bbbԟB!Ν;ʕ+prrRمBQvbTR2^BQ///usB!B$B333ڵkښ]rvٳgY~=8991|2_ǯ ovnjwȀ%%oiW___y]R%6 ]8gQeE166fOTT=zjLM]VxQ_믿CӦM:t(W45rrrرc'O$==uңG,Oz~/m߾'NpMTBfͰwޥUR]ݻw }+uLLLh׮ 25k=B<Oۛ`5jmڴQQx^Ic1B[cذa̟?۷SzuG(B!XLKBe4jԈ :!B!BQϰ;y$… ̟?_)sC‚lZnM6m?ٳ|2M4_~q+++\]]p=۬Y OˉÇ>}www'n4ݝ_˗k}&V\Ijj*999|WXXXKn:t(Ν;[ԩSG9޶m[Q2?iР˖-#00777Õ+Whݺ5_}AAAxzzoGdd$;DHHHEFԩS$?8x ...e&O }{UuZlu:yD]GgB#UGZZGVƌCZZ~~~:L!BQ'/ !˧y,Zwww._pB!B!D)d Rٳ',\ jp888u:uTd=322bŊ&Lȑ#;w{{{̙3Gccc&LQfnnΨQs!.^ԩS~i%.<'NHDDΝZ~ʔ)ZǏѣƱnݺqQ;v>Gfȑe* !66S*IcL>]IR;y$| 4^ɉ?(zX1Ѷm[FIv_ٻw/ׯ_wkkNJ,~77nuL]#ܾ}uqQݻG^pqqѪ[Ejnn.>>>8p133cСjTZݻ3k,LLLJСCl߾ . ={ёG1x`֭KhhN:׮]cذa4i҄ݻwIPP]tQ>/5jAAA7Zj=7S!BRm|||OiӦ 6l ;;___"""y&&&& 0ɓ'SjUlق۷Wvё/RcKnܸ2>Sq8wժUO>̞=[c'd$^d$%%[s! GGG"""t3sbE;vv">>;wPV-z- Fݟz߅V!!!еkb !B!Bw$BӸgCx[ޞ~ dذaTR^ @ӦMѣSjULMMy7n 68nhhJbƍDFFF͚5ԩӧOUVZmzzzh"bbb066'i,ڵk9tw>Օ?u^zɓ'ɡUVL05kT^իbǢ044x0*ċƍ0s"ۺvZUŒ%KHIIGZw_grY׿ŠAVZ+ @EM]#.dff2i$}qRf˖-G\\weرWX={!<<3gQ}|7jĝ;wXd FBRqAcǎ}~ҒX8Kv!cرZWB!%$33SIL>ӧO'!!AJ">>ooo틗ϟ';;5jp WN}tnܸ$gڴi=vAnn.@{2^/Ç3i$ΝPT7|C x)9zֳ r!cǎq1ŋ5>+ӧOMpp0yyy56mбcG]&B!B$BTh+\^V-U ŋ ~`̘1Z Syyyʵ>xWWWSyٺu+4mڔe˖ѦMɫP֮]< <<?~///t?>7nGG"ضm 2J*Ǵiӈ'KiӦ".ԻOEGGE'ˢeB!(N˖-QTLh߾=...;akk[oE\\gΜҒra􈎎&''e'w}{͛7͍ J"22RI(xOKEO?akkˇ~XB{=Nܹs8p uuHOѣG,_|9Аɓ'ӫW/6lHFF111lذYdS뻢%_ܻw 0bLMM6lgc !B!Bg !D۷7n\hԨJY@@?Xx^5jo]P`ݺuh}=V :666CVVkhhK./dСdeeyf:޽{JYyv(N:@uxZ,,,4J*?r...k}}}:u_|Qj??_1bN:EXXV}ggg,--קC$b8v?3㏬Y+V(kfĈ\t'N?_gϞUvʂ}v5o\-ZhB!B3~RX;""wwwڴiCʕiٲ%nnn( ='aOܹsC ++c7o͚5ZjJ;wxIh2d]tARɂ&B_~^=~w™3gȈM6aooOƍ\2 8M6allLxx8Νuόiii=Z)3f iii02!B!B&;\!*_yݦM Rdի'|$e允sUҒ`;>cZlɕ+WĤ}]vŚ5k裏h޼9w~ ,,vڱh" ?!//e5 6'Nۙ8qc;sLҘ͛7'55V]Ȉ :D߾}u7n,5΢Eܺu7nh%VܸqC#\~:h;;{l/^LLL 111|7L4 ;;"'vUzzu EusZ pcooOpp0~~~XZZr =z{ァ|;.SB;} !sb)>>>|۷ocbbB^:u*ZɹsV}anpRRŘCbkkoPP]tAJyF ((qB!B!I…BkРXXXJʕ۹sgYr%NNNBL INNVV/-?3.]?8VZ5ꄆO>nݺ600ёŋ~kV~mmmaƍZ .]pAn֭[M /IQթS'N:EbbF ;w.m!ʣE=SSO+<ɯ`Rqڵ+Gaݜ={Ç/MD =7ꫯ8̌viM666fOTT=zPW,?u)ī' ;;;]!xE* !t<'“KKRנA~mΝ;Gzz:G[.Ǐ';;:vHݺuKlW___y]X<=/ÇŒ٣1IYQw2ydX.\$[t}ONBBRJ">>ooc IDAT+={ݻӧX={Cxx8gΜ_Iv,,>#^Hll,*JMXmڴiddd(}ر\Y7[lQ?x8{2ؿ?XZZjeiiIll,к+B!B(n:uTEpYf /ű믿믿f̘1!B!BBBدQK.eРAԯ_=== ҥ ˗/XAjĉ4h===C'//~:u(u۶mKjj*IIIZ:B!BQVVVlڴ WWW͛G֭g֭XXXazʕ+ٱcpѭ[7<==ٶm={|3ޓxQTZ^a! PUZSWWW:t@N?>fҨ;7e˖akkKxx8u}| YߡC\]]qrr"22|R47b -dc$FEcc!?#2X_uZe#ZVVzzUdz<xUTyͻ{̝;>Gj\oٴiooo"""SOdd$鸺鉧u.]%pTڰJG!B!'.Bzfر9ÇÇqk׮SNz~:t(VVV8p@i;p< o#|||5&L ___~UIN!!!лwoF͛燓Ǐ';;.];T:jQPP?*;Vp_~jT.jxBSNƭ<BAK/k{/+ύJ˗K76++~jsrrRGܸqkV6FU͛)-,,*?*ӦMq$$$wy ծs5P!B!B$B!g\\\X~=ƍcԨQ̘1~ Fccce׫[nq-eƍ;<PU9{###### $NTà :uꄑFFFǢE4ҷo_5j5cǎ/Ȩ1f͚&&&ѵkW-[Ƅ عsZ&M(UT)cffƀBG}u:Ǖ68~xWSϟ',,Yf1sL֬YÅ B'LUO133#""///lllhذ!i&nnnfEu9uU=y^BQ[V$4hq]m'ϯ*!..~@@:*YP>777~m8@LL*SMܿA`` јy B!꿪fYXX(U၇F̙39sfVv]yI!`kk˕+Wx"zRUgccCFFUÇƍׯ/F-[(3KLLTxyҪU+]ƅ xWv&leaa?B\\7o&44ÇeյͥEj*}B!Bd μ+1Ba!jaĈjo.'&$$[΄ $٢ٳ={6 w8BZ V_& +WrJ&OaBThh(wU1i$6o͛7)..&++MÇsݻ?Q\\LZZlQ0֯_)..$,Y;65yr%SZZZ<[n9wQQQjHt_MAAc9;;SPPÇkB%!ܹsj 噘;͚5QF۳xb333"""Ɔ baa+6mR0sLZnMÆ )**Rڍ !44~ѬY3 ŅPиqc񆆆ҷo_5j5cǎ/P377gƍ9kkk6l5GfӦMXXX~ѩS'022???-Z8p k\#>>333B!B!O*\:ehhXb!ēG-W˫BoߞeŶmHII_dّܹApp0gΜ!&&Fɍ7hժK,s$%%HNNNٳ5kHqq1sN9w*5y={7obooOjC㏜;w={|/w9s2d 4 ))'-eOscN>uQ!xBԼysڲRk`ƌ̘1kVq jڴ)Vmڴ)gfU^R+TI&xyya233iݺ5\p'*$B!Bp!Ԉ#1bB޽{S-66FO"}! /vQKB8rj (KVXp!Æ #66VIf8vͣ[n888Ȕ)SԔ|"##IOOOOO<==gMbl111-T?W'ORZZJjj*䧟~ٳx.]m۶ckNB!BBAAs 333222X|9/#|:W_$|DFFbccB!B!DyS!x̎9,B!uGp_4yeuFFqMƢEHHH !!u1n8066ܚĨRqwѣ||;v|̪۷>}:JKK׺Ν;k\Oզ#B! 4ݻwBHH1KKK&LȞn&&&,\S2flիWWށB!B!/IO4Ni}cW.by?k.prrbȑe\!DW\wB!R]k*V}(MJ ӠA*憓Ǐg߾}\xkײm6&O+=61VC(**:;ҩ+HMM֖W_};;;v|U@c,U.Z!B!ģ={6mڴ\z"}Yz?/ʕ+C} Df}H$P!<666ddd~lllkkk˕+Wx"zR/^XNJJ qqql޼*.jcebbbXz5ǿLBLL $,--HOOWШQL̽EdggK-U!k'!B KߡTI= !B!u !Ԝ;wʯq~}ϟ'666j ~/ē(66X}!iӆ#F; !dĈiFa̛748qdvdWm\B!7y~B!B!B!ēN*\z \\\pss#44]O\\&M'''~mV\IAAÇ|xxxлwo\\\xwٻw#7o<5v>t̙3GiY`oNNN5]Ĕuݽ{O?+/44|^|믿βeԪVܽ{(z쉵޲eKݻU[!oN KKԾo߮_OOT333#""///lllhذ!i& &&&NfhԨ,^##J ((N:addvvvh"̙3iݺ5 61VT\\`Uދ~sss|Ç?ҜB'ծ] 1rH}SU"3f`ƌ^yrs8v옒pc,aȐ!4hЀ$&N[={Xf KHH;wԚj9-" ,X{Xbl޼۷oӥK,Y?CDD#'\f~[l!==֭['йsgRRR?>/_fӦMӶm[.\ȳ>ˆ رcJӧܹxӧOK…Pq-Fr 1͛7gڵT^ Ç|ܺu ҥKj_q>5}kddDVxqqqڏ9\vvi!֭of(jgĈA!#pqq9s憙,_^zI|WDFF*BSJKK9s&ڵSNzT!x 4ݻwBHH1KKKLLLXp!SNe̘1İzjB!BxT}a˖-|XZZjܹs4n=DYMɓ'IOOw85w^ܹàAA޽{5jG}ĉ'tN(Y`fffuaT5Ν;)--wޞ=z!R!D}b``^^^'Y!LIB5CCC"##ؾ};{wB!B!dp! """EӖШQNJJ qqql޼GNϹ|2]t!%%0f̘g)}tՠAJKKwfff\|Yk̯UV\v .+(.\FXUZhAvv6hBXnnGQ{_3gиqcѣՒ믹z*;ٓѣG+>|Htt4| nݢQFiӆ`ll̫ʡC8~849q<Ņ&M<~EڒM~7vٳgyW0aBك _||ܹsKKKՒt_ͣ*w4o\ceeE׮]eȐ!}2'Nxń;wj*bccٺupQټ߿T=A`` ј<0B!LMk#$B!#F0b}!%B!B!B[ Q}F``ҞСCqttdСC qYoN=AYҧOV\Ivv6XGGGUTI${ѯsuu 88DJJJHLLdٲeׯf7ر#Tm>B ɓW_}Ŷm۔QQQ?PPP}6dJ۷_EII _Jp1′j;5:#00cǎOqq1N>olܸ;;;,Z}CII nbϞ=U~N>Maa/{Aaa!|M-ꎣRIռysfΜ @jj7Vf(B!O<!B!B!TOWWWbcc "((HxZZLHHF‚kWA޹s{. .ԔŋӨQ#.\ѣY`;wܜw}#GovW9={j*VZFOuѣG~:ƍS;֦M6]Ӈ'N1vrr2PD! ʊ;vpAΜ9+ 4য়~b…|wu0w\^|EJJJ8y$ׯСC+[[[:vȥKHIIk׮5RRRʢ}J2WgвeKLBv "300k\r:0~xڴi͛7 '))cǎ)+t_k.0Ο?@5Q+I1pmV^ͩS>vvv;s;a9­[⭷_zS4׺ZRRh ˸PV&<<Ç!vvv :aÆUWUV6wEϞ=VlGGG3f !B!ē >[[[٣q|Сܼy={`kkK\\ѤQXX`jju m" 9'NksZyL"BK6`5R*S5y&}k.prr!B!B<=$Bݣ0k,JJJt^Jxx8;w5jĮ]HOO__Z?̢EhժZb,X.]J-ظq#aaa| /,fp߿#筷RWqFqq~Zh믿㱰=8p V">>cǪL QNeoWl/ۛ{; !_TttCBM68::rY֭r,11L^ylmmYn6lP;?;;(XxNc޽{qƑ:t{ig!u}'NS> ~R?0vIii)ZS !B!! Vp w„ j߻^뱵Yt)K.h}Z7 XJ!7=$D1|pΞ=W_}p ;3gC A$%%1qDuo֭dffbkk˒%K'))Ikey&BWw T}ܿ鉍 P^`` ј9B!B!BIB___+"##DHlllstBUY2Csq1͛,rpp 00)S?rJœ۳pB FllPS 6[nL8Q#ΚkjjJ~~>ꊧ.\t mjk׮Z!ē!A秄7oΑ#G9s&pqqqɓ駟8{,PQ?ܹs ㏙:uZ_y&B$O O7èQѣGc6nH@@B!B!D] !D111a…L:1cի166stB<.^%Kƍ(I fffߟsطo7oH x78pG7ѣzlْ7nV~F?<ݻptTxgy&9|TT={Zioٲ%DEE1fB!BQI…NrJ&Ou\p޽{s ֭[ӴiS>rrrŅmے -\Ɔ_|$]F.]xj5~uKTT|tؑW^7|CNNѼʱ^zw^>3{=ڶmK^^ &&]`1`n߾뫍S%o׮o͛7oT'[um[TZU,,,?>,[ KKK_qprr۷/vZmɓ>|ƵUs-**q a BEm߾{; !j͛7s^{5bcc100Pb RSSW_Ύ]*FuUYNm3B޽{m$[WTTĤIHIIQڲ 990jjݺulذA-;;(Xxڱy%QݻgRm_SPPu9s xS!B!OIB9///Y,%Ջ'OFXXWңG Fxx8ׯHҥk0$-j2~u BBB׮]c…J{>}d]E0`$2l0Ξ=ˏ?w7ngϞe֭lݺ@5N3<Ν: 䳵ʕ+\x^z)1ؐ$?CN3f 6m"88_~YVSq-,,pwwݝؼy3{)֤BGy-Z ;;\rss>B'ӈ#1bBEK…HVprr$''sI^}UݧtxII֤lmmIOOW[pWIBܸqVZd:wLRRݶm)))̝;;;;222̙30l0Ν;W;v`Μ9 2 ĉĊ.]ĺuѣ̟?Ln￯u ]pi:wqUӧ%B!B!xJ=B!ڵ]v=5}]166А^xiӦ)<o?!2l0&OڽzYfܽ{M-144dѢEaaa!L8K. 8yfԔO>3```={dҥj#t_cǎ 333 ѣ&M6w}kkk (..ʒK4wjYK_ `ٲe$''SRRBRR˖-諪<1oGQQZ}F``Ҟ8o&|?tRL"Bվj?vP>_n044Aoʑ#G4lؐ+%)LHHFՕ={j*VZS2yӇ'N6l~BQvtR.RB!:t(VVV8p@i;p< o#uܹJ;͚5QF۳xbfffDDD免 6WWW6mڤv̙nݚ RTTS9駟j\ԩFFFagg-RM>OOO6m̟?k?>>333BG}w\;3fɓܹs~WN<ɴiu{M]ƯƬY9s&k֬… zG!Dݩ 66lP2k,011Ȉ]l2%ibΝ:addDxx8XZZbddĀ]˓g!YoheP勇>i>Q+V 55[[[|||X`A-UUgÆ +=&sQ]@syMB!B!A*\:|%?5* !ؘɓ'l2 Ciܸuy;fee`eenaa31cFtssM]g͚ŬY4uvl8̙Ü9sդIذanL.\ĉ1556n!Em98qOK.%..333yxg%!!/#G`hh$ՔWe3|<==cC!ēc„ jU'377'88T{g ][D!D}akk˕+WxZ/j!##+buAU0""KKKӕ%%%4jR~ABw}ͭ25K-&77-ZU!B!$ N3rH}!B<t¨Qظq#GK.zJ 3gnnn><嫯"22ٳg >>>zNǷ>pXXXAvc͛7gРA8991j(bcc[n 6nHNNFRF͎;ظq#zN!(#$B!p-['|BNHMMeٲe}ϦM7ogϦCol۶[_|U|(--iӦV}mG͕?/^LǎILLdҥ^&sر#dddh$Vddd(}B!Bt Qg"##wB!Se $<<Ç!vvv :T:GX<رcՒ-k޼9>>>^?J…ꚧO&$$#G`ddD߾}2eRe%ܽ{p=/&LL:QQQHƍy76mRݻDEEѳgO[l#QQQ3ssssB!y&BQx{{s!]_}8~8ɃL>]Ғ;w၍ 9&WWWb3"i IDATcc Ҩ }.]4bر#\É'HNNAorr2y~B!B!_$Bբ!DZP%={6mڴ\z"}YzRQ_~V\ ɓ5>B|q !uEIB< ibbBxx8aaa>>\zpvܩL8w\JKK9vfff 07n\5Yj;V:ʘB!B!x:I…3fĈEQC; !200 ///}T{)_W퉈Pl۶^|EΝsbbb ܸqVZd:wLRRT9={Xf KHH;wۻj5y޺u *|ԩS^={J`` 7od۶mTE[l!==֭['йsgRRR?>/_fӦMӶm[.\ȳ>ˆ رcJӧܹxӧOK…B'<!x5o\DVVRP‚3f0cƌ*憛[}*{477'88X˟t\4i6l 33֭[Ʌ 8qfB!B!xzH…S^^^DFF; !D UB!Z_]{>>>j;q9r`Zj%+,\aÆ$3;v yѭ[7 )DFF+xzzV;ϚXj[ޜ9sBuƼyB!B!BAAs 333222X|9/#|2W_ٳlllstB!B!Kp!BB!B!6h vMHH!!!j,--*JpBNʘ1ca> !B!~ !1ZfCB!W?*[Tl/|^ɡ|4hРkٷo/^dڵl۶ɓ'3|JϭMK/E\\n׊SWT:*tU>"CCC嵶jMB!BQe\z"}Yz$_~V\ ɓuژA!B!$\QOoXT-BRgddNNNѪU(qyܱڵlڑ#G>q*::Z!!޽|"%SNqsa609ME!ӝp[3 I4?r)jiP"rCuglЖrӕx\]}~EΝ;177/?3Νk׮Jsu-ppp 55i&JM&ƒ8;;MPP=zUV}.^͛prr8̙3ZҤI~WΞ=KnݔgT"''333c999J!$B莞:̕B!B !qqqܼy7o}_PP+WdϞ=ӱcJ_֙3gg֭2bܹC{|ǺAƠAt6!zzƍӓ֭[_̖-[aݺu׏ŋ_Ҷm[Ο?ŋ˼#̞=AѺuk~7@zCaa!XZZjcI:uٽ{7&L`ҤI333_۷0`@ -Zwrr2>>>߿ի7nLJ/ۓc=ʜϢڴiCVViiiiiiJ!$B!B!BJ.xNر{]v WV{. $&&g]CYU}6sE=vذa|DDDXktUVTT3fu&; !D5n8HJJMXZ=z4{_eܸqZ]|}}U;fmm}4iƤ쌅ȕ+W¢ܱDEEC! ,,(&NXsϸq3gj yBB```oʹiӨ_57,, ???Y&.ӧOWp V^͡C}6}aΜ9Ӹ5kHHH //͛3vX 1 Ç `߾}dffO˖-qttI闛KXX]taÆJ{ƍ!,,1cPNbF!Ӊu(B*"""B=xJK011a̙̜9qLMM7oZ[ff&PfU3{{b+7$ٳĊԭ[+VڧnݺxzzYx%Ov###\]]ٰa4m Ξ=ɓ]vB[A!xa:}B!B$B<U0eRSS>?zzzjc,]T-b4mڔe;w{n?>yyy|ʮDhh('N 00KKKlll8y$IIItQ雔DFFݺuB ٵkC - cΝL0jժ=ѹsg~'"sdv gggוs.]ļyx}|Xټy޽{$''̭[=z4{%??;;;q ۇs1 _͛{lϞ=$%%бcG  # uB*߯ggϞ3{l166&--%Ko8—;w&$$DI 777G',RUW!<ݓ[B!B!"{-Z !! Ljj*:t 44ǏNΝ9qQQQc7pQL/իWg۶m$$$0i$6o˗iڴ);v 6ФI.]ƍwVntt4...ɓ\r+++Zn VVV\r\;jKiȜ9r+Wr1iҤ W\!88m֬$$$0~xbcc[l!## 6mıcPK yߴiׯ_}DFFСC ,q޵EtڕSNQ^= CCC"33LVZO!D \]]),,A^055%&&YfaÆٳBȿovA.]ӊz5j#sбcG:u*xxxTx\OOO,--=z4ܼyS99sx?O>Qi\ȑ#|J?///FIv˗ի...Jbʅ TUT}xR%%ݹsUVaooO xwYr%~~~ N!U}/BAA5{L0A(t7Ãe˖0uTuB!DYh 4P~&E\R B!Bb \P- WIOO.]hkӦv-[* 5%Y<7ʕ+GGG8|0}ѣ0i$Wu칹>wy V((Q9`;;;GeڴiDEEUZŽ{ W^[%))Im3gꫯ>Ѹe˜>}ZmRRR4>7jԈ~K.Ѿ}rm,&&&888@jj*`ӦM) fffdee999J|ڴinRcJk׮O?h>/^ŋܾ}{O1;w](ܹsg9z({B<1ggg+o^jw}a*|=8٬XhlRe.:Tl۷;w....{aÆ_Ql56!B!O?* !B!B!x9Tlk!twxyy?nܸaccÔ)S*Z>III,^=zwvv7DO?qy^}ˆwޝf͚O?Dy]RRR>}:?M6ZE9s >>>O}Ei}뭷XlYYYܹsKbccСCKBMGZ0`SNf͚J?ȝ;w_>zÃڵkCطoӲeKqrrАΝ;4ٵkgРAԩS/J 7n`:t۷oӧO̙ݿOlذ]F8p &L@W*>JkX~ѥKh7Ɔ0ƌ̻/9B,EDD:!^jGGaiipΎ Eƍ+}~ JW!ʣe= $&&`kk[KB!B!tE.Uw~zbccz*FFFX[[3eZjUiթS Ǎ7033o߾L8zzz899a4'{cbbRk֬gڴiO޽;cƌQFZ]'11SSS i|/ `ժU8p[n;޽{bffFPP;v;wЪU+ƍWtmb6l5j <<˗/www_Ybfرj;|0O{UCӦMվP*^<;v,kܹ֬s;w[2tP[11UBٳ9z((Y 44 ڵkٰa9YYYqM.\?7oVܻwduGU '**qiپ};R/Knn.Ǐ'##Ci۳gn*dgg|r^{5L2T-33@RRRGOQWeڻw/iĉ۷ggJUە9B!B蚭-zzz;v*L=Xv-FԱ;s m IDATlݺBF;wuhB!B! IUZi&&&̜93gVh m֭,&MRPTYsr]АXw?ݡsRn]VXQij=322b̞=̾OM4GcJcdd+6l ##CYٳgBMZR׭[*mׯ_gر;[re@}ӧSfM1bܹsG.uܼy7or߿/@ǎxR;uNi}}}XlrKzz:BP%Y\tI|UlԩqLզ#ċ.""]!x!!t*BTݻwԩSܿgȐ!|BbŊG}~~>Vݻ3d֬YCAAhwy'''a£ K.4lP׸qclll S6mB!B!5p!/7|-[ԩSqT999qI.^g}vf͚ `_^o+4 999|'4hЀիWcmmͩStڸ7|n\Dʳ\Y[.]caa;C˖-y4v֖8vsXf L:gggk2`{FOAACUK+ˢJ(xXiLLL;w.,^zѻwbQ+rh3_UIg?0kKu~qTm>B BBBpuuuBThh(GuBKB*%??s=3W}ussf͚pQ3gOvv6?$>>ӧ+?*i_/2j(IOO[nq=z4W^̙3駟2k,VZٳguB!9cAJZ(,,T*z<幩)Μ9s]o&RZ5QTm>B!B!VC[hh }9RjԨQo^Q QuQN:WCc^:{-Oy_4Ȉٳg3{2駟駟j(;-Zp}Ο?WM~VpmΝ / IB!B0sLׯʕ+'55U=9iiiܹSR1c߲uV=Ř,rrr033S;7''G#B!BQH…xjMB<,--ux <: !ٽ{7!!!xzz9nnn:Nxv'gϞ%::i޼rԔAakkȑ#Ņ;. HJJ%+ԨQ7x Ƒ#Gtbz*#GTF7|CPP:N!B2=z4\tkײj*zƍӓ֭[_̖-[QѠAo),,SNg76mڐEZZFbEZZG!B!*Ь)B!344ۛ(233$** ooojժx`ccӧӧSLQj*޽;C a͚5hsu͛G>}޽;nnn۷OT>|Ⱥu6lݻwgϞ|l߾]휒/o%;;vZLMMdNc̻w`~müy~z\/_ϏbҥKxxxoӷo_/^۷5bRԩS߿?+VÌ;VÇ166_~ZޥB!B!!.SJaaC6mĉyw?IxxZ5b-:%)+.m^3 kN㘪MG!U˵k8p`ãje ?dŊ8:!^q̙éSXnfbb̙3ٹs'Ǐ,[d {3f͚ѢE cFFFrY222 Ξ=FrB!B!H !1c111C*+::Z!!lll"777 U|||hҤ h7NNNDGGˋ;`mm_|iJv˗ի...yX} j(>j}ٳ ;vˋSGRe^w,XDYv-ǏՕZjz61φ Iff P.U`Us%_5:u֮+WhӲeK幉 >,ڵ]v76nH@@Yti16lԩqLզ#/t5kFRRBCڵ+LQ2XreB!B!%I9QPPy&&&&ٳ8Uvu-me)Z>24~E:pҿ1TzzzW^%P+ϋ=zpm5E pww' @ VT(O\*yͨڊOU[y^kB"{CDl۶M/$ Hu릣ȄB6lHNN666xyyQzxxxl2NZB!B!%I9͛7y&qqq8*QgΜߟ[RXXȈ#RvBbEBQΝ;177/?3Νk׮Jsu-ppp 55i&JM&ƒ8;;MPP=zUV}.^͛prr8^}UbiҤ +gϞU[xxY cv8}4۶mqUJ)/m^3fffdeeڱB̊K2edggcjjJRRg%V*BEttV]]]quu}J!B!B<9I9c{=vETT'\T*۷;w....BaÆ_#Œ3uB<ۄ: !D#;V {fƍxyyI֭믿f˖-ذn:?ŋ/i۶-ϟge^ё fϞ͠Ahݺ5Y%t,--$:ub޽ &0i$틙ׯ_g 0dE)/5zƍ/}*أG2糨QFqicСXZZRPPӧY|2yʹiӆ,4+T #mڴy*q !RXX8p ;vܹsM!B!B!D% s3B$++'OR^=>39‰'?hԨ ի9Ri5j| AAAxxx0:, 2D!ܓBTqGRRnnnjjժŌ3G͞={_7nVqww___|}}ՎY[[+իGvv6Æ ܜ;wjci{.???>{fܹŞojjq۷gĈ_;$==ǫh+޽{3fl֭[ٺuFf͚1m4b}Rڼfz-:DJJk %%X2B! ЪU+8p $[!B!B! J.DJ`֬YXYY);yabbbvgL01c׮]ԨQÇ3errrXd ӳgO<==122R% r ׯO>}4iƤ쌅ȕ+W¢ܱDEEC! ,,(&NXs^{e… 4Hi߳g_|?`͚5$$$G;vyEpSÇo>233קe˖8::Txe}IIIԬYw}ӧ+(Knn.aaat҅ *7Ɔ0ƌC:u>WTTCB[n|ƍԨQ͛ӷo_FEڵIi߿?+VÌ;VmÇcllL~JB!$&&: !B!B!O$\炟ʢLBjj'33@RRRGOOOmK%C_Msvލ //?PY$*Nr Ɔ'ODǎIIIdddЭ[7,,,]v@XX;wd„ TV&g̙3,]kkk7nLVV4lؐ9s;ׯ_WνtO;?7oVw$''s֭"ɓ'C~FI׮]>GO?ҒڵkũS8Ξ!!!©S9*NyҒ5k*7o: .8j޼Z!F`` k׮uB! mРAҧOvϩSW&Mq5chh7QQQdffITTԪUK U%ۺE oV۰I!B!B!ċO*\jqJzz:tF[6m-[Tj@NU;S^r5jHPPO>=z&MDյ=77WIX|9˗/K^^ŎWU:u*Ǐ'99-ZW\O΂ HLL$11k2~x\]]K]RM*x<|PXGUBR!6Zjùp_5ժUuH%r…'11[[[,CBJJ_(((QFtޝ &аaC](f|M<<kZѣ8p`+35=.輊؂1cIHi.\ȅ ~vTdz{n;t]<^}U7oN6m,HY[[mZ!AQ|1WkADDDE| [lQUt"DEEc0DDDDDDn999CBBB->>_>6+7>7bFll,|tl˒ ܜ|YxbiժN"44l_EbL8|6`\aÆqEϟ_h%yJ8ԩS|ˌuDDJ{{{W_}E߾} L6)ow.ʕ+L6AD0`SNʕ+H aٲe^]Z:DJ{R r9.\``ԨQWnȑ#\~#GGйsg$77???}y7 [nlذoo=_aÆLHHH WWB/Wōcǎ޽|1&&&Kx'9z(c޽⋸_el2&L@Yt)YYY,_;vSn]^z%F7nl2oٳgI&׏W6oš5kh׮٪^`0fJ͚5+ ь1I&[oXڵk;wδڲ߉' lݺu+34p!@BBBQaaax{{䄍 t֍r]5f͚3x`ё!CY}kkk K(N999lݺ^~"ӧ5kd֭yo1qDFgĉԩCxx8={f͚ҲeK>cz]hcǎeРA<δi0`ӧO;;;4i3g,QXzꅽ={ƞ={Q/Bx'HJJŅ{舻K(͟?LS"Cvv6FbŊ9sΞ=KXXcƌƍcCCC/8}4999deeqQ>C"##MJfAmFVVV dee}rzEDAフ6mڴYdA 2eNppp1bD%F&"r?~|{:tP}&LE8|p%G("""""""rVZQ7qƕ?L8'ٗȑ#9rd-~mV;w4==Χ$ .gf_~| /򋧂V'OfwK+5QXW7˗/'55gggRSS9|0o&E!"R֭[]6uˋ{իW\\\ 32j*hݺ5SLI&L||<Ѧ 6h" 999lٲ֮]k2m&$$la @˖-o,e*fݺuADDDDvEZ*xjg}YfWd=1{˯\´i4hNNNЦNVZe)""""""".DD*ҰaèWL⑑899kDndQ8|IMeLJgUHCY:3lS9)`ǎӼyslllpqq!((-["##Yn' "!!RY'NШQ|7nlVGDDDDDN222b͖EubccSd:py~JJD x '==///Sِ!CHOO'<<܂ȃJ+\Tjժ{СCO>jժNDe<;v.]mllXl_y뭷HMMe̙]2ŝߟM6gPĘ`tiBW/INN6=3f 3g$..8>S6T6 ryj֬oXGD8lDD%EDriӆUo>ZhapDDJ̘fϟϑ#GR ?i&KOBB[H,̭[K.777vŦM駟Xd Vb 8mĘcm>>CDDvvv)7| ^W_}ō7  ;;QFd:ٳHhh(LcHeQ…]l%y]6ӧOn~~WNZիSvmٳgϥK}:-ZJ*ԬY:_t@?Nnn.Νc… Feۯ_? ʢYf4o[[Ri|-O>m*kѢ)))5눈܍ùvZСw\%RD N'NaÆTRŴۭqw@pp0͛7زenvxyy'o׸qc:""""""""!K """"E{Yl| dggMVVׯzꄅネXj?VZyUwn6XJJ dzm6sw sɒ%8pʕ+&<<}uޝsΙ\t)fnyvv6qqqqEƍWw ???vő#G5WjUL Fpp0!!!uuu-Uj 0'''bbbر#w&11Ѭ]KΝv""'Nn:nnn <2Cܸq!"$$SҠARFǎIOOFHhҤ鱃`8 G'''==z4ѣ;17WɸXGDDDDDDDh \||׭[իyhժ4mڔUV1bĈbg̜9Ӵ:EiZh͚5`ر̞=3ըQ ϟϱc߿?ooVYBQ7X+rxxx:\iٳ'={4+^:,_THMMü1ݻ ,r ӦMcРA8997NVZe)"""@زe L4鮼7 "rqrr"%%EYx1ɴjՊ$BCCxN:A:ueddꈈT&p!}xzzZ: )OOOڷoo0D=3Ƿ.OBdd$ 6,w _~cN< VVViӦ=zDqU^\jݺqڵ 2w\U?~$deeyٰaèWH`d"r?3  KQ.IOOT6d `d"""@B߿OOO<== t8""ݻܹs,\QLu8@TTmڴ!,, WWWV^́Lu+U>}Tf'%%%_Ʋ[W ZB*̻˻k0DDDyjmcc!? 666slb**΢/;;;.!E^ظq#˗/'66@ΝKppR2ժU#(({C'|BժU-<˖-c„ 4oޜKٱc;u套^bĈf_nܸeؾ};gϞ֖&MЯ_?o_qڼ598!!L֬YCv~׫W5k:tY2^rr2}'<<DDt~~~ڵ#Gdjժ;Lg֬Yܹs地 Ra|||f֦M۽XLjANN}}lٲ7n J^^^keUV-?@||<&M?Rrgm۶% s2w\FKQ6|233M ٌ5+Vprrr8{,aaa3, 044/ӧOCVVG?4[ѧ$md۶mdeeo;YYYl߾^Yz5...^[[[K#"R)HHH(S5j ,, ooonݺaZy" IDAT$$~'R~}ׯĉIOO'$$ &쌍 {{{ٓ={PF ^x2HIi PfQDޠd {߭FATTřVr2dHǿ+͢Exꩧ?7osΥ{|wkEeeeE^^?3͛7I&:u%KO?Mrr2`b7ޖCDŅ0UVD֭2e M4!%%`㉎6^a-Z` ''-[ڵkM3̈́-bcc!e 8^%˴i;v,EDPX"FA7 lfϞٳ˼˦={gϞfuW7˗/'55gggRSS9|0o~~HS…T(OOO<==-ƍ-\ ovL<7w^ڶmkޞy[|ף>Jzz:-ZQFL8QF@@@Y]DDT~___;vleŅ ϖ-[L \tHN:En4h 2$mĉ4j(>*P:""[:KTw 5j(6ED| 6͛7ĉ ___ G'""""""""%\.\@Ϟ=ٹs'x{{3m4VZ1?0{e֬Y 55t¬Yx'-k…ri]o-/&)) MҧO&MTn#""wc‚ӧ(q#1c0sL∋O?oooߡ%i ϟf͚ˌuDD$t"r˳tŖƲe˘0af`)jժ{СCO>)򞶈HEQ…]Y\[.nnnfQϽf֭#..777 b}Aa JZnkk``۶mVZ,X z\yY}yyy啯?E!""w/( nww%={Ʈ]شi?K,aժU=͂XYY`mmoXGDA aիW1cݺuK.GDնm[;w.GQȃJ "lΜ9Æ  婧tX:Dhh(k׮%//W_}͛ӦMK&"'2}t~m,Cv""w'''RRR)D<<< ))o+V0|SEIۼ]:uHKK###:u0_ƍWqqqJ)#ooo%ߊ]A "[gmv %..I&aUfʕ+L6A[ 0S~zG"b-"$$ Z:^Æ t"raÆCݻA`` 'NYf\pUV_b0Xt)#55ɓ'ӻwo5kQiʊm&ZhAZZ)))+RRRLuDDtׯt"RIB۶m˥Сic)B,`0 R*U㏳` ?LLLfҾ lȐ!^p,^"2wɓ+=͍ Tj""""""""""Rq-HQ ңGFeڗŢECۗ%K]`ׯ_gΜ9t҅=zf`ʔ)tԉݻ3c ._lFff&_͍>}0|.]ӧi׮ (}:G>Knrr2tԉ{>+W=wfݝSU9WD3f ///l|vJqqO`0Я_?S5kЮ];Mի``͚5dffx^""!//4H G$""R~J_F ֭f+I 0ӧcggM4Ϗ3g &쌍^zaooϞ={ŻgjԨ /PҗGDi?G6mY.?+π$((\, 777_+o)ZB '334=;;QFdsYHLL$44kk|9sa/ggg֯_ojիl޼kkkf̘Kx9uش4',, b08pGᩧ2=roߞ *i'OJLLLuزe fժUi7$##W_}ō7 n&)j:믿ΗRQ?aÆ%$''3}trss۷/*aaa|7TϘҥKmFVVbwww'>>۷3p|EDާwEDE%b8880n8ƍwv<<DDDo޼٬_J  *uE]6.\0~O>}F9s V*S5bÆ Ν;MKRSSiР+V`߾},[,"_bZbÆ |+ヽ62^';w 66-[Xf#"" i0DIÆ -E Fzfu ___ F&"""ry饗pqq!&&;;;aooO?M\\EKuoR.Ϥ}H+\H3 *uFR___/Gv@pp0n&eѿlBڛƕz)=z4;w$ N84LJ0.^hڿk.LO<+?{=*~T/00д?̠AXr%{ݝL~'W+'N0]O2df#""N:ѩS'K!""jժ{СCO>jժNDDDpiiiԫWap% @LLW={6uֵh "`xwyw-}M rO}ӧOQ`|e-Z0=˖&MWV-q$[=39sЯ_?ٳg=z`޽ddd0rHlllJ{Qjժe*+Mnnnf۴i2{7Le,FVѿ(/k׮%<<wwwKnn.:t0%?5k;Xf#"""""rmܹs=z)[DDm?+V~ %%\i߾=Ç_t rm899gϞ5 ,?gĉ矖Ǐsynʣ>jpիCQQQCDnS󊈈e)B*ݭ+Wlq{n_N7(.]J*ֵ.:hW^aŊGlٲkkkUJ{Q<?^>n]!n}}JӮK[ۼ㩨 *+Hڵ8p QQQ$&&g:w)cYq ׮]+T>ooo-tغuk}W_}E޽Yzi)(fՌ=Q=FiL IIIWi0DnܸaH9ӝg'999BLL NNNW_9|0۷7>|˭u܈%11]X!f֭[8r)СC>N<ɨQhڴ)UTGm۶̜9 .xLIc+JI?+V0dWYyAQJJ m۶z׬NRRC ;;;\]]1;voQK.YfҬY3VZܜ)9(( `ggG˖- /~xR4הyڵ+Y^N8RJ4cI[q1n8"""""""RX\BBBQaaa|ܹtWKRfMصkΝN:>>lܸajAJhϞ=Ӈ/҈bÆ |WݻDmVV+x5:u?۷g}# QFQv;OsZ`aÆyf"##8q"p&Hq`&OLLL1 p)>tMnnnQfM,Xŋ...ӧ%v,]k׮ꫯ駟;wիi׮_~)"%%-[-ZĥK޽;6ḻayvIhhhեKXvN=ػwY9s#`kkKnXn[fӦMeVey"""ʴiӈ(reSҺuk*Ɵ9=z0;gyΝ;W91?'ϟg > … y9zYe<)JIéW}C\y&#\~Ԧ)r)p2 2իWN@@yX[:{A.]իIII4oޜ}XZjٳg9{,QjJZDάM8*jЈHIXYYiӦM]ɽ8o]~j۷o/ իW| @ yyy6]r90vĉM4i[n-ѣG+{gxYy ݻ7?1{'#55bUi߿?L3{ :+++? k׮ʌL4)߹=CP*BΝ͞7oxРAf\\\4+/$MQJzM,eff2o<^~eӧ7ddLh׮ya7n`ҥ 0:ХKƍŐŢECۗ%K]`yIb0زeY_``Q),Yvh*W5k@DDDDDDD*V)GGG2220 bccShݶmܹs_TVV""%ֳgO6olD!""bi7Y\D2EEE8LNIII899Yɓ9r$+M6e$''O+&.\@ff&?p),O3c]ӦMU^9VSLa۷}7?sۗ>}P]nUo\vݻӰaC}5k׮ڵ5k[oئ1)V́СCqE:u̞?}}sckk[`e<)JIY/z&m篢 IDAT֥Kx9uꔩ,--( ޾TmrJ=ѣG|)q2;;QF~={0 ͗ Y'OСC̙3WWWիGZZ!!!8::dY)˱۶m#++ ww|m8p`iO_DDDDDDDJH "X:Ap 7wb"rOݻC1a*''G κ_X/uQfM6mDZ1\+6mJBBǏ}:f썳QfM._\ ??!""4iPnU/"";w"MDDD  5P1ZlIRRӦM#99J_ n 6h" 999lٲ֮]kJ2XjIIInݚ)SФIRRR&>>h_h?*j 15kd̙0}t.]ʌ3|2s)oOY_XZlcYll.DDDDDDD*QM!""""""""""R_pB|_ʊUVѪU"0&^IΝ?L:u5222 <… f+?HJJ aaaӼys~ƌS!KJJ:x.߹v]z1EY>$iӦww|e[I߿\VZo]d|^W^`޼yV3[AjժV˪2& ~߹i t%)V|/={uVOO?իWYd {櫯2+h[wV)˱߯d[nMM3ȽcxzzZ: ҝfI~6mO>$::={ҞSI\riӦ1h Lׯ_Oj*<|,^X^|Eyꩧطok֬!""z`1,^z+Wq[oŒ%K^z{ԬY8}]̎Ylڵc֭nݚK/ѰaCrrr_غu+?0 EsN @pp0M6%99)Sk.tPC !$$OOO.\;UT$@򎭤9s g}ȶ;t3<áCرc97`l߾OOO>6mɓ':ujtԉM61rH1 xb>;]Jy(5jɆ ߿?7nή͍ٳg裏Z:|ׯϯÇi߾ܘnÃ$[VX8pbbb_I,^dZjERR7cs>e9N:A:u̎0ʣ P999 I0fH²Ɯ2FrXYaaid 9DԔlmh6}."q?}}s^>.\]]ٷo$A+VЮ];ٳgp)Jgƌ̚5KW_%<<}ve›14jԨ5_}vΜ9Sl] XpM 4cǎ%,,L l޼޽{uVCBB@___/{8:: bܸq\P۷'66VeB?SU[oEvv6'9!ijɉ03g2@k0`Lf믿044T{ cڴix{{ӬY3n޼Ɇ X~=Z xCaa!/^aÆ:v """h׮Vˋ7H5bԴm"##C-"##CYG!B!O>BTp %/y=6"DGfڴiB{{{eBFiϟˋ7|=z0o<ܹVɓtޝ+l2\\\ܹ3gŊX{twww[b\ ZҹsguG}ĶmTڔ^K'}((( ::QʵطoǏ }]-ZD^^VcaӦMtЁz)ׯ=6m"''8'gggCCCj֬IϞ=ٻw/J .h݇o߾ԪU ###^u̙Á^Z֭[BPP}CCCW-$%%%[(ߟ'O%XZZ2b*lga ;Ɨ_~I˖-111>3Ԓ'*26M͛7N:*uqssVZl߾7ojԦk׮$''3bZjT=qqqPvmԩ?#cǎj?Ghh(}Fꫯ}2}}}IBlٲEeb!ċO>aɒ%Vv8B!ըQ#xW&$$%\4i҄ذaiiinݚSҴiS222'!!h \t ++++ZhAJJ \zXlܿ]v͛6lʕ:N&ƒ p ._-͚5M洵ٳ$&&ҡCƍ鑒'jqqqhB/EY\\$\!()wٳZf ۷ڡ !OT͚5ۻ̺%%oT₋K絰`LWWW劮ח-[`bbTBbBprr"**%Kdbwf(QF˩SpwwW9Vzu&M|?l0~'OFU?#Gߟ;;;׵k 8KKKvءUپ};C Q&t޽ Wxw177gܿkӫW/LLL022UV̛7qyf޻woLMM9|Z_LˈB!DeY&-Z >>CB!B!BB<AAAL2[[[VZ@^^?Pn]˘1cT&(ŋ100>`dgg3|:!ݺuۛ5j(ϑCPPڵkԭ[ݻ3n8̸x" ښ(q 0˗/Ʊkkڴiٳٳg,駟wQb }67fĈ*JOjz ˕+W044iӦ 0{uoi&/^̩S066waĉ]+rrrشi:t^zޞM61|p5B!Bg >>>?LI `L≮^z%\@ݺuKR߫W/zskhhȾ}Jm`bbJ|Hu_!00u)߿RRReذaŶZ?>Aӓlۺu+L6 ={G.]ҥ ݻAkB!BQnݺž ϟoQ>FΝ; p,--qwwa۶ml۶Buܙ7 OtBq}>C!B!mVOY&Mclذ4ZnMDDǏ'22vڑ@tt9RSSٸq#Ge֭[Sy!eu֑N  رccee PN߹sJvUю%͙5k̘1fΜInn.~~~XXXvZ_ZlITT $$D;S ;wf̘1Eo///-Z 䨄AoxqDFFVvOL-Y&o߾BTK.UO3B!B" \PLWx"...?ZY͕_*nڴkvE\| @hh({=zlƍαjJY&LIIII&?Q95H'Nd֬Yʕ+ͭUE5E< eׯ_8EB( <WvB!BUnnnJ'Yi˖-UZj?ںCBTǏg˖-$3ExB!B$\%(s۷o[Zj$E'+]ݻk.Uc.W4==r/,,T#??_x^ppp 66۷믿b 6l 4hVw _7.EYqYQV!B!B!pI `ذa7gΜAQŸɎB<#^!B!xH…x.YZZ;|}YYYLNXupp ..T:[oEzj˗/lْ4>>8;;ӬY3/@uGi{5ѼysPKPB aBg|P!!B!B!B!BQH…x.5XN:ʱի3iҤ kȑ߿/raÆjeԫWWbmmMǎ+$v{{RLLL$''???LMM={61tPfΜ͛177磏>"&&SN煮gر%DTTK,aɒ%tEȑ#' @-!ήk{5ѵkW)I ) 86mQ[ B!3Y}FR8y$۷o'99B,--ܹ3Æ -4B!B!D$ G<333BBBXf իԨQ;;;ƏMgnnNhh(}\v:uУGƎJjժ{{j3k,/ f̙0w\ԩChh(;vwbccèQѣG4iժU#&&;w`oo۷Jb``@dd$X[[ȑ#K|#F;|0fffS !B!m۶m۶C!sDFVT޷nZsrIgqA~TDIIIyf ]vB!*ʳΝˎ;\x/}v͛bl=3B!BQuI…x: &Oɓu:65kooRR7nƍ+񸦱gqΝܹsG~Tʬתo|||)3F\\\:?h~55j͍`233iР$''鉩i} !/,ׯ_a!^@BPI 8u+W}#-Z;v`ll;={ښΜ9Æ 8tӦM#**uVXL#B!BTmp!FΝ; W&rcii{%G'OΑ#GXv-G!##R^=:uG}WU)v .\ 88}qYrrr077YfٓѣGӤI iYz5ܸqCؓz|0}}}iܸ1=z`„ kck!DUTvB@ddde `ooVDPPeXlN IaddTj^hh(W^eȐ!ʲCqFBCCBCT 3ٳgٺu+FFF|wjagg_|}غu+ֿ<!B!U$\!<O>|8,]իWrtBQ^ʨQؽ{ڱK.q%n݊37n¢|~ݻwooo)((P9vMN8'?>| ظMi;u=>|͛7INN&99˗zjI9e\ɑ!^TCj!((s5֭K7nfffzE9:tǏ'--MYʕ+J`` ժU)Ă ~dee@z:ujS{!//.]K.$$$w^ x9r/J…Ug$sa޽KI&̞=[}3B!BQU_n__СC+;!x">#y뭷8}4}-[###7o/GVZݻy嗉VZ%ݺukXz~#GҠAhРFܹs ĉtj׮ Ŷ;wǏcccjժE5k7o,Fw111aϞ=}ܹCCCVZrqm?w^,sYR\O3yaggGff&}}+ܽ{onݺQN [.#66Vu1tPիvvvرĘB!#VZ^L66m`hhH۶m<߿_>ɍ7puu%11Q9I &&lmmקI&k.c(Ipssɓ=_~!Cбcryyy8p-[2k֬ (]߾}8pFWaȐ!@V+O\ggR$O3-)ɓ'iժG!//wOΝ),,dܹy&nnn6O޽{w_|'OСC\~p :ĉ/׹'& ?gʔ),[JG(VD.^z{nf̘A۶m{.+Vٙ[*l dXY4Oy*VVM2MTe/2666dO!H*ʋ<=#oYXYc.oL#B!BT}p!%/y=g!3իL:ŋcccCqpp`|dgg|Rό3qssS[j?dʕ4nCCCٸq#ׯWE:kٲeܾ}~(ZjZ"**~7nXlnݺE׮]155孷b=zT/uW{DEEѲeKx7駟hݺ.B<4R>h;w.̌'ٻw/gʊr%B[wa鸺biiWe!!ij%11 ZnMbb"˖-NmbZ2⽥eⱰŅ`֭[ȑ#u/VQc$$$+,_ӲeK5Im:uVk(SrppرcW[ӧOX-4B!B!DgP/^e!QÆ +;!i;wO>)1coػwo:t(('''gΜ9lْ4b:1`233ٙf͚_`hhoƴiYfܼy 6~zYjh%B.^XӜ8qڵkǪUbƍ8::ҡCr5mۼysPXPy:[灃ӦMؕхi H+=cǎeȐ![L UXL#B!BT}p!B4սΝ;WqbccƍW9ACBY,,,y&999ԬYS6%ū8EyI5بըQP߂~ԩ|G;vL5=zUXlEr&;;[rrr055ոMIt[묭і"E$tN>MBBǏWib.C1)n;w,x!RI+oڴŋs)yw8q"&&&*2e yyy?Cݺu۷/cƌQtR9;whڴ)#F`ԩƥx_PP@PP{ʕ+ҴiS \@ѦX8RFlQK/;K.~P&\(G@@111ouq2'' ϵkר[.ݻwgܸq*ߜ6mDW}gӦM >ssb.\$\9sE<<>>ַ<!B!U$\'fҥDDDTvB^atYHپ};kVQA+lllHLL~SNy{$T$Sn 41j(j֬W_}ɓ'믿 #,,Mw^4iR!ĉQ{;%.qk{4?3?hРAu7oԩSIOO/W\*)1VZIQ6OOO{.[niӦ[x1999I?^pBBBHMM%00PRqnn.cƌҥKʺ2٢4*E߿OJJ )))2lذjcI_֭[q,]$cWN{.v"99 6hwmF:++ QKr,ٳIOLTڄ-!#5j֭[ܼyS$Μ9ç~6le˖%۷5ёD٣QٳgСgÆ ?ݼy IDATl^z%67oTV}}.^Ⱦ}8rGٳ\p']rzɉ'J(v'<9,޽[aqvY[O3X&""" <ƍSn]Zh}r=mn޼Y)IB'QF+LHHPKhҤ !!!]!6l@ZZ[fԩ4mڔ IHH ::ZExx8.]ʊ-Z/W^-5(-[=g׮]yf VnX8^}Ubs.]J8s \| 6Ub[n4hЀ9sТE Ҙ>}:ϟ',, ///6e߸8Zh֟,..N.xFڵK?\j5k퍷w.QI...y^ &OɓK׫W/zqř;wn _~S&mkԨdff*333INNӳBv B!D٪3-_|EUTL#B!BT}ʮ"B!XѣZ{.C !//YfM[,C `ʕUK?p->msqzaÆ|G+WVLtcƌ5k{OrssO>ݻիW0tPu… (M*3S@Kj*Μ9ٳEe2z(V__m̈́:!2{{{WYaÆ+U]?ӤII`ڴiiCCC-3>Čp"##q$&&0M%Sjg]SXXߢ|||ҥ iF져.*:mVSڔun7jHmƍU!X#G~*cii{%F&B9yB!B!6IB!A>}G}}R?Ç[x;wٯ.믿V\XXl#xr&AYXX,.ŋGuMPor;;28q"7RHX8ի+WJ+33S~Q*۵k=]tb'~TYM=X"## GFF>~ˋ5kG>}HHHݻ8pOOO6,_6mڰn:%cllLhh(SN&//+w5jJ ֭ݻiݺ5˗/'==pRSSYp!LX177 84IKKcbjj'|RkaĐ˃HMM _AEǦꫯx믿ppp`\v:¸qx!+VҥKԮ]P|MƍǑ#G{..\`ɬX:]xreiii 4cǎxUZU^eN }/344T~]ڿEw}xŹ}Zmzիwfƌmۖwb ٺukmuq*WO*1 ʫ$Xe'!i߾=^^^,ZE1aB!%L#B!BT]<B>jy˗⧟~"006m<(ӐD`` 7o?[[[jBADDDʾ}ԩZ+V|?={6h'ń(&F6k֌+V0f͛ǼyTuƍ+'z;wI̘1Yf꫄3x`oUNFJ_S_~%۷o̙3Kx XpMۖ-[ꫯ7o!!!%Fp VX9sWgΟ?J ƍW!qk{ccc}:6mܹs8qBQ]97owlݺUmtHH*!D(,,$77Wonq;9%رKKRZ[[s~W:v,_5\\\HKKc߾}]ŋ3hР $ b׮]舍Zgϲn:E:uJ%1>)) x6?INNVw099@1֩S,Sʱle!O2!\!y%4B!B!D$;\zqqqlذ/sSTʶO;w>}:XZZbee{, ٙd<<<А5kҳgOR… Z۷/jȈ_9spb#oݺ5)))ѧO,--144zIRRZB9y$XZZb``%#F ))Iرc|嗴l >#))Im~EƦSg̙|W8::RN^:5_~,Z3gbll̼y;Z( kȈN:?2v [J>}Q޽U]*y-V4jP ;!3Oݦf܎a%):~ԀLaϙ å \NРHJC ({7'7q]\>k?|kUy/g*-ZcǎxxxLzzΚ5;w2`ZlO?4o͊+غuE~ :dMf:g߾}m,"Ү];8׹pψ# ֭[\z?ܹsMuy֬YCjj*7oܹs*+2f GyWWW233kce /@aa!3g$&&˗/sMb֬Y\~g}իWٳݘF]eeyzzBJJ 7o$%%t&1ZG ##1cHC_T@;\W䭡e˖WAu۶mKHHiݎ&~eDPPP^y֮]KZZZcnnnv@0ϸkzwΟu>3՝W۾Mƴi*?uTNZXjz?zΝ;˕j2ӓa6lG3}t;FJJ fZjŢEL?kO@@!!!Kqss3n׮¡CjcU__BXXaaa1”VV]w>}x嗫9Bff&3f0;֭[re:t(Ǐ'55~KΝ;Yd 999(""""""""""wvF`0`08s #G4[yM6͐!Cx饗ؼy3EEEfmuV|||2dO>$ (ן%mD%'Nqw믿j* ȑ#Yr%W\nzz:cذa55k֔۩իی5 /_Naaa_|z~www&OL\\\&K?/޽Al*ܹ3ݻw_}?G}ѣG4R4k֌ fX"^U;-Z &жm[4.lllprrӓ;v`oo϶mEo`ggWi?>>>\{ ;;;ٳ'ӧO7۱jɒ%t:5*vvvyh޼9ڵ'$,,-[Vx~XXÆ E8;;3m4njv_V6mdҤI8;;ccc3SLaǎ899Yt=w=z4c}fϞmHE{{{.]ҥK&hTai='''/^ŋ}\ҬҥKtر}///j%;O><+m۶F*SQԶm[ =K}[n/۷o'++]Err2sʾD3nFD^2`Zjӧ-N1!&&aܳp!MB=0|EZZ}eٲeٓ BBBHLL;i& ʞ={Lͤ$ LFSm*ɵ}lܸA˗sE7o^} SNl߾=jJxO⡇wՕsΕ0Pܹ+WЧO}]:v|NDDD  y9up{屲eNR…Tk5g2jԨrǛ5kFhh(w?87O>$,]///'r?mJ䈎+G'"ADFBBilK,XDDDDDDDD~m~Z0 f_Օ- zApp0qqqU(f„ $%%qZY[nE:զߥKၭ-3%$=zNҭ[7ZliJ,_LǏ;exDZ͍jk|Mu놃AAA$%%矗k6Xy9ɓok)"uȑ#oĉIJJ]v \tK.qAiժ; CDDDDDDDDDD$'eff]at j*HHH`˖-̘1___I5i\@vԯN S~WJ ŋjzC|^rsshӦMe:""""y4 c'RTTDN2d3g!ȼyXnQѤI%* ‡~HAAGD7c0Du<#"""""rR…uIIIfY-ӝ[@(((0ݝcǎ_߲yf?>Ǐqu{1SYms ORBٴ6ښ^7k֬qce.OC_Q%b{nd,m"""""ri޼9Z;H)Q{XLLi;n"11#GZ;iB|}}v"3""""""*%\HBFFťNNNx{{MZZfΝ&״͚*..60YkHII1%={Sy衇pgΜTZnCߩS'~Gӧ5CdggG3΃>Hɱv(""""""""""rK4#F (([nqU>C s53f }QXX#<+`BM4\Ym+,\k׮=V^Tn޼ٳg syg ̙3ܼy^\݆С[lnܸڵk1 7裏Q?cHu}YY>}xnu9|0ϟg٦ѣGKZd>#)**2V;;;\\\2dSNSN0z-h߾=>,3gήNVΎm۶i&>̵kxꩧ /uUCDFFӧqzb52zh6lɓ'6mY'Oёgy("SLL 111CDD營x ?^LϞ=Kxx8{존_~WWW `HEDD~|rΜ9czʕ*[eYfq5SYQQϟ:t۷\o}h"""""""%\HVU2/fն퍷w,m //[mۖ 6[]Qy֭Yt)K.nCߥKBBBj^}^Zn/۷o'++]Err2sھD[`cǎv""HD @ͱn1&|^WWWZhAVx'L}ڧh"""""""p!"RK3uT<ƍiժqFbbbHv"u6i$k "@iiCpe6o̩S((({L6ެ^~~>۶mȑ#\z;2rHfϞqbW˖޽0RRRhٲ%O?4 .l{n NΝ1 ޽SҦMM"""r`РAJe%%%l۶K.akkKϞ=3fL پ};Otؑ{3g7*/oߞ˗/sxuVnV>5F(BDȼyXnQH}5FDi֬CWJ kaH% +WYr%n⥗^W_} .eggCbb' IDAT"888*9s搗7())!((sK (wÃD+Ə_DDD֐cpvez_\\̹s8w׮]3;w.iii.]"""Ti޼y}͜9իW3{lƍǰa۷/֯k?4*III"Y~=SLv8""""""""R;wr9qƍ ""To׮]\p]ӧپ};]t!==;v:~s)f̘ѣG]2=Sݻwe:"[;OdSxi&N>͉'Xt){1EZZ}%&&2`9x`7عs'f⩧w]w]MDDDDDD i WU_5o&ݺu M ׯߟe˖pȑZHnhٲiE_~sΟ?N({fuDk֬ ,`߾}EDrc#::X~g&L@RR) >>\]]GgQ^^^8pO>^{ @jj*k׮ח~MDDDDDD4e999<</_cǎ?Ο?ϣ>%!!www&Mt7C8Z`ԲeK~ax4笈4F/^瓬,oVOS={4vrrsrsshӦMc2c7R"" !> .dժU$$$-[1cjT/33oo IOOQ{wL>"vmxYvm1C """"u ?no߾V1y78~U&Vٳg gϞ=/ʀz"""_y뭷ptt䩧vHUsVD̚5kVJ(--5_TTTi=[[c׼y eu6i\ Bhh&&cTj ///9v_|~-7o&**3~xX*U>|8ׯ_'..gggcvvvm6d1C Ҡbbb(..v"RC{a4 %\?~:+V`„ ٻw/w3LrNeÆ '4 =gE1ԩ?#ӧz]t~ 99ʓL$R]#PӡCˣCfLuDO?Dzz:zv8"rا6ۛ4>Ν; 3%\Cj_޽9s }̓#ڧh"""""""-ԓ3qDk!"0qD_9`ڵk۶m`0{n6mdZ5LHrrr>^|Ecf2ȑ#dff2c zݺu3+dlذ 6Q۵kGnn.>>>p!IMMͬqźLƧUVߟj2M!!!KJs\3}t;FJJJ{ZbѢEFԩSOسg{)?l1C;\HYfҗ藟4ZIII$%%CMRR;vT!GsMYr%2+((W_O?ܼylbbb9s&ڞ3gܸq<>3L̤BΝ;ǻKttE񇅅o6!>"_~%N𠰰F񈈈utR(,,$77>C"ΝΝ;x"\t.\HIII6⡇bU^_ii)W^eݺu<5EuZsVD~СxyyѦMlmmݻ7Y\6mdҤI8;;ccc3SLaǎ899.Z &жm[`Ŋɒ%t=z4Eqo.DT5Fا|||Xr%=vvvѳgOOΪULlllprrӓ;vQx _'<<OOO~мyspuuw|iwSc4vekkkKitؑ>?^z%vŅ ڵ+{&--+VΎ;7oY?0tԉ۷ѣG `lڴ @qq1qqqgvy=z ""Jqݕ})z]]c٩S?~|byh? }wѣEZZ}eٲeٓ BBBHLL]j7N8Glڴ ruܩN666f+EMӒ>9+"MC.] ^۶m 4=*cooҥKYtYyٟ*X ///֭[ʢk׮dee̜9s*|H6yzKc|\c퍷w:99xb/^\eKFWW}1C Ҡ&NĉЁH`L0_oҭ[7LIFG5ׯgٲek9r\E``M???"""_L((( :: .Ʉ 0a8,?PRVꈈHP{zYؽ{7W^5 $$.];v,qqqDmLprr⣏>xPǜ9s*Mƨ\}=gEDqGhDh\\\rt" ưaìTAc4.DDDDjDǵkײvZwywy|UpE+NOck\իu۶mg\ff&@o^״o~Sa=/w###{ޒ-fis_YcooOpp0:SNlܸDDDD~i&""""""rP…H-?ܤK'4[c 5ۯXO4=͚5wN.-ֶ~pwwرc||l޼(ϟ-JKKM};^6ۼyrnjeu""rwvu+Vy}~nܸ֭[ +V 5kЮ];FQiU=?+_;羞"" k̛7u0|ܬMc4{.Dj7.~sYٳg˸2`k&"$Μ9ٳ۷/;v7nnnl۶ѩS'~GӧOt?@rr26'''Rۛ4>Ν; (᢬f͚QZZʵkpttW6+ҡCˣCfLuDDqg<*BFFYӾ/^Lǎٸq#'N --{1Nʎ; aݧg],{9+"|}}v""""rDDDDDDDmj?~IIIU~5E-ׯb &L ]tLJ˗suk'"T[aС[lnܸڵk1 7TRRRy&)))YÇ׸1c`0طo<#Ymڵ\~ .Z뮨oT5JDD7K 2 w}ǭ[z*~!s֩o???z-[,W_k׮;-}~:-}9+""""""""""""""%\  Ð&&22&Ol*2e 999DFFZ12_4W^y:‹/ȰaÈ`֬Yzt֍,f̘;3fŋt֍3fԸBCCy'2dAAAfۥ'K/U٦11dÆ >I&+81u'cYmPDDz,}.XjtޝUV,ZN}ذl25kӧoޞzf͚w^YuZsVDDDDDDDDDDDDDD5JF͘qFifaa!6mۛ!CK/yf())a֭0d|I^y8P?Kڼ39."AAA ?c0Xt˼[<Ӹ3yrUYle-?ypww_$,, MOOg޼y 6QFf(,%??ݻw3h M;w`0{n?""uU6CDFFE6mw޼{,HDD`ccرlݯ_?rƤIuׯ7[uZsVDDDDDDDDDDDDDD5-%7M/**bܹ\tRSS yDڵTsq9]_۬K.ٳ]777:wLvv68;;l2rss ʕ+sYr%nݪvXz*((W_… lbbbHLL$""s̙C^^7n>Ĵ2|Q..ꫯ?~|-H&/J]t!$$zm۶%00*%%%Y\퍷wyyyeQ,],鰲\wE}n___oNVV]v ++d̙S%""%υ<㜜Xx1/n-[f|M|M2KuNKzΊHCp7ݻ!4J~)ӟ~w."r񌈈ȽJ;\HУGN:o @TTiiiۗ׿ˀHLLsM8}4'N0MDٳgm9I%))I+*2jӦ Vڵk\z-]Fppi;wr9qi%Ԉ:[KOYv… tڕN>ҥ ر9?0ԩS̘1G8SNлwr}ˌuDDD sDGGʢqqqߊ4}zΊHC8q"'Nv"ҀbnbϞ=ܸqڡH#4x`}iD47i L:DL K.`ƎK\\cǎpL0 &W6ˠA%::W_}s1e~ߚ|Lu@PPiWeL _~ߟe˖kq͛gvN``_~q,ϟn'pս{w:""Vs[[*gooOpp0:SNlܸVZY9:MYi(AD*)**̙3," `k!"""""""rOS…4 IFx{{WX?==z…Zزe 3flGMڬNM&Ο?;w=zf/^>jq5a)+++ dq'x,;ڸGIIIcnRXGDD"d޼y[}aH#ekkKtt4W)B$2228t...wrrooo8|0;w$,,4m֗>tCZZ,^tSN㏦:j֬\v GGG]:,?eu҅~dl*ONN=$:M^^:t0;?//TGDDDDDDDD'NdĉCDHqq&(Vݝ-[X; i4Fi<47_^ 1bAAA|wܺuWb0;w1c0 ۷By\]]jҦq:]7bbb0`駟TgС[lnܸڵk1 7Ҷ۵k~:.\ 44\=KOY͛7IIIa͚5 >f7<dddkXf#""""""""""Ҕ_EDDDDDDDDD侣.I>}:ǎ#%%cZbѢEZ.ͭVmk׎\|||pqqСCf C'%%Opp0۴hт`L[oŞ={hӦ 񤤤/3k֬Jdlذ 6Q9Bff&3f0;֭[re4CrqRSSŘ .CDDDDDDDD9|ƒܞ=iҤԓ׿|}.''D^!6T9Թ4i:p!VW_j9::ѣGɡuָ1w\zeC-… Zd>#)**ureVZE.]ҥ [zj:t@dd$>}7nЫW/OΨQ*mѢE4oޜx_``Ŋ۶mȑ#\z;2rHfϞޝAm6+.]-={d̘1;,KIjÒp׮Ac8K "d 8yn:ϟ{-CDR&Mv"jرJ C@nn.\rTʕ+u/R #??lb\EEEE̝;4SK.Ajj*4oܬ9s搗7())!((xK (wÃD+Ə_{KAA*.\0eggCbb"888Ԫpvez_\\̹s8w׮]8T7޺[נ1H㥄 i|}}5KDĉ8qo+"T\\{Ν;r }wߥcǎ||DDD:GDDDPQQyTTiiiۗe˖ѳgO222 !11[%&88N:}v"""8z(AAAfI߹SݻwXeNd=`׮]\p];лwoXbرyT:|lڴ @qq1qqqgS[w4iff0̾*x7֭ą?1K<>>\]]GW@uF˖-M~*9}fԨQ4k֬N׷裏hՉ%!!wwJ}DD:>'t 8@jj* mڴ?|||6lX}7QQ8j{>˕+W_:wlv,33?tɬNVVcƌ7 ӟ*>3g/HNN槟~ ]tH!]xeW]yff&OOO/WֳgOk'''JJJ'776mڔ;f,3';;'''`fO<1Tm,\UV@BB[laƌҪU+Sڌdccí[(..ֶ7nUxn]p""""""".DDDDP`` o8|0F2;7|= Kxx88;;{7n`ٲe8q¬7| | {nݺ7xǏߵɄe={pCii)/2 0*񈈈ʞ++Wf?3'Nĉ<3ZO>8qr (**ԩSf MT͛^C;Iff&_|k֬a^ԇwJixii霢JUlyiii+s 5o^~kcYcK/tڕ}1n8k#"XMH.^^^s1 [6oLTTg@Iwj۶-еkr/]dWVu㭻u É4^Jw^Ǝk0Dĉ=s̙3 6~z bZڵk-Ν[n5VXXڵkYn];p ={6#Gcǎ\zx$$$l2>z6?^nMl~:+V`„ ٻw/w%\E իWsa9s&#GSN呐֭[֖~/qSȑ#͎.믿.p'콬R֭СClyz!JJJ?ATT'N ((ӱc:4{YNIOO4 nOd+--ڵk8::կHl 2228ts:t@vv6yyytX^^4=NNNݛ%\Hҥ ? GЫWF{ É4^JU\\̤IHj)Sϖ-[x7裏r L2c4hǎѣ1MNN/uDDDУGӱN:1cNɓΑ0ydSٔ)SOd޼yVNDD*L\\NNNDDDн{wӱɓc„ z#F={^yCbb"EEEfʗMO=}vvvo߾fpss8|0}s̩8"ݻYn!!!<lܸXz!>sڵkGnn.`\t:?bvAPP<#&**O>֭[kԦ19$33nݺ裏MFFFIy:"4`0DɎ; wޡO>f:.w̘1deetRyy~G|g*<3$%%?}YڷoիW9|01>lDDDDDDD/%\H&::aX 1c{aܸq۳o>7oβe̶ /,,d{\UUh8Xjij)"9MM-/cxj_'_H8M`&#IwD$/!K$?|sY{>{^lou7!::ڬkXd 94jԈ:3X]a_g#z2VڰҥK,Y-[p9ZnM0a1+;v`^FŒ%KذaG:[Uf_,k[}W\IBB4nܘ{^xҥK\~___c;3 V\ɨQhѢK#""|w$&&˗СGf̙[ω=lEy>>>DEEwc8YYY̟?M6Ahh(SN5V=p3}FWdoJ>|l{rUBCCi֬lٲ}\o9r8bzs?>۶mI&<L2ƍvZ9r`&NHƍܹEyK,aƌt|赭-eo]W={EY~߳i&rssokm배0֭[… Yp!Rcǎe۶meI&L6cGzz:}ah?//YIq޽{GQZZ'"57flB~~>ƍ3ֶm[4G-o8Njn,E/""Fڑ#G9<ׯҘBCCILL4曤ןb *?rU5SNiӦ ?! >xX}˖-dffٓ'OR\\?_ll,vHMMhG 8oߞW_}궄.]d <εWM>U}~dmAVZ̢Eؽ{7W\{aر׿6M6 WWW6m˗ b7Ql$%%[rY7oN`` &M{q3fwߥw… ٹs'G6˿sN<==ykt."<sz!g#"7H-Z 99m۶q9ZjůkƏϪիq۷/cƌ19Nqssfݺu|OOO~Z>DDDDDDD. :g6lC-,"g֭ƊZ7˳b :̙3СN">>{acu;իW`ZѣG駟f|I31/_ǏӦM^{5:w̡C={6ǎcٲeL<gЦMJ9VYՋkR&wus;`ҥ$%%uV3shMLiYYY`("bN>?:;w̚5xFTőg-U @׮])((СC6\233 -*{V^.+Wd֭ lxbۓdRkk/{ږ>?܋j3_iMKllYz [ӕ}{yy1}tO^i { 0,yDDDtR aĉ%"׹sgր 5T:ImA1hР*ko;Z6lv<Ԇi4BTDD)))CD"*[+L-xNif,M oܹ < bfHIIㄅ1tPj3nwwwf͚s=믿NJJ nnnn @\\ݺu{̜9-[8ѯL舺[S111m}ŋGwZYӔGDDf:00YfYӬ:bccYuF\\SLa֭=[U5SN:wΡC8p _5gϞcǎ~wĉk׮Uyi߾=Ǐgʕ;w~5\\\,^y'**ʬ}T0|8r/=$RƌƍIII1:NGTTpuugϞdgg3eg#""Hm8I.DDDDf9[ֱcnj/ dgg{1n8"""hҤuƐ!CXf ~!cǎSPP\Wރ>3go3gpiC5}uJ}ԋ5ԃI^^^/1>Zha):̬u\7uZ?uwI``>UL'0gwΕ+W_“O>ڵk+=s׿ա:e:m۶,'O7o @XX1ʟip[ۭzz0iԨlצ4WW˦)~""R kߥ}2l0VX'Æ 3uqqOOOLU/5{jՊ".\@V̶]p#""灀szi8p"ϓ BFFl˗ן暭~ڵkP,u񌪎)W'͚5pA͎եK|||!''n5kVتNzz:˗/O>6WҨj`Jy\[{R&Ƒ{Q!ADD '""""""Ұh :/YcOӧOgǎ?R{v //\ gM,Zg:ڲ~-ZK/Č3xw1""zGȑ#Ϝ;w~ &Md}g b͚5sұcG|b[O~ػw6*RZZJnn.o}52p@yHMMogϞİgNJqq1?<ƾ-[`\|Ǐ3zӬ6ȑziS,jcȭ7 //R`ҥ\v\\\ &))l?[o[xʕ+W?~<=zJJJ8qiii5ʬ8rmmK[Ƚ(…ܲcń۷cxzzM;p,^M6ѨQ#^}Z>W(..歷޺@|2gfСl̚546mEF1vXmFnn.QQQfۚ4i´iӌcƌ!>>[twXGqqY1cزe 7l[۶m-̙˗ٱc $$$X]weƺuXp! . $$bߺ-[ry#==ݢ\G}aY\<:Ɋ "##O9y$cǎ4#[ω8~' ϼG}ٳg[".]0bĈ*..QHbO>c\Zl *j=]^z wwwVZˍN*  669rmmK[Ƚ(…8]PPAAAA!##///-[FTTÓO>ɲe!##\g\-?0۶mc֭Mi& IDATڒ9{,>6rHΞ=Krr#o߾ZIRR憗aaa,[lp̙}݇tЁc+U/SLHoѢ >___eȑ,[ ///7&!!zpuuLJP̙C`` _/l7m47XZ2c ڴi͕{ 8f͚sNm;wӘ!ZDDִiS,Y Apwwcǎƀt##[ ϟOBB}nՕ-[ү_?Hƍƛ@hh(2zh/^lQHbOtxjs`App01RՀ {2nnn̘1 Fvhܸ1F|| 66,vΝ;.?>v͒%K׿eמ2o>+8t?ٳիWӣGk6~ _Wٳ233ILLd$%%ϙ3gXbEqV|r?N6mHJJb,];Ʋe,뮻XnYYY7[Z=ѣi߾=k֬!//:z裏صk&M`vZ0a;v0s:eeeйsgMi<"""M6eܹlذB ٰasΥI&ODOgϞ 6}ѲeK/"u{Q!qTYY"~6m"))a8{[O,E+\H +=;;6mČ3hԨlڴ ___cՇ;vPVVÇO׿ŗ_~ \_^۶m`̙t@^~e?I6m >>ڷoܹsCc#$&&0DgDDDDDDn.p! ;< ܹk..\ }ӦMpdӧO L|A5)3??AY~1㵯1ˣi5F|e*{f9s>:t0^{yyp5et֍!Cf>CƎkѺԩYfVckڴ~\ТE |4S"""pv""74WWj2Ix#ܿ00l0'G""utBCCyW)**;tv8"{!--Mm$B\HG~>?[oÇ GCtYlamuYՀ4<yf~gF*oZelEF...vپ};_y,;Z7^;rLf혦4{_DDDDDDDDKIIXjj*ҳgO\]]ٽ{78IDD)))CDP{FDDDDDfҠLVVyyyرGyl%lhٲ%Ǐ7Z4QQ@@Ǐ'!!!Fz^^EޚǩSHOOϯҘ BFFۗ{"_Z@v*-"N|SK޽IMMg#"b8zhLHVS* iCV{SFE.+fɒ%1KsN~g^8qK,aժUvu`ɒ%;l޼Gy8>j9c }]())ӓ$uVΞ=K dҤIs=q͟?;vrEy}:ӧOz kW [Ypayi~!&&=נ#FտI:5oޜ.]JAAmڴ&NHf*=4\)))8; [FYYCU/_f :???Ù5kiii4mȍcǎ|k١or*٫9BFI.DDj1cظq#)))ƀrrt"Rdff:; *M:Pg!RcZUJDիW;;ء.99gi#G䣏>"99ɓ';1:ѹX,"RQCl#%EDDDDDD&Wg "r3hڴ)seÆ RXXȆ ;w.M4qvx"R{!--aT*--|g!R+Эe…lby D޽y_BII .]Ga\vx3yW\?;$((+Wrҥz8S.]ğ'z)oKBB?Y c+O?+J3g}]͛73i$Œ^`y={6c;w2j[(""""""pi ZC1yd,X)S trT"R"""HIIqv""68;ZBDD[Tjj*CgRRRڵ+&LI&q!#_aa!IIIh"Zh#HJJ/`F;vЯ_?>3 (?$${3dȐ- OO#Gtv8""""""""bEBB)))oߞwyDb :Ojj*{aу{a"##i֬˖-3;iE߾}s1LyDDDDa?~6mڐݻYt);v̢SLٽ{7IIIsVXQT壏> 66,v` (ѣ{Fmڴ?f-AFL.DDDDDDDDDDdt>"55///}]7m@||<;v͍3w\222fС|ڵ K.q7onzzQ.n<""""ő6֭[+8t֍Fѽ{wfΜ -[j5XBBBhԨݺu#..,cKt'|o͛o \_8~a⶷m j74d@DDDDGҩS'V^Mvv6 >>C7=wŋhтxpBCCklSG\О2jGu}'㏹;Ͷn߸q#wqYynv>q8+Sg'?o ???zMdd$mw +奦H|~ƶ| ducǎXjɄk.~gz_?-ZX˔f#""" t,̙3Z^pp=zpZ-*}ӦM,*5Weq;ҶTQDDDDDDҀ 1/}vϢEXjeee1;YzO̙3͛7ر;v+QeߪtŽ;8tŀL<<<())!++lW_}eǚV߶)--e޼y[쓟O~~>?x7۷o}{CPV^g0ODD֭ʕ+,^cu啟LJ!CJ^^;w0]\\pu\lڔf#"""R[Fڵk6mb3 LXRMʩ[oÇ GCtm6Sf-ni4BTjj1cX"""8ۭ_|ٳg3tPg֬YѴi SDDNzΛ7͛7Itt4;… dggxb6mDFxWk2vj ߿6ӀH̴pq!ֵv[,Xt7nLTT?8\vÇb vA\\֭uc36X3}tZnMbbbԩSʌ55kְj*vڅ+}1jՊ".\@Vp႑GDBBB裏ٳ?'O$''^z999fm"WiGnnj]vZNUL+%%%ѲeK?nl/--5W_~6pmvHåRgNTS۶m :3h9{,>6rH>#>>1%K ȯȑڍ""""""" VZJjj:DN>?:;w̚5gϚ]bg̙tЁSN޽{ٰa9CcْMLiYYY 2NWDDĪ|~Uӯ~+`D~a>̬Y8s +V`6ˮh?~6mkѹsg:ٳ9v˖-cfu]̝;;K֭[3W~[NvJAA" pѧO\\\$&&CnQQU߳zjxwYr%[n5_P[\Y۷'))T۱g5ϩ#ڰR|̙v>}:ӧO븦ywu۷7ּys"""Xt)irrr8q"͚5鈈X&..ӧOmR{x{{ccKӦM%66,b;k 0fYU+[hтxL.*?o<˲mvHåRkNZe' qm!4(:"VLwfغu+qqqt @f͚ԩSnڴ xΝȨ9~QzLLyDDDS}>MeW딲2ˋ5f֭qqqL2[:Թywޝ3gϳeH^Y)W';w?СC 8gұcGnvZj}Ç9qڵrEU^~eYr%Ν3W:TimڥۡQQQf3e5W9uWVZqq1/_+Fo̘1lܸcJJ ~~~DEE["rٳ'nnn޽aÆ9;qڍ""""""" \Hիzrv"""SNZ`РAVuرJ:6vy?CMtuufJVH}>Haa!7o|7o6WTqu SGrk6¤QFzn״N:puuȑ#\v,dff2rHBΝ:ʯʮCYYQUSCq &L`V"{ϥ6bϩ=ڰR|}}pAAAf3C=ɓY`SL:PHDBBB8qℳjRQDDDDDDр T@@9p={48`ϏSNejՊ".\@V̶]p#""R*X!CArr2}5[믿f >VZvEaaaǓKii)y۷ʫԩ- \LtR]FHH...?d-Է-\-[~z._Ǚ?]< :+W0~x9z(%%%p 5j1;v{iu8rϫ +""" ]ZBDDDɧ~ɓ';vlyǎ˶m%**l[&M6me˖?pHOOO>l߾<)""R^}>~/,yG={|||,ҥ #FƌÖ-[gܸqfڶmkVWS[S̞={ӧㅆw^cS߶8rXn .d…K/;VbJ*M콦=6ԔV%ћ6mʒ%K4hvtؑW_}#'IIIDDD燛^^^l2}g̘A6mpss>qfعsE;wӘmXDD>t瓐@߾}puue˖׏?Ҹqc&$$;=ŋiZhArr2Ç777|}}9r$˖-˫Fj/G{k׮kWWWqwe\S߶8rMСCÃslƌ3HIIaذak׎ƍꊯ/aaadzfzM콦=6ԔKYYYi("##HIIFPXXO??6l.^K~zڴi@AAfĉDGGZY"RRR^!o]=+((h6d3{:mT"##1ڴ{֞"rkm~̐D gDDDDDDn.Ug_~ˀԩS|I֬Y?-[2a„Z-iӦ̝;?5 6H&Mj,{PD{^mX Aжm[>N8AII wq{&::__Z/b,X)SX刈CqjD9;‰'xᇝԡDRSSTE.DDDDо}خDDDQgeX2EDDlqQu&QNn>qM{R]GXjj*?/={8;c8; AӻwoOqqCiMO>\zUԐ=zN:YݶzjfܤnNׯP*;\"""""""R]p!"""RTRSS-#22Hg!""R+^|EonYhV#FбcGzHEfӻwosȭLm. ԩSwN-gggϟof| 999X޽gǎ1 -LEDJ@VViaBYj;vߚ\\tgbooO߾}ͣZ-++)Sp{k\bq.\"""""""RlHuyزe L2ݻwJNDDDеkWپ};,Y+Wh" xwr /ffX:t`#"""RڶmKll,K.Vcqug׮]˵kHII'0-(Jig}ƙ3ghժ֭СCY_R%"""""""A .DDDDDDDDDDV2 fJk;w.[^zL._l:nnnҶm[/^ @tt)Gx{{sN1vXx>qpcBڴic#"""R^|||7.kMd¸\|aÆq58wK̽ZmϞ=̟?G};;;wXK5TN@DDDD* jM'N}E ..wwwF]2ݻw}v{yG4hÆ ѱR[['ZBD*WvL_;99PPP`jKKKӳʠ""""TM8w"ۯ^… 9r$...xyy`"##iРAu)""w}}}9x μ+ӇFq9ly?c=VYWL"b ...>}(ϞoɓtؑTV\ɛoY{6mJFF4mXff)FDDDZSyxxĺur }o߾lxyjՊSNqzmjOIIU%"""""""Q""""Rn4YQ+883vXS۸q83qYlEj. Zp!"""RKݺȣիWE^߿?/"[n5{_YrrrOW^ :UVkGQ9eeeqFz聳)E 6nHVVV;Ut<`„ fƌ+Y{VV}=<]dnGaРA(ŵC^s;SOرc6뿸T,|INAAٱ4 Æ 3;%K۷/ bѢE.5k'Nnl6mژňTstݻwKΝK.̟?]vY{{R3'u888KBB_|E85bȑ'OϥKptt8fl3ƈ%-j,8 @zzz*,,$;;۬D.]w gZǸ8~ڷo_lLJz ~:znݺffv /bj+5;] Xx1۷o'==toŋ_;L^HIIoEDtDDDDDDDviH&/na*|W,Z.]p5VZŐ!Cؼys K쯴_שS˒f,e:w `ڹ(=d\~~K)-z IDATj/[]ch&M1b~g{ =5;?̙3Yb+V`֬Y;Twww_N||SvDDDDDDDv-=DDDDDj '''<==$55;wn:1bD\\\8}4QQQ;MAff&M65;i)My1e{g&,,!CФIszj y簵Օ~$ziOJJИiTyϜ>>fq70nܸ1.] ӧ{%%%b{&? ?ŹȻkZ`<]HKKcʔ)f[h+/"111$''ϛžK+bĉDFFŏM4afm;vd̘1F""""ߟ[; ZE;\T-G{{{ڵkɓYdI6lHPP޸`kk $$s̡e˖ڒ xu4lؐWQY9@`<888p=?UVZLHstt$88ѣG쌭-Ό7*{yަMѡC} b+j{u o߾ԭ[ggg&Nի5\ڵe˖NCDDDDDDDDDV""""TQ <<==,esrr77,1{ooo֮]˙3gL?s IIIL>Rs)팇c„ L0}6jԈs2wrVR;}puuϯĘctQxڶm[dLF_Zz%DVǏg֭NCDq7-)p򬝆zFDDDDDN""""Re&Mė_~IXXiRjXX...X9;)MNNW^ʵ02QFY;b>)={jH zFDDDDDΤ""""U`0Ty$ꚠA,^_4AsR~}+g'"""߿k777Zk """RkkNCDDDDDDD䎦""""RqfΜɊ+5kݻwrV"""Rdffb0ֶsP-!"uԱv "RM H1~xíBȝE .DDDDPmىy{{m4DDDKzG >\? D`lݺi:vvvY; AȝJ .DDDDDDDDDD5jFv"RE4AeffҨQ#k!"@5H͡zFDDDDDdcDDDDDDDDDDDDDzL6qY; ZA;\|Ǔ7jUĉo߾cGa۶m$%%q qqqW^?WW*;""8=zm%"/=MDFIľ}غu+)))\|GGG{1۷om0HHHsW&T"""""""\"<<̒%KԾkT"""""""\gϞZl!"5ިQhݺFv "r!1JNNO?'??ʶw"WXATTLJӪU+ 8~8۷___lBf*WpBF ^^^,XH4hPDuBDDqIJeعs' 6dڴi 4͛I\\W&&&;;;.]Zi}״DDDDDDDj-)kkf4DDDNADDj1TΤ:B @:uLOY.:l޼{{{өS'ݻw{ܹ͛73}J?883vXS۸qذa̜9Sn] bԩNGDAeHDtt4NNNѦMӱ&M0d;v,ь9Ν;[-ߪJDDDDDDvт ?pk!"R0mSS)Hb͚5ڵ/ҬY3 /LÆ MqI=znLP,5kְcӱ]v 6Ç吓ڵk… 4k֌g}iӦo$7oLaa!ƍXlqӧS^=:tPi}geeqFz聳)E 6nȄ ptt,6/9/Ul۶ '-Y&M!00/´xXޞ}kv*.ܸq#$''S^=z)fϞmڍQ5H""""#,,ikNAR >\ DjٺuӐ*ԩS9uꔩ-##p B^r%ׯ7رc;v+W0~xrss1ctHIIaʕۏqO?]b>m۶eҥfm7|CNN{8ֻwoٱc#F(17Yb4j鈈UWsQ_b>}%00Dc>QQQ\vh ɩ\pdff޼y3eDDDDDDDj-)ŨQ5jkhkӂ;9u-[wߥCpBN`0Gtt4lڴ4.44T:ui׮OϏxoË'==اBv5XM%zEǎ9{\媫p...%spyc 00=zpq,Xٳg e̙}ԅF</y] vmZpQJDDDDDD)"""""""""""5`0SRݻsѥKϟ]* aaaDDDo1rHؾ}).&&???ܰm۶,^)((N:n[} d[cDkѢǎ+r¯~婑nݒ YXXhߛ͛7޽{cggGΝMu^YU.;w.[^z/_6/}TM%""""""Rh 1s ڷo_䱈pwwgՙPeyhMo>nJJJ /_ё{ ///{翵{Mҙ3gҥY{n8{l={l,YB\\qqq|L2oooׯoKKKӳSkŰPJDDDDDDт 1y뭷ػwoYr%6m1cfHmh"vioo>Yd ꣤:nɭgYtDݝ={m6=ʪU e֬Y1(I7.?ٳgoy͎w/no66gۊyCDDD_yjcuFzz:;w_,6w5⇛ݺ;+>B;;;E}裏WԄiӦdddIӦM͎effbDDD:fĈDGGL~x衇,b~G֯_-'''-*MLL,W.UU>}*Q""""w9`;Ipp0ϟgرqqyH%%%!!!ꊭ-M4aȐ!ФIINNv妺Mx ϏdINNfׯ6l?~a!8___~_ŋ1 ̘1k|qZZcȑ\v^z@N8Ann.?!22 &2n߾=O~c1FDDDjcts=GNNӦM#<xڵk\|hL˗-b2MHXy;egg3uT6l`4pMFNN齗.]bҤI|sI-Zd6i}:\vL6ol6)ʕ+L6h._L^^?i͎= @K̳o߾$&&Z+g_BuȍEM7/l#=ggglmmqvvfܸqT~Xh<Ӯ];&O̒%KLq 6$((ooo\\\ɉb49sвeKlmm52g5j> ggg 9={~iؿ߿ 2x C:%HYO@@{ƆƍӿW^зo_֭3'Ndf(UE]XJDDDDDD""""Ԗ-[O0 ?6mbJN?>ڵ>|xO^C9bcc1bD^HI8@`` =z,Xg̙3b۶mKPPiBFYo:u-[ҡCRSSYp!'O$$$tu믿ұcG{=5k_ :th5DYʣ}:GD',,sZ;c_B4jԈuayj²GT"""""""v``KXXo9on777lmmi۶-/ ::~N8ܘw6mژňX˼yݻ7vvvt4}>>>fOXy;c|}}ܹ3vvvtŴ#î]L3[___/*t e!WvsҺukիgZqnE{,X\Ƨ֭[3H {|%{MDڤIhѢNGaaacDxW9t萵Sk]ÅH-5{l,YB\\qqq|L2oooׯoKKK(Z'O,K.7Y#""RZx$^w_~ƉF+o3gХKnݺpYSQy5DYʣmvݻ[ٙtiݺuy:;;[+g_D5hЀŋ3aoN```zZ;j*E .DDDD*IƉm/^<<6mJFF4mXff)FDDeAcrrN<"cO?DRR={4'%%1j޼9?3'Ocǎv e!WVjժ9z(O<ѣ#F ::`C=d?~znqv>UŽP&"oook!"W^l޼ׯckkktDD,T烎j*ڡr'""""nذa >srrrxqss̟~}Xb\v> /Pk(k Qָ(ϵX|39v7ҥ =999L6pΝ;G~~>K/qUyvjq|ETq/THEMJJSqÅH-5i$[O<={Y\y7L7n̥KŅ(޽{IIILLM4afm;vd̘1j[UŽP&"""ѩS'6lH\\]tv:""""""""""5v վ-G{{{ڵkɓYd)aÆ퍋 8991`BBB̞r@nݬaR&w[uVk!"rGzgTP^^Gv""""""""w,-B'!W~r Xx1:&L`R~jEDDDfC ?c7_Rf֡MTz"0n!"""""""RŴBDDDDg̙XYfѽ{w+g%"""wsҺuk+KիӦM)V;mr'SENAD NCDnzFDDDDD΢""""Uwjx{{[; kﴱN`ccS-5JmUɝ&22QFY9*s)ÇIP=#"""""rg҂ ´H㭝HMXXTψܩlHM""""""""""""""""""""""""kDDDDjHn4DDDY; KEFFZ;!Ο?χ~H||رcqrr>"""ݝѣGWy~w>ck""` !!yT5iH) juVnj4DD.`9bz믿^z-k1䥗^ʕ+\N8'bڵ8;;WZ\M6QXXȘ1cpssk׮8ѢBDj """""""bmZp!"""R0¬]) ~?N~~~m޽EG\r={Ӷm[9~8}?+WdҥիWYp!#G///,X@dd$ 4(w_"R3tR.\}gtD+%Q """"""""5\ 00X; Rki6*%"5EDDS 0 OL ) u޾b\l2^֭Kn˫;vmܸqlذ`fΜYbcc>|JR5RuR """"""""5>Ldd)Qdd$iiiNCRDFFjϡ;[VV}=<mgУG Xz5^^^Ջ/uVrrrOW^ :UV[du&M_-ݺukصkWƍѣΦ-Z`0ظq#YYYHԤI:tCT4z聗WLJ F=عs'3f`:jŊǭu ŋYt)ӟݻ7 ,(ADDDDDDD""""&,,iN:NARm4D.㭝TlNʩSLmOPP:ʕ+Y~u^^ǎرc\rTnn.3f 55NPP)))\4m4-[/ /@߾}ԩvvvEn| 999X޽gǎ1ļEpww'..iHkݺ5d:wl:̙3gٳ'ZO?eڵf`ƍ\|KϬ,L™3gLm_5W\U """"""""5v;9u-[$((CvZ\]]9y$!!!؄k߲e | b߾}̛7M6Jjj*:u"<<ÇA׮]g% /燓֭㥗^'W^!""l׊366:XSc1FD z"!!|k""U̸X/4k`ȑlذyg}ƙ3ghժ֭СCY_ADDDDDDDRi9pVNADDDDDDe0Ԟݻ5=K.̟?W_}]v1s @vv6aaa:u0rH$Epuum۶,^ÇK?Ç9r|wDDD駟r}UJ'N8֦M3s5~:udtDZ#ѤIbbb3gvvv3`߾}rq+w7v+={0|}Qw믛ŪB .>|HFeTDD\ddS;\':*7~Q?z*ODDD,3pe믱vZ5҉'h߾mCHv"u gVܳgfɒ%ǧ~ʔ)S~4<==<ɓ'oС'O&77֬YÇ~|P)}^t GGGc6c:tKhԨS*Vn] Fpp0gРAԩ1c1FD 666Zl!R˕FzXn?4hؘpqZjœO>IvxG˽XZԭk9mA5:!""""VP;OTWpBF xyy`^jDDD*۶m`Lڻwm_H @RRY񵋋m OOO֮]4i/_& c#**ǗGؼysO> OL>6m @ff1c1FDDDjWWW݉%%%}O-4. b :eUV9rĬ=%%"V5BL`` v""*"")H Uީ93 Wn<4Zn r^ur>#S?;8Fd0L$9\M Rh(JNW㔚?Q2ĮQG514牌fidj)Π9rpP?9O{G"ZAf͚rԩ5xԬYm'"""׭[7K޽ٸ} /pIz-cիW)[ly7o[ԫWcǎ;UDDDDDDDD B߲j*zaTDD۪Ul<.]G}Ď;y&ڵcĉ9ܸwQQQlڴ@jܹɒCDD7oё-[2bʗ/g~[laժU=z[nQ|yڴiðaø{./2eʔ!>>dӧOӵkWXv-iiiѸqc*Vqqqww7H QQQ;___iiiDEEuV.]Di׮䜿cϞ=\v G@@I܍7X`7o|K 8GGGc{K ֭[ǽ{x饗pqq_$..u1h hm.^hT͛TV~2~KnP>'$$_|Aɒ%ٳ'Cʕ+̜9;w@֭?~}: yHٳge_}ׯ_w߹sdt>Ç~z̙3Y~=nbÆ Tܹc,9r$M6>~ҥK,Yyzmo=svؑ>]vѯ_?zڵ 777ڷo_—%JK.DEEѵk/7 vݻwQDEEb \8::Ŝ9sزe ׯ_#,,$V}).4BDDD-]gRreO/6;Xv̙lذVXcݻ={67ȑ#L4sαl2 f˗/`„ t ;;;>̐!Cصk}!..EpaIlϞ=ԪU+y e{""YsZ*ƕ,Yɓ'ߦVZ0ydN8EXx1/^vL>f-[FJJ ueĉTVSNYnqK ;Ν;/5j___~Giܸqv61119sJ*1}tjժÇ4i.\k˩R K,aDDDիX"/&**簶m TԩSyꩧX`l۶0ͮai{#p)]4Ǐg`bn偁{\ƌØ1cС_Y5k֌f͚{qRJĂ 8{,^^^={2d"""pwf͚y|i\g:?q{KҌD{={߶m׮]ͤ<{ۜ:uʤ- -G6:+ijՌ]\\rgMۚ;3?K[y0uTFi n:fϞmwSy4O4jԈo&M:y """"""""ŁVݻ4hj'''*WL/m^ OOO}8tP",,<==(C}?jժeDhʫ0h!8H!/:t`ƍL2s-͛G@@W%J{w9r+W&887|+W+[,ݺu$''k.Zje1{5w>Cكde=0 y1{ H{K|g!}6_}U&{}jyd<~\ڶ^CK[y\4jԈaÆ1k,f͚<)|}})[,u*"SDDDDDDDDlM+\dsƍsl;s gΜa|r̘rܹCjj*7ndƍdzjժ|em#Gkoڵk0; <)Xrʜ3nܸA5Lgj6xwHNN&##@ǎ=׾}sGeҤIW?tܙW'x¸f͚:u* e~mڴW'%%ARR3fLWs2 45kܺuw}???^yc\۶m ѣܽ{K.СC-;~8Go͛77[͛SJRRR8~xmӾ}{f̘alÇ( IDAT&+?7orI"""<~AXӶ2@qicEy{n[!"""""""""b3p!ŀ8y$=?:kGGGj֬ɤIػw/O<;v`NPdI*VСC;w.QQQ6kعs۷oqf"`j֬Ijj*Yf3oHHnݚ͛`veҿ5kƠAHMMv+s22e ?=z$_~ƁPY@=jnCY}DDD BBBٳҬY3BCC9wބc @rHJJ_e˖\AIIIӴiS:vҥKqvvf&V{WW_Fn郏} #Чhժ={4XӶ2 ڵ+:u,.ȣ߸H.ĦN: OOOCJJx;;|_%&&ңG|IV&Lڵk&qvbƍ鉽=iӆEx`>t6n܈?/;7n$===8 'dɒ[oѣ޽;K&Oo[˚رc޸Ey5i{{{lmd< AAAAAA~nΝ@s_~csY_...&-ʴ7۠Ak׮c dN<٢޺u+s׮]o6)/2]\\̞TR7oε^ 9sttܻw>twwϵ-|||2SSSXn˗3LkW^UM6.sҥ_.ֶ׳ ֭߰[5j0UVtvvμ~z/y7zv=3333cbbLΓ<:5lԨQkΝ5ڵq_3gegΜO$G g~Ey9w\fF2;udT-x0wK/~3fL?K/gDDDDDD %˗/ӽ{w=z4Æ 駟,X8S#G4X3f`ĉ|!Cp]o rfΜisjD˖-MRSSyWy&;wwޡF?~0֮]KϞ=IIIbŊ9K/믿N x駟>}ɊG&--A1~xTBFF~-Ç'%%iӦ1o޼[V~ǏsaIKK+pOSzu~ƍ?|s5KAim|VNNNk׎/ǏSF #GpI <;Ê&on殧ύ7yqu} /$&&l$88׺uknܸ Сnnn:u3gРAgX<#RV\iDD[}i#FK.NCDDDDDDDV ?.\`ĉDFFRzui֬| #Gʕ+|Gy'!!)S0n8ϟ۷ի1>>>888ЪU+/_ҥKNVZל9svm۶%>>:uH:um۶̝;[&>>-Zs=g|u޽&xw^:ЦM׮]rA]n޼Irr2SLa̘15lI[.xyy~| /^]vSvmiР_}u7os}gcpa9E=fznݚ;/:u2ԩSYnϟ[S\Y0nAvhҤ =z 112e0x`gX<#"""__NCDDDDDDDDD&…؄yٴiӧO7szMFF;vdƌ&ۿ c_??3߼y777*͛?~<%Jg*Qo۶mcƍf5|eM4ի&+W0|pZnMʕSNonY3N_=y{ex7rSdI&NH```y˥ c6>;CS_|/d9E=fzTP+WGXX6jԈaÆ1k, 6,TEDD?~((d)PY%tOJpߘ#,66>}: NPP111NCDPFDDDDDQU"YsOYr%]2e䈹v`=jQ<@FF紴4\lpu۝->w7-_eѢEt֍ڵks[V{233sn޼ljskϐ}x<-xssqU~ǒy?-[Ã7_MRxVsKDDDDDDDrƍs=?sͳ0;v䳟~~~ycILQ^V\ɘ1c3f +W,¬DDDqWĖVHDDDDDDxӀ _|wyW\]ǎKRR]veҤIym۶,X Ƕd15i҄ۓJ^qF>s f͢D;XޱcG}]ݻgϽ{5k?|[;;;~s= z SA+{iOffqk}ƛSdIڷo牍… k''<+yn 8&XK/KQӧOK/W>}lcPI&m6_Ν;wxb믿Á;w.ƍcر̙3q"""eJ}"[I[' aÆGg^|EéW#..EQdIÍ[>zd~'c…TX#GNBB#F&DEEѸqc6nHݺuyy饗&==~7{qELBM9gmF׮] z8q'}v\]]׿umػwo"""ѣ~!899qQ Ã:\V 4H6mD=x^:ǏgҤI۷@}& իW3e<7nnnO.]w"7y1.]: |5m)<QDD$q5jdX#mǎrܢ[n޼ɓ޽;IvVZKQ)"Zd :ٲo>M.&{lٲag5~~뭷 +ԨQy1p@f̘3L7o΄ L|||j?~<ׇ2e ӦM3)駉G]kךlwrr"&&*U䚿kr:tc %\Vb :vիYz>фboooU.iky饗ɓe.{F}vza,,x2 GG}0n8rSn]>̒%KСC\x{{{i۶-Çsvԉ3fe~w|Iڷoτ U0Ã}1sL֮]?Lzz:>>>tܙ7x'|rԯEnݺb~c޼yٳk׮C~rLemAarGEE1vX|}}?~/s[}"\M`bUVjʪ}4h *9k׮ҥK-m/]4ᄇ$7k_~zP[[?իxA>%|zZ[{nLJ7Yn"AAA: \:JDĖbccӧӐBp5^{5JƲ~ 4ܹsYdszz:\~sC%%%{yINNfܹ(Q5ې!Cr nbܻw"_͍7ϱߟi&u@-"Nf͸s"|2!!!\xXv LݻwԩP؃%224O$""""""(܈<\]]f4jԨ(BdN<۷ PR%N8EYg-NLL4~έ<>>9so>vɄ Xbqe˖Bݺuoeʕ<߿uyQJٳgl۶͸g 1"RT^'|RFE$ŋsEj׮M||<;wW^ ::gMAa[UZ={[o=zO$""""""Ӏ yl5m^zuV_NFFǏ7`̙ <ؖiH.L^y ^z888P~}&N֭[ a&昘V\AILL4@ؼy3booOժU:u*6l(p?~<899gzzjرc_Ofc#"O˖-ٷobf7ooo\]] #115k {P}Xh6G}"GI[' "b+?<ϱΎڴiSɉXرcԬYӤ,CFY999QJ?H"|e^+W$!!f͚ѳgORDD!sN>sz*k׮l򾏟m^/k={뛔7hsأFbڴi$$$Lhh(AAA8;;N> @``8q"s]qppȱ֭[ƸUf|{/_=6C!FDf͚: Ƀ?\ؿQ?]T&/ֲO$""""""Ѐ yl5lؐÇ{_/NŊiٲ%#GYfNSD$W:;veo7͍{3,uΝˊ+̤W^ SL2-[ܹ;wҾ}{MVY<ȃ~@· 2+{աC5kYv-b޼y,[Çӭ[7233<εk^ti._̅ ʱƸаY .Q"܆2sHЬY3RSS_ 3nܸA5Lgen۶-aaa=zwr%>C:t10ӧe۷?K.ݻ\p˗駟/:ԬYSNPfŅ_|ggg[""H-5kܺuw}???^yc\a,ØL2|ܼy'OQzO$""""""p """"Eŋ̞=ݻwsMUF~8q"`:؍7X`7o|K 8иy\_ܒ_˻GEEi&Ο?ժUst%߼7zGƍP1bŊG߾}qwwϷ DDb_ٳ6oތ#-[dĈ/_>GlTTcǎחFTT[nҥK/_v1x`lcϞ=\v G@@I܃ZvZ뇏v+[,̞=5kP^=3{mk7.$r IDAT.Hprr^`ԨQU+/ a֭>}Pm9ʬ=vxx89kذ}پ};III9;;3zh2epev튧'ׯK.l۶>C>tԩ֡Eرd$''7EDDDހؼy3III& d|_1K0%iӦ ||爳<ܴ…H!~:dÆ \zt=jl՝;w:t(/ܹssy5j+P\tYfPNwܹ|駜>}tnܸÇ>}:111/?_57n0G-nܸM ""p9s&ׯ֭[\z 6իWsFFFf|Xڵkk,_~ RSSeܸqø˗ /?$##'N0e5 jiܡChݺumֲeK8p_k`Ȑ!߿[nqV^Mddq"\pBzI B ݛEQvڕ)S3舣#ժUL6Ftt4AAAxzzboomڴaѢE&;///s|ᇼԯ_777J(DFF2uTK:v숫+vʑî]pss3!"""rʱpB:t耻;ԪU3gLNP1K0%=z4ݻwt8::ɓ \oDDDDDDDnZB Ull,NCD侭Z)C,&&3gPR%ONZ8|0&M… &˖-#%%u2qDUƩSg[.]:tVYgdv-k 9s#== 6+ӧO*zٳZjPguVsm̞=ƍs&MĹsXlÆ 3Z*U&,Yɓ'ߦVZ0ydN8EXx1/^vL>f3ƽҸOO<駟=-X۶UTaԩ~Inpqqa„ L0!xK>M+\Hiڴ)yA~~~&ʷm@XX 2iҤݼy3booOժU:u*6l(p4hЀ>(` kk#OLL +W?{$&&nݺ痗cǎ=I<&L?ԫWp>UppqE֘Wںu1v7ooo\]] #115k jia̒%g#33YYӞym Ə7NNNA$YWP@DBŊMVhӓ`f&"""Rt'y8h )4#F`ĈNCDDN:@ L6l#u kmիԩSKfW漣Fbڴi$$$Lhh(AAA8;;[5._ |v2C,gΜɱ|gP~}rýܹs25ksa[-Pϟx{{皣*Tȱ͚̋5mkPZ5{ݻg,S@DԩS9r$}`ݺu̞=~y"kCɛD""""""" BӧObccmen_y|p:vmڴ1J|]6))){uV-Z ..YfO<ٳYr%+Wf͚5@[-_>?/8p 矧\r\xoO>7o/=-XӶR@DDDWNzz: p!"""""""""4 )d}᫯_~yߟ۷Dpp6gggFmlеkW<==Y~}vqq7dȐ!Znݺg!!!cE֥۴iC||<||y[Zh;HNNαzrr2P6EDQlڵkӫW| a֭>}Pm&e `$%%/40qm"##Qm2ydmbI{Zm-~9;;ϒ+"""""""""()aux8s ~~~8رc+Wd̘13+WaV""WbbbKE`` O<%Kחz GGGc鉽=١?vQ}M&OLRJtڕI&q"RDѺukϸz*6DݠAgp bŊbbb$88؆HQS@ӣGza4DçEܽ{:VӦMSQFDDDDDѤ.H("**yΜ9̙3&+;H.\ȅ xWe{f,\aÆ0;+ >"""L)S( SNeȑu1{lm% D?+Wu ""O?O?MBBڵu:"#F0b[!"""""""HӀ ylơ7={p5|||ׯ&qiiiDEEuV.]Di׮6Y"0$..Hprr^`ԨQp=شiϟjժѹsgtbr;v,̟?$&+s;sGƍP1bŊG߾}qwwB<Ə77nΝ;Ç~:}7HҬ~@z_s slglڴnݺYDdT jKJ#>X[{_ xҥ͋CyE- ;CÆ QAAA~z#ߎ; e˖8::ҪU+N ۋ]{4lؐjժUdee;bDEE/€HMM%!!\VxEEۚ'N7'ոqc<"""""""""""""""""""""":p!wy3|9s3g`}grY6mZj ZjeަMi 7 &0k,INNf9W^*m˗Yfc4slo5cccINNۛAUY"""roP޸~lVZ55jD@@/RW_h…\>ÇEhhhg믿im;ŎyDD'>,u""8p 6u"Ubj{EĦ9$UN<z*ӧOgxxxP^=6mW^勈)SNMMe˖- UV8&""""""rorǥ7'\X{'tЁ,X@pp0>(/&66 ~z|}}$889sмysә7o:up}%##)SЫW/x w(((̙3esssӿΝ;GHHHZM6>}ĊӧOyDD7ooy`:L&;2n 2d6tP֬YCDDƍat""""OZ4i;vH5{v;npꫯΑ#Gxر1_7 > ȑ#fȑ={ 2r =|pBBBܹ3۷'((ˇ~~~ݻkh":uĠAxG+}СWaL@`gg^zu׿m)R%loI/dd2qvرcc999,Y>}о}{zͲe>d21bk`2غuEz>}0Llٲ(h'Odܸqtؑnݺ1o޼b+_tٳgӭ[7|||6m999-;;ڵkG:u{ DLL ٕ;߿$[!"唓ܹ3=z`ܹ\t"ϭ ۶mcǎooo^~e,X`OSآEڵ+-*=XgggpB^z%#P\Z_ƍX???ڷoOΝyW/k"""""""&p!w\ };ݝ.]ʾ}v?8#F[nF5k'|Ν;tt֍_WW C=Dll,NAtɘ0i$>cΟ?oda'Nޞ[rUL&ӧO_}[+gϞ,Z={0l0ٳw^8::e0DDJ4h [ R뇿TtttQr %;;薛رcIOO7;wpXt)ݻwg;vjԨ-[믍gc/_ٳ899ѱc3f \v?7ndgg3rH222s6o̯ZZ_}999;CJJ [l+Zh$&&:) ܜ<Ϸ~KTT...y+wYrΟ?OLL YYY̞=j|~!QQQhтѣGz/\| ТE W\^35MJJ [t)}q<=ѣG_ _i…qE6^zGaL<|Nӧ})z=zG*ٙ)S0eʔ2羭ʕ+Ƞ~dddp!ƌc<| 8: ^Mpp0)))$$$Я_?~i=ʡC!??۷cooORR׮]zƂ3J}^֨Q#fΜIݺuYr%رØpj*222hРsˋG2cƌb2*߬YbiIII'"7gϦ@;EN8iݺ5ǎcdddj*?Yl `͚5L2޽{cggѣG3f1ᣨPiҤ K,ڟ*{Xai}N~̙3f͚9y$7}udL&yyy$&&ڵk ꯉܻm;c=f{TT02yL&WYƀA[4i҄3g[@ƼyE߾}v{o.]J}4lؐjժ񲲲;w`ԩhGGGڶm_b:qpsGQ7#"r;oߞ˗/:) ڵk#-[4&|رXvͷ~Kٴi}|ps牢̓-\]]quu-888ЦM>b}?=ѲeKiժSN`@z !QQQ/0`RSSIHH0PMDDDDDDޥ.U$]"l̜97|W^y/^Lmܦݩ6(==aÆ/ؾݚ7nзo_Ν;GXXm۶-E"g-oIm~jhԨKe^Nv'NiӦVƒ \@} IDATqРA?Hݻ'--,j֬O?;vmtHamfgΜ(qGړ'Oq[lZj7/4ܹsqxzz?޸qH駟hٲymڴ)v˗/Pfbi<""C֭^:+֞hVZVپŋ8q"ǎ+XZtt4/wZ~ӟӧy{FF{43Ξ= T7af͚Err2,_#Gok""""""".M yg7n ,`+""wNi֬ǎc޽ٿ?ΝI&wypqq瞳uHVڵ҃tR֮]KAAˋ֭[ Ry\~3fm6_~ݻw{nwάY,V*c[IKl*gm_a՗ԩCV8r/_fΝ<쳸ѦMKNNn77RhlggW9=PM7Y+CD899a2ػw/#Fu8"RsXϟϱchР=hѢI[k׮b BCCK-Օӧ3|p͛[ ooovɆ 8|0˖-cՌ?ꯉ4BZw?CDDJѿΝK|||.֯__˪6u._̢EHLLdժUw턋]vYMz*ӧOgxxx7yڴi|'϶mpqqaԨQtڕu뒙Irr2+V`֭8::2{Ja W{,"ӧٸqYQ=zp!>S~Wc;'|•+W=Я4 4ԩS8p#=--X^wwwΟ?Off&233<"rkal|]^>>>|Aȭ)e}ſGY*w24nLP8uq+Bdd$<3aСC}ЧOٶm~)Ƅ DDDDDDD]ŗO 3LV+={ŋ|2wzKw8²ժUI&Xrn 2d6tP.\@DD #ݡCHLLՕHWԪU^zIZHLLȑ#Q{,"a0߸t}&cyu놽=91CY__[^ 88w}_,oӦM8}tc4sۛcǎKӯ_nUɜ9sHKK#//F/܊Gnn.ǏgڴiFzVVV<,_\kQ~}~,e9r|9¼yԩ2}}b2NNNO<^^^fꯉܻÅCHH;wz ?jժymF\\ǏڵkԮ]___ƍG5[eΝ;#ۗ~K/Dll,7nW_-_|A~~>zf͚.(D+]t?]vqUvԩS+\gsrrXr%[nvڼ⋌5bWIQZzcEeggCvSd2+bԻT7))n݊;v7ޠv򆅅1i$Xbل}v.]Dڵڵ+GŢ?,[$\Bƍ6lzWmUymذaÆѸqcVV-Yx1ׯe˖ϒ@kfy|nLL 9rjժ3ac7փc)#FsN9B``űի3qD㽻;m۶%55M/5駟x'\<ٶm'N`FzϞ= Сv"--fHͻK :vȴiӌ91YE67oΈ#,Қ6mJ@@@VKbb"3f`ƌŮuq:u*ôjժԘy3f qqqߟ >۷sFiq^Æ -*>|8bq2DDDDDDD]BDDD2e pe裏˗/w%55+W牉fҥo̙3呓ѣG;w.QQQF>V V0` _lFIBByf}.]Ă hѢ\Ǝ˧~ٳgܹs3anܸa-o}Ub_Ö-[nK"">7r5HLLdȑVW %;;8q^{5֬Y fԨQ^|Ç_|N<Ɍ3ظqvUwa:w\jWNNNxzz2bf͚eSMDDDDDDޥ.䶊X]RD^g>/1111رɓ'ێ|ʔ);;;=ʘ1c,YnK,d2Gbb"!!!]XӓVZqaXD}Μ9Cfh޼y/˪UȠA̝;///=ju WEիIOO穧bԩxzzriIII!!!胔*h֬YsiIIIƤu{eŴk׎cǎ1m4Ξ=ի7nE&Mn2gq)ׯϜ9sh֬L>'Oi\O?ŋ4oޜsRvm>C֯_Oxx8{nO[U|?3@.\p}U5kԨ3gΤnݺ\pva 0.o==Jruu孷⭷*:~~~K4i&M*ܒⰖ3eLRjއ~V\IFF ##C1f̘w-3i^>77zO?z?̞=K.1~x6n܈}aɒ%^9997XUK/]rsKFF0}tW^,Y9[o7|3d<;7n0sL֮]ˏ?O=FoLD*܇x˝j֬IppcwVԩS\wywyX#<ɓgh_O>ӧ몿&""""""r҄ 4hr%M(^ /4i_.]d߽{7;vM6?76QW\!**SNˀ* ߿?&>>"XS˲sNb)ڶm_|r_-[2f̘buVWps̙3ׯ ֊WU9qpshQ7#""Ucʔ)N-[$((dzcǎb;v ((-[ЪU+Nʟgon\c޽A 64+hʛ/77*y*RHݚM<٨ۀ-v(o==y(c0aTT8:۟gŒs̡I&,[xg}=Ώ4^{rܬy5tb߯ {{ˍ+geҥ+''+VoY_Ԕ)S_j%99d5)NDnȽɾ,"EAA^z}?#<7/2l0 ܹsYv-N,BN0jժ̇~ ڵk|yGؾ}1P1++m۶BϞ=+\~Y~'cPY6mJ=/55ToNӦMqtt_:̙3͕L&2\=y򤑷"UU._ \(s9gU֭[k 37nN(v={H3ܴiRcmUyթSsΕ H}ԭyU}փcy8;;3sL8wΝ#!!3gZ>/"U/%%ÇsUfϞ qppp{qblڴׯDƍYpae.\T}Y|8͍.]X-$wH}mggo2_tui<""R53>j^~~ߎڴiùsضmj1l۶_TE2խ.o==3<øqX`Ǐm۶6J OL03fpU>c;ìY,v;5[l/>>>ѣG֭[ܹsi~Nf~мys"##yYz5F8+S֒%Kx'8q"~]]\\$44~??O'"r/QMDDDDDDޣ.ٿ?qqqCDJ3MndÙ0a{8oJ>}Xr%}Ç'++bΎ 6-:Ѡ"嗦A8p"=--\<䓼+la\mѼ3FWJJEԗy寿j^}",v̜f#""eY_ԑ#G,0v(V곰mEȃߟ pBjpD']E?p}ǎ?Y9?M4xwX=zhYz)ZjOX`lٲ~~`rrrJ=OD^ȽE;\mOTTe: y@۷SN1{l#=++Zjѷo_2222e z'7`"YÆ i׮)))lٲmҤIJ_^xe˖ٳiٲ%'N߯P=klݺ ˜4iqK.DFFɓy'իYj&+Vܸ|2ߟsRfvvvp6l@ӦM9s'ѣгg2%22`̙CIOOg޼ytۡCbbbX`<,^X4hV7_Vx饗/5jG[nsEk\z^x֭[W>RUzP{,"""wtr_b_kN+ge*\yw2d={$>>+WDPP;vl#"""""""""R4BDDD.Kbb"3f`ƌŎ?~Npp0!!!&xIIIa 0%00m۶q FmXμ{38o=bvɑ#G 8zL8x_eݺu,ZE~5`ƍtЁ]vVwb2LEDdjbĈi͛7ge;|poΙ3g9rű Zlݺ#G/[}׍oG[U>@PPׯ_믿&44.X]taVϟwΎ]VY|)k$''xM鉝˗/';;VZѪU+rEDDDDDDDDDn[!P8qbLa 4N&" ::frP{&UeŊ\xׯ@FFc̘15Ύ(& '>M[}=wkC~ԿEGGW' c*|eߡC,L&Cխ7Ǐ/g5ػw/mڴ8+SV-ݻ7ͳzͺur5jď?'|R.}aÆ V-[PN>}1šGyoٳg~z222puus̞=~LY|7%^>"((3gpuƌ#1<˼Uv?"<-&"""""""ri…HڵkW2}t ~~~L68d"""r8;;3sL|M^yXxq&DDD=hEX,*鳩|*͍??V4eEDDfptt,v!C0dȐb9#GZOTTv4Bld2X~ ,<:t(k֬!""q0:]yƍǂ ?~]B&\_eԨQOFǍ2vXӍsNZZK.޾IJ+rrr)vLJlBoDDDDDDDDD_QQQ<%r& aHC$''o:?QQQCDPFDDPx IDATDDD~U(qXTT?#<<}fիIOO穧"::K֭III!!!\455x@f͊]ۜf#""""""""""" lm'i0DDDDDDDDDDnv;d2+chٲ%m۶eڴiyn @pp0I&̜9~H~J,ĉ4jԨرƍ[xxx:[ @:}4mڴHo۶mgΜO>VuR˺|25k,v̜f#""wR'׉HՊa*..!=xyј+rna$''͠A$^T?c͉'hڴiUtn**77^x,\]]ټy3NNNeuTSDDDDDDb4BŠnmggWf\Rq53Y+WDDyyyADD'>>aQdҥ][uh""""zٵkM{ܹ,عs'=zqT8MR ?Ç`>|X^N>ƍ+;ϟ'33wwwcF;EEEe0DDDeMz«W2}t `}#""wŋw^[!"R7x;: []Dnkl… 2H:t(k֬!""q0:)oqsswe޽u:<@}Nʲu"@ƍ͈#W_w#Fq9r@}YzɪU^:'N4򺹹7Wݻ7:t --صi:u{~lH8s挭qqqj{EĦ9t9s ڵ}Ү];m;v,x{{/`rrrJ,d2=.]ٳ֭>>>L65]vԩSH0LĐ][)},YB>}h߾={fٲey s~8@׮];vEz~~>ϧstX /33SұcGt{ǯZb<'Odܸqtؑnݺ1o<^Z{/,!!7n/ /7HHH(a-N/2c vJ d˖-'9EDDDDDD*O;\b9gggXt)wʕ+4i҄@f̘Ņpc.\?L۶m;v,?wҤI|ǜ?xֳgO-ZĞ={6lE{ŅݻߙJ'**a!T(m< uR6ldoȑ#l8v222xgiР˗/gʕ?={vfȑdddi7ohW_Oc>>>e_[\z/ҠA["H%77cǒn;wpXt)%XCCCζ0|֭[gF35KyJ1cƐis *{AA_|(_>}aƍ5B~WFO?d?~SVn.R^V"## 駟o>MJJYfŎyDDEÇgCDXeդSOu֤;7^\/55x_ kҤ III&fcIJJb޽zhԨ֭#))#Gcǎ ߻ٷ~ٳg' ///Ξ=[駟WDܓ""""""" @ܹ3&deeq 1k,cEU>|8=QQQxxxXe<(L&ūt___jժ֭[ ??[RN:w Ҽys6mG}|+WΝ;:u*-ZёmX'N7ոqc<"""mP>֭[ 4i̙3M\-Ie ɩX)Sx'prr2vļ~:SNQFTVW^y4'OaÆTV.++ұ'`w0۷/6l(3%hٲ{6mZI}N{U)55^zBBB,+TgggfΜɛoi}sREQ񳧧񳳳sV\>HUn`2n~/… 2H:t(.\ ""†ΡCHLLՕHWԪU^zIZHLL4&UƓO>ɇ~?37n,L& NRHҿ~7FAnnnGnn.ǏgڴiFzVVVywk^ŎӴ"ڷo޽{m"}.]߸t}&cy;#9sRm?cO^}4jԈt~r߽{w͛GZZ=zyU !!~ؼy3?c >Hi ;$44lc^nn.cǎO?ٳq9Ù0a7n0]t)8s yyypQΝKTT"״櫯"''"rrrزeKՈȭSmÇܹs:vظq#׮]#++DFYͮ\kƚ5kC~~>ϟ'::QFS1cƐµk?'44BnbE[W˗ʕ+W!$$B1WyQHޓju1ϯwy=z쌓-Z`޼yƀĵkז+'''0`nnn899ѽ{wٳ'5j`Ϟ=Ŏٳcc{Uǎ֭/_u("Q\\\puuחH&MDqpp 77m{^^6mՕ^zԬYM6Wf΄ѧO}Qz!={6@vTSDDDDDDÅVDGG: B&M7 Yz5Mpp0)))$$$֭[%K0L员HHHk׮% B,i줤$5kV,~sZRRM$""R1w(5… ŎݻŋӮ޽UUHe/eSЎڌt̔f΄ұ3g-5/d(G I3o$ݞͣ.T}g#eھ}bccpBIRll\\\hp:ԧ@-訬,뒤޽{Wk|z('L%Ie?s9sW_UZZҴvZM:U6r%IGu-f@})TX_#۴isܹs…Qu^$I?S.oO=Tv?:]t1$IuV{IմݹsԩS*\ }@}qppвe4{lM4IuVYKh~飏>ʕ+r˜˝n @Pp:fjU]={OM7}[Hw?9O?շ~w}W6m̙35v*o,VVV$kkke6Sٻwo;wN{rٳܿҳc [PښGJufnyUVԩSСyuE=zиq_\\l>#UU ԧ>}(88XW$͜9Snnn@}3Rg 6mZRAAqJ*9buUi9;;ֵkWIRVVE^S A}|ꩧ4rHhڴi?O)77W6mK/k׮S^,kPQQ;pI҈#*wipHZb$iРA?w믿*##Cs?ۗ[z7Raa233fn|$1l٢k׮ٳZr][9Fд1AL+==]%kܹד'OVxxV\i`}Weκtƌ#m۶M PJJ222,ȐTqPS;R]T믿/PDD""", 2DK,)3<)Sh{'4~1ݻWٚ:uje;vh;gḓ^ U9Ҏ;tR-]byff<==奏?Xo|MI]_9Fд1$wwr 1ӧ]yi޼yJJJT8r䈎9Ru-FNNN҆ J#7:p)븸8##(gqift !2ۯ]%KW...nd988gL@2ydm߾]Zp7\\\hp:g#a`Wh>=JKKftl IDAT15 .`8F,[LLΟ? &&Nx(88tA˖-ٳ5i$I֭[f~h% BEnk.;wNҥF]bTEEEi׿|Po5m4ٕQ:S~~Էo_iܧm۶rwwWBB&MVZ 4y}QppV^-I9s N` >}*,,4/P=k֬Q\\1R\\lt@-7_߸qC'NЉ'tUT_aatIss)::ZuyvܩyzzZ,Çk.;@yot y8_Ǐ駟6:pWll1H0QpH%Iou رC+WTbbK۴iN<={*$$D]tQVVuamݺU>>>:rl*I޽EVS[jj*h#z衇J ;Ss-Zʕ+ٳg%___Vݻ%Ij׮$Gղe;vǧ}Z]vH5Vc~ WWWT*֦޿IIIJKKt`pܸq PpzWK"Ç߯O?T~}]mڴI3gرcԯ]rVVV$kkke6S@݋S\\1h2`t u駟6: ZzVXat?RRRهµkjΜ9cDkruuU^ H 0 qrruIٳG|"""̅⢬,m۶M...UҺuk*//O[.,//Pf͚%c]@HJJ2: GUfܾ4,ZH:t̙3R#)))FG(S}W\\ߊ֭[{5VXSڵkZd|}}nj0%''>c 7րѣG+''G-~;=?!Ir!ChÆ … _~ѦM]֭t{beggcǎ]*77WYYYYYY>ׯ_?b(((ЀK/)<<8P<$}'%3geex@f֭[w k„ 涉'*>>^111 60>Yh&O,IZr+44TV~SLQΝ@O#FЇ~({{{͝;Y0F%I0`$)##"mРAsEZr^u޽8P5oVXk׮Uohȑ?]rRFkyUUM_٣ yyy3^|{+!!A}U6m}ڶm+www%$$(??poa !L9:f5kLIII:{$C4hȢ*_ղeK)((H= ;(77ֈ#ozJd=p5lذ:;)..4 0: /jϞ=4i?m D||zOS*''[<.I ɓ'mΝStt222i.((Ku֏P~~~%iժUͯ}o^_~]۷o^y Ì3g^oͺuy$֚5k$ɢʕ+z%)77Wqqq:|բE M]_Ee1T]VQQQ%]|Y˗//7Ν;UPP OOOe:|vڥcVe\I*Koooy{{usrrҼy4o޼ >\Ç/ֲeK+**J999j߾$)''GǏ׌3,ԎYfPc3:P)IIIFG@n:iҤIڹs]h{͵k״xbM2E{}QE:q№.]jwӦM:yz쩐uEYYY ÇuVȑ#Y[裏*::Zvvv%322N:iƍZnV\)WWWm޼Ym۶|/yԩ-[~XQQQ־}.-[ӧO/ѶqF={V۷ןgu]'OԒ%KtmذAT1TK-ZQFJ'NЌ3ts޿ݻ[-55 h"(ɓ'k튍… %IrqqQ``ׯ)PI믿ŋ uXVVVFꍽV\)//z%I!!!ѣ$M/ٳKݽ{$)<<\ڵte˖G;vpX-?.I׺u믿*$$D:u$M4Iׯ7\Q cǎnϺ˗/ڱ)33S+1߾}$Iz'%IO=BBB/k޽M]_Ee1T*..֩Sg~7|# 9}$Ν;h(@U5USV-[ٳgkҤI[j͚578uO>Zj,XgyFFGBYx233'7VVV8qbm{?$7)klIgh(˙3g*QMֵkW-Z0ܥKӝ999Inݺenyիo($$D/_$)''G;?l禮eyB8F^ .hܹ:uŲ K.]$jbQp4`}QppV^-I9s N@ݚ5kltԡmjrC6o򱁻=~~y3*4o޼vkk Q[[[eSc-[jСz4fEEE .sgGiM]_˚nQIuUt)uA\S=b sJbb߿#FP-t8p@6lXa*nH I5V$''K7FРnf۶mх ltֽJII'|Rmu?jϺu-[}hƌ6m:r?f@ׯ ???N VPPEjٲ~ŋZ|uftU4ydm۶DASll\\\h`2@}kft4^fҬYH-_\GF⡇/"kkƉC|riFGPEZlfϞI&Inݪ5ktDL@@⌎)̝;W|&L4xl$(@ck*22RNNNFP }QppV^-I9s No\NږbUqqqڲe1&VݻϟwyHP [nzot (@󓟟1nܸAtY4tP;HP_|m۶i׮]1:jypOUPPM8`V\\ hȑ6lqPCpԢӧOk׮e.KJJRZZ$Cƍh4*JMMĉu5km/ƳҺuԺuk3PKϟ9rbٱcDkruuU^ H AIII:|0zW\\nܸQoPd#>$%%k׮iɒ%$i̘1 Srr3&Fnԭ[7c/4`F h@\pTALLΟ? &&Nx(88t+,,ѣF fҬYШQp&֭[Z~vڥsV]tѣSoAA{n_҃>6m$;>r󕐐}M6>m۶4i$jժajժz쩯 gt P(@7_߸qC'NЉ'tUH=RPPNcIo-wwwݸqC;vʕ+h.شiN<={*$$D]tQVVuamݺU>>>:rH-LRSS%Iݻw`jKMM4y І tubݼyS͚sO~~~@I7nܠꈻ{?wk -Z$*))I#Gh֭~w$U666zGl2IҎ;*ӧ%I:uXֹs}@툋ӥK*uQ޽{ڵkFG@` 4Isѫ4iڵ:uK-I.s;gΜp?9ZXfjʕ+Zx'H{L?itம_EiȐ!rpp0:N;rH?}vZjTT3ԕkRR$.7n\}FPpzWˤNÇ߯O?T~}]mڴI3gرc%ݞ"W\p&d26nP{+///EFFёPꫯ4o<w .^3:ϟ{e;vLJLLTqqƏ/WWWˀ- .d999[:y٣>@ eeei۶mrqq>Zn\u%3p@Z`  777#^x_2:pWZr.\vP)))FG(S}4k״d3FaaaJNNg{4a&`rwwG}=ruu$ښ 2DLݼyS/^[o%wwwf6Wʲ`jkhS,ZHԄ otT^z%cwdZ`Qzk„ 涉'󊉉10u 4I'O$\RV*I%F2e:wt_~1b>CkܹΒn1j(IҀ$ILm #iѦMteq4"銉k-ZJ.^˗gTPPPf߂V5j(*,,4qww/+=zTC5zgj/**ҪU4x` :T֭$)$$DԐ!C+ի1>s挂5p@=Zb]Vc|9r<<<+""BW\"I{QPPV^m~/w]󕐐}M6>m۶(r4:͌11cԬY3%%%ٳ:hРAb IrttTtt}۷OϟW˖-榠 =c ,;C\m#FЛo^(rtt԰ahz\\\qF=s=zXxht*ԩScn-ƗBɓs)::Zu;Vgx_VZ?W한l׵}vY[[W^<̘1Cyyy6oެ[nWʕ+z%)77Wqqq:|-xj]VQQQ%]|Y˗//7Ν;UPP OOOe:|vڥcV1,ooomڴI_~YZ*IӶm_k޽Zzub I>|>S}ڹs$e˖K|qǏ+00{Õ#G@#vZEGG(U*''G:t|Ci,nڴI'OTϞ=ZIIIի>[J*9ޑ#G̯+}QZ<👑x}(88XW$͜9SnnnT/P}f ̈́P˚A7o^fu(g+++5=긳ࣴ>/VҩSԡC=3ҥzq5|S[Y{h{&otqQ{JEX;t蠳gѣ4gddXuqqQVVm&*Fڵӏ?Ǐ_~Ǐ]$Irvv$={ּ"Ijݺrss֭[Xg@cR!\|Y7nH*瞓$ѣ***w}^{͢!C$I͛7uE[rwwWPPiFjev///IAzz+VH Tu!*33Saaa˗/K*vUe=S3\޽{5k, tN:'x8h\"GGGcX``٣ӧOk#FX<@?e߿_ ,^s5vvv֥K4fh۶mUZ'O޽{SXֱcG;vhҥZtLyzzy] eddͭzRh hrGsg}ftᩧR-t!ۻw:tFG1;;;_^rvv P\\\dcc#'''yyyiÆ %f1X`ڷo/Vy.jJ1117nڴi#iF'NԆ TGR?O>|dgg=zhŊ"DIe_#FE:pv8 GGG 6ΏdU<%Xdki˖-t=^Ν;uq :T<򈢢&֭[ӧ\\\cy?@}Xneo^#͘1CӦM38qJ?~"4͌ƍ7n1,rwwW``v%#͛&lÆ Ƞɓ}vj…n(00t>k񊍍Uqq14?GGG+55UFA)K.MЕ+W3f'0:`888hٲeںuΝ;si֭Zl퍎@c ԙ5k諯2:Ԫ$#E{ի5g=3|Xvرckh5k8[s7#5I-ZPϞ=u! .PN8}*%%E3:N3#1@1(4B\A$-ZHFN83f~~rrrԡC/N8KZl*}t9rĢSNZl~aEEE)::ZShhh,ZGUtt씗mذ̂AIRSS%)Mm\jU@@⌎r*SNҘ1c4tP?HMҀtAc?~ߨo߾FG@D]y ۏ9/R:u>3o$IW\1ݿ$)$$D=z$iŚ={vVoM,\P;vtah]|RNt}W7n|}jٲ$IOtOi;w.dkkCi~_֌3OG1:R{Oׯ_qHl߾]wVJJ 4h.\ܹsu)e'IғO>YO޽-֫J;5EEtbIt֭Jk*0 Tbbbbb驃͛߿5K$IZ؞'???cJڲeIWVjj&LNB0` uQyzz@QQ,XcjFǩ7ϟ_W>|X7nܐ^~ecUӧյkW+{NIIIJKKthlܸqP(@+E ZjN::gQ.]ԣG/LwaH2#ToMښҺo<;v┑H dF1U5y抏6oެ'IyGԶm[}W\V]VFGWaaa:z  LSuWJJJOfcǎ)22R*..^zhL#~DGGYtY"5kL:tٳg-F5ȰfU6$&MG}D\m@m6]*==]br)͇5%VI36 ÜRirȤLEg]oWAW.qݮ^p]~_Xm۶pssC\\JKKU4.HRRR.GFFFׇ֮]3fvGxx8F c iSO҂ `kk  xʘ#7h 2N> SSS 8&""zݼyzacc… (++Cee%rrrj*g|GqF`ԩ8tnݺjȑ#2e ?ǠAA m qH^UUUy&~ 6 4iܕ6 c>qD &&JVTT 66Uk'"""""҂h2ݻiӦaΝy&jjjPXXXL>]PWXXJKKu2`֭HHH~Çؿ?֯_/ꫯpB޽{b qqq UvVUUa̙ؾ};nܸj 22sEmm-`ѢEºuPXX(,,Dhh(xb}:teeepwwswwGYY>!""""""CW)cffyA&&&Xh-Z$.o0*e QDQ鞞Tڷo;J噘χ- ??JBDDL:/_ƛo~-[ԩ,Y#Fw8y$lق>@C?[q o{Eݥ[j`兓'O" 7oP===XYY᭷[o bݺuǥK```~盶mbРA8poooe{ܽ{CE۶mbDDDDDDDGJJC;v˗akkUVsҥK(̚5K;"22RYYYعs'ڷo;v`˖- #{m۷oGxx8N:%۹s' ---dff_pNQ l8;;cŰG^^BBBpY۷#FVXYfaٲeزe"Ǻu`nn03g:w,ӿ$̙35jfp g@YY7x8<'OF۶m#Z5D^^f̘;;;>>>Ζ[^KK둯RRR0f?>h"O>?#u]iɓakk }}}bʔ)믿n;w,,,关gʭ_a̙pppZl WWWXwޕ[GؔQ]t 6m:$3آ>WWW߿zzzزe .^(x7$_~KEq=s֮] c ~>}uՅ%'NclL0VVVׇ Ƭ|cz kdśG/)sRO4 K/?\i[Րc#1!//011a7ytq@PPv ===tM\رc2u|}}e[LK'WŋѾ}{:zN¹sХKXG:::ر#˾kFjj*MǏ믿uuA =#"5~suu_&LjjjMII<<<7n7n<<<)FK<u٬yʿN2/CCCa2u䕭222*%-۽{w޽{b3fK6h[+**ӧO R?`dd$Oȑ# +99Y044/RuΞ=+):t [}?X ̝;AW_ ,XAq;nUsSY\Oi~k֭aرJ())\\\nX?>,)|ޘˆ5󶢢B˗/˴)p%AKKKhժPYYsR^nڵkQ59ߏ7N ~'"""R8GaQ3אWիW/UJ\]]^zԯ E^ IqmWnuɋ>Ee*O|ee0n8U3fmGr=WWWgϞJg'5O\`eexxx>²5k֯_cpqqywѣQZZy᯿Byy90}tTUURAy[n_rxfΜ+W N 110tmx Y 1~xcBee%0|pܿcǎEaa6o#11lj'`oo*^ZyPZZ{PVVǏK.z*VXj;z(`ȑ ڿǏ ?޽ ooo΅ PYYTܾw!sު[? 22Rn{&LZIyn߾-sƍCNN*++*l[ոul8_Dqq16lADDDD ӦM/t(DDjS[[0OÿVhºu]v`@@=+iHV4e+QFvP294D5$y)͗HJJtuuG}$(wnAs $LLLBqq>Yx@ׯ̌l<'wg9sLǎ-[J666j-Z"us@077d\O"nUb4ϙGV6ڷo/j_% E4f[N~jgϞrj uު,q IDAT&yyO / _MM`kk+Ν;'zIyVC )[]]-Kn[q_-Ӧ~'"""z2.]* t钦CP u='oUQF BrrT* #GTZ_{\]];wi.]UWW+m!}9RpuunܸDXX*L8Qpuu6l~* L{*; ꟞=HDDDDDӧO>|XaŘ0ajjj0h ]V*_~̚5KA+WĴr ["ȑ#@ʴpB֟={L믿())Jo׮`Ȑ!-999A-s$?$߿R=yYUOQ!RiWB( ŋ јV?U=_GΝqu:tH*СCχ3GrLdtuuOq=qx7qơL4kIIIؼyàgW^yqqq۷/ $$AFFxMwO-$TUU!''K,%$C\vM BNN4@mmT!$vvv:u*ك*VWcG_xTO]qU4UI2HHHHҥK kkk  ߘxիoƜJɓRϒ?ٳ#PG\{$ʈALL n޼9sh:f-55AAA1=ЦMɚ>fffWuIVTh%7FFZ-733xɊ 1eٳGjQQQ5jt˗/-s +!)/q4&nU9* ,k.^^^Xz59۷ogcx)ҲeKM;9oUS&ۉ'BGGϫ۷o#!!*sRK֭[+θ8c Ν;5Nbq4"ΝCtt4>h:& ۶mرcaeeXYYa„ RyU裏 ### k׮Ō3<,X[[[-ӧajj6n'Fj:jbcc0)311AII ޽AQEѥK}޽{ wHIICУGG?kaذaݻwQ\\VZԹ{:xwq5=zHLLğK.aܹطock8p Ν;ݻw5D||<]n ҒJP[܀jYUOQʼn'(=_"66>>>_~cƌA`iiΝ;m1CRݻrtWʳoy4q`矱c̟?;v@uu5mۊeyNcffbۯhjʿDDDDc͘0ai^y"11Q\>Awww=Z4)))r[h@6㦛!$$DnYɠ 􄧧gbNe֬Y5kȤ:CU |q|ߟ$=c89s`Ĉ4_o7^EEƏ2\^^^ #==YYY2ʌ?~)+̛7O{EIIqY }:֭[[b̙pvvʿ>wOOO|ԩ7(ڨEYYė.]R[u5d?JṢ>ZZZ2.X9r$,Y~"""d򲲲:i:B7eee ۾~:֯_mmm,X@L4h`ݺu2MbzKi ѡChii… 2yVPgl9~/",XJ 2D䌌 ܽ{7ofϞ~0`#UpY###x!=/{_ׅ-ZzK߄rرcŒ3}}}AWWWHKKk׮½{˟)?X(,,߿/=zTprr˗/sR ¦MK. BYY)[NhӦ@XlT[033Ç.\ TVV .\.WJիRE Ν; ݻ'TWW  A˜1cHl9~ 555»+???ȑ#?#TWW Œ3~W-,,[fHbvppN>- ¼yCCCŭ~A}P]]-TVV*ܟOQ^]]-ܸqCطo)'O>  BMM%L8QX=2쏒V 5J .`bbpw^]mc~y[@"jx!U߿`ddpӗ_~)UUO rze^z%}A/a޽2urqqйsg1cFsI W[܍}Ux^ʋi3u255ۯKߧݻw7(Du_VWM߿|֕$׹sdʨ$lRn;v[q);6O8~'"""桨HۧP*((tjǿ爚6O$"""""jADDDD 2Cv-Z`8|0}.]R???z聟~ n~޶m ^P/9.,--`6{۷oǫ c駟rJ,]e˖טtL4Iݻwcccc= |T{V۞={_عɔQ9(sΈĉil\ʎMS """"jZjj:jڶmAt<bbb~&"z6&z^{R<[8~ágХKGGG\xQ=9B[Q9']M@)/^DN:u ?PRR333+9r[2kkJJcNHNNpױcj8""""jnڷo͛7k: jŒQ^^?/L֭[3QwM 4[lAϞ=55\si8yJ,[ GJq):u Ċ+־4lڴ v 7nꫯj:4""""z'Nq~zr n݊3fh8R""""""嬬`cc & -- -[tHD "66Va5[pAϥ'OM_f = SSSL>G6mP\\dlٲGV\r,]G5`ȑXd ;i8B""""z^lذo[okii!44}}@KK QQQ֭k.MDDϸ9s`ĈYtDnnnpsstHOOǁ`nn(:::hժ (j @FFC~bmۆ[nabڄ p-l۶M;w.:w ###^믿b…Abt8DFVVjjj45C\ႚ7nDbb"aooI&aW(++CDD9۷oo6O}}}!y0i$tAn,Z/6n܈{k׮R9s8ro`Μ9TwDZcPTTKKK3f̀L;qqq CFF 0`̝;W\8|0 {{{ >\jR^޶m[!..'NFDDDD6/>SMADDDDD … 1oABB@zz:ann|cwM˗/iٳg ccc:(..Q[[ Mc|uu5233p!]&.www={ƨQ=DDDDDDDDDDDV\'N`ܸq8w8ynCoooh: "+w'"""""CDM@LL _DFF")) R+/HDGG#;;Έox8{,@z۷JcznݒKLLƍHƍVw};v˗akk+nwDDlll(:۷Ǟ={p?.ٳ_ )) N¢Ev˝9sйsg>$i2DDDDDDDDDDD:]]]Ƣ+t(.]¦M-[j:""""""""j8:777tɀ tzzzpqq%Kd=r $$Aǎ 8pҸ<ɡdOyˀ.ZC׮]%|hyݭ[7qecǎ  Y3JJJ|Ɋ111ǿѣG#%%E/^pG}:t*CDDDDDDDDDDDӡCCӡ===鉞={ĉqy|嗈ٳ1j(bk_tDDDoooM@DDDD6GYY75o1dMCMqIN(//1w\lڴ Zה4lڴ v 7n gpAMZvp%?^beZ[[#// VQFضmz 2'v1bL~FFL'iii_|QXlllpUGbzzz:4j+_xyy 8z(oߎ0qE֭QXXbnZ~qqX̙#D"""jڔHDDDDٳ'>}h:z DMBMɓ'֖dІ i&۷|MpQ^^Kb}#Gbɒ%`dd4K==!]YYYAff&֮]+S_~ (**777̜9S,+YڵkbZn0tPa鈍͛7QSSBDGGCyy9,w65kֈq!$$0h }׷o_@HH222PSS q{Wݻw /H!YV7//O I,KDDDDDDDDDDDN:t 5 =%θ~:5 =GЪU+̙3p G۶mí[0~x1m„ umۦȈpAM~g\zSLQZvʔ)8q222+ghhy扟-,,p9HHHpFee%~!,,L~aҥrchժL]tqw]'OƱcp5Iɤ5ɓPJ幸=<>>Xh233O?fӣ QQQI>Oׯi999r毪̙3- 22YYYشi8@Q[[FRq0W_}2CII V\p}/6n>|T޽{0m4^$X={066V}C~~Xղ=;t.Ӿ;Ξ=ÇcԨQJ Qsj ˖-J+((XZZJ# J􄧧>}(XH-gܷA'-Z@`` ht///xyy)mވ@~~>lmmHOO̅@"""""""""""R///lذ~#w!&&ׯ_ V^Ν;#33K,[FGG#;;Xx1푗={È#ohӦ """Ǐ.fǎ)NpsN 6 ZZZ̄?N> }+[4R+fHxyyaƌRi;v˗akkUVsҥK(̚5K}Gvzj8::"33S~deΝ;˴/I;s \sM D֧Oa())Amm-\+Vwɓ'm۶Ryswwʕ++]%i:& tzzzpqq%Kd9rGGGcǎ8p@N`` ``` XRRXmԩS8wt邃?'| 4Errr)!ovM\Aرcܾ'N/^ '''}jٞ]x2uA +\P6d޽ ʳ5'ۇ7Pѳ֭[2dZl_~ׯ_Lj#`eeͫŋԩS;>>={bرO34""""""… q1?077tHD􌐷dV^^I\\\d\v Õ͕IK~jkkMC͛?C r}U2߿~ 6lŋQRRѣGݺu/7nܐi]~еkWm<Ν;3334I""""""\P&r ЦM ӧOCl\]]1k,_0{l[899I}vvvXLMqIVﴴ4lڴ v 7nꫯ>8Goѭ[7̚5 њHRSSaggKKKM,2[MJ3jj n:h׮|MɩA4LLLп888`ȑ\(RwI}OcXuueP'mmm4yǒyԤikkޚE溤iS4Byw&wyy9.]ѣG0rH,Y}fDDDDDDDϭm"** Cŀ0eMD;v,틭[j:&O}vҥK8<^u12e:RGUy###aaa|_SS#wp055i666z*ѣG1===]Qڵk˗/#55bz]؞֭[hݺT^qqXy&;Mcpss_;v qqq/By۶mí[0~x1m„ umۦȈ?o6,X]ӡi_|7n`ٲe7p@ڵkdffbڵ2e BNN|8F!V)))p,XزeT?j]ZZ8k˴mV03qDv BCC9ԞEAxV۷oc՘?>`gg鐞i>>>qG3e8q344ļyT_meɁ;,,,p9HHHht&ìYϓ'OƱcp5IIGŋ1c 1}РA2"Ա?=<eee2mٳ@qq1{6m֭[q5TWW Xz5bbbXXJKK>s9tʤpwwGYY>R'''']Vk. `kk TUU]]]?A7W_̰m6;VVVс&L(?}}}}cѰ>'? ccc>}Z&055WF!""""""z^q zbccQ]]0w}9ٯ~~Ҷoߎ]tիaiiO?{Edd$ رc._ [[[Z ;wFvv6.]\DEEIR<D6m ""8~xkϞ=/nnnƁ]vGj5 yO:vHnE}9sйsgv%igΜQTipBAGGGlmmHOO?MDDDDDDqJzbz1ch: ""3f ӈT$`$󓖘gvvv066FPPRRRw^AAAڵ+Э[7,^Έ;;;PRR"Kn@ >>/F۷AImڴ\tEa9\zѣPsssxyy 8z(oߎ OKK 055ʔkvӺukX恍b i=z !!oir0:{,Ο?GGGòe0k,t 999'|+V !!AqA)))իWa^Zn6l@JJ z-['/ 22RfU٘:uꄨ( Xf ֬Y[nH?3뇪*|5k C[[t( """""".3f ƌ0[uu5\ ++ ,ၸ8_!!!hٲ%6n܈xk{QQQ UХKdggcڵ޽{ÑEaȐ!x饗]C\v vvvJ۴;w?`ԨQ(((@hhh[^ߝ:uBaa!dVeH^~eHOO xxx`Ŋ(,,D۶m5͛7k׮sbٲe(//_-^{裏b qՇK <%Ğ={ܹ/"K.eA46_ॗ^̛7k֬;wGSSS#,, .\#WZHڏ.BDDDD0bԩhݺ5222;7o{bɓ'CϞ=7n~~~*=yd@hh(^z!((a9Æ Sf߾}}zcǢe˖ny}{xxU$1PH mmmL:Ujz믚EݻNNN{===>ػA@3R(󂔊-ŲqH fF=Jȟy ԯɬ,E 3EA 9J)QyE.;e xǃY{{43ؿ/l`.v]c93W 6lŋ=sLrr2ƍ'::  \Mi߾}۴iҩS'CDDWenkd운j*"""|ISDDJXOV ~888ٳׯ_g4n{{{7nYx1nnnU;$$)Sp=舣# >iӦY?///),,p̱cҿ4h#L<]}Ņ͛7w͸ҧO*_y 6$!!$~m[ǹ1a<==mLeW:uX=6&TtN:U93gPf "778tB^^QQQ~,\Rc\Nw]ƍGJJ VL2[_lڴM6ѧOMfoM}ٱc ,`ʕL&HVh׮ "))w}gM6%::4h@dd$+ڃ pRw$,oggg&L .ۿ2]h4Knn.^^^撙ɨQpqqp\vvʔ)S䡇Zf7sL[GjnJϞ=-7moǴhт}qA7o~U]˹yyپ};WfDDD0z:bٲe: """"""WVNJJJ:ׯՕ1cưf~>}:M6eݺuW}Ysgdd\WϟgOOO6mJHH&M'"""WٰahҤ <==2d HYܹ3ٳ#s=?@QQ7o slBoX7|S昷z+ǏLJ͛sA"##=z4>޽{쥗^⥗^u ZM+\uŋ,\:wLnxK&((Ν;RXXhSK_|O=UEq 6>+5NAAӦMk׮Ջ)S\v?9s7x{N:y敺3_pv?LLLjgΜaŊ<4nޤIYbgΜ)HѡCÙ;w.s^ֱDDDDDDDM6eÆ ~!_|c=^^^+|,Alٲ1cмysԩCÆ f۶mo߾Ug;v0gΜr|뭷hٲ%uԡQFNNN899Ѻuk&LG}Tcs= `0hӦ:DDDDDDv r][`qQQwfݜ;w0 =zRG%..,,X]E;w[nڵ+gǎ͞=kORR,]77Jٳgyg,mǎcٲe1FԩSW^ŋ*[(+ ty'+}>"""J/шhu dqu=<>>}ӧ: +44P[.P""""""OJ+9$$$j*/OFFk֬n:iժlْSTaBԩ|73apppM6b 6T8fIQQQiڶmĉKk IDATIfͨ[xӖ.p!7#G0g^}U4ib8"""""""""e r]3f ӦMc۶ml۶{#F`4qrr;tAAAe믿V8Oƍ9z(GYf͵/SNV۵kÇ+9ڶmk޾}{қ%y{{[~vssŋ^'NP~Rs}DDDDDDDDDDDDwݛ[G ""Qڷo>>>>[.͛7',,{_UVm6g YLreddX=6QU 00N:qF vɻҥKyx'W(ٳo߾=Ge<[~I%W0ر rJL&UVHͩo\Ennn̰a8}4ͳ`ڵddd+\p_j?31ڵ;;+y4mLvcVUeN*uGDDcǎ:TAhh(;vu  ĠAmfHKKu )ёTh(RKX%?{NNN_~,Y橬3yd'M6%$$I&qkGDDDDDD r]{'e„ ׏ロ?={dDEEwɓ'Yt)K,ߟ Vh0L:tf͚жm[{1+Fs=?}|ϟG) :3f{aDGGзo_K.G,^hf̘Kvv6111%L&N8o}gٳ<3d-[Fzz:qqqӇgpqq 99'8rtڕ$,  ==d| CDDDDDDD*Or "((܈ ""~[nVA_e4hЀH"##+b+sիh$66\%33QFY1 %441DDD>s[Gk`0xbڴió>'|bH\.]زec\BBiӦ̜9___vͤI8~UߥKM֭8q"Mzz:k֬!88͛3uTn6bcccÆ ٲeK,7[|9&L`0{nF͛K\٢V+fgooϳ>koŌ3%;;ɓ'믿xbùٽ{7p{HKK#??'''xqqq!-- __Rymiii*AߞADn:Æ I&Vw*OHHӓ!C0TGFXt)9 .uk. rWYlذ(ڴi~~~L4TuMVe˖L:RDFFҬY3֭kY]W4!C,6mb{|׼[̞=DE3Yi߾=Nbq۶m-|,!?O' ??TKEϞ=طop-ZX.DĊ3SN_fСYdt"""ׇ}SUVm6:uĀe42ՋӧS~}[G悃޽՗СC<#R땵K999oj_c:@PPP믥ڼ-?p+\D`0vXSd*s+>}}2unn.m۶j7_#Gлwo|Mԭ[pK;zMVVvvvt 'Nrs \H):t <;ؿ?;wܹT_OOOrrrXv+UcW!o.\NQs= :ŋM,y4mڔIǎ-홙 fdffGq9v @׮]IOOٳVE;vSNaԩS>"""""""Rs􎖈\ue.^sq di>ކDDDDDDDD.88X_pƍ'''Ǝ[kb=zĔ)S2eJ{% wwwN8AHH]ٙ^{QFȓO>ɝw a9t#F:YfVm͛ssaU1tBJJ YYYⲲT$""""""Rô…JΝcȑ$%%qiػwآBFG}đ#G(**ѣ1f.^X\~-yyy@^^5vn"""""""""׻iӦ/[.̢E [nN:jՊӧXn<J\\FOOOqssG,^Z(Ę 8;;}GLL =+W`xyyaooOaaa 7ްׯ_x @ƍq <ŋf5N`` PH¼څyU}͛Keڼy3LDDDDDDDfh  u +h"RE >|M2sL|}}ٽ{7&MV}.]Jvv6[fĉx{{Ctt4Y`222V0KKK׷Ts[ZZO>:] +]G}D:ux>}:'/k׮ё ~mbΪU[.=s̱xYΞ=̙3Yjxzzɓqrr*93׮]`˖-8880x`ΝKz,}J^Ӌ/2uTV\qttu<39ϘfӧOޞ([Gb6lXjeGШQ#v777"""p^JǬ_>e3]Bs1Cu(++RADFFy9BBB )>~xƏ_^zFbcc \2335j...WDDDDDDD*OrU1`[k\p}`u%6l@TTmڴ.ulҤIKV}׭[@tt4M6e˖L:`*,"ݷopiZhaGDDDDDDD_|EYϘ1-[}vRQ_xҥKIMMݽ9ٳobgg}usVg={Z/\j&LXm6mӧ+f[;rN<̙3K_l[n1aquu%''ٳgо}{'6l_} <==2dӉ>v"R= L&mڴiU8rrr{!l"_¹N8\ZI6sٹs'ϟgcoo޽{^`ڵꫯ)(( ---ZpAxr|7Ƞcǎر"~'vw}G\\\\>>>YV\z5{5c|7p}]~r"));'O:M!>>g}1~0k,zŃ>Hhh([!/gggNʚ5k8z(Ge͚5L:jE#*a]j*Y,e0.ۧgV00q׮]ؿ/lٲwﮱՙu֖NM)͛Ǹqgܹ1d|8-Z`׮] 2;ҷo_,Ycǎuww $$.]UjlsC=T("""""""֭[oڴ __riѢ, .X[n 4<ӤΫ_EDDĕ5ѵkWmV_DDDDDDDDDj/\UAFF5ٙE-B:uhժӧOJ\\FOOOqssG,^ju兽=6o\*͛quuO>WEDDDDDDjy(**b͖\1?0\pcǎ+`0իWɹ,Y?nY`0{qڶmK۶mZg;w&//;w:\ȍaÆL2Ūѣ4jȪ͍ލ100@za4%77///rssdԨQ\鈈vmtɪߟ_|c&LΖ-[С>xWs5i҄7|e˖qq|||h޼9$22ѣGNxxqݻws|}}iذ!T߁Oy饗pppuJbb"!"\z=H uF^^&L 00WWWrrr={6۷ W_}EBB$$$ɐ!Cjt. Frr27h42yd=Alٲӧgݺuc5j[ošC(((`ԨQ888; ]w?^};`00`֭k(ʫݻymSTTĀlCDDDDDDD2L&Cȍ!,, THP`%ddd0k,>2xbJ57/.V̟???GDDJ""""`@/55nݺ_ү_?[ǩo"7e˖#DDDDDDnpZBDDDDjH5k_́(,,nsΌ9ƍ:t <:z\5ZBDDDDHbb"!"""Hhhc }̝;1cУGufHbcƍu ۷j_~U2ҷ"Vb۶mtԉ\x"""""""7+\)**5""""""""7^xdɥ IDAT;hذ#]D֭KPPH 7n)))W\pڱc ,`ʕL&HVh׮pTp!""""Ֆ@BBc\5aaa,[1DDDl*!!hr0 Ӯ];Fg}`uj[f Up!R \% 5jjՉkL< @HH&M"11ggg'BDDDDm:TQ׮]y饗l"SiiUK)ÃKpBy[G.]0aomGDFsq di>pBDDDD~ 11P[GJJLL1n({GDDDݻ;н{w[G";wԩSdeeѶm[[&ˍtΟ?7O='NWp#66uӨQ#}QFy+V0o_S\^^111Dnxp>s +VqƖ&Mϊ+:t(ׯֹ܌Tp!""""Wh4"""70[GZ`ԨQp~4h@ZZ .p9FÇ-m{[WXXѣζ=z8X`vvvVnj5SNիx"QQQs޼y9sR<{kرcXӧO3}rϿ:_NUDEEyfRIHHsPjINN'y̪nM,,, M6mڴmٲe'W䚰SNlݺQ{ǎc̘1>}Q $$$pa6mJ\\[neѢEV+!-]lZnͲeXjڵ#==5k֔:y|駤1b6lpEclْ4K0aiiilٲRP| 9Sܾ}xغu+qqqxyy˒%Kʝ*-5GDDDDDDD*G+\TVZk rMo׽I&n:bbblE!**6mǤIx饗[h6m \*~:u*$%%luLdd$͚5.YTg!Chyi&L&{믿_~a={S"""""""R9*ƍGJJJ;v`\iժڵARKNHHH|9\J;v,;vd"5d!`lZ,9sسgwqݻwۛR7S<%9}sT%y|;;Rme=7"""""""R>\HtlRfիׯ˗/)o\/".X/Z;y$۷og̞=_|ٳgSn]{ɓdff;|!ClM*aذaDFF˂ .?66r\mr#Dڵkyꩧ -7 pmEjHXX| d>|87nd׮]rrrbرU&ѣIIIL2)Sڿw^pwwĉڵk9U6ٽ[VUݥKRRRϪoVV=PODDDDDDD…$,X~ȡC(**"//ݻw3sLܯѣGGq8z(qqq3ԗJo˳, < RDDD_:w_MӦM;w.?yyyw^f̘AIJJ"(( .: GycooO?ͷ~)**?dݺu/T[ۗA& d歰|w˖-N:5^Ps_'… qqqaذaz Nbbbh֬H qvvfѢEq-PNZj,7puu%..ш'ѣG/^\UjbW^y@qtt#&&RrJƏ^sl'ҷo_iذ!dѢEeW%w߾}qqqaͥټy3NDDDDDDDr…>S~m)**"))YfrJ*oҥdggӺuk&N7999DGGΚ5k&##jiiijnKKK'JWDDDDnO?4ݻw?[näI桇"%% /(%227Ҽys>3ڷooaÆݛ޽{/DJJ ;6J}spppq<<ü̙3QFnM&M۷/III|r^'OcN" 777VXC=Ĝ9sxWmI)$$*hذa= @F܈ ""1y+~宺R|u@=M)~믿^*^zFbcc \2335j...!"""""""ִ…8rׂCVZ_E`͚5Un:iժlْSTa}мyRZhaGDDDn999RŵnݚYfڿw^ xyy1|p~ ` ggg "==~F]wEݺu[СӦMɓeSl~,X3~mb:tW_} .,^yweW׹e^ZTVLL ~~~';Y~~>o&ݺuÃ:uШQ#{16nX{iܸ1vr3W1 jժ`0p뭷ZW|x^[8p YYYYU0yM>w~?Sxzzw[oUXUUמ9Gu^g999tЁzWjT̓>̙3}/rMpٿi0l04ibbsBB5"M$""""RIFd4}l֡CJmWS9[S@@K.X筎l}zYndرCR L:t0uԩ&.{.TzmڴfJmNNN1e-9;;[_x޾̾۷7={3ɓ'W\M7o.ɗ_~irvv.sz֭[Wym۶T8GGGӖ-[IOO7կ_kѢE ӱcj,[IU}&4f̘J]_QF+sW:WYQk;SV܇~hL pӧO=w`ZvmTz$''y`0-Y}~wy 0egg7{l` +s9syNu-Tw5??n2 ~7 SÆ M5ܕW_|ĔyLUsUw%^zY~8p`},RY/^4NS^^ Ws111徟ٻwoÇFl S.]LG19rԥKӿo[Ǫf+XDDDDDDnLZBDDDDj\FFvk!00)Sжm[ywׯWr?T|%0]Τvv$7]kEDD9q̙3;_~ϳuVFIaa!FGZg2Jmі~0j(oF́(,,dӦMu]رٳg[cU֭K׮]qtt;vAqyx ((( ++'xs1`;V#FG%55sαqF),,d̙V}ǎ˙3gxg_(,,$// 6prA˝wk"[qy֯_@HHH٠At\Mu6N=-ϵ]?Vo֬Yw}G~~>۶msΘL&^JYqIF嘟~~Gz?_P/J3ׯ_28q":ub۶mO?DAAC jݺu4h&23a2]aߌ3F~~u%l,gfXΝYQQ¾"ֹsgDž9}Ju:u`5kTHV5xR$իWOoQ^|:/lllʍ2ֶˋfʋYXXYr[nC],rD.]2777pq5+?{1###Y:]\\f/yL-uG^t`W \vƘ~ϝŋ3G)oaa!swwWYR CTXXȬ}bH!pA!B!'ZB!| Jo>d24m͚5ks/_ڵk!J1~x>/R͛ݻ+!BHwQɓU=z4ĉj!e˖)?'NT*+JÝ;w\Gɓ΄[ze.###|wǏ,?i$/k x;~xx8[l%rlllʭ$.NNVT%nmY[Uyh _fw1rO>|wJeD"fϞQۚhӦ 6mK.ٳg|#GP\\ WWWԮ][eٹs*X_# 899!##QQQ C@*S;iӦ)D*>رm!Dpuu  !B!B!B!0bѡC#$$z4_PP5j :? 333L:kkk G>}$X !RRRRb@isvvܺuKmcǎݻw_BPa۷[(&l=^VR%X!{<&&CEÆ ѰaC9GAA^c+I d*aaaU9}ŭm?k*mq\$Çcܹ޽;b11Qy]}?WY]54h =QGC\ܩ|ZEh kni=w\~ HTwss+}U7pA!##СB!B!RЀ B!Rbcck1oi:|۶mCdd$qA~fI܊ X=^r5jomm @j ܊ >x;v@~вeK-t9*hpY+BgmU5-n{{rٳM4A&M%KɓxVmR70f͚ZPoE^^BY.ԩ2gG6 B8|0D"__N|r4u;1cCR-ZӧOkz!B!B!; B!2 __rjӦMôi׽{wt]! Ç>|x7!B!x5233>7@ _~A˖-ն7ohF(͛ѧO/>,ԪUKLff&>}_!::Ѹy&0e:tHrp۷:u###ڵS1@Y[U}h̙3ԯ !C-ZШQ#ԩSNNN`M\LkJyZj''';v )))xd2b1ڴi{wL%TY^=xyy?]b׮](,,D^`ggSYYYjϧ:y׹C(߿é2nn:p@DD B!Byoр B!B dĈ8z(1c @xx8b,Bq5\p={Ը\^^ ɰhѢ2}6iHJJGuĦM0uT3s8ׯ_+<ߤI\z/_F=\tϧO 6ȑ#1rH#GЧOy}Ǧ=z4VXm۶apqqQ؟6mڠ{yHJJ֭[Voddbd2wI u͔[lӧ̼GH IDAT=`7n¾3ȨEuDGGիJՃ+jРAX`.]~z4WK.J/^nݚO?;wķ~۷%ܩҲeKŋ*WvrѵkW4lС(B֭A >}:7oѣG:_~ B!ByiB!BD*V077ǂ p!<~?ơC`:/c}'F~~>|||NHH@ff&6lgggL4 ={D~~>u:(䷵\|Y0ŭm?/ϪT59s 99ݻwyo߾ 6lҾ5k?ۮ.ѷo_իS(;V\ *zj"tGrPfM$&&"<<ɨU 5};uqQTT_ݹ5.u>#DSXr%dhxè2NfBCC!B!BH!BP`` TZ[RwMxx86m6m0t8Bs}iedd5j0k׮ҥK,77:u;0H2dا~޼ySn,7od 5k{ asvvfܹsթS`| [~=KKKcL&Db 3l޼y KNNf~~~ `wU(գ3gd;q{ +,,dۛ` t91&Y߾}fllFN<^x ٓ'Oرc60[[[>bvttdΝclԩLoqkό1 Y~~kB#v!ֽ{w={:6a&YRR6l¹w^_f ׯKIIa,99ۗYZZ_*ZD"UfY~~>X=Mnn._FXIƍc{ڤIܩJbb`,>>ׯZjK׸Խ*}V2_xxׄCBB377WQrCPer9kݺ5ݻC!RIKhq^B!By7ovB!Dc&!ϵM=z}bƍ %Vj0Xhh(*󹻻+ 3332...HLLĖ-[УGb ͚5øq lO>v X HXÇ#.._~ǡ bbb0k,lDpttķ~8888TZlڞ?P(q,Y۷Gڵ! aff Vajje˖ST011+91c-n]yѣ,--!P~2D"ԩSڵ pm3f v܉> Anp,Zsl޼Ytc|ggg۷ {C3)7pHRD"4nӧOGbb"Zj_ -Z&Jܩ1cs䄈 6Le]*WgTѷo_ 2Ϟ=3t8O?k֬A  !B!B!Ֆ1*Rq%}C!JаaC: 2d <<\kB!Tvݻw+V@JJ N>mp;(-- h֬nܸzSSS@T4kLe>@H5&@ ѧLiNNN8z(!FREDD`Ȑ!gү !B!D ::"B!S`` B!Blذajy&6m۶mDv;/ }Y&~Wt۶m+sU'BHv >}Brr2޼y-ZK.󃵵mr+^ƢGx9;;;|߇?ѣ<|~~~裏pqS?O[x"q\B!B!WO!TO40TDDD: B!hf&gΜAj*wm۰rJرcޫWuΝz !DSnnn8r<==  !DG~-Ξ=4@&a̙pBzBBySNe˖￑4"::`bb?^a$''T qqqX~= f͚> !B!\B!_/aB!DK 64tBXz5v트8tUi@ `"ԪU900mڴKݻ:BH={VezHH.\ub„ @5S߿vw}m۶UVٙpѥK}܀!C`툎Vp,?-77sE!3g ss*B!By}q%}WK!-4lB!B!Zj۶-zjDEEݻ(,,<==7Mo5h/yfK!;w2t(q9ԪU ;v@ݺu}ɓaaa7bƍشiSJp'82 qqqȑ#g\|011p.ؾ};={>m_}vL8рB!B!Ɛ!C]-!BpCA>?""" !BD !z_>~*iZgUR$RDFFbڵx葡C!D^x~ E&M0|p̞=2 8y$?:ugϞ=z4?XA*cccqaa[4h 0ТE llٲN˗/QNtcǎʺׯ_WH|2 ԩSEvHMMUp^| 6ٳE.]~+dX|9Μ9333&M)˖-[p <~hҤ Vݻ+R)vލaÆZeB!B!Dszp?B!AIU6 !w }^$B!|9s&ѸqcCSa2 >>>ѦMCC $''GFFF4h 0~x#?~0$%%a022RVbb"%u,--W_)y_}'O ""/_FXX,,,QC[B>D sNnݺO]p^zH;wӧOXnbbbc̙={(R\r"B!B \B!B!B!B,Z`훙A**<ʕ+1g|G`HH>Sm۶3gRޓ'O.]f͚A(qX`رce% u*NR[h y&={f͚~ڵѢE ͞=vvv  RT)[e޽{6m(o۶R|}}Us2[.222c4nXerrr`eeŧ=|кuk\̏=Rf.S <==quDGGO>ArrByU222~ʸ4\F RL ŋx"6mڄQF!00fff|`mm!B!BH %DGGc̘1prrLMMѠA2US@apuu-D"lmm_ $$zolݺUr@Ko\i .eաX I,!!!? $$>>>U~]\\ Qk׮A UV:Xu}[YBȻoŐ!CT1}tcݺu !R"""MmFmFHuI&aHKK3H HIIyZr ~XT1ת͛2s.\6Orr2:w Y&+ehFFFHMMEqq1Ο?@ rn݂@ (9\H9.MLL߽{w?~C֭7;gFFʏ|pi:!B!gϞ!((ǏWڗ o Dnj3~z/233q\r?&O~wرAkEEED||yΞ=2=77sEAz3g ss B!믿Ɨ_~i0!JjZ .\@@@Ο?_֮];oݻw1L::t@~ "j4hiii_&)Ÿw>d={ıcԪUKaqq16o zPW޽xmʊ5Bzz:㑘(kٲ%jժ5ں4ht\v7@RR杢!)))믿sNY/׮]O++C!B!Wx)qqԫWVBrr2d2󑚚ŋǎ/r~gG駟  1rHDEEٳg(,,˗/qI֮] ooo6p=b;LUmx)Oyyy>|\1蓣#N<#/CaÆA("<<yyyeCDDD"/!"Tr)3iƳgϰ}vFF!B!255ݻq ̚5ۯ]6ϟ Tyw^`͚5T^^^e˖!)) rXlRΝ;BBB"|k׮T*J Ѷm[<AAA8qQXXdL2/^ϗԩ`ҥHHH\.GBBc<Ɩ-[x;Wqq1ڵk @ @vPTT0rуڵkHNNWUhRL&CӦMѬY31y{)qy!B!R1 #GDzz::v5kT߼ys̙3_~%ڷogb͘R)vލaÆں B!~BDD B*UxxxR6mM6aذaҥ zUϟ?Jӧ#44m۶5t(2dܽ{O 9s Jeaԩk[[[X,Ç˗/DŽ ٳg+ѹsg,^] ԩS>FaÆJilGK*yzzȑ#|2_,C_7n(n4A]hbĈXt)/_˗++^ٳgV(op !B!B4S+\ܻwcǎEÆ ajj X!C %%Ee~@PVZll, >&&&hҤ fΜ7o(;w?>WlQ ֭[bĈ [ny\pt氵/NinSSSԬY .Dff2Vm_ZZ֯_sssDEE) (M"ѣ066͛q η&4m.UU׌-[mÇطoڶ9yyyѡCԮ]"uA^p:MIIQn]m۶]/>>>rE\>Ut郒qm߾-[1># 4H(*MkI>羬 U[g\~}^_eX ccc4mk׮-.mš˽޽{H$D>}4 B>,ׯǶmp}B&!11K,Axx8ǏΝ;# Ô)SP\\\f;QQQd sڵkL'N!b8J+IFmFR 2#Fɓi%z-r'ssslٲY&D"5kE? XYY!,,  ANcaooP|Zk.|7pvv,--ѵkWlܸ+WBؾ};uB(nݺ_hh(H$lĈƍL.4W_1D8PqL2I$vUbccD"aSN%BDCE!C}My󆥦: B ~?y۴iJ,##O`RmݺՀ}y~B!BȻVѿdggcԩurssѣGxB9bvKm|7EEE?~<ܹpttD\\~>tt4h)<==fx  ???$%%!??IIICNN'OsԨQٳ'3gΠI&(((%KN:3f nݺd2>}-[ݻwfhl%r/ogF@@ԩSZW3+5`f vZ/_k׮ѐdŋ{ԥ?233ȗINNF~~>].]`„ kjj.]֭[J+ \~ر#,,,*fϞ 777\xyyyHNN/1tP<\TZҵMMMT c ^/U?ˠA|KmU[n!11YYYXzAHeJ [y镁#22^BCN< Xt)5kPƍccǎ*ҾF)!B!B!TQ:@*b߾}x5qomڴ1pD#FŠ:t##B!B3K2,$$De=SN-s?'&&D"M>]aߌ3TcW\a 8 *Ͳ̞=`;wfEEE XΝUoV+y)լYS!իWJeV^=V.F {2 )7ʈ[~./)/Ҹ,,,ʬP9[T֡.][K.Je Flܸ`?B+W2lڵjh0Lx-ZrҴ*.]bXrfoo+W0{}J_x1|||2wwJ9U^ONK*Dy[ebڵ`,77W!_<Ν;+WIL"(cL"0779HB!DyQ;ӧOD"a]ve1&HXϞ=+=w!g.={SS={TaTRV j-[LݺuSX)bcc{{P,R5h B!By?U GL|wJeD"fϞQ>>>#G(sTΝ sss]*6RE/222/** > ?ӽ>/U>6m>Hei;j!^wǏc޼yhݺ5qF~2VL@ r4.!BIII~P򵋋bN[ 2ڵcƺu_B!Rf̘iӦ fff022X,?aooo$ &NUVaժU4iʿeB!B!Dwj(%% w-Ǝw+B۷oZnQL666Dvv6jԨQur)ƥs訔fii (..VH={6Fx;8k׮ӧz V. YYYBڵm и:ĭm?k*mq\$d\|.]F(U.=矫,Qpvvƹs琝 kkkdeehڴʾVE>P=44߯JE/U_Ъ[>轀F) |}}닔_عs'֬Y~x9޽{8|pT]6<,^s ذ~nEMa 3Rּys{bҸ<By?IR~۵k`˖-Jؽ{7`ݺu~zپ};={>mxon!R1lWǎ3t(B!B!Wl7kzff&cj"ׯ_7|@_~-[, M8n߾= **J7oDڵ f/.-33S!۷/^{!,, FLKKÔ)S*%6]Ο`߾}Zcdd$]v*A>//Ooqƙ3gqE;w.BCCqAܼyS6u 4uu 8z¿ hOqǤn*}^^=xyyh׮](,,7:u1︪󽀐zѡC#$$VQFHHHСC ooo033ԩSܠ_٫KҸϝB!ӧ?>u777|.;;WF^޽{c͚5JwrIn߾']vŲeː+..͛wwwt#GPWzK|5t҅Z29numgggcQn]>?`0O!'//AAA>|ǏWz;Z~=bP!B!ByTـ n5 .hU.//dXp!|}}mҤ Zi&pz~Za)^˗/,s%|ҰaC9q> .oDضmsf͚aɸqn֭$XU&)ՕKҤekX Pt ;vPX"88 (((x{{ΝSܹsB!}։888 66;vvЪUL&S9Gv p !BH155Ǐ+ ]x/Fpp06lhp!B!By=zڵkHH 4={T}.88 ǜ9slsΈGhh(?0')) Z޽ ,^^^8y$ }BeXj̧{{{իXbw >tj*@׮]r4jCRR>c}']a6鄄dffbÆ ؼy3ƍC!??ݺuCڰ˗/qetYa_XXֶ@ 1b~V*m̙3pttD޽{maÔ|"77u鏾}"::W (_F"^^^gϞO>055-\E`ҥBZQQ~@~TkGf͚HLLDxx8QV-t˗BPac5.uz/ |rZ__2rlll0m4L6|ݻwG,--P<|zqƩI! 77hʶsNx-[Ē%KPN8p vڅtcprrBJJ Ν۷ocǎ8qBX`>c",, OWۿ?`ݺuJ(,,ıcǰ|rٳC All,'7FXXLLL~trrRK;|M!صk>37kEŕ֎.,X ̘1С@aa ػwC B!R l'F8} LSNYI}˖-S?lذERRV\ T/^`Μ9 3O08}4gΜ&O\>V|0`N<r$%%!00.,Y}pssW_}?/_D;EEEظq#222`kk|_'`رF^^0m4lܸQoqk\xJU^3ex1> ooo,_ UR7;Rpp0>}"$''c |8t1c'Npu"%%CLLV}7ocǎaРAHMMEAAлwoDGGO>QZfff4h`ʔ)޾J?HK &@,ܹsի 55Tq?T{ydee qaza}F;;;ٛ!1tPFF! &p(EGGO !!!Ł|OO?1ZnٳgN:T3аaCbȐ!0474<<xX:tH` m[nU}5RC!??H~%PXVHNN?| 1+ `0!g$B!pVүK:z(377gTn7nT/H-|,77W)Dִi2 l޼y*СCTe9SSSv2b/oVVsrrR={nŦr`qf͚rB=W\afff*oٲEoqϝ:uR{]mVVV*UUw.͝;?۷OuhfiiLXXդ=zp߸w^e*VKHHPKd}^RLLB+W(*=&&լYSev9/P dj_Wqq1jԨ\\\@ `FFFUVllϞ=jCחD"bcc{{`W^5H,BHYv49͍I$_f>www&HX^^Bz^^H$ݝO.((ӊ'**k׎O`Jũ.ttǭ>WWWeD"annneK!,<<\kBYf1 Timmۖ}7V.D"aEEETwKYB!BywT Y1j(4hƨQp L0A!ZZm5 gϞEϞ=QfMEXx1N> 3332...HLLĖ-[УGb ͚5øq lO>v X HXÇ#.._~ǡ bbb0k,lDpttķ~8# 1|ܹsK,AQvmBzªU0|bٲeT011+91c-n]yѣ,--!P~2D"ԩSڵ pm3f v܉> Anp,Zsl޼Ytc|ggg۷_ц]ndҍz\W{׽{mI{{3rHӱ4oޜh >E+>ʊG}@pWo '''4iˆ#O_-CM6Jhh(oNKDDDʐu[n&uzŦM e˖\~ /_h[dgg^U8mܷ\_NJ{^EDD>}:[fݓ5:wlUlٲ-[0w\ʾeDGGey{:s >ǎot:_hԨM4Nnj&7/E^^^DEE^nڵSN̝;7ǶTݻٵkƍtOOO o|IR2tEJ7oġC!)))׸Uһwof̘-[6m믿xgJŅbѣG~ԩ$''su} }5u `9BVVGa֬Ytҥk >shܸ1M4rx9kԨڵkIOOԩSs[iӦq#"""_zHHH'/Ν+5ߡ٩آi׮-D ӿ""""""kK' "RXڵcǎ 0PڵkG _O={6o3}x蜋XINNܹs?gϒٳg9<Ν#99n޼iگk׮yy%.\Ȃ r Gfĉ4l~C4\ 6#G/m9r鵷7۷o'11ÇիW/XAx{{LHHHWWW5jpE9[n^3|:v#ǝڝ:ubΝ=zB IDAT,OgXgg{6wJJMMcƌa̘1NCDDDDDDDLQ8;;SN/_ΪUr3|/^Le̘1ԩS~C~PV-"## g\~FcrUV%22?;vRV-zȑ#/X[[éSxҥ ަ'LFFFs?rW_` 00^xHǝڽ{f޽Cͻ{nT?_!"""""""""""""\Օ̙Ö-[8}48::ҹsgƎK-CE\III!)) .:R$%%Dzzi?kkkԩc*hذ!]tN:ԭ[֭K:ukO<Ν3={6QQQ<#1j(VZbMfff'"""Vll,N㡳qF_Epvv&88qժUcL<9߸|zW^Z_mʕ+ɒ%K8{,uٳ>|QF)"""""""""""""e .D裏2o}P\{ޘ1c3f=[DDDad0ɼyucڴi;!Cn:,X@Ŋ-dffbccc4DDDDDDDDD\?0K^8a|}yp7n0geeEڵM&hР:uHڵK;ĽL޽?Iz^^^DGGuDDDdyzze4DJ6mKhh(oJDDDJJLL ˖-cݺu/_l[VV7oޤB E?55CK/TtٶmקQF7oҡCFȑ# .DDD… f.\ 99T8q{aEffi˛(&5kٸXtl޼icc6EDD ^^^NLS'OOOK!"""@ƍٶm3g$((4AAAhW9u}'N֭[ :D ./0aSLzDFFrtREDDDDDDDDYEll콚ZDDDK~,F]oFJJΝё… dee6B󄣣鵃VVV<ʲ_~ϜvZK """"r_nݚ9s0vXuF׮]ٰaYpqe"##ٷoGܺu ڴimڴ^Yf%ӧX"˖-#22???x7Kl ){Vpѿ{52W^X`q5>ԩST,H֭sV888Pvm }9u7oޤq_%=='|jժ1c x >|8-b޼yo߾kݕxŞ={X`AIO+"""йsgƌSvԩSMR>J:upqqgϞB:u kRhSLݡCgժUdgg3`4iBV,gdffZ: ) 6O<+pqqAlْkZpЪU+SC߾}7/*ʕ+G-Lc#::I&GpsscԩSkHJ"11XWSHbccS}V =u֥N:СCnbYvvrx9r>6mڐŴi,m '''ݙ2e Y8C_ڵkb y׏zY: ?ۛ?J*ۗŋgW0}tBCCͶ>>YYY8+++lmm[nIJJ 4 4+Vi̘1E&'""""ܹsysީO>L6о}\U;v,#FYfm'OQF%?`(;;BBB1c%/G<KK `,h۶`0`0qF}7oތ`?>wq7xwܹ3=z ((?lTV\I۶mqpp0;::b0Xr%< """J?я~'::劔{ pEy:#e˖4hЀ5k;oO>رc4hP"&$$SOeffChh(*Y ϡCxquuёdBBBpppw)ҼׯlܸÇ|rزe iiit1;vlݺ^{(""",=ѿK Rxy>|8ڵ# wyS\߾}Yf ^s/_ptBll,C533'|4cݻ눈H:~8M6-uk@uEDDW2c bbb8{,NNNxyyHŊdŊ| 2Ĵott4^^^׏XӸ^9Ύ~JʕMɼ;[k׮nݺŴiXjO^uFQgo>kLժUyw%((E1uT]o*(={`ڶmˏ?Ȕ)S8w˗/{мyݫ 2_~DDDDаaC,X@@@_~%lbΜ9fwԩCeĉKr5jT'233qqq_$));vЦMb!"""""""""Rf„ ܹ3ϛ -CgժUdgg3`4iBV{""~:ݻw7̙3slBrXp!wg^z$&&Kݺu .wΟ @ZZ-͛,^N:q)>\r%̞=>##8r ~~~E8snm۶xzz믿NBB g)rO?4[|7ǏP~?f1""""""""Rz+WqK/1l0yL¿/:t耣#k֬ɷ $$M4lذ%$$`ccCf8~8-'("""""""""Rx"Ν;Kၓθ3es""03gk׎C?@Ν믉z,[+W0tPn޼7W\aҥԨQla|_5n״iSN8AZZYƴ=44SNѨQ#[nܸΝ;9rHE7oƍpBJL>z-5jDBB 4.|۷7{o,&Mc/^q'1FDDDa_iLL ~~~s)ƍk.f͚ŬYh߾=ǎ{8::ٳٺu+@t8vM4!>>N:b  X`RDFFMc "%%H f&""Y+VUVX[[ӢE .] M=z`ܹΝ;cƌs=WbkѸqc?~<`~ڵkygk׮|G9R s%<<|M_ rf?w/idddgmm֭[f]Nrnj1"""" &6cWI&1qD8|}PDDDʕ+?9s]3f ?8o&5k֤f͚)!!'OҳgO:uĶmۨUV+s.Ο?ԩSyh߾=dƍ9RSS3g/۷_fܹ\z,X8rI|}}ܹ3={d֬YfnݺŢEpwwCtڕaÆn4u缷>x =z`9br˩0ǝک\m`qtt`0rJRSS9"""?РAL?Ƨ%$$SOG- )5\\\LN7>u(=a;7Ǐ.(UcHq ===9xԤIG۷3o<:w޽{ڵ+|n'N(5h Wv"330jԨ 6ʊ>TZlI˖-"b L<gggÇIŊ zܹx"UVͱ8f).֮]˾}hӦٶJ*1g/|||Z*}666,[-[MBBիWvڤдiSׯӧ FaL8/CϚ 0qɌ=___|}}ͶݾoY[1c3gd̙9b^z%^z%{;;;MZj̟??*W'K,ٳԭ[wÇ3j(Sq%EEĚ3h޼9 L2˗s1\\\xwhذ!gΜ!88n:C||!)NUЮڽ:\Rc|x.Åw>8{_Td?|ի6m"((-[ru.\ȋ/_|QzuvvuFFF9nb""RRUƾ}7nښ5kҧOLŏo qM | 4ښ7nj¨P;v7ߤvTP5jK,Օ+RbE\\\gٲeELvLɩH憛ǎc۶m,[siVVVdggs5Tɓ'ssSV-|2j2veSHIQc޼yyDGGc|С :~ 8HkyQYp! .k>|xkKbggǴi;v,C `ݺu,XD`("""RΜ95Glbb"nnnΕwLSWT) [ҠSNy1 ׭[7'9r,9¬YҥK~W1 |礥Ѹqc4ix;?5j`ڵs)BBB|ܹݴiSog3ƈ<ڴi/[oz󢈈MѝEu'ͫիnWWT) TaÆW_q^~em#G4f$&&xtzDjooo Qqz5jŋ[n^3|:v#ǝڝ:ubΝ=z4 GV""""bIEOOFDDDdGgΜaE檮"""""""""""""RX|QZW^TZ7o{Nj/hZ*ʗ/ bҥzmwwwh֬ҰaC|||xwMq'Nnݺ/_|?~<TV [[[:vH```;{MJؽ{wywM*Ux }>DDDDDDDDd5kG%++Su~'n޼ɟN IDAT|``ѦXuELupvv&88qժUcL<9߸Frwss-zE^ 5 ǝڕ+Wӓ%KpY֭ ٳg9|0FRJ+""""""""7oÎ;8r6VbEƏoz"""""""""""""RVYÅ<8qtt$**4S """"""""bvvv,^777W5M4aښbTBDD899Q|y֭K.5N"""""""""""""RV{/p?L6mcǎeȐ![ PbE g'"""K>},Pll,trԬY $yq{{{wNuE\HԦM|}} ୷YHiI-H׵kWW^TR3g{кu]ۛ 6ɓuEK+P'OOOK!"""HTTQQQNCDDDq/|焄bF%î\ȃcٳiH!uܙ1cX: )a'O^zlڴ_ ԩC1b%L""""Rd~-ҩHbccc4ʬ{ݵ\rP"""""""""""""R'QQQNCDDD )ƍ8;;sEj֬ɹsPӃ=+WS]""""""""" Zf /^ŋ]X8=hHxEΝ)iEDDիgDDDDDDDDDD -""ooo-[FDDD.`x¹g.DDDDD,4ml`0[Bׯ/H@Ǜr;.sα|rHNNƆG}g}booo<^!..ӿHAҧOK!"""ILLd۶mԮ]? 6_ocY:|EDDDDDDDDJbϞ=xyy"""RDEEY: 0a;w,p78qSNJ*<8âN8ȑ#vi,##Ǐsq֯_ϒ%KpppwK;tZl @&MhժS1TA=ɭ[s.\IKKɉ>}0sL_>k/_޴։'hڴ) 4_~)RSLaƍ\|f͚1i$ RW2c bbb8{,NNNxyyHŊMqnbڴiZӧOckk :#F(tHYr:\dgg߫EDD$*|Ν;~gŋ?>7n>+yל9svڵcر4hЀ,~G̙O?Dxx8ӧOwܯt wwwLBll,vvv%DEEYDDDfҥ0l0,XҥK 4SϏK.Qn|1cYLbb" ,ҥK|駼̜9Xh[~=J=<ڵ#))4СCyi]~ݻ}os9p[l\r3{lS\FFqqqq '""""""""Rܳ #ܿf͚L87?Z:B;r3gΤzX[[ӺukqwwgϞ=L"##III1aРAXH|}}-,XPN)1cйsgK!e9}4Z駟UVjՊÇ7УGhт{utm|,\ʕ+޽b|Zp/9$ 899fΜi*(hܜ9s]v,ZN8Q믉0uXx17o{dddgKXXq"""""""""eI9K' """"D=Сf֭ SQZZaaaѡC^y.\HFF)}r#/YYY>- ܺuENڵ+Æ cڵfqy_W͚5իG||<۷owңGFZ'OחΝ;ӳgOf͚EzzlSSSYr%m۶4``ʕz^DDDoXK!""rƒh4 -h,Y$&N"TnݺE۶mbҤIԪU7|M6_w^:6 6FƍR /&;;q111XVZammM-L]7/_nR s%<<|M_ '""""""""RE 8~8M6u[LL qqqoߞDDDDJk׮1b~wO?;3#6##ѣGs1XRR=zpʕ+|Mqvv6/^dΜ9|>g("""REEELDDgŹ|r; |ЪU+8u7oa`0жm[z֭[)_<#G,Һb{XXg׮]jժbo߾cooϲe9r$>,o&111f8v~4h޽{>}zԯ_իWw^|{y9nj1"""""""pƤc<|w4jԈ &?c:u0j(vͷ~_~Xqnw!ٳDDDЩS'GeѢE)KTpQL;wu<==@<<C iQQQp%<<<7+̸zڵk?`ѣxzzr…3x`lmm7ydիG LEW\1m/g{q";=f1"""""""p`ӦMdggٴiY\Qlٲ}YgLI&QBؼy3ʕ_,V=G7ς/9}t9deeרQaÆg8?O.]_DDDDDDDDD \y&22h4h)))DFFZ03̙3nl5Glbb"nnnf}ɓ^a4m^x\G-̺ƍB 1o< @DD]zX7o ,`۶m>}ٳghiذ鵽=`t|/^jժ9nj1"""}[~Œ111GLL}JDDDJ#GwѼyszkL޽iڴ)w-Dȋs͛~:4b%kggg^ux":tG)VƂqƑHZZ?qƦ} ׷o_<==9x YYY$''3i$ѣ)aÆXYYGJ˖-iٲ%C7 '""""""""RnbѢEӡCvʰaXvmش4pssC+,\ SLnOkHMMeʕmS#+WzTDDDIJ ƟfO]/NW^-tɬYL]n^شiAAAlْׯpB^|E|wҥ lmm ..@unQA>[Xr98"""RL0:tp&Mĉ 9C)-,Y1c+++ƌū СCM6|4ڴi NNNRR%̙cv,SB<==xg '""""""""Rp>$--f̘ATT).##ѣGl2Ν;Gff&IIIDDD0n8f˖-ѱc:vHZZ[n-y=cfs{sos"ЬY3 Bvv6\rXƒ%KOʕ+̝;c 5Xa:tܮyt:MVp/泭U/_α8fVj);wu<==@<<ۦM+q#"" Odd$))) 846h RRR`f"""b TT)ʕ+s%.\-ٹv%kz\Wη~K:u*lذ,x-iӦ<gʕ\t7nG} WF ͛ӧ?d͚5cժUfFg̘1E>?EQ϶SNܹG'GХKQDDabڭ[Xx1[n%)) 6lȫjVAXd _}.\G^`Ĉ oRSSYr%m۶``ʕ 2Uޛ$"""R\vӧ0qD g$"""""""""T:\7 *Ǽy0`\~,_777 ɓ']ŋ^6cDDDDʂۻΎŋFձI&TBDD899Q|y֭K.50qD֭K(Pߟ7o2gΜ"NPP͚5[[[6l)nxxxPZ5lmmر#w1;v,t֍ڵkS\9lmmiҤ o&}FQOaݻ7*Ub9ٽ{7UT1u O>!11LHHH`ƌDEE:.##ѣGl2Ν;Gff&IIIDDD0n8nݺo>[l!---"ӎ;֭[K=NNNUVw}<[;o{C^h߾=;v/g…,_z^{ 6÷zjۭ(W.g=q#""""PfMƒxG/9{E^rI&1iҤ].uss7y@i߾]kromʕdɒ%={upY>̨QT]JDDDnzj0 dffqFBBBXj^^^[|9ǎŅwy r9p֭O>޽{h޼y\c{?>:KDl67iyt:&p7[ Ocj8cjFgD*ṵgM7"4.ÓN I9iteBd(&?="|Z\}}>ko׊`'?[;VnNv!ĉe]\ɤB޽[&k[pz]܅ 5FJ*88X*,,/,Iz衇 Nj{w}WVU111$*ɤv@s n6ޞqwwץKdZu3F oQݾ}$I$ 0@ 0ac!!N$ׯ޹ש0w#@oЅ?^fYo*++u}iРA$ggg{رc%Iqqq:y{}zd65g{m"%I2l5\G}T?RYYz!???͙3G{vѢEڰaJKKU]]-I Ѻu딓~Nyxxhܸqp,,QZPĉe]H:ɤB޽[&YzR]pA{s… (n.$p|3Zp.\h]ppCE)))*..$Xǎٳnb*..Vllqw}.Irvvnvرcyf)&&Fwj۶mںuf6n(針*jkkUTT}JRk(,,@G`t***J޲Z1*ɤHv`5))IG?8I_f̘+??_ PHHn*WWW-X^۫W/I?8OJF!I*((66jԨֹyhn &ɼ7榄=>}$);;[ׯkf@jE5$,,LNNNԙ3g$I}ѨQMͩPjj?υ IDAT裏>RYYz!???͙3G{vѢEڰaJKKU]]-I Ѻu딓~N֜yxxhܸqmz@sud኎ի%IsέT@ ==]F`X6PGhhBCCozmS<==pB-\Ѻ`ѣ,RRRT\\,___IRqq;ٳgtdX͛ &4S߾}TTT}]YVHVL&"## Nuu 2m2ogyk钤l_^h@_^FdXdZիWKΝ+???S@}4\M:iӦYvhkEШ6k4iR[M nCkOطo_EDD"""`t Nw9rF7e6%Iyyy 78u뮗\IR`` Z"==]ӦMki@3X,YVc^xAoQƵ=dرC>^YYYrss5@7o&L`t T@@ڬ"""صkzfiTVV)SǦN۷+--MS5\)**ĉէOܹٳڹs?PYYY:y._[cƌQtt\FoV6lUUUG}T/ni*++}ow߭{L3gΔredd_^^^qooofeddhٳgS_ZІ֯_tcЦV,10\m(++ӫÇFE7oԱ\IR``&Mdp"Ρo߾2:r竸Xӧ|MԹTxVXѤ53Ϩ>﫢^Y]]9sĉ@rpp$}TPPPytaݻW'Nl=gXdZ@֭0p#裏TQQj;wH=zTzh":vѱ: [[g|Ϟ=pI%I:x?w$)''mݺUӧl٢CiӦM_Z)k^g?޶mN8x@OaÆζ_wAIСCejp/' >\W\QBBQUU%K(<<\&Ixeee1M]wݥ}iѢErvv֕+Wo>yyyiђO>D/{9"ItRs}ǒŋ?$Oש5'IJLL$iJHHЄ gM0At)IR~ӿ:5cXÅv>ZW\uM...햭)TVV)SǦN۷+--M4~x)''G>?… 5k%IΝӂ _ԛ}W|C=TV,**$6xϟ?/Iٳg:ۘN<\IR``&MԞM9!>~l6l6kϞ=u}e6[׸vvwi9r}Q-]TΝsmyy222////f222T^^~@畗W竱zJ_IҞ={`B^y}ӧ"##l2eff6;Q '}ȷ2![n$?l5^дiLW\O+W57[ӶDQQرc%+ɓ'ok&٬9sk,I*,,mV:ULSLM:UeeeJKK30vk?6h)eXgOSL~猍UPP*..NGkN:%Iׯ__77j(}ܘ1c$IK.UPP"##~ɓMZ'22RViif͚@EEEiȐ!jm0=䓒3fWdd[U ,3bI a5jTޮ^7*,,L?F֮]VVV_Whh~a=z7T]]miwxT^^ ٕ[fY*//o;!whs{5`W6LW_Ο?/]6gƻ888h„ ~h\a/*88XnnnrqqOSZJf͒$رI9\\\i&W^rqqѸqM-YsѢEJMMbd<==5fm޼Ύ!!!rwwWNNN 999иqt?***RMM*++uq7jUWWkΜ9ڲeΞ=(55UիW]>Pee Reeͩv'5T[[k޵O껞Sݗfv6vиYfٛ׳gO%&&:lg=z(66V6u`׫… p6bQJJ+I*..ֱc4{l7:$ܹSl6F{QRRvءiӦImۦ'Nŋ5p@*11QVvv&LwAIСCe_gΜQrrrƇ ӧo8_~~~GJX޽%I.\wmVLTT{$]p9ۘ:EC׎\R+WWO')66V7_N7ңGY,Xb;vLgϖ{si*..Vllqw}.Irvv׍;V7oV\\bbbt}mۦ[l6kƍ~ةVEEE۷$i*--UaaaƊB{ tvpEEE[V>fZe2i`2܎$3ѣ+..Ng1c|EFF* @!!!ںu\]]`{m^$Iaaaz'%I#F$`5jT!/.ZM VIIJJJ˻m%,,LK.Ր!C" 8P3fuJMMbd<==5fm޼-U]]-I rrreɑƍ7 mիWKΝ[it4QVV,1FBCCz:OOO-\P .l.88Xuz!ŢWT\\cǎirwwoM@AOX,|HܶhP@@1EEEwߕjULL$jd2)22t:h@͛7O3:@a6dޖ>Lܔ^ӧO$eggkruum,VhlÇWttV^-I;w N 4KzzjjjZIVV,1nE[X,eYEVVV[M c .&""BFiI&0ȼy4o|X555X,ڼy$)//pTnn$)00P&M28.ܖ jӦMrss3:6>~|94ѣJNN֎;T[[ɓ'kРA6l]pd2j*EEEpϗ$>|\cC]JK,QxxL&$),,LʢYm`;S-[h}fYf\bsppK{Ek4iʔ)SLiii&'vtm#&8fٳεfbcc]s׎ێ;-_\#Gԣ>Kܹsu-//WFFelVFFoU ^yJJKK$///-^Eڽ{._/jϞ=zgtE{|Jջ>((Hڻwon :.vWkmz˗BK.իWl2UTT(!!A-ZZ~:TٳڶmCֻ6f@gg64Psōbj?Sg?Yswxs꣏>Rtt$ԩS~ջuj+c ;w^?~\ Я~[/00a$I_}}~ez1[ tepධl2륗^2: ~ V֪[n99uիW%Iz.mkllc@WTTW^yEݻw7: Ͳa>}ZΜ9:mӧOp:G$s=޽{K.\Pzۘ2.tyUttQh#G(==]Æ Sjj}v9r^ӫW/IҮ]TUU3g())s\Rr劎=DIRHHfzl5ЕpK;zVbt\ rww׊+d?^l%Icƌ$[NFҤItwp޻K3fP``}Yɓ5#F$Ի66jԨֺUhХl6+""(H^^Z|}RRkG㣘wX`uwEAAAZd ]vF)'''yyy駟ƍ4%]999ɑƍ{V}O'|nݺ1[rV\Y'O6uT)--d$ܹsZ~8* 8PO?/^,IuvWTJJۧoFw}{1͜9S...T}Svhr$[n5e]Iz6mڤ{D8pƏ &4WcyK_Ν;;Su}3ߍ}i&-ZH ƍe;ڵkݻ?ϟ/777M}o˕!yyyٯlVFFO={{].vTQQ3gꫯ4dٺp$W*..NR>@ w.((H޽{5qfh1ժJ>>>JMMաCiӦ?m68qB<駟*33SÆ Ç-yyy7-﯐ݻWzg[$ܹSСCO+Iڱc-VSO=Dyzzj˖-zg#Sff4$ 0@Ԋ+ӯ_?ܹS3<#I裏]JLLŋoi]OOO*%%EotE]^ck5gk :TlDC m9m޽%I.\w6f` .th_~x -]TF7Nj*ʕ+:~VZUvر8ы/X>ZDsKWsȵlcp2:4&..NӬY@6m}oӌ31c>c+229WWW-X~ܫW/?^aaa2Lڽ{wsiٲe={4qDs=^7**JJJJ@g~̘1ڹs֭[uIx;vO[jǎڱcGyi漷#FUPPPuIҨQZ=#c ֑#GUVyB6]^^)egg0΍ۍ7*%%Ev풯$X&Lٳ5sL"uMVUcn'pú{??4[ѣGl6ŋuU} z!FEEۻNjdRdddfP?~[IIIJJJsW^5kAp޺)!!A?O.In4 Ĩo߾z_Z?k̙2:"Z%իWKΝ+??4\AE(he-}otLFi&9s( ti555zw!͟?_yyyFtp4\0m6c#GW|}}siƍ*--դIM+Vڵk>c;VC }JOOo :xW>Q]ѣuaӄ ZuN:#!phEEEO EDD]ѣG}vرCFt$ѱlphдiӌ ]K/$M8(?ז-[7*44]_~uMڛjb1:Ц@6PVo>۷OWn݌Ξ=۷k˖-:~YfiذaX,Z @[.իz饗__=zq/O<==7|SAAA|h"}Fp """.Z!!!4: Tݵ}v{FGFhhڴiFp jkk.UV2:ׯycthfYqsmSNi;z͕$jҤI<'.lOzz: SՕ)33SO=|}}fx5j\ѣJNN֎;T[[ɓ'kРA6lX+%[Cw}lm۶Mw{VtMM[JK,QxxL&$),,Lʒ-jkku͞=[ޚ:u;믿O'O˺5fEGGݽ5lMtjÆ ڿ裏j ^5m"~Jh߾}otw̙3b\}[fY>}zԗػþ(F9Jnf 㒙i)R ).c9)j.a4f[&$~#e9wp$-""""rW(BDD6Xx1qqq:u*WLF ӾVҒyؾ};'Oh4R|y֭?O߾}-6`̝;wy/{λqŝE|."""־}{<Oi;x gϞQFTV3g2|,_+W0a„"͙Fhh(gϞ5mذk׮Y9F#'))\믿`OyƍIOOjæMx嗋t""""""""""""""w_;w:ЬY3͛Grr2FΜ9ʕ+ر#/W\uÇӰaCfϞ͑#G ''Oxx8?8+WƥKlܩDy233mׯsƍ띜l)`ݺuׯk׮|9xvٳرcG[d gϞZj,^]v1gRRReμ'g$$$.]JRR,[~h~iÚ5kWU 2S[R…H>~74iBll,<>|tHNNfĉ-[өS'mcܹDFF{z*lٲ^z+WУGM]w*77f8"#Go֡ڻw/o&<ñ9???[lI ؼy3ׯ_ ;;͛7Sre7oٻw/'66O>)Sp"ǵm6F4hЀw}ת̹yf"""]6ԬY{_B ? c=f5N5,ڈؒpqZhU(_E}:uK.4k֌Yf1h E`X|9:uX"[uߟٳg3i$VZePEm۶uֱyf 8}4K.eɒ%$''Sn] BݺumΝ;pBvAVعs'/_&,, {{{ ‘#G(NR3gx'-ʟyw2ӧ~7k… -[֪FDDDDDDDDDDDDDĖt…N:EXXիW///zIRRR mn@nݨTNNNԪU#GZuǎRR%bbb->npdzEժUqrrjժݛB n1UVINسgORRR߿?8;;S|y}YƏϥKS S3Ѯ]B5j޺ԩS<쳸/ZUD=\2NNN4hЀk8WIdJs}EJCnn.#Fm۶u8""RL jՊo|~ȑ#5 ///[hs _K77OK.~SNȑ#TV`ƍGttt2%Jܚ0`;v y2lgg?Mʔ)"""""""""""""p!""6c:vȕ+We,[V\I5ŋy71?~)Si&v؁;K.TPs6lhQn:uFFFXx1_}V*p?@˖- 33kײqFmF&MmK֭IKK3FYhwJ*ۭJ~իW,^Ǯ^zޛWhh(0l޼Ν;[<йsg>}UiHiꫯHHH`߾}EDDJ͍+CpttuHGyƍObb"۷oET\f,XOOO0&&MܪZj?~o.OLLj{'szyyq)֮]{+˗XE˗mDDDDDDDDDDDDDDlM'\M]p]Ɛ!CHII!##]vѧOF#AAArss"""3 GNNĉFoߎ7`ʔ)qqq Eggg6m,55ݻAΝILL$++D:w̵kxWHMMwPyv۶mVZF&MdvȐ!ѷo_RRR0uVׯɓ'?~|ŖWI7_~iӆ3gč7 ϴַ^痔¡C|2ӦM@K 2?Çiժ (Ҝ)Zׯ3j(xm@Yb;wVE)yɡwFvo]va4INN&<<\70۷ ""Çy s9MO>}\ѣINN&'''Gmԩ<)V2S[R…ٳ9wF⣏>7nܹs>>+W-[2j(;I+<<___<<ѢZjV'lcOpp0G5'''i`ԩCjj*aaa4nܘ^zQn]EtE`` /"p5jpAiԨڵcɒ%0d8=)f͚DDDDDDDDDDDDDD&%\M[A[ߧO6mT/_GdggӮ];&OlQehϏ\N8a.L2ŸK7ow[;;;F@lllZ6`&jժСC_u>>>rR-``ԩEbѮZjnݚ_|;Tj坻4ҥKٳ={IOOŅ8}BcڵkXoI/\`?TRqn|(ZYDDDo7n`۶m,Y,-& IDAT:t@Ϟ=ԩζԳgO򽖒5kgժUΞ=K.]ׯ9y[DD=@TT[EDDDDDDDDDDJt…ԭݼy󈎎lٲ^:ߍW^~b7EjmqP`FٲeIoAzMLL 40/,Z_~sR-;]ʗ/O۶m '66TCff&cƌ)qrrOo߾|x>{"""b{YYYֻSn{= 8H"##?iqr)BDDl}n`0СCaÆqA /tϟoU`F沆 ҦMRSSyWIOO/p3gÆ 3kSr >7n 22֭[{QԨQÇJ3_޽?~<,tɓ'дiSrSr˭K/ӦM#77ע.77DI^~ E_eADO+77;vЯ_?T޽{mH1m4MF=l%\M 8rOٳglݺ~k@DDϚ5k1cO>$m7&0ggg.\ȨQHOO3faJ03g=2c ?Nvv6$&&AxxӜe˖e֭h$))@mۆ ghڔԭ[76o̵k&11 [I/$$Ο?OÆ y7ʕ+￳~z2e ?b2eCvv6FcqۗUi&uƑ#G~:IIIڵ(ZϞHQtEo ;Ƹqxi޼9vbر"""""""""""""""rg YDD,[={u^ׯk׮ddd;g}ƀ~~~۷1k'4iw}Eybb"8=GJXv-ݺu#++˪ٙ˗ӥKrSHA?2>lJWgs<> VZ;{n޽;{'uhZ={ ***k?@TTE^ :?Jjptt\riӆM6Ym?vX 矧|899Qn]&N֭[-|}}9ts̡}xyy舋 kצ_~8p d _|xyys %]xw_>888ó,-J;'7ndÆ kԩS777QF?GZ%[,\>h\)xyLJ|zQW=Z.\`PDDY&ׯL6M"""""""""""""""wN(%CRִiSz-[رcx{{Svm~g[sG?Ϸ`6 :ŋA0aϟ'k׮ɉN:1}t<<<,3p@qvv^`ԩxyy87իW4iќ={///zɘ1c,4ΒuAΝ;quuƍrJsrssq_~%'O ___x Se+;bٲet͖a5+V """""""""""Xbv?+Vh0D>FFFG7o>MA1gĉY&}{n&U|ٱpB-2[t)qqq߿OO|$ s٩S`Ϟ=lܸ;;˃JgI 1:rH>Chd޽+W0tBY>}(z-mQF\~a5ݺuzCDDDDDDDDDD侠R7 ׿`ԩSWWW`>!>䄿9 &&9/xqpp~2.]Zjqdӧ3sLsrŅ  2eG駟r9 @nn.'N(ȟANNׯ'88~SR%bbb8{-DDDDDDDDDDDDDDD:.yWlSӦMR) p!Mƍ9y$ׯ_iӦ <ƍ:L)e>ѣGIDD""W_}֭[SV-["EШQ#%[_Znݨ^ %\EQQQ: )={: صk۷P^ԨQ'OcJ:WzXvm$$$ʕ+2e CU…<233qqq)\r+WF$w⭷i""""""""""""""Qv@DDDD",,~ׯcsbA= K/&55Çc0hժUw72 =z\VV- 3g$--zz ybȃƍ|PR%m!p!""""rTRƍР9UVsN}Y:777MVjݍ*UĹsS='Owޡ 8ZoѢ݃ȽŋYlOϏɓ'SJ[&"""""""""""""""wH "T~vMVVё3f7;vdĈv?""w͛ywٷoիWgϞԭ[֡H)Q…Xcq)rrr\25"$$^x!R3!!y}vNc>b-nyiս{wwn.44B9;;O0uTZlCR 1;w{&66֪̙39s+WҡC <<>S`^~e6aaaKi'RAAA: RѼys7on0DDDDDDDDDDDDDDD.R…oyG:t(?<߰ĉXS~z:uĖ-[ppG(ΝKdd$1@j֬ .]dƌ|у;wgEJU\\l܁h[ uD6ml]q1-[ƈ#prru8"""""""""""""""b#v@!!!?~-Zpa~mׯ+NNNԩSpvI5k~`|,_qO=+VuЯ_?_ΤIlH;}4+Vu"R+Vϯeff2`V\iPDDJՅ ={6<̚5[%"""""""""""""""6 lԩSlذ~ *ЪU+FM ێkq)Sضm/_Zjt֍pʔ)cncbccT111/_9|}}߿?seРAL<-[SR%ڴiѣy \ˈ#صk...4k֌p6lh/%%iӦe~\]]U:ubCqc+LqϴI]v;j(fϞͶm,Ikzgff2sLk>˗)_<5bl2wMSP}I/dĉ=z*Uжm[F~oɓLVXvm1ݣ73رwwwǜ9s3f &Lfeew^6l|ݺut֍ >Zݻiٲ%NNNl۶&MK֭IKK75j{nTR*#RUǏgلw~ [\hٲ%/5kرcۦ8 %Y83gdV}\]]Yjm۶5~oUV|w,_ƴ8***k)>mgkypYL.^HZ4hP~oEFF2bZh믿/Lٲem'l_ͅ ڵ+iii 2222صk}h4įj/77+""\?o<'N_~пN8hdx{{sLbn /P{pvviӦtޝ :wLbb"YYY$&&ҹsg]+Bjjjcǵkضmjh42i$C !--}h$==[R~}N|8vvvlٲCrhٲ%FD˜N_ypuuٙFpbY]y6nH prr^z\֭[sfϞmn{/TZT]_?sY}5lؐ6m0|pz|yСC:tgHDΜ9ç~ѣu8""E)yUXիߣhDDDDDDDDDDDDDDDA{lݺu 4(>}iӦǸ|2=z ;;v1yd-[0p@~~~r siJ2eq'6o ;#Ύ#FoVe 6ʕ+ժUCDEE/|||ٳ[^%]?ԩS9x Æ իW\rMDDDDDDDDDDDDDDDPJn=͛Gtt4e˖exzzZz*P+LEjmqmJ(h#KٲevZ...EwXl_Xh/2[^w~y/_mNll,ӇLƌSyKjժEZԩ&MbENwM2%TnQ~'V[a`ggGժUͧw}΅ r9wn.ѣΝc…w}~ٱdmͽ'IWu]'|~5kFJJ 񄅅7(3p%Frrr9rc0Xt)/tS4k 7ѣGXņ;S˗s%K/ľ}8u , 44ڵkp1ݕJ~eʔ`0p̙B.[,6e˖ѽ{w?Nݺu3f g=zHc$Mh/ΚUԴ.˗7Í75k4i҄͛ªUm&kM0}~ZjE4c=X7olْƍӱcG"##Ο?τ hݺ5[a˗/;+W6?|;:HDDDё&MGJJ jղuX"""""""""""""""'{tΝ;/33ݻԩSmMK4M3g,fޯ+W`0ܳgO}Lo/M/իW'$$?vZmvWb+^  IDAT=Sv;NNN "y5kGa„ ҩS'NnܸQxng! ǎmI^?Sgk.+O?yqi_Nzz:bҤIDEEi>4œvXԩ3e˖I&̛7Ϣڷoٳy,SJڷo8ߦh :t(6l$00B`Vu jԨa.kذ!mڴ!55W_}5ߍ&gΜ!22;;; f.o׮SN~ "##hݺuE50 >|ت.ʥ[I֯w?:ɓhڴEyKy^룏>2Q<==T,Xo]"##N6?E%_󈉉`ڵ۷3rHK,j֬I||<&L(tl9ĉhҤ/`ȑdzsNfϞ ;,YٳgV/f׮]̙3WUFDDDJߠAOIKKŋL8E_ΥK$m9`Yp!F~#==|) dΜ9MX7nkFRR۷w^ׯOll,| SLn9t˨Qё Z?cYՙLmDDDٳ~ sb'99tƍ`>-uKVVԨQ'O2mڴ6m 4jԈpu>LӦMٲeK %$sթSGn>tr_ޤ\9sذaYYY>}z"s1}tWԩSE+"""""""""""""""R Z^J`0e\~u^ׯk׮>ck???w. ӧp-}...剉u{{{Fm77t֍,:ggg/_N.],M#} r 7ȑ#Z*K3ŋ޽;6mʷϧk׮&̝;|3f ˗/'%%_~5kFff՘s1wkbzK.%88تOJذa 40~o9ٳ'ps#~R|˖-gϞ~I$$$$i&ƏoYvqq!44 {lΝV `ooO xg۷?`Ȑ!~Mc4\N-6nܘ|3Ȯ] {L"wEQEDnh7Ζ?O>$׮]3'/ʕټy3m۶^z$%%;SO=9~8};7-Z0~qd}?<=vvv{?>(gϞm۶k׎.]Pf–,֬YҥKٙ]ӢE c:t?Hhh(ժUёrѦM6mdYرcŞ#44Dݺu8q"[nJСC̙3兣#...Ԯ]~q|-^|EOpp0^^^888믿΁JÃ]vR~}\]]qppۛ~X!4c+xzzqF6lkF:upss5j9zU… i߾=888裏зo_/^O?3oa„ 3Yfi<<>>N:鉓5oo߾&&={$::ȣ>ʀ駟,-~`UXy۶meر4gǎIMMeƍTXbӮ];Xvmb)M:O>aȑ=zjժ:RU 7댌 v)d[{ 9NsŋZ?$"";oFNN5jɓZ})3r.OOOBBBcϞ=1/2tTRE"""""""""""""""rQ…<0z|@iҤ GAvS'yK=-[0vX ѣdSѸqczEݺu|HLL35k֬oEDJdd$YYY9֡|8VZZ|wc.Sk߃jժ``̙\pӧOuV2AVDDDDDDDDDDDDDDDA $$$o]`` cǎnݺ899DZݻ7Ǐ76lUVX1>mꊓ>>>L<0K3g]v'''ڴicNɫ]vc;vPLڴiS""׹s9r$*Tu8"*Uиqch޼9OϏAgȑԭ[;w舗1mڴRnUR%)\4yHŊڵ+{<*"""""""""""""""[ ""R:uSNi۶-m۶(+(vu)[,֙.L9r՛oݝ ϟٳgZ*gϞ_~+N4ieʔ᭷޲u("իWӫW/6mDʕ b̘1ا\rܹ &_sY<<>>fGR(0ɵkԬY3(P@?.^֭[hѢZn2'N\rn˗/׮]$Ij߾}FF<ŋS0~L7;t#GjJ=zP*U̎eQpa#GH*U0]re2e&m&՚}͞=[˖-aСʖ-իgxNԘ4igϮwy(I@@̎幛QXmQm۶Lh9Rm۶|||ԦM1B ~w͞=[Æ S|̎ ۧI&@EX,… %IsΕbҥK%If͒b13&k׮cǎN:ڵk 613ԴiSY,]zթ߅ ;8t,^ztϝ˗kРA4h/_njik̘1*Vov)kɪP,,X7o G#?| W;vԺuEDDhڴij޼|}}բE M>]l:uJ 4h':9sM6S^xSWvX׽~*SJ;mGDDhҥzgUhQ{bŊ f"""8+V$;vi^hh$SsN>GuX8ٳgkȐ!~aEEEiqT .:w0}7rf\DGGkȑj۶6mhĈsܹsرcmׯrέUVʕ+5k|}}͎ȠG$U\tz[`/UXQV+H.\3gΨx RXXSN EVdIZJ;wT^$I[l_j$i֬Y {I-[&I' Pz)ܹScǎMNl۶7WPi6WorZ$6ETT8\rgϞʕ+٣X~f\ڵkرSNv횂M m̙S F˗/kjժ},d2&{ӛC Q%+W. >\VU|󍽟PbZgϮjժiذa͛7;{С*QoOϕ+$)$$D˗/?mjj͚5ߥKT=lvĉ-(WRH{ .bQ=#ŢM69lR>LDDM͛W-Z鰜c_okرjР4b~lS;wvʕ+|}}P䕐Ǐ;'>^$irwwΝ;[Qjժ\ri…:s挊/ )00P>>>:uϟﴽzJ;wرc%I-ҥKOh ܹsuIem;vvos='777 [IYd$Ν;?/$رiP͜9Saaa ._)c 3 C;wT~㏫SN״i7@&GEZ;w$rʲZ5k$FZOzz%INJ$nM{{{jS˖- { S>CJnݲڷ+ $ݼyi@Fw{m\wVxxZj{^z-[HU*{V &IڼyS.]8wmݺU4l0UTIٳgW5;-k+NPovʖ-"EH~Q|y]|YgϞu<۷o޽{UbE_^|&O,ItZޓgϮUGI:q$dɒNF3o<=WXXF .hݺuܹs(`+Wr:#ް>uT]zU1112e,^y{uJ&LC)..Nĉ%I?|ݪU+Y,}׊O?eJr=:ƅ ΂ JV^h9sF&MJ~vr$IϟwZ@hذ7n ٳ}]/Hd IDAT$ҥKjժ9g$]|i[/JVrww [!C?۴mѣGO*_nݺ[n?~-[3gHraz%Iׯ_$͛iy[!66B)djٲw͛7… իC%J8%G5aM4ɩ(F ,ׯM6ڵk]gݺuj*}%I~~~N߮si۶m:rCN05(4m WTVM׵uVծ][ 3;w˕+J*h|Xe˖իʿRp$SUX1BBB.] xt.\X5jPLLʕ+EJJ,?JHHPF޽J(K.W^U^te(QBzvtb/H۷|}}ս{w/_e[ݻ%[⊭`dϞ=ݏdԨQS.]o?~ܡBԣGO>z*V:2֖vf̘w}ס @ܺuK!!!jԨJ()S_?N8AC.2)Œ.Ml_Μ95zh[ڵ$i͚59srar:qTƍeZҥKU^=y*88X_|nݪ[ V ԧO˗ル<==5w\͚5K?nݺ_|QÇ׆ WT\W!C(..N;vP||~iuEgϞܹsl2mN>]|vڥB iӦݻ<==}4i?X;vPncɓG5JV>7nͫ3; PݻV\f͚iٲejѢ̎ favBBBr:(ȼ/^}IggΜQ 4k,8Cٳʛ7{1 1E&EaDB̺w^!!!:tեKH'T^̎doNrĊ'|2 3q7;r̩ѣGk͚5r劮\5khʑ#$Ø1cO?Ãj )o_֭[|JHH0;2!v5kԀ4uTI믿5j @rxf20-\P˗/͛7U~}?^ rw}pD)՚i0Ϝ9s4}t|X˗Wniv4H4dŋkźqׯw}W Ird$wm69rڶm+oooM61b38%@O>DCU͎ٳUB駟~СCu9mڴIݺu &X,X,fHWv:vhoԩ]`d-ӶmoHG 6jձc'0;`K3gThhUtiuMÆ $YV{ߨ(jӦM?c饗^R޽)IE#V"""tR=*ZObdXtRuUy͈jԨav Fiv t-[֭SxxܹǏۋ-k|ܹ+W(((Hoֆ %???y~~~ƍlZh14C#($$D/^s:@aW_رc\/^ݻwk^٣5kHrjڧw)IPӺmm>k׮iƌ^V>E@VAEX,-[HU*{QFwӦM &lٲʖ-z)=Znݺ$u IRɒ%*UʡZb7oŋk̘1Umۦ_~E3 ?^38רQé $I-[tSN%ׯK4f kݺ6mڤ&Mh…jݺriv,@{nߣڵkDf j:LF=#0v>Lrmrkks]+>>^ٲe3;`7qD(̙3xbcLcGO<ӧO_~QZS_ooo?^k׮wUpa]zU7oT…ݼy6oެ~i*TqIR͎x($$F@f<z5i98>|X'Nt[^=Iuq'|"Ţ~ rʕ$?iݶ6[a6lʔ)Cҝaھ}z^x8@Ν;~йsԣG$C[nաCԥKy9r զMy{{kڵzm69rD5jpXȑ#>mv !jϞ=ZfGCZ`-ZsΩf͚ѣN}> ޽^SڵUD cp_\̌r̩sjھ}"##SOK.5j<==}ɣ ͛7O[lѵkה;wnըQCW2e}O?TW^Ull$I&㏵cu!ǎ;'OWBBF6m8iaѢE;wv)ooouY]vU*U̎x͜9S/6; ( B iԨQmW\$=cӠA4hР$ٸqc5nء-w P``.]ŋK.]_~ʕ+׃Ceٲe:z,Ybv iyl|d f Ȃ:t Ţu7ٳgi&Cy1;k׮Yfz5o<?~\ъŋrJhB͛7Wxxq!CVZ/믿*::Z￵sN1BO?V\l߾}u R#qu-t]uٖ{X= i\@Իwo4rH(QH$zϟW͚5;wn%1=&w/+*{*R:t#Gw̿ߵУGIR```io럚s6}${bˑSR YؔiM!!! $XDRD] $)$$HUViѢEڼy{1uQ3f̐;u׽O;vP-4/GZr5kОԃْ3gNEEE٧,XW_}UN}yرCs$Osȑ#5v$#Io޽{UV-yzzۿ{kNNΝ[WVÆ ]׮]Tn]8֭[Fܻw4hJ*]vOl:5t:s挾 enW:a(""Bu]~͚5jѢKҺuԶm[qĉzl4cyfIRtTg{ؖ2eK_ucοߵpmy{{ƍ:}|I~gΜQ2eT`A]rEo~sc`ĮA[w̄'w}O͎gϞV޽;wnZJ/^'|Bնm[EDDh:y򤢣޽{+66Vrra8}&L`?os;vL InvM4IWJ(h׮]S Ї~SrO(::Z:tum8p@5Jt;ɓ:|n޼iӦ=PH1۰a]v)&&FGU˖-umuEg3IԱcG  aԩ<==OF]3;6zpu %Hi>|dO@@;0 @zz+W.㣏>2; LҾ}{f͚ƬY8d{'L`H2r=LrMXXaH2 0oС$CNݻאd*Uޖ;wnCq$aÆzz/6͛7 8ȑÐd?N9rĐdY%$$C 1'O_4}㧟~2\U0%K}9;y!ȕ+WKq7n!h֬2w1ԩ2f/6M=%*WlDEE9̻;vlr$7+w6$8x$c޽a<_Y̽ϽW= @fRQHH˷'}`?P믿nvdZz7TP!i}7x޽{K6nܘ:n޼N:)..NM4ĉ㏒8-kXdΞ=ko$ɓ'{hӦMC:tw}W~z˻RZ$IO<$Yf nWR%K.Yiʔ):tʕ+Mڶm&N \rھ}}s؜;wNaFN[h IDAT|II;}РANxxx؏cZg|R%9RrL6rH̙ӡ˖9=ժUK*Tŋay6lХKTreY,I~nǬk^zpu yx]pAsմiӜC||N:r%'Gرc$oo$#ݸqC{ў={sNm߾]OV&M5k7Μ9GjϞ=ڽ{<(I2 #eRrWIJ4odؤuĤ-}YIS)%w{Oz饗큁܏Y̽ +0>}iȑ#ꫯh"/:LRoONyiʛ7[,XЩOddXQLYV?~\kN2qqqߟh#""$)тyJnݺr~J^УGϟ_Ǐ%Iϟtڸqz4v=w+P7nƍs4Hȑ#nݺdmkٲe6lΜ9wKqGW…]A2ʕ+MOj_,clϗ/}/[׮]5|pYF)?Sk׮tAu$#W YO<,}ĉ[| Ai mL6MӦMSllԩS ֨Q={,T%KW_Վ;( ޔ '>>i_UoW_bŊIn#x? 6$o… ;)@VjTV\)04zh=Sڵ|My{{W*UuV={VcǎUҥ͎CL29EĨcǎҘ1cԲeDȑdcǎ?&|7 6rݻw;K+%JPϞ='Nhڵ[KԜjժIݏ&qqqZ~(I3g~W;VzR˖-F8IHHHv |vrmȘ,i߾}.mmׁ&QQQN˜>}C`IRppCMz_?1@fC#jjCm69rڶm+oooM61b38%@]rEjժO Ҕ)Sav<___}嗺r劂/2iiӦ}C˗/5k>x`:twԫWO4ȑ#rssSRmjRFtUu6/^ԩSۛ4i"I2eC :u$AIfORJMGuԖRsl3FJr'N$;ۊ[)IrBӧNKjJ4i$#؎#cjiӦ9kɒ ,(u@PPPIZ[VT@>|X!!!:z *TOF_3?,,12`]vf Iԩ]f@f3gΨy jU94{l-Y0dm;wVnր?~ܹSM6՞={-[_~ڵ<<<4a2k֬ѧ~Uj…#KkK6lEEEiYsUre}:s#G裏>b_#F8^S޼yeiFǎSll;6mh֭ʕ+x>:u$kN6mҭ[#G( @ #:[j_Ϟ=秿[jқoP+>>^֭[6mhИ1ck}ͪUXI![(>>^GUnfSsk֎;Լys/վ}DG1H֭͒[:㊍աCԢE 'T^ۊjP4h>Dصp9rC~[ҿ*6}d5ر4! It:15k4j֬x?7^yx=z|rgq-c̙/l-Z0>SN9a?1`k?o 0# iRjǏ;7FŊ OOOw1")QQQˍѣGVJ~?9s4$|gk֬h߻?w 4\SO?6ef5~Yr˿Ź~7o4*ThŋΝKl)=aׯ_77n|s/_>cŊN׭[;wn9r|~| 0Œ … ].^S%ڗ/_n,X)KbŌC9-w^#G.}صg޽N}ܸjk&c'[<_={͛ .Ν;Ç5~x_ll ˺s玮\ I*I6lPTTˡ7H'VΜ9UL֭[_~ɓ5m4)RD/_6)0 }'OraaaׯթS'߿ _ftAKOWŊ3gNyxxL2zwt,Y2ݲIRaڵʕ+\r]Sڵ5foj۶jڴr-?$G ,P|aÆ4vX9R4gΜD_~Q>}DZB Zxvrʘ,]vںu,<<Ӊj{ĶYn]EFFj֬YX,s֭[I&Gk֬IQ }g\ ҥK0a<#Fj;vhܹQC}_~z4}=PxtYCNCՏ?ޝ՜iggBb0ֆ,e1Ѡ ɾ `B#;ed-Q4Ȯ"i{n>9|L{˗/ЫW/9tZKlllۛۧYݻlܸ7r- D߾}122ʥB!B/|>/ė)_|ESė'+B!Bmv_M+`h:  իWN6mݻ*09s(_<ժUcٓō7VRJ!Bիԭ[Wxdd$]vÇ\xQ-TdO@@st.FKiܸqn*B!B!B!B!tоcǒ?~Xd }Ņ,ջ{.VVV)_$4y9EU+SSB!ˮ]}HH͚5JEsqqYfTZf͚ſKRRR֮]Yx$[!B!B!B!B!Iu/^Tyw<7XZZrAׯO\\Vk׮ڵ+իWi+Q<uB!򪨨(߿Oڵ|}}iѢ_}TZU~^O<֖ӧOf f}SSSׯώ;h׮].E)D湹?j; ![[[m!B!B!B!y$\|4%yXYYaeenی344d?>zXZZ+\0[*T@DD >B B!6g:wh>~~~4k .peybF~PBsVX!<]!!?CB!B!B!B+&&&lٲEa!B!#KHHݸB!B!y$\&Nȋ/r9b3*O^B!Df,[ӧOk; !?L˖-B!B.IB!j; !(;;;m Ǐcoo߿ٵk]ve֭j!OW\\+μ{N˗?3|Ǝ˜9sϱ cʮ˧@K!sαclllBdǎSa!B!" B!ĉ $#ٽ{Bd;vB!BΝ;̞=C#J,Ivpttvj3Z`.^9qQQQcccԩS)RL2xxx%[|nݺ̛7#F?%\0w\y eʔcǎ8::bbbu%''s&Mٳg)PL:&M eѢEx{{s= ,Hձ_~x --ׯ_g͚5)R///V7niҤ ˖-c̘1TVMi<)$''~z/BCC)^8ڵcڴiԭ[7ݾr"3xm ]vݻWYhoߦJ*vvv$$$0{l֭[ǣG^:'NdРAjĿKPPQQQ/^MoѦMgD!B!B!B!2G.>m 6m05< @a!ȣv>>>|wDGG+EFF憇v+X7nd$%%)3|9 `˖-1%Kۗ=uQKؿ?666yFylܸ;w{n:tΝM6'ݻÇs 5k{ڷoOLLX||wwwm Bŋ7Na!ȣ$"y9{&&&q1j(*VH@@֭[[[_Ν˔)SXvIJJbĈL4 *燃̟?3fpiu떡kȟ?dH˛7oѣgĄP~ 88Jt҅_ r̭[5kW7n111 :'Rre9wG&88?UVHl;q={*ӇիW흭 MXfʔ)tR4h[ٳgvvvRL\%{CCCz**URIQXr%ׯ۷ <}ѯ_?pqqƆ/^0tP>̲eT.͛˗SWQFq8{,gV&\|B!_ɓJZx-GU9 N:ũSر#'Z37b oNrr2}Ԕ h;4!B!B Dakk"WݺuK!!C# yyիy1̚5Kysss-ʒ%KXrJyJ~~~o*˜y-}Iy‚[߲yfeŝ;w믳|]˗/իWmtttStЁǏb ժU+X[n?CvU IDAT{%,X@קM6Sn]<== ٍCYԮ]; ԯ_7of]vԪUcǎ)wL]6r6mZĒc>L@}:u*uP|Ŋ*Ο?}s,Y A(R7jի*POݴi5Roڴ)6mĄt[!BH.8yd1{l=J"E2dڵ\rDEE燳3^^^+Dy-͛7L6޽{+ܭ:u*;vPQ!B!B$#ٳ!D?j;!B!Rةa̘1ˇ ’%K8rHׯtԉs窔{{{0j(fffjyx?Q<8qr&MXbi;%ŤM-|,ӦMS߿?.2+uiBgSSS[Zj}vZ*B!_333 M 8pPJeYɒ%ڵ+ۗлwoի?c<֯_?n5B!B!Cpبb(ȻvޭB!HUpp0r5(vEdذaܾ}5juVtuuU€퐐CCC^|ILLLRƫHHIq\[J5jP;Vpa޽{r|ʔ) 4grY}F޽;}9ۇr *īW2=/_Y%|Z,)gғHIMRT~Gq)}} ;w -B{.z=z<<<066ѣر(]4mڴaԨQ*THc=={ʕ+9y$o޼]vL2EcT_S_[///TdIXl2B3g7o^^^вeK~gJ.R/e'&&5kp1={Fҥi׮Æ Syhm6/^L`` C;V;֬YÑ#Gx!T^=zгgO~mƷ~Kٲeǿ+ضmhѢH!B!B !By eʔvBKU񡔻5dڵkqwwhѢxzzRD :^2>QF\x6m6*+Pk P+VYfqeݻdž ذaիWȑ#j d5erei֬Y`bb6٥pb ųe>6 O!$IOiپ};SL!<<xf/wޝs99u Ν;Szui zt%KvYUeRKS}%%%~: c˖-Ԯ];>2:9x?%#n޼IRSSLfWLII["twLMMucǎ(eu w@5k !...S}3ŋZ,`Vѷo_éUӦMcݺuxzzrM%BW)&߿_ݻ7[n`ɜ9s___V^ Oۼy3qFΞ=˚5k4@>?ŋʟlBpp0u͍sN 8<{Q{ .{+VjeOfٲe={ʗ/σزeKqi&éP...={uQ|yذaZʕ+3gpppPW{9{,Nbde3g_8#B!B!Dj$B_UZLJX~ &|rr9B!gϞ@>}ؾ};n".."##9tC ayz%*>>3fо}{r $%%M`` V}̜9xmi^e}:@݇; jH֔ =yd7o>S.oWԯ_)Sp16'NRJϟ_㇟(vpuuݝ/^лwo.^xX,rj}TREB!B! #N<7o6m{ȈcmmԩSyM.G)SSzɓ')W;&((?~< ĉ 6L*Bxٸq#:::ݛ 6pn޼ɥKشir2 $.Rs֭\V׍78<7o}} &hr;\Cv]]]<==ymu߾}˞={եgϞ90ҹsgDrwwwG׮]UO0@mu֩]v|)'4i҄;I>}4NW> .DGG &(w  M .R߭!3TB| R+k޼ڱ-+F38:wj ϟk׮3rH %JPK%*dҚ̿h"cIIIʉcr#g0߿Zŋs1^B!===zAttr___; 5k۷o'<<МP^= 6T>޽ J3ߍe˖Çi3""BU~nР(E)6&<׎;ǒ%Kӧ...j;=vRSB!B!"5pe)Wܬ_Ǐ+W>ׯ?fZL!ħfĉܻwM*wQ @__jժ1zhv܉!ΝUa2͛ǹsP/‚%KKiѢ ,Ãrq9ӣTR4oޜ5k0l0qtt^ʔ)>|8ͺ"::VZQL\P5jŊ̙3tܙ>|8GOO9s(ٳ+WR^=6mڤ&#G$_)S#bcc9v=J5kPtieԩ*lFIѢE9~8Opp0֜8qB 1f̘la~ˋׯ_ȵkװӱe$:A4h<{D>}ʑ#GE<|6mhLhٲ%Æ q-ƏϪUR]8p>}B||<|w>}U1cɬ|RJGDPP P&>1K!B.^Jͪ` ~:qCiw^BƇ;d(=fE䐔+;eWZ^ҒP~}Xj]ve׮]jm5Ŭ8{e!B!B!GV|޽{ǚ5k8r>D__իӣGcccYn^^^|D OOO (kdVn>Y5qD NNN*eӦMc۶mrer|B!T/_sssΜ9õk8u[Vx˚b6n&R266&<<˗/w5Ȉ;ww^ҌW^8pcaas7oi&;|@ժU;smT (++ ʊ`=ƍYxRJITTJRi#B!B!DZd/Њ+Xv-w%!!X^ʬYTVgĈlܸÇqqqaرi<pabccU>\Vh޼99r$ǯO!D]p]m6>|HBBO}_6*bee֭[ocbb {<}4bK)ݝwѯ_t-j׮ ޽9wV9r W)|5666̞= .Kx%'Nرc;+qiz}x93|p :tXbq=uݻǹs(^8:tkĉ IIIܹs3g쬱Lj+;I ͛7,X0S ڵ+888`ll>Ŋcǎ9r#GO---jׯs.\===*VVƆ'N`ffժU~ի|7KV3UCeƍ4hЀchhHطo3f`ڴij>x !B|*zERRӻwoΞ=K||$&&LO=ž;w 5kΝ;jPSB!B!"5p`={SN)W)ݾ}ޖ-[nݺq9iРϟgϞ=mk23yT!Dx%G888p1ٹs'}!!!ccvnR{M0AY*ё$lmm9y$7n`۶mT\`V^ݢR1=|~gС"$$CѡCbcc駟xsN4֭[εkpssRJ$$$b fۗcǎq pssFܿe˖XlT,͈ݻώĝqV< >g& jZ)Cabb;AAA\~]vѰaCYjUxDGG3vXeÇ޽{i֬rhJ|w$''cuvArr2ݻwG__?GUϟ+[nxyy)e˖\Rcƕ޳}u4řT888`nn}:H7n Bc֭<{QFHʕɟ?? 6dΜ9Ö-[>nݺ1sL*V~-7<O4̪M69NNN NNN͛5oҤ NNN4nܘ ҴiSe˗U*4i+WFOO дiSD{ooCYdbDQ~iiJܙg&R`ѢE4nܘ ```@ XhY۷ 5k5j@__ڵkaÆ4WիvRY-));wkd֭ЦM-[FjՔt\}.nj)E\r!B!"p0MtttY[[+wcP߰`PNΝ˰aEb``5kݻr;j\ }N0 *K||<E[[[А6mhyxb,,,(^8:::(QVZxbT/^L˖-ӣlٲ IDAT 0ggg u4řRѢEY~=?e˖EWWeү_?6l؀aCԪU  ^:*+vԉB v)x!B!Bev";????prr[[[ (  Kϟ?HMIqLQG!DqqJ|||?~:111;$ZjoRN7|@\\8}4ÇGGG5TGGÇlj'4^W^R9ndd۷qpp4iD9ADۇrbccͿRH޼yvd%̎sf3Y/S"[ԩSUX߿de<=j]]]FPRQaaaкuke=Tj~N^>je1Çt\}.S)B!B|Ά gEeΜ9SHԡ)ɣpL͛7lӨQ4f$\+5jxF֭A#'sHJB!y !DnkѢ&MvBdٿ?ʄWWW4.$B!B!DJp244TѣGٸq#/V&\qݛYJ"22(J*R#"o۾};p¬YbŊQ쨐*WիWuV&5$%%$z(RKHoŎѫW/-ʊ+ Çڵ]vQR%6mڄqܿUȍ7hڴiܸq@yّ3;ΙLfݻwrʥ[w_6YPܯC gϞ,Zooo?Nɒ%y9GEWW={ϩkDq X^DTd\}. ,B!D8<{k׮E9p@"/)X ӧO_~QHg-[# !B!Bϟ$\|zADD'Ok׮('}z۶mٰaL8^|ɖ-[ؼy3fff8;;wHNNݻTT 5kɝ;w++֬Y37.Y!D&,XW^$˜9s&cԨQ#>^~Mllloz*>>>Jٳ'۷gIѩ-'XZZbiiɃŋ\xw2sL֬Yelْ@<R۷os,n8gVn?3q9fϞ=7իӥK*VH%Q۷pYĤgrhѢNÃ!CAbb"m۶L2Z5)\p;(Rs)dg !ceeŒ%K '_Kƍ5j .`*B!B!ơ7VZѬY3 TR@hڴ):ub(P@9q 5ݻwo7 p5,,,> !Ȏʕ+pL{- o޼aرt!>n޼swȑ#zJe"!000Pcz9|ݛy?G-+~@WW۷k'oߞӧw۶mPWy'޽cq(#Yzfғ֭[ҝrJf͚ő#G7n666tAewޥoVWjlڹ>Իwo<<<عs'rW6VM dLIJJ9W^}.B!ByP,h򹱵eѢE,Z~i;!B!B %jLXZ[[rҤ1d "Eڵk9~8?p4jԈ#FZ>aV\Idd$tԉKÀTbH"t1X!Dfnݚ V\I˖-U)߿??3[Ey|ܹЩS'FfNƍUn޼I.]PNޯ߲eKN>O?ʕ+S]=22 <ުU+]իiٲ2)OtVLW$ fGVСCiZ9-'c366fС8;;իS͍7f͸ѯ_?E4iDCCC^|I`` *e;vȱ3;]޽{2Κ3/&44ʕ+Ӯ]4*vٳZه~T&k񰴴ŋ[-[$A%''v4СܸqOOOBCC)^ZRWN^&;vggg,,,ԞQոR{Vs)B!>7~`ywDB!B!B!ޓ.PVVVlٲ___|}}پ};cƌhѢ* ?~<{ܹs;v $[t;wÇPpalmm  ""BY7""4M!eggGѢEt\ro燣#+L0Aۛ͛7SV-.\2Zv… y)o޼ٳπgϦD3ӧOKRR7oTպu[V㱴ÇX[[3ydΜ9˗/Wo߾$%%e"##)V ,P )SpE޾}ݻw={6[lɱ3;΀oÇDBBBcLZx1Gggg7oڤʗ/Oz)III2a/_GVƣo߾+WƌCXX1j(7t 3gセGԮlٲ\p!Cpuu?S;_d5Ԟռ\ !B3tT !B!B!ėNvٿ?L8WWWrtB!4)WK.ebmmVgƌԮ][ oIܷnJ*̘1'''T5lؐ#FX"nnn9۷o3}t}O?1vX_}K,a̘1x{{Rn``%KPBgȑ#&,,Lʀʕc%?x?n+V`ʕ899cǎTwPb˖-7Ne1cΝ;Gٳqtt̑3;uܹs3FyL\̇WfyBXx" 6 GGG\]]quuU)3f *R(\0˗/ޞCq!6gҤI&Ջ[ Z5)RVSN)wQXpZ"SvJYs)B!+++,Y0==P!B!B!R#QsZno9_`AO/\ewϞ=,[ j,B!2M6۷իWsiCe˖*޽>lllR NNN*UD=:t(jmLMM9x >|?.˗ܜSfM}oߞ{䄯/Ϟ=TRhт#FUEaΝ^///߿OBB+Vcǎ >%K~2{tuu۷/;vLJPbbbӣtҘbnnׯqvv ???vܩ<7|Î;Xl.\ 66:u0fڶm1"+qge,XiӔ;o)S&ͱ̭g&-RjU,,,tj۷o_Ϗ aaaP~} D۶mR &L͍Ν;{Gƍٷo˗/̙3<{jժ1vXڴ;Kܚ3QrEY3ב 82s+sfZD,A$G{V܈{Td("?[|<|<ι뾮}s]ϟsajpppܹs8::MJkM,3w\mݻw)S llEJJ"|.%}=o(W>>>Ybccc3;WA,YzU. |||?gֹsLC$1nnnƕyyyy'W_v-C IteK\v OOOΜ9?K\|SL;5H^ホ^|drT"oSvm>KDVvxqG!Ch Is2dٲecժU8p E EQyYhTf%FWWWrDDDSD C^`DEEuVJ.ʕ+TRG&""""""yJFDDDDDDDDDš IUT 0#GRJgΕ+WXr% .g"oƕ]ED2V?AAA\v *DժUi߾=3@8me+V0uT>l,kڴ)gΜaݺu899='NЪU+ߓCb0%6-{o3k,*"/񨈈,p!"""""bԨQsIϒ% Çvڙȋ+666C,Ydv"{.};wLp͛lذ 6P^={ɓ QJf5jTv1e|}}7nsm۶i^2wo\]]0DD$h<*""""""/@DDDDD^M|ǔ+W9r5kV+F-wޙ;wЮ];v믿_|ƍ9ql޼C;wnvA^%iӆYf=zdGvZfJ6m4sν;HkkCD,G2; WNb=ztf!"""""l\rڵk?/_>e˖4jԈ;rڵk&E,H"ԭ[;vqFZjh 6ׯO"E20JQE:Zz5W0DDDDDDp-5kkƖ-[z*mڴhѢdvx/ӧOSBo___@:u裏224vcΝܹs^{~S\fU;Ƃ ؿ?aaaҬY3<<<ȕ+ޡCعs' d޼y -rttd=+W&H8wg޽ܽ{B ;пJ.u;wG2uT>L٩Y&J* λx"-bϞ=ܸq9r`ooOÆ ޽kHnlIIKLJ?y .… ˗<<w19sZܧ<~ׯ?s̡riڇd5EDDDDDDD2.҉Vy 801+ ;wp}GFF2fڷo-m۶ei{ѿѣ+V'O營fӦM-Zx ϟ믍W\aԨQJ޽)V?ɓ,X!C[ٻ~]Mo߾aÆ 6ҥKsEOΖ-[0`7nH" yٳ'NNN=z#Fpe̙ŋu'NHxx8:uW^(Q'OpƎٳg5kǏOJ{\\| ժUc899qelСCٶm 8tshccC-ϏC&hϏXZl5fĉ/_I&쌕'Odĉ>|y8"44ESZje<Ǐi޼9&L0׬Y6mڰzjc(W\맟~""":u0|(_<ϧK.߿uժU$ڵk_ƟiRא矓/_>eFڵ3gM6e˖-ƄWZ=\\,[̸IrLHvͪUΟ?owM|||8x ^^^ &2ݛ&ZO2j(̙O?dͱc8v%OϘ10(iylܸ\\\sqqlڴɸ2HJ={:u$Yŋ5jW^TR|wd͚Kx7,)O<}jRޯ_?Ç;B LvMmlq3'9Wڵ͖5~Izi۶-ӦM$xܜ+W‘#G8r'O 666űQJZsHR3$JQѣ&ѣ\vڵkcggYx7odŊ-];wĉ)\03g_8䧟~(Q &opIƌٳgׯIۥJbܸq,^///onbXvԨQh2e +Wd7V-[//dku'ַa%psS e{$Iv+ʕ+Y~=sӓ'XcER8vΝh2:@LLIamb "##Ϟ=Ek׎y2gN8<j*VZ=?vvvi[\}~IU@k5|[6moٲe ,XPnJ֬YMV{X~=| W\IX ;wf'v:~*i׮b& o˗0bZlI,Y8v{f׮]?s58q";vc&kI'-[ɓ'T#GK.1yd<ڵk|$`ܻw/U}޽YfQfMN:ѣ~:˖-3Rƅɹѿcb4 /}"/5jlGҺ :CW׍t1!{n>3ɕ+F"((_XoU+W5UTaȑl۶-AÇޞٳ'7o ɓqtt$k֬-[q${@l[/%}1\1!0Li&羧G)&'KǾkd|H,Ye?~G'L_륥ŋӾ}{۶mO?e[J_z9~8ovÇ WTӧDFF&˩}OϞ=_>w\&NHΝMݺuӧXY%rqa/իaW:xQ[0,((lٲѺuk,Y®]ػw/ߧW^ƝsܹÐ!C8uTv% %ի&i}… xwfw^i[J_zYhQ,X@ӦMM&&=z4y~~~c9(6lH|8}4k̙֬3k .]@6me|h_IiԨ?gdx4MF~Ml`eeo<5O6SNagg;_~evt7!'L9"-|^BFrv?XbbqOJ}޸09==O-9X:XSDDDDDDD$)"ըQ$u`ҤI(P;w?sbbb"$$ Һuko{sfۗgٳg۷/ gΜt%e˖xxx{n"""!$$ĸ\'2 cu֙Z/@tt4>>>&Uoʔ).$羧G)&7Kƾkd<%\nJ>}WuԡEL>zwe4hFNr놅b j֬iKbŊQF VXAXXX^pe} >4 2w]nˋAFn>|8~~~fW\KKbHO6l`ڴiL6 6dX"":J*} N>7oŴiӨQ w&OѣGy G믿nݺuԨQ_~ʗ/#`~˗/? zj"##9|Hiݺ5~)666 qttߟ7Lhh(Yf5HߥKD'6hЀu1|ݻw)T}\rɾs/‚ ؼy3W^%::%KҨQ#zmGl}~Yf-[#FpI/NӦMݻ1i57|???f͚šCׯϨQ3m4ƌcH"fUV Ν;#of::u"{xyyqYlllR 1קt 6 Vׯgܹl۶wRL<<mLg '[ڧDٱcG9rw߉/ߍ˗SLOI {&eNNNt1U>Or{zo0bWcM1b-[$K,;v޽{k.ck׮aggĉqttرcfy{0Pg%\H JxD?ڵk_؄Į$22ʕ+O<\p<<]4iw}Ǯ]ڵIv"O<4j(CDD$1eʔ1c9NQ5k֤f͚:ƆN:ѩSY\9[;w.E˰aNψؒj+WŊYzű8;;`dWdI,YbQ6mzn?2رŋ'Ab$ݛxUXYYѦM<==i۶q>'Ok.bbb(_<\pOOOV\iQ… ̞=[ϨQظqc47ȓ'^^^,Z۷s-rM-N֖~.]믛OI3f/d,XMҳgO=5==tl_rbt쫱HSEKlh ԩS9sCCc;v0rHg+| 4Ȥ=}4JJakCy5}G@MOӽ{wz0h @֬Yy7RJ_&~,]݉gN@@*U` .3lْܹs'/"""nݺ~z (щHcM eV<6mNΎM&[/&ad˖&aҪUc2s[ED!22c.\`vh+Ν;g,}69sQF~<ܿڵk @ٲecH9|.DD_뭷ޢ_~L>SzLJDDDD$\p/_Ή'hܸq&G%rXSDDDDDDD$(%o>6l?~,XM2o<&O\2OfҤI JN *pM.] ҥK:""H,!~/W^8::D>}lmmnܹs߿5kְtRca,Y%22}09;tUlYN<ɦMԩS㆝&%3<^{;wkfr,,,XGDDDDDDDD^|̚5X6}tzI312 IRnX~= >ooolmmqwwDD$3rJ.\Ȁ([,Νc… ֪UիW'PT)[nQFܹsE=7NsլY3N<ɯJDD 6֖hN<ɏ?hIkyKΝ;\~=Ab!atȫ꣏>DDDvZscٳg[nAQHΝd9r_~|WlܸFQddۦM<==YhQ '''5ke~g~gVd]WZhժk׮eÆ lذ!yŋ7IrLI<իٳgMٳgx뭷IƘuoi&Ν;ǽ{Ț5+%JN:4k$1%'ly~'+!ʕ tSDUfv"""֭""/yҥKt۷cjՊ СC:wLݺu8p`D-"""""""""|Ja &פ(PJѣڵk׮MBR ۷`PJ:ubɒ%t'']DDDDDDDqܹ!fhk֬DDDDDDDDDաt憛[f!"ruuD^*VVV4oޜ͛gv(Rly8}4*T0{חPN%""1|_5>9&ŋ)]tϋ/((m۶q>|HܹqttaÆT^=Ǐ35ל6lѣG\2M6D2W5gG{s#q% }eccC…Sݻwx)nE4y9("h4糷DDDDDUsNc>̜9sXr%tGGGV ȋ`ڴi?fΜ9 < (( \\\߿?֙gF8u>>>|2l0J.MŊ3;4v0?}4&MbժUd˖.]0dݻرcٶm4hЀqƑ'OO8رc ƆyZSÇ8q"\v [[[3f 9r0˒% mС,]lٲ{.gݺuP@ O}ʸqXr%/^ƆX .zIR\~6l9s\r!)1C """"""")vi<221cо}{lmmh۶-GϏ9sfd"""bH.HȞ߿\rѮ];j׮M… ȑ#w^EKb4nܘ"EаaCf͚Ō3L&ˋ/dŊsޞe˖w,_ʊiӦ?n8VZew}BCC>7gTT7|_tɓ'sA6n܈9xzzO0e2o<:@DD3Ia˖-ZW{~1b|Ǐgƌ :4CvMHl\q}.\0&}k<ИWDDDDDDD墄t-_E^>x{{gv"""""I22˒%Ku:u2uܙ˗d뗉щ ~& $O}LTTo'NЦM)B"Es weܹܹH>([gpԫW~+WDE`ٸtޝ#Gh}b۳gSLa2p@ .lgr]b3fѣdϞ 2x`sӣyg>|Hҥڵ+͚5#,,-ZP@Mv`|2m۶DYFc^.DDDjԨٳ3; yIӳgO^j, 6;Ӈ'OnܸǏgΜ9&O۸q#$8ٴiڵKU{ 1ٵ!$$[nQre+Ɗ+L&'óp<<<,3<<ѣGs-c!q3W_}Źseoߟ3g0j(OL?ͩ^:˖-ԩS -^;v# fʔ)ɓ'v㊈`̘1&?_ݹss1&N eܸq<|͛5&vZc'OBHHhx6a:**U&jժ;v{ҨQ#G2uC`_ݺuMo 9s߱cUV:qgggks=aaaeooo,szbccUvvvcq'lmmt[ne֭3 L7[=Lvذa Ο?ŋݼy+VƏh?Wgz7<ǔ1uT֭[eԨQ@{zJnݸsg2vXbbbhٲ%;vˋ-[иqcc@cƼ""""""""/%\ /^ŋgv"""ի/^'opر(٪'ORJ9.]ɓ9x k׮M6]-vϞ= 0ٳGODDDQF?~;w$\&&гgOիG,Y8s ƍ?ck$C4$pichCъ+f%ƪhK-mVQ%BjQHL!RRC544"w7'$ #欽^}N{uϚ5kpeʔwߥ|=z3fX͘UXf 'N^୷ˋsٲeQ/NY.]6{2zhWNbb"ӧO… ]]fgVWٳRBN8_~ə3gXr%]v5;Ãʊ+Xbv2.~'#4mƜ9s}۷O Xvfsu~vr:?===kg:@n܎aj׮ @N:5jmڴ@|LnFMjՌ|`w۶mٳbLVWeum^5j~~~888PF #!4gEB޽;W^5yo߾#G兓cƌ!..bŊѩS'=Jll,p2ǁxg4yS'[n5)i¨Q:M㉏'<<`6mjڷo3f`ҥdddm IN_df#9s:~xcv.Z1SݦEDD$g4oޜ_~zo>_NΝ?_NJbb5|f;Y5 yy boWWW.^ŋqww ^3Yʕ+cYJ*U<==ߟy@^5k,]Ӻuk *d3U0]#3S =z4T^}qڴi9۷'$$@KjԨKOOh֬7o~ =s:tlڵkShQ;;z*4o; S5 A.]䤄/v5k4r8::ZٳԨQì=\ ҥK 6Ç[{ΫPD~fMI{nϭ]\\<csYуK2o<4hHvќWDDDDDDD񣄋֭[s< 1[Rǘ1c͍~ RX1~wVX x3gmnՍ77n:u2СcǎeٲeٖWӦMol܃*Ӷoe]T@n;iZڸ"""OoMU.U}Yj[n^zDGGcgg/l '112ePN<==y=;U0-~~Ƽ+dTR/sNڷo9;w4g5~ӂ\k܋ |ʼ3Q{ao,Ykҭ[7Zhavzz*U;/^^ze{ΨQXr%Ԯ]옓~Zj={X*6YٱvZ7onA"EHII_D̟?xƎ˙3gE&/ز{oV9gڴi>|OOO^z%*VHj̪e~UYݻ${#}Z{:8840{bɒ%ر#-"!!Ըqck=XDDDDDDD BۉdɒDDDf`Ȑ!8991k,f͚W_}ehs6o<.\@׮]nݺxb͛ǠAlG*TB CDDDS8qPn]ݴnf>}5kstv\]]INNʕ+3]DDDr͍5jw^;F\\fL㏍șw.}դʔ)ٳg9|YUcǎY1}YΝ;̙3ygsEl۶ڵkeԩS^,ѣTVx|ʖ-Yđ#Gvl7]nggg5kFf8q;wdժU̟?H(^8.]ڵk/^k׮}ƈ#ըJVŊ#66'wߑ M4aĉt+yr=V!!!m۶Yɓܾ}B ѦMUƿo.^h$(PbŊ1|yzz޽{3,Í+YMWev--Z`V^ƈ7ۄ;=M?K.?NժUs۳gO/_ҥKΎ 5y<-䡹t4oޜ УG6l؀pg/4h@6m3z IDAT5kFXF^lڴҴiSׯ?>6Fvdzݻ͛3p<75k;tfN_ߦGf׮]O?uƽZ삓9~8 QFL27n}5,YB:uL2dK18oʔ)$$$2eEf͚w* 9r۷ose |}}s];Ϝ9cUT ӧO[\f#"""yOzz:cƌ֭[[~nݺɓ'>}ў]5jY}!n߾ i1S2_|ɓ'IOO .K.L0[R%4i͛7 ܾ}K.f>Cn޼IFv79vod^Lk-άԩcb['Nĉ>ŋ3g~7bccZ:XT (Q}N֭[\|""##j 4ksttX;vӇTvi6ֳ>KYjOȑ#;RJQJqUVlٲ&L<{ƯʤIرcƘ&W^ϋ2_EBBǎlٲlƘ4i1'ܷo'OM^1=O>!997o2m4|}}-*&=쳴mۖW'yEDDDDDDD?pl'#G0zh 8Cmϟ'<<f̘J|W̝;׬-99%KpU&N,44k׮y_|e3o]~7|lסd-Zݻ gn57oo5III1Ϗݻwar"""r'5..ITED{㏜:u>}طO>[$.\aÆK(Apwwg͚54lؐ[K7n`nLDD)Cɒ%?(Sum6f̘3,?yYŊiӆ;wr)Əo7lbntqqʕ+ :g}3gҮ];ѣGy+T{6k0`nbǎ̟?.uvAnb,>X"ZYmۖ]vqypgw xs]v1g#q$>>>Yݩ%|Rƍqss… xzzU;֭[Gpp09r.YуM6oѿe˖ s;yQ^Ue SNh1J,i1'Z*}]n?x 6nH||6ng֭ǎc̙ԫW8˻kW{YڵkQC:w-k >ѣG9v{!11 gcqX[hFܷDGGSH5Ν;sIf͚e#Ν;2d˗gt? .4>r /2vvv޽;jՊ .~z\]]ƹ|2-[t|Gc/ItXDIqF PCz_NѢE;|_b]tt4Æ \rV;ҜWDQyu=S _|Ѭ=kyY3g`Z2๑K/]İa8|ű8(ndyo777Ξ=]Ti{XRR5k4kz]Ν;) wL?0ffj3ٳ >e˖Ѷm[6l@rl#1w\+WPX1F`ٱe5k/7nd̙ѿt钧S4iBJJ FgggN>ԩSyݻ7=w[ӣG:Өm۶l۶k՗׮]˳>?G'KD yRRRqٳ~󊈈<^pݭ5YA'ׯ6\6mӓ^z+RZ5c0T.ٚer5jٳfp믿oPZ5ΝkmMNe歽֘Y3Z""u9'NtҬ]֭[:G֭[ҵkW.]JLL ZuXTӦMi߾= DGGF6l={dTZ֡CۊyGVX|9!!!/Qc)R3tPcի>}: ~c< .2e mڴ`˖-|*Tщ<4ibmf9E ''Nԭ[h?pE_wwwN>͚5kpwwܹpJ(@bbq<--W,JG^^cݺuDFFҪU+J,iv<==ٳgA֭d N:WF ~rՕd\ٱ+W}DDDL~gɓ' ,^s1uT8@ttpwbUFXXy\p˜>}:5btI_H?ɓ'IMMt4hЀ~Ǭ]6 O>`V+>ȽZ*]ve޼ytMI""͍+W˘1crMs^LJ.-Zp LG}Dʕ9|0SL۬Y3"""3f AAA c…|72{lΜ9}Ÿc^y8q~UkJ(ʕ+ر#ϟ-5'y ޽{ӧC A.\G_sN6lHN16mJDD'O棏>jժ:tx7n'+kUJHNNO6?#Gh"^{5VZ?o MF׮]qlݺa=?}矉Gf .̰aÌǦ:Κ5k[ӦMYn[?r~~~Ί+ruI^ $$~'N0zh5k֌> m{f͜9s}2kkհaCnJBBN7 y<}Ҙ9s&~!ŊcŊkaڵk9t/I&DDD{ƍǂ 裏O ТE ZhArr2۷g֬Yxzz2j(F,"""""""""""""""""?:]\\\)R0(^8 ۨ$huvv&<<@ݱŨΐ#([,S#Gߟ"EHj՘2e `ҥΰaԩŊ???ƍ8r%Kd :jժ3|xߢ=/個-ɓ3%]XfEaԨQ v^foB ѳgOz왫Ŋ#((dmZ=32w\([,III߿n:UnݺujaYlS_UDDDDDDDDD%\cZb億bvDf$RHƏСCJWf.\щȣ{n D||<8;;:|_eڵ|w7nСCu6{hٲ%Ç'..7oҦMN|@hh.D.]:ɧxwlCw-͍DDDDDDDDD$_PE>?'O$55ҥKӠA뇛C]6 O>`8*y._ѣ3gM4aTZa;ӦMJ*X3UA6OUYDbccٻw/oGaɒ%(Q֡H``|>>u""]dd$CDDDDDDDDDPEfgg7#!"tIOO'<<>,X@``  uhΙ3gXx1aaaVoooJ.Ͷm۞脋jժ[o[o?omۖzj*UdhEdL>aSwߥQFCDDD0}ton0DD!|TDDDDDDJy ۷A0aŊuXVhh(nb…lݺeRL֯_h}[PM0 ggڷoOYd -[a"r-[FΝm<%-[Fvl`ݺuhaSfݺuV+>*vHD Ν;Grr2 \xѢozz:vvv6xg9z(/ű2eʰeO֭׿ŰaldeD)Ji"_ZZyu""h>*""""""%\C,X 2223g{֗oXmOMM%99$׮]{⫅XK0)\0 ,z9_Yf#[[ """""""""""SCԽ{wV\i0D$-[F`` >>ˀ>B +{hN)""""""""9Q…#r1 ƍٳ'ӦMtҶw ymcYfڵ֭ˊ+TE)7c ,X`f̘ҥK_ۛZj:4y̬^CreBBBxw(XP=JGlٲ;I޹j*FM^_}:۷oƍTX^z1zh|Νƍx"J^_~8::;~cժU(Uɒ%ѣӧO3v1]'&&6n܈#5wߥTR9wk׮͛|2Jyggg,YP)T Q"==06lqppbŊmk׮dԩ^L|}}Yd ={""b?Krr2Ǐ3,ꊫx"5hЀ={жm[իDzeh֬GŅ8t6mbұc8^ti}r@dddeW#<<<8u^zF4N.?~n:^ʭ[8r䈑lYjj*d;w[nqyyr4i$~5`߾}ǦNʚ5ky&W^eݺuۗW;,^4Yh#%%{nn޼ɕ+Wo 5Ϙ19spnݺEJJ ?&22~zRRR>$7#%% 6~DDw:vHVU"((HTx爎_e˖̞=!#жm[|}}Y|9))) x{{;涟)Yk̘19r۷ose |}}8p״3gJ*piXMm>"""""ر#oߦO>ҩS'vAjj*GaرF{nzWy&ӦMחg{mSrʕ+qX|1Mw^x҈gʔ)4n8oOSHΔpEFFrY<<<gǎcpB:DYhv"**Zj{nV^ g;vΝc…9Ղ HLLlٲ}ϝ;?NDD9ʕcŊзo_lb_b_~%;v`۶m5 KbbbR6STBBBZ*d,[ OOO[&H999dƍǠA~x8q" 4\v 7xWWW?AF୷92Nƍҥ ŋ󼼼HJJo߾ԯ_}r9 BsJɉ.1klfnJ3f 5joL6n{{{*TXn]qܵ\rFF?35j~~~888PF C̉ak֬iTؼy9AAAxyyQP!wFIb/:u"..HF߀; Y/_ެ<6mD͚58q"F"!![%b3 `ܸq,_ӲeK[%D)QСT\GGGX"}a„ yLxx8coo M6%""l'#FPlY퍹l˖-qrr"::"hiѢC|FDDDDDĖh׮pgba2rH)RTV)Sп|s8::FN(QhN^ƴ6)BBB pQՕyOѢEqppJ*L:VZe{aÆѩS'+#~~~7΢_nY-ZyѥK͍ܰnݺK)EDDDDDDD$'2<+PZ}l9"'qqqJll,Ʊ7oҨQ#y寿z...F) tڴiYb^^^^'11.]PlYVZevm۶  6;vd;v6}PP!9::kv;ԩS6l`„ sTpaK`` 6ƨ_>iii9IJ1%GDHEDLΝ;Yt)|FRܱ~ڶmWjժI$_Zhݻw7ڳ>={6seʕ-[$ڵkǀׯ#ryä%?МR|TDDDDDD'pv"ͺ#iv`YM">4nܸݟ,ڶm/˗/'%%^xooo:FJ8}5Lm>"";<={YfxzzrA&LdHʕm0Z bܹ_bԨQݛav 4O>O>cDDDDDD19X X9u}ɱo>}矉(k[pa f<.Q:tݝ5kw5/~'BCC Yf7j %Kjժw;޽{yfΜ9C߾}͎yyyYF޽}0a_˖-qrr"::8;;GDDK:u9r$СCJxL?~t^x["Ν;tua޽IDDDDDDDDDDDDDDDDD$Gpϕ,Y`PT)vs?oҤ M4sŊ?N+FPPAAA9^/dkxgy@ΝKRRe˖ ))3`r<.]DPP4oޜPre[%ĉTXƑIʕٵk:7&""Ν;:,T"kҤ ,_Wɓ' /#|2ݛ2eiEFFN=l< ̞=ooo֯_ŋ駟l8v{?~H,mLe9BroqJTÜfu` %ƈ*ffϐK yE\z=_y߻Ԟoikk+kkkʕƍ5p@SӦMSffѱ V(:t蠵kW+WN6(ٳdɒ6mFw}W/BVVVx yyyiܹ$ooo5jT… 4iK9;;k߾}[ѱ𘥤AF֭~z 0Œiڴ>|tbt @c> xZPpQCFG#Off5a+VLݻ7OFjժ1...WΝոqc}7j֬ѱ P4۷Oڳg 3flٲFB:xӍ@6m{ァ9s襗^2:J+VPݺu5b%%%Oŋ7:G}\ۯ^)SM666$WWW}GZfJ,$c(žDĽFY,(lĜd͚5Zf1њ5kgt p5M>]N:۷+88b ͛:{1iĉ WhhڴiXa`tsΩW^6www;wNAAA&uYV(@!!!0:<4:|ؼyuYM>]FRbo nKIIQ͚5o>5h8x:w]vsjҤ"##Un]cE­[tRm޼YgΜQUZ5uY]t7--Mҟ_|Qo(KKKIV0sBB._UVqƪPOŊUVwUҥU(Nk׮_T˖-RJ:Ɲ,ϟ׿/m߾]W^U֭5iҤ\ИIy[=ɓ'kӦM1c:t`jGM6$?bbb*Uvr{rk<3]|YK.֭[uyjݺ,kkǮZJWbbJ(6mhȑ(9+@ĝqi5Jk֬nݪʕ+ ̱c$IUV58 BznݺYfZb:udt,˗oܸ$%%%ʕ+t :T4=s,???{M6)--M͚5˱Yf͛խ[xuFvvvrppО={_ݴ/11QOVӦMUre-ZHَ?{VZK.iƌy߿N>mjtʕ}eG[τ o>}jԨ*VgW*T0\pA}ɶhJJ|||tMuϒyRSS5`?~vY*>>^9 h /Jrut-M<9_YMM>L5kԿo}Zn/+L2FGAy?]]v233<ӲpB޽[њ0a$iզ~+VUn]*..NaaajР))IL111ZjȐՖnlﲵoܸQ&IZr111ڵk,Y"I|jժ={E<>#GX-ZHGB'OJ:uyRRR;΅ $)"> TX1uYAAAڱcZn]vŋ}Z";e}YtY.w֭|ga P4Osm7o\M*44TڹsKլYSgΜN[L>>_]&uA֭3{MCz$<YmY}@v*sss}7< ~:ʕ+wޚ:u=V֜Ob9q(c>qxԹםאc^yug݊/n9kEspp|X={4:"7nɓ')(zE+W4:)S:u꤀-_\}ѥK4|SI҆ r$$$(>>c /ؗՖ@ё"dEGGEVl$jȑرcgdd)Gʕ%I{֞s׿ծ][Ǐе_~Y҃W*[V+WLm:G^wZtڳ޻KE_%nZNNNݻ6oޜkFZZ.\N:I;vy(_i̻)!!A[nkSM6rttT^q_^U4ŋUNNNrqqQ~P6l Izw}Q={jذaݻw˗/k޼yzw?~}NII[oi֬Y*y_|YVRƍ]GŊUV˹^K.iȑjܸ$iϞ=M˻yZjRD=o5l0yzzj}!ܹvZ'jJ>7oZ`4tPS߬pNյkW9;;nݺR3/.\>}d+HIIn޼izO^Z|rS7n())IIIIr<==/))Ids=dkKMMՀtqSٳgxTRyy;s2d9׮]Ӻut-M}h̙qLFL?W۶mSbbb"t+++5ʴ]\9]pAц Լysm߾]+s gۛo *ܹs\4im˖-qF'LֻwomٲEGM۷q~^mn՝r{םteM6MJҌ3TX1M6M:uV^ҥK_~Rbbd;ϠA9F˖-?\$uq[nɓ'տlrE^0g( ѩSdkk@޽[K.ue+V[BCC05h@񊌌lK,߽r׮]5sL)SF˖-ӠAԢE 6Laaa9Vȫe˖鯿Rڵhӓo/*Iᒤ j݊ք $ݾ7Ο?/ITRރ˗T RRRW_UxxbbbL_"OyHjժ|YmY}=zTm۶~m>|X cѿmٲEwVft #s'U>>>Y,--eiijժo߾>}!YXXL2jٲ=s̘1T,,,L۫TRڱcG ;v쐵ڶm[ bnn.]H=Z5"رcծ];,YRSf͚e*߂RK.ʕ+'KKKm6ׇ`uF[嗯hܸqt!&ƍӹsL/Ԯ];.]ZŋWZ4{lu5jR͚5Ӕ)Sr <&Yǃ(i# 6l=etO<)IԩLkZjo߾/e }bŊn zm`2@Qǜ)ftA޽ rn}vw|M]zU7n4dT>}tR:vV77y헟km׮m6_^?b y{{[nԩSڵkVP~TNYW5KM?67YsՖs@^5j֭[ݻ>u%!(=֬YSjȐ!JNNּyT!<[b~oZJ,iӦiĈzw%I/deeDp'E+\b+W$ޖ$I҆ ߱jժ%IZn]V۸\bjm%_~_~L2ԩ|rG.]ߖtI5q--^XzwLg럵uMRG /H.^c_V[Vx3gj׮Y6mRXX8333͘1CVW_}:ѱo!///͝;WsΕw=1g(Z((ڶm+I5k$͚5+GVZI&OÇ͛:,X  :7'O%I3gRRRTEGGkرƑrI"""tU?~\95o\4w\={V׮]ӧ~*u5s;w֮]4۫z꒲ ~NNNjԨOW7o˗u 8p@#GTll7o.777q-[$͜9SPbb|7Unw5$NYmY}aرC 6ԩS5f%%%]vFPC;vСC#G xlr+H/xxxh޼y7oi @a(ftܛ~8qB}o߾}j۶mJLLT޽ҨQLʕӅ *mذAZһᆱZWիsꫯj-[*<<\>sIRfrׯ_?EEE)11Q?4hPZ飙3g7GOk?Iհa/hҤI9UVMYߺuN&M\FgQVR퍎a %KҥKթS'-[VŊS5c I!YXXL2jٲV0fUTIJOO71B~~~jٲ^z%RիWװa_V_QFM?,--լY3M2%u RvTti/^\jٳաC|ϵG5k֔,--UZ5WӧOw?I*_/_#FN:*U{9[ל9sdeeҥK+((H=zP daa *]*SL׼n߾J*;vcY[[VXu떖,Y5jh˖- OETXQ?:vwyG}ё2fFx)$$Dn?NgΜQǎekk~~ /V@@"""TR%I ޥK 2D48)I uݻWÆ ޽{5bM6MFBh޽1: 2~&M>}_%J0:Pyzz*kZ~6'}<=GObF(--M&LPvdmm~Mgϖ$5lxG}BBB4n8IF{68-I{Uz"V17nԩ#=zT֭S "GFGCƍF֡CIZn&M{JHHPr4x`iVdIM6M:sΜ9HM6MVVVF QڵrJ}W:z퍎;Cݻw̙3jҤ~g#Sݍ޽쌎MӦM3(i ظqdgg^/I䩻xdo񆼼4w\I5jdp*Çm۶iU华".##C^{5)Vvmǫ{rvvֲejt,!rvv6:j>|1@<<|1 W\ OOOyzz@yxx(~'y{{ĉ8qƏ/KKKcaU]vCZҥK @ࢀ@>L-gjر QߪjժFBL2Z~&M{Oɚ9s,,,Pp@w-kʔ)*S֭[]  ͞=[uuAtFGP(( =z0:|PHH1'*!!AÆ ?QFiʔ)6:j*'''_^ժU3:ϲeff)xyxx+xoy{{iӦ*UۧٳgSlyx(QBM6O?dt$!UfM]VAAAڶmԩct4x+ڱcz-o^-2:Lj UV۷v#GޓрG-[ETRj*M2ERFFѱ<\9r?TXXLo>C7333ʕ+0a6lTjѢE*[?\AAAF@fff)Shڵվ}{]pXȷѣG3}퓟Ǝ1ch…ڿN9uE52:~zծ][K,Ѽyƍ x~U^\]]sNI&:xё< .o۷oϵի2edcc#[[[꣏>իWpJnFGx~WuQ]v:_dz%##CǏWjՌHׯ={bŊrrrw}gt$. c<AAA:wzejsww׹sd`2(:'N:_e}תXрƍ7: `RBmٲEnnnܹ̙ct$B,c޽jݺjڗ!*a IDAT SNrrrRǎlu/^,WWW999ESDDDr;C,|VZƍB >+VVZ˗/?@ѐ &jժ*QTɓ'ڵk>233_PG2333ewmӋ/aÆʕ+={V׋/%KS9֭[Q5TD .]ZNNNꫯڲe^u͜9S>>>ڿZjPG$k'+Q4gM8Q}׍ 6|]|TȐC>gΜQ``'s۵4~~~Z|ߍ7$]rE>gn6mڤ45k,Ǿf͚)>>^7oVnPd\vMZRBB~̙3M6\ڱc>hBvvv:y򤼼TR%-YDk֬y1ԪU+?^Bŋ͛Zt$Vuq1tR'L銍Ull.]?0Oՙ3g4zh\R:uҦMꫯXiwq/_^˗7: QFvի;pVpZbbb4c IҊ+tAխ[WSXX4hxEFF $-\PwVtt&L IZz_^yM 혘IRZrjy1o<%$$iӦڷonܸY?%Ie˖ղet%{yK.)88XʕSff鼙q5jѣJKKɓ%I|iܹsuqk׵}v%&&8WV?ׯtddd/PZk.EFFj[H9zWnt ڷoݻw믿Rƍw^# .0lKR޽eiiiڎ$͜9SիWViӦI6nh[T)IRHHWnnnJHHVsȑ#MUT",,LrJ5h@ŊSڵ,vX֭[kԨQھ}m6 >\mڴylcdYpUdI5JtIҒ%KԤIYZZE-q.kkkIW2ӹs4l0eff_o85iDcǎհatAu" 4լYSqqqU|M W1 jsիN>@5o\iii'NPfffWFFF&MRRR7nhĉ}$EGGgk͵rԯ_?ܹS?~?4IJJ ^zСCݻ7l4h~Gm߾]Φ\7oZ`4tPSΝ;Ak׮UZZUzuIٟsf{Ma5jԐ$o9feڵ$C{UFFΞ=cL[6ݺu>39;;+&&F...Z`njѣ5F^K hݸqC2dHժU-Z˗/~_+ij̘1jԨ_x-\Pe˖w.@o*>>^7oTƍct$w04:ijL!!!u^$I yޗ}ĉ9R@@!<<\3gul*9gvtIWذaiÇv_hʔ)ܹ P\]xQ:|p}JΝ;հaCzuEϪRN8L2JJJRٲe/ܹsW_}U'N 6yw}e+WfdɒlmwrppٳgYfi27NEݻ~-ZH3:…r-kkkC666P2eԲeKg[IU>>>Y,--eiijժo߾>}Cs̘1T,,,.Ij߾J*;vȻcY[[m۶}{a?vڥ#GW_UbT|yuEB///ZpT"IR.\SNK`UZUŊ5F~(QB۶mӰaK/Dѣ,Y!CFJVVV^Vjݺ9CRl0kkkk0`ƌ7o (X1y.%/V@@"""TR%IӧեK 2D48!pop]~]gϖV* \R P-j*=FG,ǦO>XbCBBdcc޽{ (իWOsѴiӴo>-|o͝;W27gA)q? (/%K^z5j +++ . HXXOɓ'5bEDDW^?lllֱ W^Ք)S&U}^jt<Oط~:uhѢE;wM+0ӧծ];?~(@ggg;wEjӦK#O . `ͻƍULw޲ʗ/:(88X˗ƍhhނsΩW^6www;wNAAA&$ڵ:v(GGG:tH| ȑ#Rҥ<{9YF'NԐ!C4w5]#s ZJ̊c*PԬTMM Z*iQJP55d2 5U[J 8Ny\gz˩>{ۊ3;#ؚ).\;v˲S2eԶm[k.QHhΝr .W_}U$%ZÇkӦM^zTRf)Tz衹sSժU>}v){{{yzzj*\p}?xn… {n]zU V&M4`˗/y͞=['O^~e1BNNN[noQZTHEٓ<\,..N}L"777رC͚53;>}Z...z͎E'OVJԧOi͚5*Xl_|˗'|{;w{X 4H u/_ŋԩS/R}+'$I/Ry<==5w\;v,I͛7KZr\\\|ͷo֛o'իŋ9Qƍ }_^%}o߮Hկ_?I+((H;vP|=r??? Bv)I:uʕ+'.]Z'O$}f$ڦaAު_TjՄ%\ǏUNժUӸq$IwNfرzg[͛7:?p͝?c*Z٣6mڨUVO͎dK,F!O?U.]xbEGG'oIڴihFv$IgϞM"EH._jK.%u&^zuIŋS=gJ}TV-^xA7I2e$Etcik׮Iϟcul!!!jԨ]*44Tݺu3;GΞ=( l2M:Ucƌћoc .Ç}R;+h۶mjժ)::ZS˖-~8۷oZL^|y|=;F|XAAAѡCIҒ%KR Z|$%'O&~1Is=+Gb$IǏOt<{WZ񩧞$ݸq#Icu<[lQʕW_i̙ ٱÿ/FfGrVZ_UN8av$ [`E6ֶm[yxxoUddʖ-rIJCƍ%IǏWxxݻWjΜ9РAo%VZjHOW8EDDhʕ߿ԢE U^=I֏>HNR\\;SJ7ojkԨ$iԩ:ytIM6MԠAK8/_^믿&9G:r]۷W֭UNiذa1;tWڵdv שT.OOOmذHl:uOӧ'*{ν{޽{uI#Q=GGG; ,k׮}rss͛%X#&&Fvٳqƚ8qbY *޽{':VR%u}?|ͻwo}&*{gK/~INJSNI؂戋Ӝ9s4yd-ZT;vPf̎@UP!m߾]ÇWǎ5eM0!a4a >|XN} *^*Sz)S$˗//^,///F...jԨ.]hWѣGxⲱQlllq{{{M>]gV P׮]iv,0 CEVWs+IJqHN3|hƍ*^$ҥKj׮~{UV%9õkEiӦ/U\9c#ٳG:uRɒ%i&(QH@b o-!UVM=z01e˖B ڲeV\;wиqc*&&FkV@@ّ,ł NNN}Ze˖5;ISPPTvm߿H@` [S͚55n85J'OTf̎ ;wNfy߿_ 6/E :[f֭1uԩS'c׮]|||ԤI[Nʕ3;X,IaV?a:{l;\dff9v^xc 1{N$,o޼Zn&MԩS5klll̊ X .2ƍqFc@b/_1cF+VٱҥK|bvh׮6mڔd9aXrӧõf(PhccE&ɎC@vAC @d}Ν;'IÔM6LN]tQ2eԮ];խ[W)Jy@TT{=U^]wQ@@ [$={V哛QCAAAʟ?֭]v x,paE֭1XuԩS'c<l٢Çڵk1c*cH'";]ȑ#l2ꭷ￯WjСڼyզM}rqqI>22Rн{TP!jJ|#_ܾ}[~֮]K.Mݻwĉ$wFsf'Ojȑ󓓓:uꤙ3g*o޼ ukZf.\ {{{UREo뗁;̖y奯Z˗/WϞ=ڮ^ZݻwWNnݺ=܋qWwQ`$חڜܷoW_}Usѐ!C8@ֱO9XJ .  &d͛7OJtwZdI}ŋ+U= &wtt4hÇ'/kʓ'UrfzJW^MTZpa4 CcǎՌ3IҬY4rd`΋ׯ׭[tI=TjU˗O*TPsa'Iׯ_W͚5uDu^{5mٲ%oIN'jРA3g1;@na>|rه#..NgVJtIm߾]k֬aKĉ/I2dlllHM4Iy$nݪm۶)&&FTRt͚5+>g͚ÇN::v޽yzzj׮]Ip}Z?~$モ=~A111u͛'IS #U˖-͛7իW/ݻwOon޼K`) k=I9sΟ?/www*&&F?N<\EǏb hB׮]Y.YOC Qxx}]y{{X .} һᆱ[#GZ΋q)88XjRHH{=e˖g9ࠥKjڴi;vt .VqƩz}_hr~) @w5h IRnRlKtQ)""BcƌbQ&M/3y ʔ)#ŢJnRjTZ5Iw-@y{n}K/iΜ9ڽ{wBDf̽WԾ}t]iI>Μ3f6mڤkתiӦrJYbav@uV 6LW^|CX@|{Jվp믿l7nnݺ OY /`׿/矒%K… Z`AbiF6mJ H뼸~V7nĉ*U.\jժEruuMvNd܋QztرDu릯:Q]kɐiFahӦMzJpxl/^TkVZ Yl+VLK/1ch -S@iĈ*YlmmUP!kN [Cf5g.]Zώ>>>Q*U[˖-::/ ]J*%I*U>s]xQC Ȍ࠽{jzΝ;kIZcNV\YzgU~};3aqqq;w&ME/+bv,@ (ӧ&}9ݻw[oiѢE裏mv$@dkv@!C(,,Lٱ27nܐ1d";;;-X@+Wȑ#?dd}SN cLe&MҥKx”/_^X4hoHxB(7NիW׭[/RfG?ov &(X{G;w֔)SjVغu W>А!Cdkkkv,Y` _]K.ّ` @"/^TǎkVZ YlL1p@m߾]?4h/ O\$IqqqOUbE8qB?֬YbŊ <7n@EEEN: 2;0 C+,,Lٱqㆼ{nHě;w8`v SÇ7;dpO[ .`M2 [*P|}}5tPCǏ͎O@[Xպuhv @6`kv9VXѣG+OX!gϞ1p%Jо}ԴiS+Zpّ9ԿUXQrpp3<;j֭f2E*rssS˖-5~x8q"պ'NղeKY?vlmmU`A =z~GZYdȈ/]v*Plmm#GXH?QŊR,;s=zTE?UedXs%76EDDKUP۲1;dؙ3gXp*7h4h8c?S-[T h"+**Jx֯_^{MZ͛7͎DDDh۶m:uLjI&iԩڶm[{{tu?~\fRJrJc&ǣGڴin޼{)""Bŋ׀tDx IO_/]n`&\@t}͟?_+V|}}~z(Qhʕ+u@.aX4i$YF/V˖- WmۦbŊi̙ Qddbbb>@6mڰ/ݵsN&[]vdɒY,s?CvR֭^z%sJ|q##$I6mݻwEZܙgϞѪUjh^ZѣUs`0'O ~)$$D[6;X… ջwoce:u}),,Luӧ͎&ϫaÆ ѨQTR%999^˗ׄ 'WWWO?ٱs-[ڷo_{՝;wԲe,N5TH5mT;v cv,q]IR֭ekk+''>gf+Z7oׯkÆ ]~_͛g$t,Ν;;vj֬)0ta͘1Cy5;@PF PB[vav$x"0`}Y988MݻwWhhh-#?;|:uꤧ~Z*SuD߯m۶駟ֆ b*Uh钤?IoxⲷWջwo9s&뒤CI&rrrRզM%̙34h WWWլYSSLדmlI" ?jEF珔qNinQM5jҥKoyh}z饗SOV VVwt9nݺH"W5y3Gj!?#O-[&{]^zɞڹlI vW@6sխ[WcƌѰa͛ Wj۶tխ[W!!!fG\k֭z+~Ivؑ9nܸnݺ)..N͛7״iڵK4dȐ$m=<n8G"Muzdoool+Y*W֭[ʟ?nܸlٲɎO8g;wH IDATg $0>#sԩSZj%ۦ^zi ɍ5]Jw,3o!oooW_M8P̸0IR͚5-[#a\{` bd6loH-X@}b1;dX]V-ZP…͎ SH}ZO=TqFm_(zuZxrIΝ;#FdJǹ_XuD?{ttUνw^I)?իյkW?^*TĉM63#s"M|R:Wz%7־;9TX15kLw%I˗/ݻwռys-Z45Gr5gRZ`\Y[ @<\ 4sLU\YO֮]l2=fGӜ={V f9r6oެ7iӦ?͎$/]յkWEFFjʔ)jӦMu˔)#I:uTݵkWIW_}|wyf%lDgU>}㣟Y7o֭-arqqѶmdo߮yaÆYZuܹs}{i…֭[Z>$͟?_aaazշo_i&n.dUT$9r$6~dxXgt,3{>$wޒe˖I,Yx "wwwk֍_޳g$egNsTT#Ȝx%IfJÃa mGJaѱ|_bmUZjBBBTP$ά9?R!IӧOOv'Fs46Y[ XpY2d^|E.\X'OԤI@<)Ξ=R*V )SFڸqّ G2d (E Rtt٣gϞԩS/TժU|D;K$grppВ%K4n8ݻvx .T…m6URE_~Ο?8EEEԩS1c<<<_i„ VZ>ϯ={} UllBCCվ}{ݻWz뭷{ u&Iԩvܩ;w(..NN=nl)8wYZxzzJ (::ZΝȑ#5o޼t/..N/_͛ռysM>]vvv񑭭mm}YI,>?t=W^vz#sddN_ŋ׎;ԩS'ݻ Uiaѱ|aÆ Slll:ҥ$iĈ 5UV:qbccΝ;EFs46Y[ @" }׆O˗/7߿ov$vZhaDll1h b}xyy^^^)~֭[ '''CRy%_f>yaXW^=#***I`lٲacccN|}} d988}]6eT7+xƅ 2-[z_JYZ:d8::&… Sl*ȗ/_c\{3qĄߦ22'8`͛76/N;aѱ|F%9Or9tծ)= WWdX|ym2+ɌxΝ;-ZKZRxxz7rٳ*[1 ;;;}W/4i$u]fe˖:~gyFvvv*P5k;vh;w.}W?^}U^*T|={1I*U(88X .T-&;;;9::\r8p;)S$g֭uQCnnnzcǎ]v龎丸_*U$''']FұcTdL˖777 ʕ+' ?O _~Y}QNb '>3-X@ӧOWNtcXU5#FbŊrrrJ(]O12;\@hڴi>}*T ???թSX0AU|yuA 4J(av,)Q>Scd9vtUjU͚5K}>b 'K/@ݽ{WjՒّXpitK͚5SQF֖͂ .]Z~~~P&MrJ#10 -ZH*TО={~zg5;kƍ6lzq0 c x-;"$$D R@@?P3;*!!!ͮAr}'T pXBy5;dիWݻf ֭['///c!vdDEEiܸqQ> ߯3f@ӻwoڵK.^hv$tuQN̎@tIu1; 6x?C꯿Ҍ3[o)O֧@f9͎SjӦjժ7 \m>|1*XpzwfuQsQb̎ޙ3gTlYc@ynݺQFZxwaQUnf Zf⾛[*K,KsOL5]1HM0\,wADp~|yy{;,CݭKD$Gl"""CPPcH. "Rݽ{9s@ɒ%Y~=;vv,#66N:Y;H*Vk׮e̘1d;ݰ*""y#H. "R׏_~#F.\ڱDD ɓ'…vvvL:g}âE(Rd+oooCDD$֬Yc""""""""""KZ;5ܼy#G 'Od G?$!!WWWkGyd~mmݻi޼9Νv$@.DY~=5kd|7۷ڱDD X\\\DDj֬aaa$&&ҠA~gkGy$.^;mڴܹsӡC+'=?+ aĈ1GJDDDDDDDDDDDDz4BD s內'M4!&&~ako""Rre ʗ/o("" v4oޜe˖Y;Hf͚f۵jղZw7}aFȑ#1cGy EDDDDDDDDDDDD=e,"^rr2_5>,ǎc֭QlYkG)*UĈ#CDjJ(ׯݻw?`0X;HIk…q۳gO֭[?]R\9ʗ/ƍ֭[8ȣ "EDDѣ6lGm۶֎%""""bbggǴiӘ;w.'N[nUDwww`޼y?0c ݭSҰpB.^HnLeݻwŋ,\ЊDDDDDDDDDDDDDr&\Htuy4h@"E8r&LDDDDD,ӧ[la4k֌?ڑDDM6xxxЭ[76nܘݍ7/xWcǎ|ܼyӬq"Ill,I&nݚI&M{.sˋ ҬY3zŚ5k̎uqChժOŘ;oܸ?@zx'Lm|Iӄ7nsEDDDDDDDDDDDD6{kn+Vw|acccX"""""ԲeKԩgڵԭ[ڱDDJDD;>>>TT+Wnnnx˗/[obccHIISNܼy~'O]p`lwZ}߿g}6qe6"""""""""""""&\Hw5~i7oNrrrdLB͚5eΝ,Z iEDDDD^ɒ%ٰaL0`X""8f͚sZhh(FbŊ.\"""XzqD@@?<Ԯ];w:ѣX"NNN*_n7DHHW\k׮DDDnݺ ѣ96nK?7~+W6k#""""""""""""[;Hz2mҷo_O?Āy;vln>m1c5kdȐ!DGGpB *dh""n=eLbڞ0a&LHCv?VZΟ?@ڵԩcv{]xq޽k*6l}aaa1{lu-|2ŊKUg,3Kppcd%"""&\He0_~ѣ,X>ݻرc?>ZbŊTZڑED$.\ȩS裏ED$W߿?UVۛ-Zf|Ikslllj{'P!~ڵkwfڵ̚5%K0h tz`0am6BƲ=""""""bAAA֎!"@Y1DDDDr&\H|r6ms=Yx1}eܽ{ŋӣGkG,ضm֎!"'nݚp:vHzX~=/cdHDDo߾ԪUEq ‹/ȼyIerYbccQFʗ/ϩS8r 409rreŋ鉧'رŋ_fhlll0 ShQbccS-)]4.\ڵk.]ڬڵk6""""""v s&\Hd"")S}ڽ[TXTXL[ogѬY36lH@@////:u1C+6m믿Nɒ%֮]˱cLjѣf͚֎/""YpI*VyիW',, ooo4i’%KxWKD$Ks9r܂ٙ?wy={n:ONBNDDDDDDD… <֎W IDAT,|""""6MJJJdɒFaoo =>>>ԪUZjꊽm>>>4k1DD{͛73d8q"cƌv,'֭:u* C0DDDDDD$BCCYx1>}x 4h@^xW񑰱`0dq7,X]%>>bŊF۶mݻ7+W>ϟѣrJ;sS >sYD`0Ӽys{1tbZDD$U\9G>)3g5j`ĈDGG3oM߾}X"NNN+W___-y~x{{SLqqqaرܼyӬݾ}شieʔaժU,Y2ܵj>`cbbx뭷PTP???N8~gZj3JӓZĉWWW(Y$u壏>իldơ7Em6ԩ֭ˆ ppp`V}ZiYp!5j2eo6KN+^/"""RɚdK,vL\2M4v n߾Mꫯ @PPi{mooog~X۷o;v *ʕ+СYyz78;;`^x1o6)))֩S}QHǼy?~<qaԯ_GGGS ֭[)Rk֬M6F-HLL4wttd4lT?Ӻuknܸa1_ʕ lٲْ[2{Fȑ#-f̝;O?ҪOs)S0zT휜Xb;vL͒S*̹w̹)""""!K/:EDDD8q0p@Ý;wGD ٶ^z饗^YymGҥK'x`ccc>|ĉ[n8`ݻPD ß~'Nh(((T~I0 {5MkԨa aaaY>hѢꫯ"## o6DFF^}U`(U᯿2ϘF^{j7޽b :t0ۧqЧOÉ' IIIî]Lc0`@eWV_z !444StΝP~H}z 0Cbb!**i %J0\x1GԹq_FAAAfo 666L6 OOOkGyÇcooʕ+EDDDD իWӳgO7nLHH%Jv$pɏMDDD-3+\L4>T#F`ڴiiѴiS5jSL1Ս3)So?Az\2E%>>k׮QxL?L8-[}vlmmMuwޥM6ڵ+ոO߿?g6;]hժ%Kʕ+rgggr %K4'**ZjQ|yΟ?-U++ׯTR\z˗/ST Q|2KN5̮*uj8;;=G1Ǐϱ,9us\VDDDD`yVevED$gkٳOOO7nڵkqqqv,GONhhcdZ&M:tcdچ >>Y;HZ;H̶o Xb]RJjsM 7껺ALL 4>6&i$_X1-*T(CQD >:ٳgYh-Ņ[ơCaÆѣTZ5<bq {+ԹqHAo'\L6 OOOka֎ "+9sdOVGH"X|M~7>SlllMDDDDDDDDD2H"\~Wfz5~wy,YB5ƍܼy3C}4mڔlْ ;գSN,YWr5{TW 0>akk9s;vJhh(;qqq 6ue{\mr!BBB25`nb`HpbbboIZ9*YdfɩsqeDn~T """"q \+'[[[&M… /x35駟2_bb"ݺu#!!>(cw ٳg֟իWsu}fKŊի ,_ݻwH\޽{cggǜ9s2|M9B`` wXBBB2˒_~byXXk,}2;}J.DDDD9y$J4???mݻi޼9#)ǏO.$$#F0bBBBa*)ڷo~JrrrlllСYȑ#9z(^^^7.>Zl  REFFbccCʕMeקm۶\p7xFΝcԩ2rHSK/)S{>weԩ@+dFʕ!***U]FReg\?WWWLbb"۷'""">*T`TT)Ge%%%1ff̘AJJ}s֭[yr< d߹2r͛uH~.rK.oeˈ#11;wp6oL޽yy )))\~h[^z%֬YcX9ƸdaHHHyyyX&k֎!"R -Z(ݧ*U 6гgOvnH"&*W .ŋ'}O x" .b2o:t#G駟%Jжm[nݚf<ߟ={/SdI^:&L`׮]&[ԪUcǎ1o<ڷoOrpppPBѯ_?>lq@N8t=z\rS\9|M>*^8ޣF8;;coo+.Nuyvf3rttdڴi0vX)^8vvv,Y 0n8N8W_}eq9TR8::ҠA~Gf Ҿ}{)=O=T6޽wwwR Fرc&%d:WYWFսr{_DDD ĿK׮]9{,e˖ߟ-ZSOaggٳgٴis%>> cggg9&'VX\vǏh"v؁ ϱD?Cƍ)Raaaxxx)SL{(p!""""y_ͻKϞ=;w.֎$"LV֪9e鄆r-\\\x7ySIHH`l۶_~޽{Z47xWx9sYdÆ +V,G+"".DDD$'=h*+…4Z"=z4gϞA,\]])TTRAb /Nxx# )]452-=h0 ))]2ehڴ)ׯ_g˖-ݼy3ׯ_YfYl!"""""y;Yr%ڵ֎$"eݛ7ruܹCLLiŽ߿?/?Ν;_2l0޽n_[l!!!FkԨ lݺ5&"""""""""""""9N8<4i҄իÉ偯;vNjhѢSL!!!?̞={xǘ5kŋO3G`ٲe9r$5Zj4jԈQFqԩtpQ|}}Quԡo߾;uƍe˖T^^xOOOO-l˨QFQfMo6mdVg:߾}@uFݺuqssnݺۄgXyիGjԩ;vH3s.] I7>;ǚ׌qѪU+-Z2~VZ.e)""""߿ӧOS~}bbbID$K8w˗'000o} &+'r={u̘1,SDDDD${s4jԈ˗Ӻukk<=CN`nv _|q1tP۶m`ĉ/_*Uҹsg6nHΝTT)U]ʕڈHUVz* ">>vItt4+V7Ν; 6/5rHSIL>{,Þ={8~8?*U"::sܲe wwwLeC%116mڰyfbbbؼy3mڴ!!!!C?X<1ch޼9!!!DFFLŊs|YO>xuΝ;9~8QQQʹs>}zeWV_fn9sEժU !**~+WRN fPY9Gׯ_gذa}lBLL ׯaÆ?b_t-Y|9N:c˦+l۶4&M0sLe6kmcþWO:ŦM_ ɍx vIi߾=s̱v$L9}4كEI;s ^IZe+V,UFDDDDDDDDDDDDDr7bҥ\t@JprrN:L8???nܸ%K=Ç6m}TLRR &L੧ޞz_[?5HHHÃٳgSjUZ*gÃk׮[ܿ~̞=uL LH:d8a̘1TT {{{ *D L3oߞm]/=ʕxऍk_Pn] *#/_|1113+hٲe\r 2{l\]]qppF,Zjժ_.]Xr%w5KIIaŊfs,]7nТE ON*ULc Ls,ٝa͍߫ERlLܯPB0p@FaHZ"""^*IlmwʫikWc~EDDDDDDDW:.I_pk.zi7`߾}iƍ 65kƨQ@=RsǞ={Le.\8#1 @~R!֖~{n[:?< ]TWjU駟-۽=IIIۻw/qqqԬY3USO=>HVю;Ύ_ڵquu… վ}qsswoΎ[/s={~KD M666?`fϞ'7n IDATv,z_߆}~TB"""8x`}.]k׮3ۈHeoZll,;uTu;wJ*W_agggVi} e*Z(ׯ_'>>bŊeh'IܯvfWRTe 9p@FÇ9|05jD֭iժٖ^q֭[%Ξ=ίʯJtt4!e/ۧSL!$$͛CBBLjbsV'ܡM̶[l6lH"̛7%Jjd|ŊJ*q12tc9@JJDx4'l''ϸCFt҅bŊ7_ʕ+Yr%+V3=5aa_F={e> MdUVΑqiM )^x}vܙ/۷se{1._̎;sfk_HJJsUgg "_]r/f竈'<ׯ_7ڵkǺuطo'Oۛgy3g0a„0/Áfu1tPΞ= >cܹر#S}f=hʍʖ-KƍINNfժUZd5kF2eLms|8mڴIh~w֭cwؑ@O)F[nؘ͛*VHdd$GYf9r䈩]v*_<]vk׮ܹ>}#ٲz2*%%KкutΜ9O>ݻ]xwbkܦ#777"""8v'"=z4>v޽{Yj{fŊ+;j+ȑ#h"Uq%ew.k}{H>UhQV^ͨQy׳c_ @ΝSwbbsԮ];"""X`M41c0oM6/^ǏvZN8Aɒ%SM(αZҶm[>̜9shڴijj"ƣ:)hNɓSN[oa=z4AAA+W=zdk_"""""" :EDDDDD$;z/#Gz>|׳rJ9ri۷SzuNjvc%,Y+VPL(RGᣏ>R 駟ҹsg/oM-(_/^LXX `ԨQTTӧO3={|sةS'̙?~>-[Ю];޽#:)hN8jONNN֎ "y{7"":b >3>3RJѷolٙ?wyYºu>}: ־DDDDDDDDD`8s ֎!""y#InEٲeꫯ4h?^^^|ԨQô=c bbbx}pttL6mbժUlٲ._˗Ã={RZ5}nݚ3{l~'.]Dҥiܸ15㰤hѢXsm6Ν;ǝ;wxꩧh۶-˱l~鱳x<34m ۭ[7 $66GGGj׮M^hٲ%+WfȑӾ}/+nݺ̘1s%T°ahѢcƌyԩ qqqY6ǚV 60sLvɥKxg>>D("b ? ""pl ' ӦMo%{E 3gвeKyT+5ȣ7|pMfڶgʕ֌%"H LDDDr~""MmoKϏS Wj֬Ibb";vrʩ-[@Zq24B$Z~=:trdƍ֎ """"""""""9̙3[;R֎/h…HUvm2d>kɉgϲl2ϟ@ݭTDDDD 0("{mִ򢈈HAtqU !,, ^GMDPJH"""ӊC.D򨀀zAtt4=zHUoccѣiР҉HALppcd#HywٳgÇ7߰l2 onnn <}VDD yH.D5ki&[ǹsseʔ???ԩc""""R 2DD1c#sСt+""*Vh""""CٓuǏk׮+W///ƍqvv~1EDDDD$p!=䓌71DDDDDlܸ:X9Hd>l """""""b=VYA2g…\xnݺʺwҥKYp!b:&\H6m֎!"R mۖÇ[ӧlEDDAAA֎!"""'ޝpGd+%Pѯ-S ըPTjM-6UZZԾ - Kb%$ b,,7 >#̙ϙd|aѢE>|gϞaiig}1(O\\˖-c߾};W-`=zi&@}F'Oj\QN>O?vgÇjlW)S!޽{899Ѷm[VXŋILL$99[ne>tӧO k#990`...xaV;}t~lӧO'00B0Ǐ uIII5F1gN:ӧOIKK?~iӦQ^=RRRCY:^.(^n1]B "##2p$B!q}*Ud0B!BUy$} of͚xzzfP(q|xxxo>F΄ T߿?VRx[dI={888ЫW/z }t2$3efBn޽KVZj|W|Ԯ]ccc^/f" gggŸJll,[netRFa_ou%((dg<`jժ 0kCRR;w&$$ +͚5㭷ӧ:u ///֭[ٳĤ+ HkPPPAAAC!M (_CB\9y$~~~0tBIiذ!666899iMq8<{-$CcccJ*E5hժ wv=<ܹ3W\aǎ4l0ϟkkku_Vm1q۸q#3gΔپIAv2ťK_e>>>{={.ЄB!B\WqmeEFM6UNF-[@m$c?FXX/WWWsࠫܶ[PLeJRG!^7&::۳uVgҤItޝm’%K0PįŋSjU9fٲepQ?>Qc>c7n!!!XYYQQBqttggg8q"s5PM&\3B!PIz B|7h,a׮]ڵ ~wJ.m(_M'$$"##}ho\4>}JDD[ӧӽ{wCV zɌ3~ȱ=z( WnF4o,\M6N>}I&T!B!DQJC 6`dd3&Pdqbmԩ8pm۶?h"֯_ϨQٳgjvt>5ʺŊX !L6]vq]*T@:`Rn?Gz?y$3gϼ/_k…^vN :ϸ^{@Q^cv>/\e(]4AAAԮ];cмysy?>ԩSGk{V^ͯʕ+W(_<:t`4jHkjsViQ zm.KC jlmm4hBBGBEՋ7oRJqppFsMYt)`С[cccCJUh599#GPzuhwA.]Ě5kػw/Ǐ\r3Htޝ_m۶waffeϟ}v:K?Eef @k@bb"'OW^XXX/&M/%J(0B!BUR7omjժq5N>uUlYqvv&""{vZΝ̌HOO'>>^I822R^nۭMŊɓ'TXQmٓ'OTu":'|ӧOUe111x{{ǖ-[prriQ]?4UYtt43gdϞ=:tRJ~zN rܗ666qqq!11QUvZ6o֭[vr8v$%%Ď;ؽ{7e˖'N?$..NU̩S8uk֬!,,*U% )RRRݝSN}AAAtMmpuƟcYŢ|+(Y$̛7Oc?t!vuM\z5/^`9&[(4i;K.eʕL6Mίʄ Tccca۶ml޼O>$m֦(ߋiJ&0tB')M6ݳBE&L͛ٱk. Bݺu177Ą:u0j(6oLٲe9v^^^@BBǏ׺<,, 7BRxq*VHVXlC%==OOO#*UD۶my)wζ]x)ڵRJ04[[[ը?W޽{WU֯_?ݻի B!B]֭={6111$%%1k,lmmOU~ӧO̙3r~ڶmu놭-7o&!!zamm hƍ9n`֭$&&͌3nm_>rT̔2BÇիqqq7+WѣG2dɸr5OZb W^eذa1|p^Jrr2nݺ3sLUPt钫6ѦMوbbb۷/t֍ssΝ;Gn݈whݦ;1sHNNV;n8/r $$$~6lȵk[Fy=9+W8{,Ou:tʕ+\rEmم },YRygg76m:֧O.ﱷ',,$Ο?3ϟ?gܿ?Wm֦(ߋDxmg7oΆC!(^ !VDEEQn]C"(bn߾͢E !66ӲeKFị̆؟={KF\\899ĉPB-ZDٲe܇5&L`ҤIlڴ>L#ŋs㣪fuCjԨg}[%3ƒ1bѴlْ˗ɔGL/^`ٲeٳ;w`bbݺuS{z \ ߿o̐!CT/3S\\7nET\YUjժڲqFH2e !B!y0x`8s(_|Ao>nܸZ5kjƠA>}:3fHh֬zңG,,,رcGtppϏ~~ VZimuքp98Ν;-E!2Zt)S󮽽=)Syd;,,)S/m-YϟӧO/^*o۶-6lE[~ /ws,XgXc燣#g…Zծ];X۷oСGQ0k,ʗ/LsppLJFm6-Z2(11(|||To;VkI&ѨQ#ջǼoҡCu6mʮ]h۶-aaaƭ-]ϧ3srrbΝguƦ|*I)| 4`߾}y6l_qɒ%L$$$nݺWWWÉٓM6S߿ggg/޽Kjj*wϏ]r,yiKXXϟ?ӧݻ>}V̙38;;anܸAJJ qqq?]2DĊߏYSgPX1 ޽{ ӦMĉ<~4?~́߿?rMҭ[7vÇIMM%""aÆi<()WaaaܼySk7or1ʗ/۪́T>iii\~SdD\ٜ8q"ϟ'11Qcv{ .dŊiBBgϞVKmdڵkIIIΝ;Zcŋlٽ{7 Z;qjՊ٣ B!sI@bŊ^N:QLLLLxw9sZG2eʰzjzMʕ166r׏5kd;NVz)ShРbee(Ǐz8qիʕԔVZi툗vkG}Dɒ%9tv:DҥرC!2R:u{xxh]>dl9>y~G}H*?rHummmIOOիDJ.CK/gW:8++ĉ ԺQ4}>}V;/;{yy백!==۷o-{4J,{ԩSIII믿k׮Zo߾FY^ڧ$9N8QcŋkNz>uΝ;ʕ%\uƦ|*1+WNX.TK.ҦMrY)k 3Mѣ>>4i҄Ǐ}v/;(I6FwStIB!B! Kj՘>}:ѣ[?P^r0a;vkdG;;;~z9‘#Gشij3Dvԉm۶q1vޝ>J(~޽{9rϧRJZ]T)\]]9}Z۷osi 6SBEDDZ;,СCvueÆ -:69u %^crfJ[ffK/UƠ9J2ѣG0`5k֤f͚ <???$Ɩ>_fԭ[WWW={vu};-Zh]'ز%/Gkjժ R]O<ԫWO뵨(\WMT~c $.mxEQQ! 9x!gfРAooozM޽ ]v1k,f͚Ů] B!Bhذa<`ȑxzzRV-hڴ)ӧO͍8֯_vÙ7orjvggg2oooҥ SNF/^-Zh֟IBB,^zabbBzXx1bH@c 峲 q.,Y&(m9sh޼9昚ҤI̙`^M6#Zlŋ[.&&&4lؐ5kP~,׳gOl٢p0--͛7g[ٰaqqq8880|ԩj˪UlϦ֔.]*Ux V+ <LJGѫW/N}j*sz7n^1{@Σ=|@mNRB!B!ty}}+Q?۷oΝ;ܹs۷?h'I!ěC)Z;+_iii^p1c`ddiذa{mf˗X{?Y'OxĖ_~)7ׯ_gժU:GEE1v-?篠}:ʲ̲VNz|t9yXoN } bر#>>>: uf5ޤĘ{(peԸ)SqF?%)) ЩS'*UDʕqttd B!t4J룏>ڵkYnӓ[nQV-~7_ロwr3LVx)(+em$s%$#G^W_ѦMڴiÄ ؽ{7)))z-#}իS^=N8:qqqkBRf8PWn$%%ѷo_f oŋF_ӧO՞*<~u;VO_j֬Yr%.]bǎI- }[:y}/㓛6mPlYIOOgݔ*Ug(؆ 1K,ӬZbŊNV]XX("Q0޽{ӻw,+n666+V  -WnÇ2ޜx![n5`dB!B.dUoӦMST)-[uz΄ 3V(𣢢rU EI6"9י^\2Anٓŋlȑ#ԩ7o[l)HIIQAҲB8\UR%Ǻ888СC? y\9M 4ЊݻcllLpp*އw^U+Vmg5) "l(QB} ʖ-3+W?dРA<}s\OIٱcF"ɓ'|讨X"}4$L#B!BBߩʜ9s3g3t8BHΝ6m}||022i9s=z0iҤlrJeΝKKKUOǎO>zܺuٳgSX1Ə*W5kƀZ/^`@####Ο?UVe- }~)s(==]r|t=צxtؑ;wͽ{СC- :ϊy[ݺu ))Ν;{qrr"99#FfzlΜ9eiii̜9x=ڬMQ'\d'c"F>}T3&iݛJ޽7o:\̢E8p ,\GO@@5B ~5JB!BADEEeuu###:u*FFF̝;W5jIVmF-ZСCU5kW_}*S:ng5ӧOb˯N:}v:̙3qqqvܸqSHl9)߯oN ڿXsl[$Soѣy&VVVxxx0c .]޽{ug^i*UкukRSSϏTڵkGJTuVmrjKV}U ]nݰe$$$P^=C䶞rŋ~wlmm>|ƍg6Qʔ:B!B!B#GR\9>LΝ9~8III߿aÆ1p@/Ul߾?9θ;bXz5=w%!!}fpssS[gٲeҨQ#IMM%11s믿bkkKll,&MR{2bʔ)ѣ$''A=8p%K#PIsqq!((xRSS9w[^_AK WΞ={pqq… AϞ=9zhb|Z?+Jǔ)S>g0() 4Yf۷iժ&88Jll,{͍֭[s&U(ӧ/^$993g'Jڵ5fȪ{(pOC B ZXbccYb*T/vW\Ɂx9Ϟ=L}ZTV^z1c Y|9-/3[[[J.MHH26@yƆ G6?ٳgƍGGG\r3J^ cٳgն2ի*b̀1l6J̴ Sq! 43fЮ];Zl'͚5ӹ9s`ggG}ĺu077gܸql*=zk׮n@tJY۶mx!B!B!kzx{{SD ݋%JC,[dϟ~^>.]4FFFZcѢE/PjUJ*Ň~iٲ%~Z\߿zqU<<<Ą%K{1abcc2e ?5j 333mۆ fffذm6RWʏw} .ЩS'J. wz[^_AKʔ)æM(U[laÆbcc_ł 066)]ϧcddDtt4¼γҼyse'լ);/^f:vo *U⣏>bڵ3fvڕl!>>>bffFƍٵkUVe۶m{(p 3~V*U Yp!/^`…$&&2j(UƓN:7?3+W޽{ܹSU'<<:uhoeeVG!0ݻw i߾=㲶?VVV#I/\x>QFe{{{]|+++ڵk*kܸ1mڴ|Z;+bbbX|9ŊcȐ!re{K.:]t͏vaeeu~ "̌iӦ c)))ԩrpm=X":::RlY.]Ķm۸r ˗Hg[ر#K,z),qe5pa7'O8%tAѣSLAbjjnnnj/r[tҬZ WWW,,,066lٲ888f)ƏO166VGQdI.:tҥKa!B!B!ȉOݝwyʕ+Gǎٳg#FP> ?|򘚚ҠAN5:44jԈgϲl2:w&&&cmmͰaHPtڕSN1`,,,(^8|gk}e˖ѣ|w4lؐ%JPxq֭_MxxFgj}Ʀ+ yi_֭9}4}j6oެ %V|kcaaAӦM`[ugetܙRJQxqjԨQౙ2o<.^ȷ~-e˖ؘcggǤIr [ 2qRxqԩ7|ٳgytj6E^$(l{U9YiԨ]taΝL2˗/2 ʮfС?~\իWTc IDATf)uB! Spp0nnn$$$:!D2`֭[ǏAclق1ǏW̺uhРgq~z6oLJpssTR>}ZP6ӦM{9TVnܸAHH+VѣG|嗼j\v-aaa1oZjqu~W;F%8p`a׮]Yd Lӧ$6]_N^@vZr%"Oz|t=*[usm喖Zlխ[_~E;?qĉ\ͮY_EB޸ү_?Μ9˗Q7nAUO<^L)S!Bh,--)^\~BJ*o5#GУG:? 6T}VxbI8KKK~';/^ŋ5mڔÇըQoooFkؘ/cǪWZyApp0j˕81bdРA˫T65>cJ122͛@.СC\~>}-6mٮ<%K2w\c:t(xyyi${xxxsN]ƙ3gʐQ^RJ`صkvR[g̙L81đ={a On6KfѢEsA<|ZvWF8vBoWˠAˋ &充 0ptB!B! N-ECB"J,Ibb"/_^z˕Yԛ7o^ء !N޸pgPfeFHOOWȚÙ;)xbŊʔd,S(e9*BQ\cGS!ě;wtRBCC>%Jq|iF7tއ ,^pY&ݺu/Tckkkc\xbllLjհgԯ__>?Cvŋ9r}={6vvvqUVX J!7.ᢠlذ7n:x{{6jddDzz:,YȾ˗Q}x"Ȇ˗'668ʗ/~\\B!Da֘K!kfƌ}hт-Z贎)}o߾ygݺu3gN+r躼L2?^kˆMݶonf K.iڶ駟駟jղbUg(WoT\9mٳ':lkVժUcڴi:jԨիoC9rmԨQ4kQ !B!0m0BMzzCBqs?$<<?Pc3fBH…;w4h1uTh޼95lٲs.\Ȋ+3f }Ŋȑ#<õnwĉ,\rѦMzꅉN֭?qvvV[dɒj*_B!UTTu1p$B!OOO @DD XnddĄ 3@tB! BQx{{ӿC!B!BhKX6fƌQ]vkNܜ!C0dȐPtilc+Q]tϏ{>ݻŋݻ7nC!Bߊ+Fnݰ2t(B!Yb֭[PR%lmmqssiӦS!B!BF.]D.!,, {{{z] !B!0p֭dΝsN*U'|bB&jԨL!+BfVʤI xEx{{b0B\aY'44a!%xS|ׄpIe,\M6N>}I&T!B&W9F_~k׮߿333G'B!B!$vvvl!₝(;/...E!^{A"!$$Dkybb"'OW^XXXУG&M/%J(0H#Bd$/$\6lH߾}Yz5aÆJ!B!BiFѣ B!///C!=oooo0+@իWs=*ׯ6l`Ռ9Ҁ !a!!-I(b:5h.]ҥB!B!"{III̟?ѣGӿ B3I(B!ț/^l2Ý;w011ʊnݺѽ{w \ ߿o̐!C055Kɓ'cƍhтʕ+TZ[[[6n)SL!Z!WWW\]] BZ !J2Bkva7nAAAC!H'OǾ}O5tXBJąB!n…矪)))={g]+99Ç{VZŹsXp!Ŋr?w&!!VZi,kժǏgϞ=SB/44: !EԫпU B! /...D!x(uNNNA!huNLLdΝlذAQreyn޼" !xlݺ!!ȇVj0xc`lmmIII! 3fi&U뉈QF|XYYquOپ};ݻwɓj3[(> ᆱRvaIBqnܸ<BU_򪐄 !K3B!yuA!̆ غu+|,^=z0rH]p!BQvZBCC~:iiiT\;;;L.] b022 ==]oԔ *дiSO~\NR.\ʕ+ٿ?SLر#C R]|9&LѣGˊBĄ3&.[ɒ%y^^^DGG@^իZ=eFӧSZ5ԩ?@ {YҥKԪUKcrQ!ěHF0BѫҿUZ'\7q: !!!B +++C z9r 6cԩ[FҥKԯ__2wޅBFwnnnj,unb˖-899aʖ-k(_?H`` ~~~Jk,))%K e??~3g2vXO?tPmK7cǎ$,,0/^;ݸqydddy!eʔX)uB!B^ۄ QUQ(tuD!٫8"Mǎ%T! dQ#uq6l3aѸRvm}Z :Rhxx8 .dӦMӧOiҤ"B!,wޥUVDGGSZ5+>cj׮1W^חYf3/ھ:-f HMM%66[2~xXt)#F0@%%%ѹsgBBB011aҬY3z->}ʩSbݺu̞=&]dEfo*3[u {{{8mXhׯgԨQgٳg.Wfіħe7 B!B<5BaP8!Q|999d0⍔H'h-OLLd zIDB}fddOxx8TVO>aÆajj 4=66Ǐm6ZlIe5jm4h*Uzg-'**O?Gew%88Pvܩs٠6mė_~I^^\v.\ÇB lٲQFQjb:t(M6U3޷onnndffeofӦM޽.]h<ɓ'ر# (0t߻w/"226mȲOs<~X.&..86nHLL oVV1~999kE\\<~ӻwo:gϞw6]t]SXzJ搛7o֭[ٽ{7ݺu=~;(ρسg~igÆ <{QFl!ѬY3Xf |j2/f !,,Y%eʐ!C?>쳷:HΝ;_|"gnnNJ*<|WWWj֬޽{i׮; *Dž۷o_֧'<.W uX ay+ @ bѢE*/E\p`<==:t(\x4兑=z`ӦMܽ{۷cee7oQQQfXbׯ&'' Ο?ϼy 岳5j6mp7nϞ=+CA۶mmۖ >\'AyC>s?~INN&33h Fvv6ܹsG^~~g߯^ȑ#cԨQ\zl=J 8{, .;@^Jtfff|GS%ԻI޽IHH ++zӧOׯwxL///>;ӧO֖l͛";~x?~INN&;; """hҤ ׮]cΜ9e2_IUINN󤧧tR/-- Nbb"YYYѩS'mhEuFhh(P"1::G2uTo.mٲ$ɓЬY3N:Ş={NeǏ{ I2@ YM?@ 077u֬[cǒ_lja֬YL4 yիʢZ LLLh߾=[n`Ͳ%euq}:uDhh(M4Ԕ͛sA[.㩫2͛7СCcjj{Ν;ܹ3>d͚5Hv̌VZxRʒOrr2M4)t7]K IDAT~UVѤIvڅ^Zy@–-[8q'N`3F-&L`޽|8QN>sW0 000(^|iӦѪU+T1UTёӧsʕ2oEn:/|W7oĉzj(+]m'-Z`֬Y;;; Jl^i{}(ͳb<|={e={Ço'NNNO?)QMK{{R,]?NllDyw122~̞=Ο @zب@#3FaÆ)==KY`~) ѣ:88V D~`N544dʔ)8p@c}2GGG=zR^N*((7n6mJ~~QiiuSiҒs璓7|Z>w^LVؘiӦil8]]SWyeOJ&MR k~w&4t-Zi @̙3P)SҼy@ (C F**Y&_|E9j&/R``2667ミH"@ ^,ZRGap,]|&N 7.#M"%%Cj\py&7odΝٓ[/%#++ɓ'b 洴4N>ӧYp!cƌa…1bĈVAgHKK#>>xXnzZꓸ8رc111:`|2d'O߿Dz} "..BA:uݻ7P(عs'<|*Uо}{틉 |c֭[ٴiӽ{wϓ'OX~=gΜؘ-Z_֒%Kشi.]Ą6m0h ͵˳gرc'N 55ccc֭;w<}cggGժUjժѴiSߏ3*T(^._\*0ꫯ*q3g111İj*\￁蜚믿lÇjQ<$@ ^uYfrR$|M1k׮ѠAn݊~W_VVVci$}%CH GgkӦMӓhCΝ;̧~iLY_aͩ]6xzzҥKAt9ZlN6m[.㩫2~pW\!11SNqIzҒ'O<NQ;xb*<獶 y‚ٳg3vX ={駟T@Pt,e}+Q:@NNח۷3p@@5iӰ̟?_:ܧObcc4f۷s=_@  DHHHh,l ˗/[nX[[SzutO?B(-޽{iӆod ++/2w\*Uqqq!77~eʢGOɡCHII!''СC޽;YYYx_䋭޽{9rggg Me9p瓛˭[Z# Ȉb Cxm ɬY8z(O}ѣGٰaCښ={6ϟ'++'ON```Ν;s( .]Ě5k_dǏP(h֬15kBĉt@ x yЭ[78YQ(\={sNY'O_ʮU8brYygr(]WBJ J*j2oI3VHѺe^Õ"k3"(>}q.C%44T%7ظq#}I&*~K2?Mkf$''TL}OO[ʕ+6]]SWyeTR}vlmmŅyNjjj稩}]_ǔr96lqj4KUxQs[ -Z0zh,Y’%K)̘@:"9X4mTFd_~*<޽ҳgOgϞ<|ݻwf@P>xzzr>cohҤ ҨQ#f̘'\2;W.o_&OLdd$ɓNTZ.]aN{{{-[Dy믱yL8QŐHP(3gpu/_̸q㞋n>'ՑF۱Ce<6'~Pv4 ݝ+WиqcfΜ?aaa\tIށ]ر@%{2s[ xhz-OyҥKYt) (7=e٩aѢE888wΝ+}"@ ʟÅ&ܹC;vcƌܽ{̙СC0`GfÆ EFdƪUbEf(McnKPĘ1cLJm۶)NŨZj4mڔ(:@ ׯ3b֭5kdZ#&I8E} ֘bkkԩS8pքogg/֭SE BZ055VZ :TkjveO>MNJ*h5INNfԨQ4h333*WL-3g֗OV˗Yb:t͛y-Zo>LLLXzZ4m]^w%mj.`u?ֶ% ˖-CSZ5zEdds&%%1`W)ݻWκкuk0^Sѷm|||4g֭ШQ#2222E_/˱/j~ <5kbbbBÆ <.zdJz3O_N-P%Wm ʥߩ#FbllL&M5jq;v,666RvmzާÆ ^zЫW/>|8o65|_O[_~%5kԔO?(>کq߾}ӧOŋ~GT @FHe@ :R6 ]) `Μ9hXHUhEk.=z/m Mr`Wv- ֭'ZZEY=O9?;;;Re5.md}ʌ3ɓ'p1bi3''9sFJJ "Q(4kL8͚5CPT #**>uq {.hѢؔP޴i[p.\HT lQ4F.СC*ޞ@n߾MNNofӦM4o\&'OҡC"""P(w^ڷo/_J>}-Zf._Lvv61{l7oν{L:~<{#GYfxyy3wRo}YW^5 =@%է&?~Lv0aQQQ<|P6Zۿ?:u_~)qGxx8l۶Trrr8{,{viZjL_}uikʕT^UVػu>>>e,--qqqJ*DDDh5r TZ2{M߿{{{6oݻw寿bܸq[a^^^^őQf^n߾ {ァR^x 8Xq㫯T`CS 0ԩ4m)SΊ3 KjutEu}J~FO>رc<}Hlmmf޼y*Ǐ >d ""&Mp5̙Sf)Id%_ )boS[~V6S.*ZËft壏> ..H9___hڴ)ǎ###BALL mڴ!??_VGZZrDSNx{{uYYY$''xbxo*2\Sk[ժUcժU%;?Ì3hٲ%PN~~FCI0`e:HMM\x,Ξ=K׮][Wɜ?t/_'90P7RSe\SV裃=2-ccc{I#mۖիWMFb۶mx{{v<5H (s̡qƘ Dx7oܜÇhJwѡCLLL:x UfA7n|9ƍ ֬YCF`ҤI*XZVqm)[|9 6Bvf(.~`ŊM~~VPWWWf͚Eƍ1553:T٨bŊA͚5122ʊ;qF'RV- ,--5fbŊڣ@ :G7уSNP(`ȑ 4ccc5={>/x{{cffƆ 6m###~M^2mJ]jժqʕ+撙IBB/3c TT\]]IJJ";;$\]]Ғ1cƔ <}\5/k>7|pjժÇqss… 䐔D߾}Zh]t֭[mOOO9ƒ>|CҮ];ܹCǎ\ۿ֗͹sO9vZf 44\\/@ (k/R/uX  ŋ^ׯD[n7 ::OOOĉTZʕ+DEEquD sĈrz&M0|p9 DiڔǎK]6>>>|WDEE;oq2m/YOb޺ukZnMJXl~~~E0k,&M$/bCA쬬,ϪU۳uVZl͛hC%eהӖ+3yd#<<GGG9СCYj7ndΜ9*yyy!dPc5k֐NϞ=ٶm\ެY3O4P/kJ;Og̘!/lZYYם5jp O}\Pښ۷o燵T\txmUX'''|2111i&XrܿǏ?eAM4+giذRk™5*M/$RSSA5Ο?Vּys{J2H^_INd8|0f̙L>q...*~aeeń 0aBrݺuHTPu떜֭[3r"uUVZ믿ҪU+5+WdS[ϝ;'JkBr6oذ!+Wdذa,X@Y6m:uJlPx6 IDATYf8_B  #,,LeAAAԫWO%aaa\pA xE7}yUT۷ӽ{wvΝ;U兑N?,_/[[[[k.ɓ'3bVZ^ 0sLmFrr2O3J)cʔ),_7k122bر,\333 aȑjA9jԨAXXZ6-Z\-X<cSj;v(o%ep2d___}GFFYєuÇRfMKvIHHP9.@ (s%ȈN:k.Μ9CV8{,O@wccI^_[~֕ybK9&]u;w^LVؘiӦN8880h AW˗Uhܸ1SNҒ͛7#.\СC|'Cq-dϲ{MHؘ)Sh|Tzv~%nD۶mپ};֭㫯~\|u֩:::{n~GKիǣG_سgM6o s玜,E-M2fRRR6l:t^zr߿۷+$<R@zQ8H_$DŽwOi}¢DE[Jݝݻ{n9~8ӧOgɒ%̟?_%Yy0dGPP'O >k֬P W={… ݻXZZȤI2;]|Y6hذ! , :: ׯ4iFm;;;Ο?O`` ܿ###֭>>>*N8;;ǂ _IMMښ]2uRQʊh.\HXXW^%''z͔)SԜR7]Ek׎xΝKxx84jԈ9sгgOrzյtO}_b2{ly뭷pvvf̙jkÇ̌e˖qpttdܸqՋ 2x`V^M߾}u#SSS-[9rK.S*UDFڵ+^^^[QѨQ#K[.}eTVMM~Æ x{{EVV5j(/#''GkQ@  S"11QeEꊱ1!!!c:uh߾=C 夬ׯ'""*T=FXCA6???޽ub*:@ ( s#aH:wLXX;Z"** CCC9DDr 5jԠe˖ԩS 0/5.MŽVΐ!.l4\Vҗ@+IIIB&''k7b]F غuoA9U|QXYYǏy7JTG{arm"%$'`M'r:uйsgOU J2_ŊIOO'==7|6$e!Vug]y׌HN.8\rDN:ɓ'JOH I-[XM6Ŷ|v}ٹs'#88XZ\})}wjժYkVX,1dNÅ_{M\p(b֭[{ЫTrҫW/;ƭ[ԲO>>}K.1e}fff*ץ;kkkT{aQWJhKSӇkײ~z֯_"ޞX/r6O={V?jSjURRRvZDj-+#ے7 ״LƍIKKݻnKi`ٌ;Ag~'N7@ ʼ@q?zѾ}{#1|pWM4a%/n=A7x3rM+XeU_󃂵M6KN>Jx*Jf@~)8q6_-h#((H,AIؿʶE# u@ݒ^ Szu>HNN&66V*̛7AѱcGc啨-)҆d4&ɐ4mJQ2 Ql۶M"JHe AYQ֯_OHH*U",,*UHJB2\xbrssUOmRomt1:t(*oܸƍ۷/M4QɖTZݔg$ϗ$_XSR9]YW^5+W^l۱͛'G}C/m!`mmM@@.\Օ\S[Mggg:u+jʌ"1h سg<~ݻccce5i SVYUy_^ܓC4annٳqrrRJacc#gq111e---;w.zCCC*VH˖-U+xzzRzu ]}E-MҥKyw011Ą:uЧO߉vannΙ3gԎ{,--i۶+T6mrif̘kCA\\ܽ{I&a`` k~nm) {BBgΜ!55<,--/K-Z0zh,Y’%KQKg/@ (=h $9`@ ː!CQ:,?BWuF||QwD||wܑ#8::{n~GKիǣG_سgM6o ܿ۷oݾ}[A xiii:gpcǎ-[EZVn%n߾=:tVZ+%Zl3[l nHOOWqHKKu+ >3>3o~W;ƱcǸt/_fܸqٳugvӧ՜ B2fșfplUZA~֕}Bdd$=Dpp0 ܰZj{Z3)hBꤥi4OOO/qVVVl޼:p)fΜ>הw=z(p*ۿ?={(oӵkWS(w}WdZB[[[IHH]vݝe˖j*Ə5Į]x葊-gΜԩSCɓ'enݺxzz /l0_ֺ3~Æ cѢE_QFaggӧ4oޜnݺW_ѨQ#Xn P744ٳgddd9K)BoeJϺR^LqɎEESXz5#GTwg04,:>aggDZc8sFY}zڵk˗/gŸ9sM髯off& "77#FvZNBB@Plڴo 6ʔkI&DGGA$..Nce:FP0l0ڶm%o(p<ݛG/Z[[駟vڵk8q"QQQ書W_aff2iܸqڵ[naeeEֻJ ݺuve֏?`ȑGRRPp?駟y @ ^o.]JΝ9{,;wVo``N~e ''~@ 2(gDa@ [5fP^ҥZlOOOڶm9&&&4hЀqJ s̡[nXYYabbB۶m tiӓիchhHNNPIpܹ kkk X"-[WXѮ];9s挚gΜRk4q@ (-Onn l'NȹspuueƌE!Ek 7IHH@wё]r]OFFc߼y%K`hhĉݻh"={RٳgCBbccj4~n;ĉʢgϞjΝ#-- ???6m| YYYt҅:KNRk? ֵ~p?kE^30c e5(bէ?>3k~~J|>̛75k3Ǝv|})}wsҤI矸f۷믵w޽\2ϟ'((DVVcM___k+}6w_y#_///ݙ0aXYY/Ajj*DDD0z"י˗/8ַrʬ_?'N0uT5e˖iiٲe\v@5'x{{cffƆ 6m###~ www@=ZڵkV???\Bnn.$$$xb>3f?TiRJDDDJRR$%%Jdd$3}(=8ӧO%!!A6R^3~P`gq Zn͗_~ɑ#Gx-[1(H1 /_f„ \ֵ 44\勼f"77;ww^w/&&&cl\t4cĉܻw<|8jøqrrrHJJo߾DGG+`eeŋ!88Xe>הÇYr%o9sۻw/nnndee333c۶mG\riGhݺ5.\XVZ?~z=t?<͛ǂ 4ꡉ̝;###,66ۣP(׮]ˈ#Do}ɉH9IMX"7oVkWӱ׭['wafΜɶmHNNo߾O?~ݻS:xyyadd~СQQQԮ]?KKKy>sA_}ui!--M@VG_c3p@Y˂x班-ƬٳgǏe1p@ 򦴽cy[ Wg4+o  @ (?@ J([G,cnn , g @ٳ',\p޽%L4]ȗ0^^^4lؐ MFFgL4Ioϟ'00P߿u }m:;;ǂ _IMMښ]2uT{=CVVVDGGpB¸z*999лwoLsM022oeĈp!HKKĄz ;;;x1,X?~9sӧOi޼93gΤW^Zu[~ްaDEEE5u1+Wwߥ[n5zDu˖-… ȸqwaU_ 8vǓۜ)MP$sDֺuըQ#ըQCΝS:u[HgQF:qf͚/RU)SRpO+յkWc Ȓ-[̙3D@ $+\Ysٳڵkq]xQ/uy}ZE[o)074:)L0Aǎܹs"]rE*W&NT~Gڼy$Ν;ǵvZ*UJm6%$$I&oҤ9۷sOq=rٲeZle_h߰@ PڵԿ<3gMںu+BF<۬xŋ/^y,y̘ o,YO۷okԩ>ݾ}[ӦMG<}i:xV\)'''OZv%I5kLw|j[j߿KS7믿}jӦM&$$_jܸ:v@%%%Y>^+WӧB2N+\ѣG>СCetx&O>rwwך5kdkkkt$s(K^^^F INNG2d>SNJ*6lQFi_}UIҏ?hiv횤VxXj[j ӧOKԩvN۸q$_&IɊ֭[g)XvΞ=:uhĉZbcc5{l9rDPLL BЬpEcmmK_WDD@kȑ{YFȘRRRdee%'\gH.$#S8)-]jEڵ3iŊӭ[K.Eҥt钦ߎ;$Igϖ$J*6m<<<%La+ӬY3WFRvTT)#@s=9RZ`Fat$(GiW^dR6m$TQ$WWWF!Qsk=zs1gŒ%KtժUK.]R@@@o߶]p!Neʔ$mڴIt|}}3?ӧu];vLgϖ$kҧz꒤tǧ9m۶ںuNuΝ; 6l`闒qnݺp5w\-YDgt(0~lR{Ν;չsg#@@ O3zInϞ=ynDM2E]tɓ'+111S(n޼iӦXb1c)btMIR-$I .TյkW=s[lYG5һᆱ+WVZ֭OӦM%Rۚ7o] @e˖I8111$(&&iJVX5k֨w?4|KGGGI͛dK}9rs@ӧ6m޽{F|… jҤ~8pnQps\}͚5e˖d2),,LZJW^U-m޺zVZe`2y߽W?Ə/'''IƏWZV5jtҥKFM4є)S2wj֬)rʩW^Ztlll,}ڵkbŊ):::*Q|ͧ>7W#6buN:d2iJHHPj,Ikٲ$iҤI:wݻ~M-dҠA,}SW|r>>}_%rJf9::p*UZhիWyرcU|y.\XIIIX H'OFmtwչsgC_|(YF?!-((Hjժ7j޽z%I+W[f.]kʕ:xVX!''']pR(+jƍڿ'Iڵkeƍ%I:xky"u$}"kF(UT5cƌl?޿$f͚MmKMH+>>^6mlmm@RLL^z%IΝ;ݵvZ8p@кu4tt*UJG͛ua}7/_۶m_ڶmpuqYpգGV!Ŋ֭[;v򅔔:tO*\ѱ ߢgXԮ];휶o>IҸqTB+VL&MRLL>sKBI&W^֭'Jtc?^*TPѢE#I?,S8pҥbbb=zMwFΟ?/鯂UT)MׯO~~~2d\\\r+\Mo?82Zp4i$#@W#O4[nzAUV|]T)I-m#Gu!:tH|'ٜ姥8}ZTN sCӦM%I~~~rܹsd2߶kѢ$i:y޽'OjΜ9͛?ܝ:ud땐jժYRƫc\|c)SFi&%&&ҥK}hիKbccӍږV``l٢/8'l۶M͛7W5MVIyo߾ڱcN<:Zݻ]|YKӯB ڲwޚ={|}}E}O'j̙2:X\򤨨4;w4$fϞ~K5~Gr9^۶mնm,agg &h„ ퟕhŋl6kŊS%Iqqq:~F|&""JFG|A3gԘ1cԻwoըQHk޽*((HK.Հ [[[lQpp%88Xѣŋ+$$<1٬`cYڵ1{OV7rŭ[h}rss3:<(k&)Gl>>ڲe #,Y?ZdQ"##ժU+L&ڵb ȣ(.88r ˍ<+bcc%IK⒮˗%Iu…,U,i2<OZk;;l1K*%I~ǧ9rOCСCOԯ_?fZ]vMTdtcCɒ%իW/K;lٲ򒷷5kBx$<5vXM:U^^^rrr2:Ç_~ dE vIJIIImeen܇<֭[#u}-[Fq_@C/^ԉ'kYO8bccyf9::fLC2\JuY} չsgI\7n>7nܰrZŊ5aM0AgΜQhhBBB秚5k{VjՌ %ŊӢEe˖FG%%%o߾Z~V^^z pΟ?p=ZGVxxx.曒9sӺ{N:9sI&ܹsw~7-ZH&I M]iٚI2Ź2.:ud땐jժYR3W.IM7nj[j Ԯ][fŋuAiFrvvVÆ pBOF䂎;MÆ Srrq<_zK_~lB#p@.3f٣t;[N)))֭ꫯr+ӧ#Gwɓ'գG4lmm5j(v2et5yzzQ7oNd2=r>=i+'[gϖ|}}wqq|ݴiSٳGON.IO$5o3J5RF`} մi4fhBfY*Sq9_jŋ5fxF]|YnnnW^yH' ={d؞)SK.rtt<==5yd%&&rJ91110iٲerwws="EY3f̐$X(QB+Wl .RJEZzuƎ˫pJJJʶsy yqN5jF666Zӧ[m׮+tFGGDK#.\Xm۶ժUtEDDL2:tokݺu= ʕ+״igtϠ'NqƒRlUJJJ!+++l6g'fvCN~ٹj/U\9Iҕ+W>}h`(cGvvWǎlOgҥZb6mڤKᡁ'͟gƍڴiBCCsNߖڴi#kkk#;wTV-5jH!!!F ٱc:w hƍ*]ёO.x_USNUVԸqcC۷odNJHHոqcuQi1qMAb IrppdRXXn޼g {e2~z{ 38!Իwo988)988Xѣ+]zJqqq={.^(777999iȐ!ڷo@|V/Vhhvetψ SNںu+Qp@&n߾+**J񇒓u9M81]ߤ$ 4HAAA駟x\R#G9׶m۔&MפI%$$hvnWJ|}}ժU+kRLLʔ)Ӵix+22RӦM,{5l0߿_/^ԨQw^5o\*U￯'N:t:hСJNN6:n̙۷Ǝ5kH 2QNNNZr}5{lYYY 71:1LYj!.%:i$+$M}ylu-yyy)::Z:vht$@6LJի=t^|YX.\x\׮]$,Y2ݾԶ>VB5lJM6UӦMh"رC:uƌ-[l6SK6:. 4vX@q)..NvR F\WH]"'WLJJJ팖{nݺc*T(ݾ6$/VHH1PfYFP [ozKڲeBBB{ޓfdgggt\lmm:hjٲёs*8p@UT1: Qp@&ŋ:q^{5K'uttTll6o,GG'^W\э7doofߍ7,}(H!@N1:bgg.]K.q6nܨu]ŋopFHrssSǎ5d8qBFGOEGGC5jPdd$@Ex7uE͙3G3gT5GsI׷e˖Zz&MǫZj~֮]>L&IK.J)))|*T I^\tbcc-} Qrr1Ȳfc ҥKo߾۷\p}*W&M9lѢEU.\q@>^zUk׮ee3(( >>>ꫯ?O>ۧO޽['OT=쳵ըQ,eʔѵk)GGGm޼YM6՞={ti9Ӓ͛gω>|X/_6:|B jذ1 aÆ[///-v>|K.)44T!!!Zd*U$ooou]QTre_Ҍ3d6U|y#G͛qiذa?>+@G _9l2h޽u떪T=zhԩ-QV\˗k׮]z/. 4H/رcd]rEIIIvi…V^䈎V%oΉ`ŋ+$$)٬`c@L*UhҤI4iN8kΜ9Sʕ+)ǏWPPƌ0ݻѣG+ @~~~=zё lٲ:ujxI?TR=zc?LmVm۶MVxqfXBqqqg8p+wO<< #>>>%>rKݺuUn]j Ղ 4yd5jHڵ^|E@gkkŋC8pZlit$yXbbz表(H\Bxו &m۶*QbccGIի[[lQppƏ/I z葭s@NU@B'VVVj֬5k jΝ ĉ5j(iFPҥ 5x`;;;uM]]v۷޽{FGrʚ8qfΜ8C9ƍD:x b @cc*THfYf9Ȉso>i߾}ս{T\95lP}s$)%%%zX…UdIUTI[֐!CTr͓ԩ3gѣWc=zTW:ut)Iϫzi2[9hѢX|||S>y8󒤟~Ik׮աCtY[[륗^oݻTRig6NV2p:tHԨQ#u5WVL;zwO?)<<\j׮^|EuUfY 6)7n4zh[8͛7[o֭[%J `V)\zUj޼/_s)11QIIIauAnnn?[ ܻwOׯ_5o<ժUKk׮5:Vݻ$iŊY/8i3F>>>Y|={`'p]|YIII}Ο?e˖GW^}8F;v4n8;V:~ѱ\䤑#GkΝjܸVI&̙3F|V-Rxx0ҥKo[_|'?qںu3g(!!AΝ;3gdɒ޽kt|'%%%+))I?vܩ;Ν;իlْYϞ=UpaΝ;{H"ѣ \ SըQC x8IF9-11QSLQ.]('''yzzjJLL_ԩSuY=zT;wg}:unݺUll1 qssSN4d%''RRR4qD S)RXQp۷.]7xCgΜј1cTV-FիWɓu=sڳg.]jt|ZʕS֭+%%Eԟitlvڸq#nذAׯ_WvK g2d2He˖رc%I NN<)I"EVٳ%I32cZJW^U-m޺zVZe`2 W>cڳg4i"???U\Y͚5Ӓ%K/ (66V7: \^zOVҔ)S#(GVVVxŋ^>>>F3bcc5`UPAE|||t g>,&&F^^^zdccUj„ uV~ںu^xmܸQ=\ԩ#___Iҧ~nsԻwo/_^666*_߹sG ,믿.{{{)RD?ܴ{'iٳgrF...ڼys#IZb#O*޳_^VVVj۶ms~w*T wQVVViޛ^UUxquïSUVjܸz۷gX qرd1OR쑺:QFGYW߿KS7믿}jӦMieQyu^e˖׵|X o~}QjJ p .hj֬Zn9s["͛7 \rvL&͛^YTP!O͛7bŊ0aԾ}{YGcT\Y׿4k,@.~z-}ڲezmt$@ZGCqСKە+W7jÆ ruu}1lwѽ{,m.]G}۷+::Zŋ$]V4h ͬҧOծ][Z-[+͍?~zmڴImڴpÇEs玤 6oެm۶ijܸ~֭[czRR=Gj:t^|lxR*THV"7oE:zh~MQQQںu"##աC,4hǎԩSc;N:i͚52eh׮]Urt}.]ݻwlٲrwwl'nnn*Y٣7odɒiYhRRRd^:;۷okΝ*Qܞhތޥ~/wq5_Jvױ-A$)2d63vcF FB|%0 *%3S:~>ky}s=iΞ=򦤤0x`A;7o$((SNpBLM>Rw2k,Wv.\<55bbbxQ ߿?SNetڕ&MPF ,,,\ٳIHHAq}ߟM6lӧL0ڵݱcsuuѣܹnݺe|B!,,,ر#;v$11-[€ʗ/OJJ SL;b0`.\ %%DKj?s,geeE&MRw2<~X;k!Cm oooR'((>>>:YzrMqww'%%;vs=V\ uf߹s'ɸcmm… EL\\qa.]k֬ԨQ9]6Ge˖-iDGGkhVXpqq^zsN0`@ظq# ,8p@?/EյkWMFYr% y 2đY+WC_02eʰqF:D߾}ػwxVCZ1M&B!"c666x{{en޼dɒ׏_Ug"!x˗sݻ"fn_f9}Z|裏T&M֭u Ѥ;wΠΜ9MwrvvVӦejzsm:tBCB!Fqqqj֬YYN 6Lu!xyxx*Uc"vR T-[L{4!BCVBaM@T IDATtB-l&lolFuV>tw6ܿ`ׯtС\vM{z(~z~ɗ/?~a?u;VlYkQxx8|^sssƏa{קjժ4c_N5pqq4^777)¶m۴3]pB;w.&&&,^Oj*T77l͛gzn"r uIOիWpwwY/ܮfe={Pre,,,h߾}ñ"**9sгgOHNN68UjU?>wf|7wrr2(_.X Yw@z4i}a !Q͞=WʪBAVcǎtԉ_~… ;$!1p!B!ⵥRz\p!:4/VPPtk^5t` Y> MAӤgp  h_?|0~~~.]ҥKӧO6nHJJJ~yu1ĥKgҤIm;;;RN:u z[F\uFXlNf_s<3Y&xzzr> z*ڵ\r8;;s~g:ĝ;w*[>O3pH44[fD>|0˱,XI&ٻwK۶m[oɓU,Z:aÆ3PM6Ņ[n>3 Ҡ|w4 =6B!T~)Ν#::/_NթS|vBMʖ-K`` SN%..!rȔ)S`ĈY?BdB!B5d5?)P7oH"zy4/bfٳg }`B _3sf04:" 7~5VXAnݨV.]ʱ؞G2ͻn:ʗ/Oqwwgʔ)ڵ;Kmf4fNݻ7ffflٲEMxx8{v7[l`ܹt ݻpB|={|vecccPy7hNvTxxh=TB޽QJ1m4{3,Xwww-[ƪU̞=[G3xgGVVxVժU4`ܔk[X1ߕIB!BgggfΜիWٻw/gƌ899ѬY3/^۷B2cƌΎ#G;!Kz  >oeƌ2BȀ !"...; sexl9#G~WB46ܻwT[ZZ^3g0l0LLLXf ժU{a΂޴iSvaPSX15/){voƕ+W o߾TT ?ÇJl/s o> &4BBBҥKTRI&l26ofvHSFsyեaooO6mHMMeժU]Fɒ% =;פe˖#<<Ǐsa,--ܹ3=z_?`˖--Z֭[TզMOΩSx 111L>]/o˖-0agϞ%--;w0o<\\\Bwɀ !"*߿?$&MvvvĉIJJzQ !9]v/?yD111C:Gɓڟ/{ٲezN: NNNڴӦMٳ'̙3155et777k>}̙3x^!011!66V^ZNƖg'K ԩ ^x޽{={߆>ڵ+fB)SF)-"]tpLll,E}ဢehmݺ///={ЮvEYt).\sss*U_|-AAAbgg EXBgѣG_׭[=zƬYծ''OJ*XZZbiiI ?1b^^^*TKKK\]]4iR1ft^Æ c…hтwySSS,--TC aAퟬʵussƆz"""ծ!B!xulmm%<}ZXյkW?LRJ 4H>xa5yrttTfffBݽ{WOLLLJ(}:%K${ҨQ#urrrb޽TX˗/R|y,,,weرܾ}ɓ'`PT)bT^+++W͛"882eʼt7Urڶm-ر\-;Y_3 qwwgΝڲ~z5ki,cǎ`Ŕ,YsssjԨի4i+Vرc֕>*P֭#lذjժaiiI駟X`fff/l; ۷o*D;˖-ufIƍ֭y .͚5ŅS._}֩Sdɒkӂ.B!x1GGGF9s g֭ԯ_J*駟r9c)/eر1j(>|H`` ʕ '''&L@rr/&&&^Z~gwlڴiC)^8C ѣG:y۷/ŋښ^zC})'OrXYYQ@5j?NBE}aaamۖ͛g/nXg !1!oޠ.os޼yڴ5kd9ǏU޽7ddzRJթzj~!psΩ}*GGGeaa *ڴivܩp:+d=}pRURE}*999øҥKUv픝PS*UR R'O|a?*???egg͕zӧ͟Qw7nVVB jԨQ֭[[V߳u=ŋ+WWWg,ʕ+Uڵ*Xjݺ矕RJZJuWVH).\}mիQ%&&*@(P òUTQQFy3?D);tt/X@j߾}֑{EdFjʍ>}zSnZgfεV7V7nP7nP7VBeB{=ԩu5c&ٲm6pg٭[~?RJB iWXr*TrppPw;$PR+w|*W^N:};f̘ ?5k֫naC^={255UފB!Dv7uB1͛7W*22RGi=t9-[:wz?T~駟2E)}$::Z9;;#Fd!xdo/*@UTءqyb\iii*88XƍzN:SeCFkFFBBB^aB>!B侴4gկ_?UHejj7o/^ܹc"K<<<;CՠAu bccU&M{mݻw+ռysբE ebbvڥ͓3SL1 MyWWWuy&LUxqm>MZ ԑ#GǏUҥb(T/?~ԢErkE6d>bZZ6l211Q3g~Bv3QJ)BWĄ`|}}pqq14mtЁ[nѰaC5jD-ppp+gh>.\-[צAݺuYti<Ύ;(VNw͍lݺ%{B!ЫW/4=&ի ؐXq1c ||| 1Bo.yB@~ !Ǐٱc!!!lٲ'O憏;D!x+WP\9>}ʥK([A h޼9Ӧ5ٳgӰaC6ls711VZ᫯` S!B!"YYYڵkuAAA<}J,/ᤤ;T!HW2e033\rh *SfڴiDEEQZ5f̘i;.\RKEӧ4{5)׬Y3fΜ5;wdԨQL6LcNVx/޹s֭[gv)-B8p!e*mۖ۷3ydjժErr2-Clذ!2Ç/<Tǹ&MG!B׬Y(\0'N *U_ 3hѢqB!B!xlmmcܸ֭qok׮ѥK4hyAS!^}Ogx3{׆ Ѽ zy~:AAA4nܘD&L@RXdA r_vp/N2WWW8x`6B%.B,(X ,[UVσ={v7$::G0bŊIB!Un]bbb>|8UVsssJ*7|'S!B!h/ΐ!C?$00#GвeKJ.ѣߌB;PJlO#Gfm;z{qjWB!rB!]tҡC*Vȵkkٲ%+V`„ ;+r=֬Yիqqq abbRWRti*WL||ɫbeB\gjBCDGGm<==4fHB!D4i҄F( IDAT?'B7ǏٶmϤ憯/;D!Dc}]j֬ɏ?hpȓRRRׯ֭ᆪO>I![!"ٺu+;`;;;B5aaaxxx; !&,,ݻ; 9rDB7NXXC!deeE׮]ڵ+ Oһwoˇ;mV~ !r ,]vܹm;$!ɱcغu+mڴ1vHB!22B!%WꍎΕzs5}Æ wlٲ/_>#G'BdMjj*=z0vB!^.⍢YB!o s- s-ZݻC&M0555vB<͍]GqIyB\\:tΝ;DDDPV-c$-$.B73Ce̙|ԭ[Q !Y,/ !B!B!rL%r !!!]%KHϞ=իk6vBc4o___<==eVm!?>۷gΝm! F:u*&MbرL>c$B`S)dM6ٲtB!B!x_z Dfu$%%1i$OOO&NHRRRD)B!Yfffmۖ˗)R~!vvvtڕudBk FJJ⍐ƠAOYp!3f̐B!^/…BzŦMBڑBU...>M|rnݺ6LJP/_СCB!BmOOO߿ϦM W^X[[ӵkW|||hݺ5W;w.ժUc;!^k=gϞݻ 6ХKc$BW^ȑ#9]"޽CBɜEnCS,--)^8 6$ {{/F_(6lH=^YBz*ݺuё7ҥ 7n`ƍ8::{n8{,/^-Z0tPlllm#ٝ;woٿ?IIIjՊǧ[>6}6?Vbb"˖-c׮]/^ӿ,--Xv-գD%Kڵkݻ7 0kB!BdSBߟJǎ)V=zLJƍ,Beʔ!00SGR[nѹsg.]Ğ={hРCB!ҫWV(x_!!B)))ڗ.\H͚5V8q n:RٓJ*Qvmc&EKŅcǎqIɓ'~: 4ёŋl2]_fBB}ڴ_~G}6SRRM5?~<˗ʕ+L6Ge<<<8tUVՋAv!p!B!FTlYƏ9y$k׮%44jժ닏ʕ3vB#bΜ9tԉݻw{;$!^GsΔ-[p~BM B! ၯB!^;!!!lڴaV{Uʊ*U0g9<ءlܺu ooom,_C1:!DviߙGEEQhQvѣɓ'ڵ%JЬY38R3gΰ}v.\cxq۷Szu֭˸q6lNޗis׮]L6 {{{ʕ+g}۶m8weʔѫI'B!BY&5kdԩDFF8q" 4ח=zPdIc*0;ҥK>CN}ҥKٹs'7o‚ӥK ϖ;t3f`׮]XZZҤI>c/vMXXgϞ%99ŋӢE JZZ:uH"lܸSSSmW鉃7oδ,,,`8p@;"+/&6Xt){Ν;/^VZ1p@lmmu޾}sA(_4is&Md쐄Bk;x  jժ`ee#^^^lݺ2&&&96+++С<}4Gk)9Ν9r$#Gd0*!ͷzj_#+W,]46n 8|0 00u?x aooύ7XfMmȡC ""^zaccÊ+tj\4m4s(Z({ҳj*.]AAA>|e˖aooŋu&..{{{mޥK`q!VwL#Ȼiذ!ԩS8p͛<7 btY'OԖ#ǏI?u^ޗipGj+V գIB!Bˋ7_h"dɒcҌ"3%J0zhc"+?3gd2B!G\nݢC4mڔ~gϒDJJ qqqlذN:ѱcGQ^9!ěLMM1cжm[gxիWg 8u%K.ˋ"E`iiI6m+eѣG)))/vvvQ`AZh+tVpssFVDDiƠB!Bf(W'N$66=zL͚5yw6m/_6vB`.]*66WWWRSS9|A !W>ʕ+ 8ҥKceez711t{^tt4ݻwwҒÇuEDD}vy6nH…3F̘1^ٳgKKK… /%JҒu %뙝믱n:jԨ%/>JNNfΜ94k֌bŊannNر#K!#ěӧ,YOOO5jDfӧ6m˛Ȃ pwwQFtܙEi_t<;$!!kR^= 6...]\\gWSݻ7[HLMM3"55_6gki:y(V;IB}$::޽{+PӦM#""HV\I6me͛HoG d׮]DFF2}tlllf۶mټy3Gaǎ dȑs̙3P^<~wyB!B:U_}'4k֌9sP|y7n̂ 믿"ʖ-رcu>"/ٷoM6L2Ǐ>N:zTLlˍ맡Y&/_|۷/Ǐ'11ڵKMjj*'NK.{E2%뙝믱d%558U;wػ㯤Re# ,e7LXul!|g 1a-2ӐbdFY3i0wΧS*t,q?}}]WZZZbرDEEqm=zĭ[رc;v.uxbVZ˗y!$$$0m45bݺu7nh\ogݻwSmNNNz !".]0c ĉL>~өS666?}tرc̘1x2*ĐӧפjX"={d׮]ҠA| P$11oooV )'u'3fpq9~83ggP>sL1KHHI=KrmS^!T J*Z=C !B!(.nݚ%KpuoNڵZjt҅uDjyw?~C K.tԉ={L!UWl.n߾+iii3$222aСdeeT*q~**ע~`ժU/\y^^^\p,ߏ5ǎc֬Y|'j [X;ٓD.={׏dezzzٷok&++iӦi3f iii|g$%%Ezz:;\x)Sr*+(㜣甔DBBweΜ9Enߝ;wP*}N8Aff&tؑ#F<3V]|6Ncƌcǎ=zLN񺊈LqFN<=!!!:tp>>Ldd$@8pA#ByxxPn]6l-[dРAԫW4x}m۶8::@&MrPe˖|g$''ckk[u;)L4 '''<<<8{fӧ8Jhw`xz,Zj9t̚5KgƤ6h vlOOO_f͚\rEs̆ •+WrC׬$B)LMM  227np "## s!B!o.###>c֯_OJJ *UÇSJ\]]UQJbo>}# 3g",,Le !xc[+V/sښRJѲeKV IDAT\ѣ{.K,ɷX&MqP*m˗/'33777-[F͚5122M6aM~蘺p-Eq=:t@DDvvvcggGDD:tc:o۶-j 333ڵkʕ+8xVޣG0{l122Ԕ֭[_Xl9SFFL4c3?XXXPZ"oʕܼy;-4nܘ]vaoo̸uRSY95nܘݻwӤI_>?#~!ofŊ/ׯUVbbbB-X~=Ǐ/(r>83V^/u+qqqNdx2³ ԪUgtS(֬YS+Bii|R|yܹD~\\\4icccj׮uvl;w.[dɒTT|rO7n}.3gdذavN@kƸm> cPF }fgƤfnnΚ5kׯ*UАJ*P(Xv-|уrQdIlll:u&f5ggg̈UgTTeʔ̘!oM2rH dԨQ:; !B!xƖ-[q/o߾TROOOãG"=zk׮|dgg;!Ǐf̙3SD=*Bt%۷::ygϞ\3;ݽ{BAvv6̜9Sk/ȑ#sT-##2e19?~|/ %J`„ ܹSgF+yjW^$vʨQh۶-իW.W7[>_~Iunk׮]o۶mL0!>%Kח=zXr| {> ?'???J~0n8~Woߎ?/^̳kG]Gו7SL!66X-['JR˗|8w\ܾ}xiS>S_Nxx88;;SreP(hѢ@m!iР . V+xxx?J~B[7“'O`iiA<6l/^ښP hmԨQbRVUwx:=msG1}V/111xxx`eeC !""[N/=TRX[[T*ٻw/yUw,ȩ(KLLYf:qtt|fܺb)),ltéStn?<۶mcĉ|GXZZ۹C(ԣW?=u^/G}Ν;4i5,]]?jw^7uWP!푞έ[40@=m?@jj*?… C7nРATRE3}@\S;k-[Vg~׹=2x`"""hҤ&ʕ+]>}`kk_Xl9=ST SNkӢOʕ+oyRSY9^uzzzVM]6kצGL6{>2ڬxXXXУGV^4h̝;WGi۶m:q|xw'39=M#B7_۶mqvvɓ9k׮|tؑ͛ӷo_(_|=LMM  227np "## (oh!B!B!VuaҤI9;W'Ndlݺgٳf4tԩyiu???N>ͣGu ,///M^L/_֤խ[@EMu:B!|*UĄVZ1\;nƏرc_>JDXZZBpp0ժU˵OӦM9r$2j(B!B!Biܸ1}/^?ɉ@}Zn͒%KHIIwB̘7oDEE;! $>>GGGyN!MQl.ԳI{{{=s"ǧ y_: X|9NbԩxzzңGc?~\6 :4hd4m舟f͚?~Zh36lTR3FW=CfVZ?iNB!/ V/ӎ;8p ,RJ/PD J%6l ::Çm6|}}mRdΜ9̙3BQ !WHH",,!!!B!xJ(A۶mYlfV5jCժUҥ ֭+@Bի]taȑdgg;!{nڷo=QQQTZU! !/]⪨K.3}t:wLɒU̎;4_}Ǐ|С'W|JHHP9;;U߾}_JlE9iQڗV PGuIUVVĉ޽{J.gRSY9c._JPN>Tǫ>CRjV#FU٪DՀ4K.BUpppoe˖TW^դ]zUZr#BOo'RR*y !DA~B&7!Bn߾Zjcǎ*CCCj;w>|x,,,T7nw(B2uTOc}#B+Jz!!!u1Oc\]]YҥK1bfG>3u1tP;::oQT)D\\\HJJʳCCC2eJm۶mo߾dfffbbBXXX3 uumOMMe˖:uJ>ժUԨQVWv>˳/J83ϵOPP暢Y|c޸q#X"vI&+W2l0uL80ӧO;ׅyo.Q*:V׏6m0~xfϞMtt4>BO 88XBo!o"|B!7nNhh(bŊBAV4!^tׯOYnlF5kXx1^^^I!(v%]2k,Krr2fff4oޜqѹsgwN:̜9өUgܸqޞ֯_ODD7o+++:tQhР:wN||<3g_bŊtׯ_vbaaALL fb֭\pRfMzɄ XK8}Z?oa޽ԭ[)SеkW<==133+t,=>=E9jؘ\2ݻwgĉXZZj011a޼y:u 7o7| ua,_\ÅRʍ{)ԔF̀d…B![#$$}!x M޿BB!v=ѣGsyBCC eҥXYYR>wB1̘7o :6m;$>nnn۷~ݻ;$!B/u !^u7p}Ƒ /~7ÅZHHfv' ^Bjx?==bC|>CBƬhѢ #R7yBB!B #44/R~}Q(ԩSG Fpvv&%%#GPd,?Cݹx"۶mB!jLUٳ:Z MwhB"WLjETyB!BF-FB!Biذ! 6dҥK̃{.?#sl/LwMzz:NNNqrr"==={v!B!^,}_EY CGs!..-Zp1>|ȉ'hݺ5 AAA+Wu֑y "55kR|yT{W֭ٳgIOO\駟49<:tL?,u']vIZZK.`ѢE>fFFF|'lذ133cĈT\6oLFFFud43! #,,.]йsg#-BBx3\DGGp] B!VZifuظq#'O___j׮5[dd$z 44x2 [100 !!ÇcÆ \vի3m4lllHHH`ҤI/M5j ʕ+zj57? P~\8@>}^J[B!B!x[`kkڵkfƍ :;2fΝK֭etաh"ԩ1c>}:ol߲e +Vymۖe˖'hUL޽ܹs9qzbĈ1)Ɲ;w 44BzBPЩS'>!^5cJŊxM|wL0ooog- IDATst$BO˗/9myfy/MJJ ƍ]v899xbpppwhZ;v,cǎGB!D?ϟ\+C`޽̘1 UرCw9r[[[vɂ 5k۷oaddD&M_hƏ&&&"̙3NOYV!B!B!ċ<4@X[[ƌ4hЀXlmmf}QukP֬ΝQkm*+00SSS×_~I 1cό0ʕ+'{իL6$>Vȑ#joɓ'cjjCG1zh|||;w.sΕB!D.!R!BBB4 Yyرc,^M6RpssƆ>@ߡ !ou˗/УG7 w1cpԩ\T*WаaC<7.RRvm ´Q=2y|괜 !B!RBݻwf``9zFs^#t"3[la8p???1c^^^jGaTR// .Jhh(˖-z긹T*_[!S2eO?e˖I222`ǎѷo_}$BB7Xlח8L8WWW,--Z*...BWA 3W̞=SNQzu<<<sEtm:w̾}سgtB!tBZppp! (Q"׃fRRRpwwפ) RRRXf#B!|l۶-WG8>ɫA7ݻw*KzZ3l$&&v0m|w#٩yB!EJIIk׮iӆUVqi222իt֍O>T} !++ ,BCCcذaܹsGa!^a{@TOvv6Ɍ7:vo1o!]}̙"ѣYt)Νәܹs,]ѣG\&;cVܬY3zg???رc־v\<]Gtzff&SLutؑI&qM} YfTTI^J #--9B!ҡCӧO#nݺł ppp9}LL YYY>}M.]0c M'N0}bhQnicݺutRri7|9;vGE[ڵk8pֺB^z u%99aÆѲeK DzEi8تU+@l4H!B 2Ӯ];N8_~-Sn]9x ʕ?`ѣGywYt)*T ..'N;4!@ٲe9x ԨQ%KRBzEll,7tVr -tY&-ի9 PV-J,Ifff( Lj#X"&&&OÇg4i҄RJQT)aݺuE}AQZ5V&)))v9r;bjjJѣֹ_RR^^^X[[cbbBrhڴ)SLь*姰OM BtҸk>F.]iӦ.]ݻk9y$ J*allL&MضmyzK_|9uȈ:uh?|W1g͚5yUB3o<.^Çu? 4 ARif+R8p *y!7n(T* J+F],]}ؾ};I!x,Jtk-ZĢE -[ABB5Ҥ?~4:t@dd#|=Yͅgxr1:445khFBFz~:0͚5CŢ~JBB ͛9N 6ϏQFk7Μ9@5rFG!E Ǝرc-ܜ3fܦtVti|||yftӕ^6.]RիvժUڵk >33+B!x5EEEѭ[7x !""]LSSSu駟#M5k{!**ҥKhB Ϭkv}v5PYn?[lSN:3"==[[[.^Ȕ)S^Xl9ڵdׯ֢E lllqwη${.sΝ;(JMN8Aff݈#٦Zd k֬Ν;?t JB`׮]˗qvv`…Ze<<={בB;#ҥK_ѹsg{=FMllϋW/毿X:t(| ˗/oB!KR,.∋LiXF Xv-666<נAؾ};w!;;s1i$Դ˖-c„ q=INN&,,L3հ.-<ˋuquʹAAAx{{\4k֌%Jh:XEGGc``Ea)h{/^̪U|2>$==MOa͝;4MG^ИvMzzfHOOgϞ=Enԟ 9?Kr^˫@ɒړE߃JGm45uB!Dq255% Hnܸ7$ RJ;}:C L2T^ U #{_Ǚ3gb8::RvmHLLw-gkk7~~~GӫW/زeKYڅBb  oO[7obkkKDDwi/p<Ȋ+'S楠UA0DFF‚zqnݺ͛7IJJZ̅Y=mEaiӦJjժŁ:ujc/hLAt]W==dΝ;-ϟuKsYǵ֏;￯I{w{niEEצMu߮Zjժk}=F!ěƆɓ'siӧ6lA4hЀ3fh}8M4 333wOر#_[nI!xÅu5ƍ 333㧟~ۿ?G֖;w`&3_TE wAvƌ`hhHZ`ǎ:kѢ*J3QTh)h{&<<WWWtv)() {Ac:s dլYS+uBBB I&rMe2228|M>D9v3fL_Pn]NSB!(NJ9s0g B!B'O\\RRRe 6/bmmMhh(ZՃ4jԨ@1YXX/ ufvϙ:ֹJ. kyu'<<<ʊ!CkV-P233i߾=VVVZjԨAv$,,,ϺMrRm֬}٦zz3N[ZZjm322Y #Ҹqc;.^iӦ ͣv8::pBx)SyvZbbbxIoy&@!BX%988hϞ=ٳgkֿlׯ_͛73f NʵMR_A/pezs{^b7oΚ5k8x ݻwtna)h{2e Ʋl2<==Q**UJg=P ۷077U:MGzJKK# 333NJɒ% @P0yd6mڄ9۷'""3|,B n{ySre~>#]$B7 4Å ̙3t+Wۛ &o+TqŚ+c_d!^zs1RRR8~8mm۶ڵS^=Xx1NNNlڴ);[6vmcR>'2BN_"EGGg̘G1b+VbŊ1XUCJɒ%122ޞqe9shڴ)E|ٓKw9::bjjJddd###)Vmڴ !B!B!r8 E?iiitFOOkbeem9evWR.8g_ze*SNZx}b{]]vɓܾ} z%jU!Cr.^9;'NиZVǦ<:4}u u^B!DnҾ}{V^ͣG xQBvILLu`.]ŋuGvUVԯ_d !Ȅh[.,\kkk Z@Z;v BNԶjOӯ6M߼ݾ}yQ8qB~FFFX[[Oxx8|gY~rs<%Jٙ˗zj|||xsQy9&&eJbM7e ! M2җM:h:tVCDGG3uTLLL9r$ѣ,Xre9dɒ̛7cǎsN i>///Μ9U̙3x{{k\9L!B!B!rzlJJJÃ&Meq 9enY IDAT`ɒ%lټy3Ϟ=S[ KgVߕ?~\^~R zb\r۷p7[^ߪU/TupI۳[(IYBBBm1Bebb#Gɉ5jp]@=ĮmL5kÇܾ};Ċ۷o!D^c1b&BBBB%!Dmذ///]!BQhk׎SN1uTڴiC"_Nٹs|={Ǝm-[̙3,_&Mm;<~)UV֭[ڴiCDDlܸ1EhݻٳgrGGGN<̙3i۶Z/_2{lZnIʁ9oԩSGm}[n_ZZk׮믿ζm???iSN xvÇ M6j` 4)]47'Ne˖jۂr;?W_|<B~+Y$zW^/qqqŋ$''sE\\\8p9N:І'fDDϟ?'55ϫ&kĖw^o#Gp [=J-Z*Ç2255e޼y]H]# --~1yd/^/-BY .=NlٲXEERRիWWmRSS$--5j͛7 dWѶCODD2n8ڷojyf[b bٲe߿X>0`*&5jԠL2|) 6?"SK&...82Ӊ"!!jժѣG166Ƌ/Xz5<~TB˖-8p ~i1\xӧo>/ʕ+G6m9rȔfuS~n͛7IIIܜΝ;wQ\76/..333>ݻU6億*UDRR111*U*spu{"""믿Y&&M SSS/γg2w)&MDdd$ϟ?~7:dovu[n^9[nW_ɋ/PwU.B??Nhh(aaaP~}pwwrʺOE]Ƨ~:tZx1;v/4jH! !p!DaÎ;fĈfffx{{8:!B!=!!!: !;fÆ 2[!dZԇC9166o߾7O}ZYYf4=%KdڴiL6M'isJ*Ebbf ^:VT`ffq?kkL?r7x-7n9vҺ!B;;;0m4;4i'nnn|ᇺUr5j`ĈL8www*VD6nܸAIIIѣXZZ:$!B?}] Daabbĉٶm111İm6&NՊ@B!B0 777tB!SSSv˖-Ʀ B! jՊe˖͛TÇbŊ899fu(FEٲeu("'OI&+VL&[!d"={6ZQ !B4h uB!B!Ȇݝٳgcgg17odٲe̚5 ~8R!fll3l۶uѧO^^^899ɢB ٙ>}вeK]$2سg۳qF/됄BL"///t=zУG]!B!B!Bthݺ5Ou֙1c ZhQ !bŊƶ-ZE,Z={~zFŐ!Chݺ5PTOYͣN:?^N>*T`:$!BRO8|pμB} CBkW\f͚jeSjUzA "<(㍎Vi:.vZ;Ç144?yxxxPD YM oRXXǎQFt޽@B!B! }/!B ر#;v$11۷n:}쌩J*7 &УG,,,t;+""nݺaggGxx8ŋuHB!ȃ7BT !2 YrA' xW2a+Fpyq]z}sUYrr2W\ʕ+l߾˗S|lѵӧOh"֯_BKKK>s]&B!B!DKa!;COOO!!3^^^:LLLL޽;ݻwӧl޼u퍉 xzzҶm[tx Ce͚5|7ܹSἓ֬YC>}޽;˗/׌B{c.Bݞq'O7o;wd͚5vEVϱcTVT.]D@@/_fѢEL<9v4)I7WWWUNƎˆ 011)8B!B!x>̂ t5:A.]d2@- U,Y___|}}eÆ [gggJ. ^^^4k ]+rАŋӼys6mDnt;eƌ=f̘!;BLBV n~^)SÇsN.]prٳL:RJPHׯϴipqqú 1G+V 66U'X???F'B!B!Ds6lMס!riÆ AdMWb˖-!ʗ/8p wal2޽;:T 0d)VCz륥1d,YotB!AOɉRJ믿r=tBdW\f͚ŋٓrʱcK:wLLL X[[~aƱchԨݻwqDB,XÇ={dрYBB˗/'""?ӧOU G`ߵyOLMM4֦_x޽{ :wL.]r+x;2e#?~pTJjG{`` ÇҒKfKxݺu̙3gbll_|!C2Qh{mYn 4|+T-֭/x΋B!B!o#Y!_OHH[BI*Uߟ+Wʺu?>| xzzRn]]*0sLjժŤI5ky%%%ѣGv%kBzryԩ}醚k毿qǏCjմlQX>}EYp!gΜuXBBӇ;w3RRR|j@~z 0UVRRR!((!C<ŠP(￙={6߹h"-[Ɲ;wHII!!!s1e6@~Xp!O&%%%Om͙3xߟ'NӧOٴisQm͵ݳg gfooOBB{!B!B!B!tÇAhPfM&N˗9uݺuc͚5|ԭ[Sr ])Q\9MƼy8wyk=y6mڰ~+-BwLpq`yW,T7o%}Zbb"ƍ333*V cǎ%11Q !t,88{QbE"00P`krE֭KHHǏ',,?'Nm6@=;Dttt"? 4ё{b``@߾}/@xx8 .$**C1rH֯_Z*ڵ+ӦMDZ}Ҽys+HNN֪jժq&Om|87o|qK.1|plll(Y1vXnݺF5~ׯϬY}64oޜcaaShзo_lllCP:۷qppΝ;>|]$B|V .H]z5ڲn:ՠR[[ۂ|#Gx:O<СC-Z:pgŊ*$66+V02!ěoGʕnjg}!֌;6SL6 KKK V'N`Νy?f~MMMW puu%::Zmbƛжm[lš5k믱̜9////(z#FJ*2>={L=7ʕ+IB!B!]-_|'''GVx^av}1l0 FXXXF%x䄃˖-$&&̽{شi;vCj߽KKK#..3gk׮uX"))Rn]8uϞ=S'N0uTjԨ?)))~6śGӦMaԮ]'RreZjE`` OS$$$w׎]Q(.]9}PS؜nFN%7n)))L>]!mۖ]v1~xիGRR/ɉM6evmrpp֖LیرcZMv CCC٥*3QI:a!B!B>j…O  IDATDƍ+fffTXƎi8!ի7nܠy\po+++LLL022f͚;GRT)<ҥKu[АӺukmƈ#P(ߟ/^:7fС033`&oDGGsĉC:tr^ŋ|amm۳ҥKRX1"""2eƆ;v`hhȂ q}X+++ )WqDuOA>/^ooo~bbb7oGѻwoCZZC}(Q-[FTT)f͚ŗ_~ɠAzAJ!Bt2"tƌg}!֌;6SL6 KKK V'N`Ν1biecҦڇCceeŮ]?>?_BBi-^7.m&vSSS0\]]VqD!Omڴ^<<;wӧg۲eKߘ˗/󱵵ejw*TT<۹sglmmٸq# ԨQKKK@=#DҥWܸqClE f͚INNСCG^O^֬YorHz2e!B!B!ě#&L_|AFZ||<tЁFѱcG̙;+DׯGӦMiݺ5ӧOWK.] 7YfK;NԩSjJu6}M1渳;>>uѠA*T0BI|gܽ{d>|HHH666rbbbxժU4jԈ7cRRRq?͚5S[|kڵ 02eؗ/j;vښիWRRRxV~Yyq5kIJJ"..۷i ?'<}SN1qDׯϣG-S@ӤI Fdd$O}Ν;WX/_2`>bݻ7iii,_\cYfѻwo.]Djj*?&,, [[\Ȭlٲ׏s&L h׮+V믿( ]^up /_2x`Fܹs5k _B!xȄ C?3g֖ݻw߫TT83:SAj+tмҷ%J>NʁJ?gϞٓ)S~zm_|%K~SMyXb8::K\MC ؘcǎ1w\ "))I'Ofg,SBz聹9wחFѫW/޽/={ooopttd͚5-ZC*'4ЩS011a„ an޼~}||1c͚5qƌ3P[آE ͛ݻwv쎫e˖ ׯ{{{Zhaø>UVeРAy>?ykۤIL+ߤ/sppx#q !B!Bkl!!!Yr%jߔ'OÎ;#55ׯ3~x j!#RSSU`CBBBω'HJJӧlڴI-+EXlw!%%Ν;ǔ)S*9s6!?[={}m$$$w\#O<ՕxʵkHLL$**>}111j)L?ӦMSm_l7oҿ0`7o$99CQzuN>ZDtAc066iӦj H>|ܹ3ϟŋ?Ν;swÇ5ٻwoڷoÇy9‚dLVwСӷo_]Frr2 ߿+++nݺŤI-zriӦ:u*z3fԩSԩSÇ@RRǎq( NUy9Gqqqxyyp/^PM4ꫯ4̼-www,'6=Fq1pμxooo/גRT~[RJ :Ǐs5 āpppܜ#Fp]t-u(FRRO[:$!Bp[A4#4I6_<̘l̙\tʕ+̈́  ˶ݼoddDNHNNf׮]ڵd:t@ѢE%%''Vm۶e׮]?zꑔŋqrrbӦMzk,B"Ą@)UEҒɓ'Ŋ#((///000Dhт+We@>|8*U@{&tޝ4ԯ ǏVZFݐ:t(,Y###7n\1fu\fѢEhтrʡ|WYF5"'rsm155%222S;+VL1C!B!Bwruj|ZǏceeExx8k׮ JWƍTT X|9+V몉"UZp9B޽ؿj{xx8 .$**C1rH֯_vY}jժq=<>rkԮLYGQ~'bcc=z4s̡z-ZF?3x`>}ʏ?m;ǎcڶtR^x;K,CCC `͚5,5ghk…￴lْpԩu!<<-[?d9жYfӤILMMi޼9?3GU{IXիchh -ZPUغukŖ^~]T\d[O9o4ic={V>r~g?~LV ###ׯݻp5M^z\zUZjoagg1VVVl޼-[S.]+o;իWg̘1??ooo6nHU&M9&^%L0mKLLTM~_?8::o>كCB!DAS`Ef2>V:yFP(G*lll}T{ ʏ?Qب\\\666k}*Ϗ6666'OOU,MJoV*<== B驰QF*pk.\X`Faoo*СFL<~XaccرcO nY>OP뫕רQC(7nXvΝ;o4z44000s @ivrbϞ=ٶmƾ|KII(?d4ɸ0?^^^Y>Y{"**J1h Pԯ__1k,ϿNbbZj wwwBnʕ*l;;w(ԩ\!B ,EnVQ>=}tΟ?Ojj*Νcl1cpe￙?> 0 ױgQQQ$''seƎ*_f;wVUTA\z{rUVZH-[HLLƍ̘1#S޹sglmmٸq# ԨQKKK@= rwJ)ү /x4kL~3^|͛7U ׯE^PB*Aj 333 4v!B!B! -l۶ xҲ-wWy+|ݍr\ J{~V P^D ^|*2d;vsNPPIIIZonnu=nMߗF M*UR z"<<>}:UVŋ_:Է^ѢEYp!ׯgҧO>3@8quu%---ӶsѸqc8zhe B!DS('\sss޽/5W^ܽ{7S]___9{,Ț5k(Z(Cuf-?~]50t >J*a``9[hAٲeL2ly+CJɒ%122ޞqƽֹsqqaԪU ###Wr9#DFFf/22bŊ!O#F`ذaԮ]E...kuSNy6661{lfϞnr !B!B.&00x[tt4 .^-ty># +V3gʕ}V%pvvf^={Ɯ9sԞrCAM֤lٲ*˔uK}\\ "Mb_tڵkʶm+#ܳgV^Jٲe2+g5Y@ڵ+'Oѻwo@?!C^i@BBBƍԪUqƱ|rnݪH6r(eU[ʱaaaqÆ nZo:u"$$X֬Y1B au[ 8zadd޽{ٵkVVV/^sss m~=?̘1x5~}mۖݻw{B! E ;w=__LƏVd^D ưaòm3Ț_ŋ3m4үݶm[ڶm300񦊶qe<.Fȑ#_8;;m>////_Uk߿ϙ3g߿*^^^E䳼^[y>!B!BQn2ο)M4aݺu̞=iӦQT),X@XX+WfźM/^d@޲wܙ3rHQw2gP(ܹs*Udfҥy [l[nĨb5]fM>|۷3M}U^SNqQڷo~IIIxxxɓN‚3gpy4ic̝;%K0tPl޼gϞeʱɓ8qvezJ*Ջ^zvI5i!cVZZjcN;uT.]Jն)LO2sTn]>ɓ'5QjK>}={6K.V)Μ9CPP[c'OҪULǎ^z9<<<#<>>TRӻwo5jD޽yUTrfv|||WĚ5kFƍ3f ZҥK4@E̛7wq[m[S֚V Ve)kO:LI|={Ǝm-[`噶?===Ue 6M6<|wwwl޽{̞=}}}*wtt`̙|Rm/_2{l annƁ3^?m; .Pzu:vm]e/2ӶYY}?/k׮P(Q(}2^:_5IIIk.LY'Nɉd+U2?Э[7mʁWTRݛ߿ԩSq:ǔp[J)))hт˗/uGnݺkp駟rܹ-_-Y[nwk ?ҥK;wLޝ՘%Jhe3cVHS$#mo܊onbflCT YZ ْHeHKFwU)9w9Hxx>>:ty !*%]өS'Zl qqqUb###||| %99dBCC|O!B!B!B^NPN+΄*UHn05s! IDAT5\r4lؐ Mh555%00~aaa> 8kbffV豝1c 4pwwW-/&BרQ}}}= ۷/+V;;;O׭mݻcllLTTy011k׮B77zh*VHtt491cǎ}{8p@\\\dggV AQ:_~INN&,,ݻ3|ʕ+?e˖XeUɓ's-rrr8<_}Zý{^ܣaÆQF ۇ .\ٳg$$$o…t҅$:t(޽{dggs]ۇ;$''ӡCIJߟ/JX#.^54x=ƍ?T/].]JB!@񆂂OQ)-Z( Ŝ9snzӐE)8qbĉ7:QHoB>"(((m!BB!BP߇_~Eakky͛ [[[իu(jd(UwV)?+VP߼y<__6mq;wNamm3fhP ;vz{j۟hذa1֨QCq[azՏXyOz,nZz{tQE zhS|΍7Nɓ|Kpp\ի+Ξ=q\?BP*\]]3f֫WO奈_AS*ʔ)ׯw%Eݺu֊+W;}P?^Gѽ+{=EjjƾǏ+ZjW:N!%Q):RL\]]UB(BAsN]!B8!!!A!B!`Ȑ!޽ <== 777G'ĻgϞ9s AJJ ƴlْ)ShTrJښyqq222[. bʔ)jӤIΝ;ǯ?{.ԪU;2f>#cݛSN1o<8;wV]veԩ4lذסǏgڵk׮3ԩC>}V[_~ʖ-KJ[n9{@6 CCC~G.\!-[d kkk+V\+:ޞ3g0k,"""sg̙g``?Ș1cg\|ǏcjjJڵ+jŅ_sjժ3TZU`` F"**ORz"?uߢiԨ3gd̙űyf֭[ǜ9s0` P=wލ ͣK. }vOxnݺE`` k֬ʕ+ԩS[ҼysE+BDVBPN B!B!B!e͛7g0fIBQ$X[[k_f L*+S ۷}߿͛7w^,,,hѢoj eɒ%|w߿ 6PF ^A111!,,aÆCq:B9pDDD- dժUܹSSS̈#B!J?IBSULBw}/!B!EK*Q\¾B"תU+>LUVr5֬Y?u%Orѣ=z]vу2eʨcܸqoߞ'O>}tu*WԬY7oG~z]V`999L8޽{ӥKn߾ڵkYz5YtB!Jpѯ_uj!D),- $!B7sN]!B!B![h;wtYcϧC9~8&LȳOӦMcܸq|嗪e˗/H===f͚E5;v,+WΝ;dff@5j `ΝY4nX! !+ZjRԧBkȋB!B!B!B!5|:APt|!ĺux)=z 33Iff&>X;;;ׯ_1EvӇGҶm[.\P.ϟ㈄BQyƍ4hPQV"8ۅ90.]D }˂ulB!B!tB!+++,--VFFFTT cccX"*T333LLL022SSSʖ-)z:gooϥK8pCWVVM4ѣGTPA!zk悃֩ƍٹsę4iӧYl[lAPпlllhڴiE*B!B!( ڶm+ !BQV^JuF puuuB!(Z…:z$\RM "22ϑ۷/899MHHFFFo<B!B!B!B!TB!ĻWL!?-P< Q`` ofi&=zB!B!BG2h ]!x B!!B!B(۷oӳgO*Uy&XXXI.]~$$$W_QZ5vM2e<Ӈdh֬+)#88ZnM~t]s Y&۷oߧO}vj֬Ɂ ŋͲeزe cccCӦMuRV-lmm9{,jٳgIJJUVԬY_~SRRؼy3iii|3==Tm?&cfee1rHTmϲeT ~{%##;;;˾}pvv.5 !B!%L@-œBMAUCB!BPIPnCɫMddڶ3gfΝ&\ر''۔۷/&$$###G(x8;;￳{n eդ}i&NJ޽ܹs1~zY&gƆs1c 3..Nk 6@&M6mVVV$&&2w\bcc h6l-::Z.B!B*...: !B!B!B+ DSm/]Kd mwΏ?Htt4wޥjժ}߿ϑ#G(_uGGGU .]vSZ!B!B!(.]D &&&֭[K5!Dk0B!P. 啾ۛ7ofŜ={CCCtU?#99raeeE>}TR222'"";wPjUz7|F,~~~L<ܱՋ`:tu?!;;={bjj  ŋ(v!(HRҗ_~ɺuCNL2j c .… ԬYceeEƍ b2Q儉e5ɘJ]!C]dbB!BQ& dnO=33ӧӷo_,--prrۛmB!B!B:E||<M4Ѻ*kLJ{8t*b,][[[={Fxx8g˖- 6@&M6mVVV$&&2w\bcc ըQn]000:ʊO>?'O:u7nаaC5jM:u*{FOOs1b qg+uEGGаaCmђp!(V>[&::x9BՒ䔥077իZ&^VfM^ʩSԒϔr{1---ILL$,,L%a^TBJJ TREm2Qv!B!qqqEa!DB6ܾ}ȦM d:N!B!BQi.)'6nXxzzRV- U iiiR&9ټy3-ZP[zڲyf !o֖.]pMlmmٳ#{}}ĉ8q"B!BQK?8]p! .TmϚ5Yfceeϟ?W?3gCLL R|yU?V_mdWҥ -affFZZĄݻ޽{ &p(\gA0558MG!SvԬY-[С̘13fhEyqss\tÇڻwﮑP1͹>NNNXZZ;ٳW|y&Lڶ'22x5kWخ]W^B!B!(W0?o͛/2m4YYY9U[rr2dzl2ʔ{{];;;bccٷoTfBlҤbz&M"22RӧOl2lقBдiSD*xَ;‚U~,,,VjJ*6]]رZjѼys]"B!JbI(*ʕS[xnݺuu>|]vb 6l1cT*A[uTPݻ76l`Ϟ=ߟ={ŗ_~pB.\@͚5i߾=VVV4nܘ~h,BWɽ??N9m!xS*y_LIhʔ)dggENNָq5زeK. cҥ8pǏӾ}{ػwk9yd/_NJJĄ֬YáC}6*TYf9z޽;?QQQ|Wj1DEEabbB׮]_ymB!Did=0BCV-9=ʺu8z(`aaAV:t(zuBYMQ,6ۅ СC_<~SSSlllڵ+|MT*իWɃܥ9!E57o3{l6lȹsj}7l@BBM4aڴiXYYܹs%44GGGT 8:: jġl !+B]DFFjmdKKK&$$## SEf͘?>wݻܽ{$N:~1˗W%b{jIͩT*UҺ?ժUػw/g 8]%B!JbIԩS >&MvZ?η~Kf+ppp8uXxTKKK S}HP b׮]ߟ]vh|@@@\zU?;;e5"=== ?VekQ,ȽR )))RJSSSU}BVu"7SSSΝqJ/'vhKPSNeԩ-ݺu[n}TSB\]]'))5ję3g1bC!(N8AHH...E!Щ] ۷oΞ={4ݼy7om6zɦMTUE$66X,X;wڢNo*Ϡt$"tyj7q!㏁׿7""sPn]|||ptt$<<GG<ǺtkاLRB6aҥ,]@n߾̀TmdӦM2zhF'{FoǏs]nݺJ¸{.wܻwׯ߿qeRR%$ mN%U*UW9V[[[HU߿ !BwW}(٧O:u*={ښPѱcG֮]X[ IDAT[C6l뱵eʕLzqjRWV-Zh*S|e5kFݺut҅W߫Ҩ\Fsss߿Ν;qvv&99k+uկ_5+U}B!C{n^$giiB!+AAAC!Щ72h ]λuvvv\z'ңG>Cv!!!,\pؿKO<|P߹ۮ\jSkdhbbJ011QmMMMT&&&TXQOqhٲ%?-"22 f͚Ŕ)SpuuY>e˖i}Tڻw/Z٩tvv.«Be˖zT^W&c䕤#IMM͛SL!;;(rrr͍k׮ǖ-[~1aʔ)CDD2}t\gA]駟⫯R/** v'5oޜѣG 1c'B ,YѣGuBQluN%&&2g~7nݺEʕԩ^^^4jHA&߾{\\ ,äRfM\\\D/**={PZ5oߞ&M0|Fի5..^ȼyؿ?wܡZjt///.Bw})_<ڵۛ-[jh"VVV8880n8PS… abbBDD~an޼9we˖,YcǪU~ /{> @~O*UDN>}FF,BB͚5r kҒD°,XUT!%%T쩩>B&cql߾Kbkk˳gglٲEpahҤ ӦMʊDΝKll,8::5!/:: jĠl !J*(!?O>ѣGFzz*1jIiiir]_<&333q ʤJ*q%ڶmGGGIOOgΝlڴ WWWZnݻw/'* ܹÿo9sfJ>|8[nbŊL>ggg>ݻGDD3f 88CCCصkF-B[Exx*;y!ϲù1qD&No֭ݺu8}}}ݛMMM;w}Ç3###NԩS_kA v*T+$%%QF 8s #F(BQڹ>B!e'N $$]"BU!!!A碢/HKKSqFoζmٳgiddn:krrrTmW^e۷(*T0rH*WʱiܸF"ݻqqqQ(uֱuVvI.]ĉtЁ'Oػw/M6;;w&==]ՖũS8uk׮%&&{Hb{Ya@?ȑ#_lԴiS<<B& 7ֺ#z*:to߾W_DDsnݺHxx8ys%j׮N:j}bhhX$ɲ999܉j ek׮[FOO ,X@@@Iѣlذsss;F T{=֭1ׯgȑZ3B!(jŖp!9 ݻ R}Y%nnn:N!B!^puu%((Ha!oƍU+ھ߿O߾}IOOg„ =>3gO@@\pAmtmΛ7iӦfUk1b99995jOf|*[z*5j€̤O>̙3kkkOصk#!!իkÃ=z0i$>S~wk\ٳٽ{ HOOgذaxzzRvm9qcƌ!!!3gbŊ"-y> dAmϪUؿ%\hbiӦѺuk~'6mʕ+WMhh(nnnTVXb8 4~ׯo_www>ٳg5c*_<&LPms}$,, {{{"##רݮ]0!;GYBi…,\P=k,f͚佐cQ?~<3g$&&~<<zٳgLRR 4ӧ*ooooIMMAL2(eIŊ5mݺ+Vp)222ё9s横|>l100I&||7uxzz%[faaIdժ }`Ѫ*zb…rOkÇ}vRRRٙSocϞ=ĉ9vFFFZ0L!%˻+>>>Lrr2 B!B!BiժUܾ}iӦxbիGiݺ5Wfܸq||Ì32eZȕ+WS/B:u(WڵcӦM_^_zÆ _.]ʣGر#۷oqиqcoNǎy˖-zg}۱ؘ۳zj;ɓ գ\rѡCصkWŖ<~'5*O>˗/7ѠA^;1,66SfM:wٳg>}:7nСC 6ʕ+vl-"..VZqi={i۶-' @WP>cP.pB˪ʕ@UGOO ֮]СCQO?b 222x ^QRx)ߗv{:u{?yƾݻwg>}Jtt4u,ZHuX @ݺu9vO>ѣԩSs1|cׯ˗ ;v !)DꪶқBʕ+0B'$$D!!JW^-ĻHO򢬊ù~:cӦM믿UHx333>|Hzzz^W$2e2|Y"Lao^k{-xb"Dq`ԩt Y`* 2ݻw'AAAXZZVc != mšO>$%%1uTz쉵*I6k;vdڵxyy鉵5>dÆ _[[[U-=== 7nܠVZI)))$&&j$V(<$9::k׮WV)ݻ_oƈ#8p 666E:M׮]Kzذajс-Z'ϟ?GJKbmm ̯_POfǎ_ՖBɓqY<'噘ŋ9<5QF?}Gmh{X?;;;֭۷og7DUVѦMYjݻwg- &0g߿_{&BG.BΝ;ٹsB!(=ʺu8z(`aaAV:tT?r%(ϥ W3ߒqqqY#GpuT 4G 6 :wIPüb)L}E֭[ǡC?IKK:uжm[ ][S$/Wk(l5k)v\ϣGOԯWqqq\xVZlʖ* y%c˗/_qݩX"gԩS߬]kbeež}[nĖ<~666:uՄ8{,jCqxy%e sK]}+獋gϞlݺkjnnËtL###|||7n 44%KP!) C aܹZ_W5k;ٳ g˗g„ msss߿aooOdd$j]wvvumĈ̙3͛1 (~ٹsԶ={ƨQ`ĉJJIfaZZL0p}դdzɓ'=/۷}1}t?~Z\{5HLL$111߿߯^ |[njm۶ʕ+yO=WkW.]T)eE _ !).B!r9L۷oӳgOڵkǚ5kx"deeqMm_|A^ <9Q3 >\*6SLe˖Z .INN#::oooٶm[-M?ÃF1|N8&##ť@)6<|0S999^pqơdž hԨQctr{˗Rڤ8eB#sS>ϔ/'OH@@<\Jlu ׹sg~IxY^=TR"Dqdĉ4lؐSL,--qrr"((5j͛7g˘1c4& !x9jɉ3fРA 000 wwwfΜgbbB@@XZZ:t`ڵj)&OL5'++ ݻcllLTTF QQQދ !?ɗ_~V-@r}}v튿?h"Uoeff3PrIիǤI Tm$%%=xyy**A>`֭[["`-rrEW'wPmQAB!JIB!B!t֭[iӆ={ӧ\xYfajjJxx8dgg:lQ^___ʗ/gϞѣGdggs]ϗ_~IZZ$..N!.##N:!#Gd߾}ܺu,8::Ү];"##Yr%cǎQĢؼyd*Uйsg:wȑ#YjgfΝU&M"66:uEdJh׮ڵÃm޽{޽?SN1gvapp0G'Oٳgqrr;1:vș3g^m_||<}klْ]4 IDATAٺukn޼/eʔaɪݻsI.\HnV|9@ N:$&&Oƍi/^Wcǎ?gΝ9Fll,dee1fU%sssݻGll,;vTyP(<\h":u֖ pvv.XB! bȐ!޽ <== R"{ ;vT[%11s޽{)S +V **oSV-nܸѣQV󻋂[ǎw"}ʕhooիWUlܸQkUS?!&&bcc" b%TH%/XmlV, ڒ>JyPb& jo)!PIjO-!,~3OLb&5/{{f{9ݺ1k,Xx1u!>>y1w\9x M6ÇPJuЁ#Ga,--7nK.ΎK.eϳgϸ|2| Z+ϟ/_[[[\/}nݺ3=[w͛ӿ֬YC-K=PnbL6DprrҪСCYn 4 ,,ÇhtVպNҵkWf͚Yn:uJg}1Ec&BHɡ+Q-K^Wf>?Ļ!۟%/yK^oKhsssS2]~իW!C(+WV̔ *(nnnJtt9/NRzXZZ*JժU+?HwPʕ+￯@Sֶ .(W*V*+VT<<<,(rIY)RRT)SNʉ't 6LU Aʔ)S'',/qA111Q,--={*Iv}ڛ(O>4_EQk׮)baaw-Z(eʔQe*:tP::rLEׯRBDS.\IW^U4h+}Yc13uo}i\\RP!4v *2|\m(ee i_~?d07_>eGP?VN8$''+T)&&&ʙ3gرC]ɓW.\XoV}8p@Y(5믿KKKP>eɒ%˗4%))IT̙{ LDSSSex%--Mww^C]ٺukߗ씐%99YSF)R$c:7oVҔJϞ= .()))ʟkN.<U||:+UךnݺTPAkj̔u}f~Ϟ=^WΝ ӧOWW,c֭͛7J|mUT_Uܿ_엽^c&B';\!C!BCnP9rD)Y EQ~7}^uChѢWZL[~};ʤIӧO~WhѢ:,Vͯ_-ǎɓtlll۷oZl/ˍgoΜ9:(\k.l{VmNjժ X"|*+ d(:zLL̙3uئM߽zQ,5,/ڷ!SeСzCQ^gܖچrR`hګe___ 6z_+FFF:}GZ+DFF*ժU˲ ccc*;wTw޶m׵ѣGC1VTIDnfSIIIQF2#Fd9ԩS:?o/Ӹ[niQ-)KJWBܹs%U<~ .sߏoS Ey.G=Z ̷8x[uQʕ+@iժѣG+ҴiSPF]oCPퟱW||( RS |p<7*U2o<˯>0yȑ#*U(&&&J2e]j>4z_K)Ubff4lPټyVP +aaauΝ;>LTr):uRvء3nJǎSSSRJرc6lPV(*T?x?RR%Dy#Fh ̮1s̄BQ)ѣ9R ??B!.www֯_sY888p=F>gϞߟJ( ʲ̙3g2qDu9nnn_888?~<*U",,AǤI6m5k󄅅ѤIlTV'OХKf̘AjոtرҥKsy*TO55x5^:| ԩS|\|:oqrr"44!C0n8TBzz:'N/xzz+e+?~&&&8::pBի˗oٹs'\trʽ2}Ե… 5j| ݻwUV|*THg9퍏5kdŊ4hЀB qYFiڴ)Ǐי!޽{TVGѫW/N|7nǏɓ'Zq4ʕ+{ny1*Uv,:Oj߆ӏ>08@6m^Y>-}|>rY+F-[4__^~Yll,gfܾ}sss7nرcO5Җ.]2aHH3g$%%QjU;v, ֙GJJ k֬a֭cll5|Ԯ];Ο?̙39pݣ\r|L0ygfǎ_accC.]?~Y^f{Y\\ƒH%^:~) >2NJHHԯ_I&ѱcLz*B ~V]ϝ;_|ٳg?gܸqXZZXT~SfP7oxG!D~zv^277'999eʔU/ШQ#"##QOH"~Rjyq-R)))$%%QhQu^OI&ܒN˖-g31XCjln,5h߆SC/eʔ1>>-}e։)bTB<!y?d4!>2@YG!D~*.T2cllLzzz9c5k… JbbFu<x۷oߟP50ilS~ !-)2dS!B!B}`o޼(nҙOtt(5ʴ*666 mVIOOޠAP>W RٳgzץQF ٳG] hɓ'Zӵ+TVM>HYnile+zCqM6eY&Mqf%**J3fRV-Hkx[[[mR_QrL6m۷>1^xQk[Nby(ڷ!X>gnǐ6ӲtӾ^¬b|[3] k^m|BhwG!D~zv^^(W^+ѣWeȑu2 }֬YS5W;y2~xP-ʘěB!C !B!o`ee֫f͚\t)<իWcÆ ZPn]bRjc룢q:֫b{ֺbŊh̚0qD?N߾}ښuVRSSs5rbH3jժ5ŋő5k~ 22w^{ڵk9/_m۶>}༯\®]4i...XYYYf1p 6ԙWӦM$ʕ+kˍXEyKj߆S9zAe҆rZQvrYB{u~K^zs^B!D~֭nnnDDD۷;v,FFFiF,X'''BCCiٲ%-4l>}0l09BZZ'OdذaZimmm122bٲe<~u몯ۛ\B!xwwB!B!뒐t?3AAA(Q;vPti4/WYvvvsE4i>阘'%J 11QSʀ`DDDpuVZŪUe߾}TZ5Wb(/3?#KKKUhr(U.../ѣ矙4iw+M61qD\bp SUQ-[6}syƒSyվ ivvvDDDpy>#daǐ6ӲtYzYN? 3Q!  d]΍ؼy3nnnÄ ؾ};ǎ|ܜyC @%Xv-^uAdd$J\rܽ{իSJ^w3f [l̙3Ӈƍcxyy奱-AJěːQB! ;\ܽ{:PT)vJQLL իWe:GGG .#7odݺuqmLMMyiժ{V߬y+((0={爄B!x3+V>|Cp#FȈuQFLx1O<ѫ-Z޽{pKFԩ֭^wr((bcc2dî]|̙3,YM6( zޞzwhB!Bxۏ;Fӧݛ$MFΝ3Mkkkٳgy̻w,Xe˖1jԨLGWٶm riN|X1COfټyHLLI&ѣG駟uK,0jL4=z`eeEŊ޽;}B!!3f|<((###njùsΊ3Zۢ022Rиqc>Sn߾M^HJJ4sRP!ƌ^߶m[̙5y9s?2v}`ddDttֶf͚iز{)={ٳ5.]'Oji95/0uTΝ;e3gIׯV^WEtYxvUyY^ĘXrC^oC~СCy m۶Ut-Zs|EYm_^B9ut[PPGfB!B!B2Å~~~/^ŋN o23f̠TRP~}|||޽;Gz+WݻӇ 6rJ1:!B! >///.]Jhh(ڵLJ:upq6nȪU011GΝ;Yt)ua͚53K鉯/+WB 1%J_ Ay???5jĞ={U| ۷ښ4._̞={7oL>>ĉ ܜ?9>}a֬Yh"5kF…xHs3윿{nzԩSZ*/^d=z>@k4'''vСCYr%ܸqKlٲL)^8?f֭tڕϟcffYjǎq 2^zQN+9u?3۶mĄSꕷ5qqq3XZZrEfϞڵkZuӓEBǎ={6\rɓ'gbxY^ĘXrC^oC… !88zѷo_\]]iذ!e˖%99/?s5^5/n=:?B?| yQfB}C!ؼy3nnnB!"FJ ]=-ˠEfy9~~~۷[najj-]tk׮^ǘ1cg Pf͚133ɉKKK_Uʝ;w%44'O`ccCF;p7oŋ<}KKKZn٪WRR߿{aiiI/B!www֯_s9ݻwӣGLGSӧ_C0x`裏8x EXEp5s. IDATtRe<]puu%%%Ek[…ٸqQUOk{BBM6… :TTR%Ob3eWvq}PPÆ +T}]pZhӧOcС:svv&88Xc*̓ݻ7Y%KO=~RI&qF.]/1CBviXX۷ÇZYkW1f'}'m^oC)@jj*'NdѢE:gȨJ*|( 7]VvWV׼zׯWmkSJNNu e/$DnsгgA!4B!B}ݻ9{,֭dɒ٩Au>ǫ1yd={FNXleܾ}7i Wjj*ÇnݺE@@QQQ,YB <3f0tPu놓jT10$2p@ܹNuW,Z?aÆ#q۲e ϟ?|Rme޽$%%;dԬY3N<ɾ}4n !B!uЁg2{l۷177qƌ;O?T#˗ .cРATV3grqZ*;… kSV-"##Yf [n?$>>cccqvv/p4@N`̙8p{Q\9>S&LХdɒ?~ٳgc/ҰK.?rYlJ/8{,|7NkxWhh(SN%$$DׯϤIرcw\OOOBBBHIIB mKf޽ݻsI_ӧO)^8tЁaÆ{!C(\0 ,… .\ƍ3rH:vHj߿?˗/ϕߡM6?dܻwjժ1i$Q^ŘXrK^oC)seȑ^?huXz5ԨQӧciiɂ ضm7l ԩFFFDFF2l0BBBUuqyjժĉڵkpIvܩ1^n(V^իWcffFݺuqvvk׮ې׬YÝ;w\2ӧOޞӧO3eʔ,_V qʔ){C.=_o[ y].44T:\!B{{{NyyTx}hт-ZO…2dC V5j`ڵzE̶[XXハOf*?:up)ciР;v0<ٓem۶m۶zǡOϴsA~ׯSkkk //bn,Ty;#C۩ᅬHvv}^Nz5?5z #"|Nߙ =^Y3Ǐٸq#5B 8::qFWgB!BwY׮]ٱc\?BK5k\V177ɓ'_+WкukzA=.o߾l/:0f S|:t(G[G0vXt>rpåK7ʟTTiB!B!B! }g7kͶ2B;vΈ#;|̭[0aB~"B!" u }дiSٱc'[/R n`byTg5>^͙3 .PreZj-5k֤gϞ:SWMq-Zݻ5W???²1Z\EQT-_Ts2lCۊZo|B!B!BI2*qss0k hwܠL ɌBzİm6nʩS3[*WX(BѣG$''ÇINN&99~!III$''sRRRyӧč7ݝI&%vsscÆ Y~ WWW6oެ^zVN>edܹs=cǎQhQ\]];w.ŊS}6'NdΝ$&&ҽ{wVXUϟ3e6mիW133VZ| <8BZ:\СCUV|W4h???}J,IΝܹ3ϟ^2ΝӸ|>O{=_N\\\Qudt\rE===]s>ڵkڵ ++,@DD[lSkkoccCLL S8GFFj522BQ)^8{ Cחвi+e˖Yƙ KB%$$VZamm*UbŊ.SFF$%%|r={~Ç4o\9@ʟ0a?z955U6!!ѣGg !((r6U区t那# IIITV {{{@  1c HOO̙3жml סy̝;۷oS̙#ݺu㤦rE;3\xgϞh">|:m>}>~HMMɓ'9rcjihOӧIOO'**ӧkU۷̕+W5kV:Cחːm+իW׹%#:U!B!yGQWl(D~v*B5^Z:g&OLݺuy)tЁ-[.;3g$3 !D{1Yn...s%֭ p ڴiCuH)Bݻw;|UZ5>~N:=z˗/sM>˗/s9N8A׮]ԩE}qϛ7p4i™3gHKK#::'''(UW&!!3<<w\\'N %%Çs9T4~wRRRx1,^8BZf:\xxxìYoРҥKs}wv282e0`u5jԠW^:g6"u8p ܹs|g u޽ɓ'3ydr/^13>0`;w}j-RFR/;;;ӯ_?֮]˦MشiV UT믿V{V6lz}ݺu~ƾ[f֭,\ h=;KeH͛sa4O-Z0NB!B!B!yIߙIeFq!O>el߾;wO5ׯݺuaÆÇ,[,#.xXd tЁǏsdBQpyxx0gj׮ߡbŊɦM>|8lܸUfOrr2W?ka>ųVΎu1x`EGQF1| cĈ|'VŋVFbƌ3n߾+VиqcZlɲeرF^ŋѣG̟?hv튧'0!NpQݙΝ;͘1c!::ӧktؿ?~~~,Y{/.xT.ټy3Es$%%sdBQp?ԩSCw(W^9sŋӨQ#VXiڃqutUVHКLJڵkF51΍2@2es8>#Zlܹs)Z(ovB%^ wX?L&+...hl[XXs3;+S+e%JdF"#}UdI%}4mڔM)W_}W_}eE2a&L^9ҷ|Cڍ>ːARX1ƍTT 7npY  #B!B!ĻD5O͚55u S2dRSS!&&]vO17י3gXd 6mBQzꅽ=Єx;ӷ etܿݻceeŮ]dFq!Ŋ㧟~SNxzz3ǏG(O=}^~ȑ#L2///O?1h2o-ڵUV899ѢE *VZ؉Al:EU(d@w޴mۖ۷Ohh(̝;By^[ !Dyxxo~zƍ뱲Һ B!Ļ.00C!S7o :\JvʼyHLLI&1U΅ 7o/^dɒ%L6-W}$''3i$z聕b͛7Sh|P۟Sѽ{wLLL ʕ+T\-Zh3Ǝ5FZiFӧOYti>DXYXXGc׮]iӆ __ O! зo_͛75:]LLL>}:1^z_'''Eg_Ν;cffѣGY|9D˖-iѢժU˓mllիTR'NHdd$5ԩS|,X WGjՈȑ#kN>,,LgҥK3p@ȩSزe gfB!p;!-ʔ)Sعs'n֭[ܹ)SPHO!BI&wB!Dsuu}gկ5k燣#7n`8::g9w3f)Bճ=z4?C|c\wһwo>}p]V\ ѹsg֭[DZc8v6m?%JV:,vĉ !ڵk_0w\F ԫWgggNʳg8s ׯ_gРA̟?'Oۇ;W^?XYYѳgO/^ٳgyY֭[7܈ ==۷o3vXhӦ:Y`NNNҲeK-ZiTQf }aÆq8y$Æ Jkkk˖-ԭ[u3i!x3 Ba6ls/ԚZ!Bw__a!JJJb̙>|"EвeK<==)[: >t,]ÇL6m8qδeʔΝ;ST)m:IJJߟs=,--i߾=ֺ{]~GBCCIOO}3f͚U/]-_ƍ?>Νp|'9Rc;wKhh(O<ƆӡCllܸFid(Qkbjjի[.DFFRT)ʕ+ݻw^:UTիza1cưeΜ9CVCLLFq1|pؖq_!owEAB9/zSnnnwB!BXwB!k(J~کF*Uزe QQQ 0{{{&$$x9uׯxǏ4h;:<3f0tPu놓jFvjj*Çun" (,YBB/&~!Ν;[nQ )_eذaqqqL>!$y5ϛrOG5#ׯ;wŅBO.]gϞg̘354,YaxgIll, *[r !Ȉ5j@޽y޽;ڵc۶mZXk/iѢ}ȑ# 0@=+.fff4k֌f͚1~x?Ndd$Gѣ|+V͛ӲeKZnw ;ƴiضm7nܠdɒlْiӦg.^E'ŋၗ,ZooooRRR * &88 &/@.]Xb6lH;l0LMMYtz}Ǐ7l!;B!B!ԔwB!D dFR`Uİl2իDž 4i7n`ڵxzzjlk׮ƍT\ӧcooOdd$'O֙[n+V ___V^ի133nݺ8;;ӵkW֭[U'N֖k׮ɓ'ٹs']v`͚5ܹsG#ӧO3eʔlϐUT”)Sx' C;\^xjԨӱdl۶oD IDATu C AuBw(.ٷe =|E||<+Wd֭XZZw *saɒ%|\t ;;BLkV_pvvȑ#oߞ-Zg|`SNbȑtŠ} *Dݺu[._}bГ`>/}A0J.͂ X`A@_ܻwoz2 kt/Y$2A4hPe !x3I !B!o%WWW\]]; !"OI^fM/QFxyyqС\p ĉ4hЀo#F.\'̙3:u -[Frؿ?>>>TXU2evݻ՝tҴiSƍǸqU?CW7nYHHHPo?z(cǎUVwN111/SJ#ĻM"ɌBa4Ə… 2d?# `ƌ0f֭[>Gf077pqMIOOښk׮I !5j~XWvm?Ni֬wt0w9~~~= :_#Ghth֬Z2h !B!B!B!8saΜ9|֭ ?1Sr_0`Z ???~u=o:w387oKӦM6CWU]dI?gsPD tu4B!B7nУGXv-MMMٴiڵcG믿RlY<<<2dժU0@]6o޽{A)*WLHH]ve˖l۶VZco7C;`8;;ӸqcLLU!o<222ʫB!Bbbb^`>RSSi׮ ,Y33\1W=hɋ}3 ",, xnϞ=sBn/_Vcccӧ\*oտ*n,hтdvM5ၟ3XW/{occc%cYߐU2j> )[gBҩ= !B۷}RlY2MښӧOSh+--Yfqvvfĉ|駴h"BI5sEtt4[Vtivڅ[fΝt)|qa>QD ZjL6m]\BFNNNvB!xC͞=C!y#GM6(>IHH !!`\\\r&7t,Y¦MP^zaooOz;4!;(<<JZXjǏ端A/::ZcӧO/FAT122BQ)^8{W\2W\!""f͚GEEiupp ""-[驵ڵkf+++]Ʈ]2bbbbJo )__ׯ_'..5jdΐ˖-۷ye˖#u!BL6>}|r3#- W^,:vȉ'ػwo>G'eʔ!**JÅ(lܸocjǏ?h04aS# ۬ F*V1mFR!L 4LD.&\f 0U;qN騜yޯ|ާy]PR%ƌCntkN1/.BKll,~~~L0ڵkӭ[7wNUvFB!JٟI!%СCDFFҧO]";v`gg0D)WlemݺO>_m۶Ʉ _3uT[뇯/2B }3f̠iӦ0k,zSF n޼ɖ-[߿?W^eΜ9Z׫W/gƌnݚ3g(kȐ!=zP޽N x!G~P(Э[7V\4i҄0V^9AAArfΜmڴ̜9S-¶_ sά[ϛo… ٰau7mڔ,222&V(&N_BBDDCBhInEYquC!-ZOJiّ.O"ZRYD"11I&q!>}B6mJӦMqss#''dbccٽ{7&L ;; *'_X[[Sn]]-B%0b߾}8::wBWHnnCBd}姟~uBڱcC(.UVVFL2}q!/~m]'hŊ\v+ӆ ڵkYbnnn:Nz >*iM6Umee͛Y` ,P̙35JnkkЭ[7իY~=ׯW+aÆ7Nٙ{Jʕ+3a+=ZަM.]rnaۯM5bIIIO>Q9XQۺ;wL\\Uf>Cc48pCB :p @__})w=>};ܹsuB~-Z %%?}] ux===ڷoOÇ$%%{nbccYz5=E }իWuB!^Szbrss!yC^Ro9B!B7n0m4ɉ(~hfѢEKKK>SyJ>Źϟ͍.]н{wfϞTѣGUy,66WWWر#| ';;[yh[¶mɡwЫW/rrrضmZނb.lk?~̼yڵ+Uoݺ7]t[n|ܻwHע[6hCl<ᆪK.X[[3m4nܸΝ;[ ԩL[.[;wB"`qIx{{ckk>5kdРA(;a^:tԉSj>!!!SF GckFOO}}}LMM3f WF˗ZjXYYrJ] dԩS+ҵkW~G8 ~m/Zjb zIժUX"͛7gܹ*gjS-o j%$$`hhֱ K:/yȣh!J\~'>#ڶmÇe/_BCCyBRe˖۷-Zpvɓ W/e>]ve$$$pMvA^?VZtbbb~SB!JRp!BQUV*B >\~ -{˗iO[-Çquu%--MvUBCCIMM% ==5Fͭ[شi999/R?ȝ;w,YSVV̘֭1#k[nn.+~)tu ʕS٘wm͛7͛7+/[zlo}_"=kܹDFF*cݱcǎ#,,jժk.5nީS':DTT/ !=WLLLy~5kV兗We9@*U UBǎرcUZ5&Nĉrʌ;cצګ)Ӧ*U˹r ʕ+;vѣGo<>!J}pB]!( 6:!Tܾ}#FuVf͚{9pqqaƌlܸQe7>!Oj ~QFQBrrr eر޽[+ ݻ7{.Եgbccߟʕ+ciiܜ d8BQb;\!Bhr11B!׎;?uF˗166&44BXXiiilْ5kpA6l@۶m9t6l͛ILLdȑٳHe6nܘDDkEbb"'88@sAGQ>׶ÇbjjJ&M055Ԕ?3AƬMk+55kײ~s̡|lڴDF@||⾾b MgHlR ;p̓@W)!é[.Ȅx1DDD: ! _Q;v sssOtt4SLA:fddg}ܡU!*sssܹr/&>>^x5[ 0 Ξ=KFFԫWŋciiIZ۷/?3Bb%Sə3g}Æ $%%OW'8p M!(uO?0%߮Ϧ9rD98LJ֭[`ff/ƍS ?ӉӧOΎ;v`ggr' 4ёPn߾]2W>'77SNsNΝ;Ç{n}m->3{n݊Z=ƬMkK9 }&&&hѢP;/jSoÇ ~7 95Q޼QR%zzz)g-./۳L_mWlBUgWC!BΝ;=.]"22?X! ʕ+ѣcΜ9j !ЬVZ̞=777FIVtxʗ/9L2pPrss133Ɔ=zйs|CB!@. 3[b׮]cʴ!CvZ嶜B!l~:kuHU~}9~8|2jy 22R9ᾨ̤$BCCQ.\Pq+jSmXp!:uR;~ƍǶm&\ݺuKG!Br]ڰaOǎ 5\!^W`jj_>Çחup/XlɂAJ*aee~~~ܺuݻwMDDg7ޠk׮ٓ=z$!j ^t M5899՘Wvv6-o߾XZZ駟Zfsq֭[uQ[.[;wpkBQ97={E4k֌Ν;Ξ={tKcbbI+K֭[GϞ=5ӦmeO߿?۷Ԕ-[bkkG-z˲^#n=Ǐ9qgVۭ[7|||8}4O<WWW/28Ç9}4۷o3Aff;wSNbii~KKK6lHZZΝ+0Vm]1d˖-ܿ .0gΜB6Jjs]6k,e_'''2b`[FF !BQT_5%''ɓ4i-رc/9B!(=zĄ 8p NNN۷O&[o:!(SXx1:QT^?@Ν; Z*3gΤu1l0V^MVVCBQ l ]w...\|Yvi>|WWWҔiW^%44Tʮ]ָgN8tQQQ߿B!DI￙>>cӦM>ϟ[޽{3om}„ 177gԩݻ\8%q}5boIJJf͚ e$$$0l0044TN"%ё-[: !(S4*Yb׮]cʴ!CvZVXB!** GGGj׮Çi֬CZܹ3Z"((H&\!fΜƍou(oooݻG\\111DGGO?OΝGo߾B^ڄ 祗 ={OWmݺ5fff ccc7nӱcǎ[י3ghذڱFB!DɥKСAAATVMxƍqwwƆAqA" QVsqy/_6pZjʛN `O׮]uŧf͚j;C\zzK%ZjL8'Xf~{5̪UبQٳ'={Ժ+[`l  wRly~}}5yaDlիWW*U˹r ʕ+;vѣGˊNB5`] D5`4h0k$''z*+VĄ>Ll/_Ntt4ׯ_筷ޢw*wɝ;wXnԩSGnݺn:JժU_By }fb0j IDAT!e+'O~څB 5kdٌ=#Fжm[]$ʐ*UЧOL\\5k$00PmE^xzz&\dMZܹ3p+ڕNJJ s%99J*aaa;mڴQ;ŋ,[D^JʕiР=zYc ^hqF.]AjԩxJO 7[ *tפIƏ'\߸Ϙ1cp,]NTTΝΝ;TZm2j(:tVy_#/ސ_xuڕlٳ'ddd0w\@(پ};xzzOw522I !^u6luB! ) _~EѣG8q'Np=x!)^zPRSS (p]vMNԎuԉC% !^Kׯ_ёx/^CEĔ)S_ =V!#Gl2<<P?~\-DFFbddu]j"++[nQV-cnRB!S/ܻw#GDݺuIKK#""+SDIn۷MY]ΡBQd/`AD }6$D{7{.\p+++W ?4nܘӧcggǎ;˷3gаaCc]#yu˂ 2e ^PIcфqi!k׮xzzbgg'GbWR%ߟ7nKLL VbƌTZnݺ)'`4mTa !(j… ̞=???5kƩSTb*t֍+Wョ'M4%,,իWcnnNPPtbnn.4hMEFFĊ e!Bcٱe儋?{/MǎYdr&MdJRRWخ>@%:0{l9zJTjժPB:t@@@z"&&F9ᢨU\ׯ(Ą0jJ֬YG( ŀ{mTj7I&PhZ(j]= غu+?-ZPm۶-?֜>}QԿ?M1⧧ |ɤ_d'G+ `C!s|W|w$%%Ē%K9r$*;effܿsMVvL##F믿2sL<==eW|AAA2B!^̙3ٰa>>>,^XW[oŠA4hgϞ%::h|}};v,5Gbmm]E#B,=]29::ҨQ#.]3;vdĈ\tI-35"%%''':t\rʁ5j_~|ty)>boBQVٳCj<ow᫯ɓ'tڕɓ'OLLIVZN\\2<]uH1ZAOOѣGw^k֭[p]tŮ\#Gd֭\zUyI&+w,(*막6C|Pm"ҬTQ유iIl6֕w7"ȏڟ6l )) x}bEMUj۷/}%--XVZŏ?Hm322|﹵j"++[n6-e!x-]qaiiɚ5k[C%lL8PLp6lK.͍$E*TK.t҅3f?{nXz5~~~TZkkklllU*ل ]Y&ӦMSIS[oWV'2qٳ'={TIR ,_+WP^=\±c=ztVB!^5[y֯_۩R !!!T^]-Ovv6P+6lȉ'HOOm۶:ɓ'*=C!xRJTZN< }ФIw鼋t֍+Wョ'M4%,,իWcnnNPPt\233iРM6%++  &e]vv6Z ///&NHH'OָӓB+W,,,X|9...IjԨA ;w]v7|5_!Dx&\tUOϞ=144$##s;E >۷'Tu !eb%cǎi\r700޽{^‚'NPvvvtޝx: ܹs7|S۷o+c+'ȑ#9r?L )؊rJB~XѦbU[`!Tj7:uɓڵȟ_k$ڶm&LN(ݻ75jޣ{Z^W: !DEDD:R! %)..DScƌsT^͛7/0e-[FV^r>SN^~~%""B> ÇϜ9s3g1333忝ٻw/)))jU\Y=>y&ȈH:wL\\*?wBΜ9=W^%22?X!l,\(zpi۶-nnnx{{xiҤ M4ÃǏ~bbbصk˖->GҡC*Tx !KWe 8r}aƍo֨QQFkL>3tP@ RrbK!(+=z>=xs}&L@= #--gҾ}'Jxx8#GJ*揊ݻ*4h@jj*)))tUcǎ)'ccc`|$%%Hl/zJJjj*j4o\%]OO߿JfffZSmf,[p:}k̜9!Cv999ZQLW G1p@]!B^rHHr9EXh+7顴ٷo ԬY+WRN1ccc<<\АP-[ƞ={vUT WWWEU&MŋÇڲ`6lJ bccZ,/ODD...ӰaC]$tytڕ`p!/hƌlذSpW ڵ+]veƌܼyX_zt [[[z"B!^k''NyT\===ׯԫWl߾=nnn̟?ʎB!P^x1O4;w?ӱcG>~G޽OQѣGU%̛7]bmmMPPnۛ.]Э[7[ݻRvll,XYY)c?>722CLkРA3Fm¶fƌtޝN:뫌Y~׭[Juܜuqu!׷o_ؿ?gxxxDYZ5&NHdd$d̟?_mpHϞ=ٺu+d׮]#8vW\Qr ǎIma !( PonnNLL ̛7VZ_zj7޽{ڵ={CŊ %էԩSp?Ύ>}вeK|M$))]vMHHzzzXXX`kkKϞ=BR7lX!J=yB!Do`ٿ?S3c }EOȴj*߲hԨ3foaɒ%,YD%_v+*;Y1cp|oZ/_cW_׭[~ bbbQ9O?T,;m3Ο?ՎxyyHl/rJҢEe*k&88J*{xx@FF R96k,|||4ӲeK<2Mz+SmRJZb 6nƍ-^zL4}hRu5 &xxx믿rERRR066V52^W$""B~B+hGEjj* \|cccfΜI9q\vM%oXXiiiloooLLLߟCm68rro׋.\@zy椥1uTΟ?ʕ+qssS9qƄk׮aÆ/1gLMMٴiueժU#GdٲeI!xʕcŘl2F됄(}}}vJ׮]5k˗3}tWNݱgȐ!Y! %BRʊ_`100M6|tE%ffu 0Fd> 4>/T`jjΝ;ټy3vܼycllLǎ:t(Mj IDAT6Xg݉dɒ%߿Zjѹsg\]]yn&lܸ`|2=wƆѣGSfMWCƍ"-- ccczѣQZVZ… 9|0ٴhuy1uT9ƒ]v$T۾X"| #F`ӦM$&&r9nݺE122m۶ңGʗ/ړkTTPΟ?>mڴaĈt֍F1i$֬YC^yY 3wB!^k֬QabE-4>Wسg>>>n333|}}7nJhj7nӱcǎASlmڴvޭ6IwX///@<x4lC­[Ǔ˩Sعs'Νӝ( R|yZhQ{[߿? 'OdĈhт˗3hn<{m*TPC맸G5"BQprrVZ}W!!!̝;W!D5d.];III.!BOOҥK B!B!_WA?f֬YDFF$33[2{l>â[=xiӦ?O||<666|w^>>l޼z"/f͚Ell,`mmoͭ[HJJ"((h*V2UL3uT+___"""000qBmi3ё~/\`ݻTU\ &(רQ7oү_?4N>|Ç{n2339rʱ +++viӘ6mӧOөS|;gƌCzz:jw ???ʕ+LӦNNNrAʉ \Ν;Gjj*fff*y}(M !({كUVx!...qFtBY-Z`ܸqbooOڵurttd͚5C!J"W*  6l(eԚ5kزeB KOOu2Ξ=˦M'88XmcjLǏhLCeg3C"lI!0)XGL҆J2gD}1GRD?|t:T$q\u_4gefӦMcl޼cj)Z:s Gڵ*UD>}hӦ C !<<ҴiS\ ,aÆ =ŋ*ϳ*S xzzr>} vvv̚5Ke&X|J;Gݻiдo^~B˗/qwwgΜ9ۗwOO5jԠ_~xyyI…Bٳg? vBQd 4Pyk oi!{p!B!+>dDFF3LMM>|r欉o>oTB޽qppPH aҥ;wRJ_wߩ̰62e i&HKKJ*tgggʖ-7k;~7o&33\ƎKRhذabONNۛ#TB׮]qtt Ǹs*W>3g0vXʖ-cll0BG|wbxcNNNtޝ/Ҹqcm#-/^-iF! +kkk+[KB!B!g)))888p=eY\\A899q%eYBB~~~\pOOOtuuU;v,IIIyf^|[\t)+*u ɓ'̝;7A={̱==O2zhn޼{pp0111x… ٱc܇sIᅮvgбc\cl߾=˗/?/ r_wDArNCjj*j[XX޽{0`@"(:vHjj*ӧOGp.\@͵Ж#Fk.AAAaggBQ?~p) Mnݨ[.^^^XB!{mȐ!YqK%B!,_sssYʕ+9n Uξ%KB !B!ĻĽ{Q~~~DGG23B`` .]I&s BCCi֬111l߾]mO>0oԦ QQQʤ 60}t8vWѣб9bfVlȋ챯[7oRfMF\~+22˗5j*ׯ'>>ZjvZr5c N4ݷ `4=+ȽsVTVXQP)!>}`vJV&66+ >,eʔal߾ؾ};gϦtO:::|ȤI|Z]MU?111ɵݜd=>>xzܿ@fTLpp=eY~O=֬U9[%(9~}w0ٓp rgs?P|ye:BIWW[[[lmm(EqBM 4t8@(ƪT !(3f ((SѪBKX[[k; !(vpq(%A>>|>XmmΝ;ͣQF\tIy;t蠬[bEnʀHHH`9ϟ={ՋUVܹsiڴ)W\]؟9_~%vGGGuFʕy!YgϞѫW/5kV5]79oSay9''11;w%VܹsGYG!B!( .0`{nzɉSd*Tp⽷|r>sVZń Vdd$k׮%22;w UF֭5j_~C|'g"׉LJ#GpmөP 4wތ3Fex5kB+q~ ˗\~ʗ/Oz޽;Ԯ]Џ5)J[>脋0VX9ϟ?'<< qFe"E^r%4i+ܹsbbbؾ};*K7*VaÆj*ʢ$B!{O>Ga!D% cСܾ}#GZwȑ:tsaggtL4)/6;wLxx8fb֬YjⰰP&,XYYaddĎ;}SRdI6nȺuT&.ȪVZL>1p]Uuܙ0~~(TTI6jԈ+vvv߿+W,ٳr VnnnDDDtR.]VK.̜9kMq圴k׎ÇsT*V#yWI&BٴizzzC!B?~<-ZԬYS!ӦMcݺu20X! A4ifbРA2 9ݻwmwcӇ 6`hh(_D=zDTTQQQ,Z???Y, ^q/_Tcbbba…|wxxx$rM%\( ^W5m+[,O>%((7oҹsg To߾}xxxPF LLL={6ciicQۦLRB!Br ZLȑ#<}5kʺyʕ '''ԩ ͩSѣGyuΎ[nƍ`ʔ)\D t%J0e˖-[<*UШQ#zAn(Qkc/_<^C#*WLn3fʗ&MBWW}3͙9s&{ҥK9~8*UW^888͊+ؿ?)))t 777٣v|}}},X#Gزe ϟɓ'|G|gXZZұcM^tMMqٓ_~G2|p=ݻw/ ! ֭[ٺuB!BEjj*ǏgڵO?Q=2CCClmmYz$\!D!9s&8"/,,,y&5j`ݛO?%Jp-6mĢEo߾DDD,%"]>ӇݻwөS'nJ 4ֻp:tǬXoG3ؽQF\|m۶)hիׯʤGh}AaܸqxyyajjʦMh޼yu4iK.-(,ٿ.YW =zЦM:Ķm8{,V"00+kpO>uUۦ(SB!ćiѢEZ}eN2ڵkӠAwB;ѣܹs h1bv"((FFFi9:!Dn xM0k; !X ɉ&Mp)Jfffj ///IBBbeeEϞ=qvv2þx/ݹswww~w/*UD׮]qssQFj2^5X.\ȡCHJJVZX[[3c =ݻZ*aaa9&[4i҄ Ě5k.?>TZݻFݺusWff&'OdڴiDGGSti:t3hժ~׮]cɒ%DDDRLLMM۷/'N؇Ɩ^k׮Vύ+WСC*G!sNKK_e˖-\x$*T@֭:u*;wָNnN rM6nȼyz*ѣӧOx]1߾}+++ڵ+;vx{{c``}OsEڵVZ|rLLL4~g]F ڵ+3gΤI&j5=5)ʯI}dА}˺u1bOLLViiiX=zPjUU_|IKK{Q !Dӧ ,k׮j kkkbccX"ZP_eʔal߾ؾ};gVYTQlڴM6i; !K6mݻC!(6;v,vvv=#GH(ؼy3CBbc\vUVi;!ѣ4mڔ5kNbb"hт|YLk׮M6o<|ϟsM.\HǎIIIQ ^gTk5rH=ɓ'Uwڅ֭<vZ7oNDDDm8q;rAx1;vCDGG=y$-Z`ܸqt8ugϦy_[v~)k׮͵mccc233yqbINN]vL>}Pn]O]K.憋 u~z^edffr]듘ȝ;w+ܹ#B|jc%?[nQLWٺu+C btBQp...{nnݺEzz:h۶-TVM! ! E8;;xbƏBEa,,ćHVB! ױ͛bmm퐄ѐ!C} 2w\"##/ԇRJȐ!Cxݝur5ضm ҥKT^]M{{{zO9y$GƍOڵKYwҤI$''3f\\\Oĉ?K.1geB֛ƖUA߷~ˤI;v,6mʊN:ѠAtusu+,XSNѸqcV^9sI&;_}U+^vڕ&Mp زe 666)緿]vcllLrr2r5KK|y^նҦM~5kƍ7ؾ};vvv\vUۻAp1bՋcǎm777ZoȑԮ]saggG֭ٳ'ׯtL4IYbŊƾkڵk bUuСp:/B=z'GƆ)Spz)))0n8lllpvvfڵ4h ݻ̛7aÆaooϚ5kTVx%7ndĉ2|pf̘Un_t {{{̙VGSLwNNII!<<&M8PJ7nLxx B>֖ILL ;vU-S(W{oCAω-K,aɒ%ؼȄB!B7[ne˖ϣl!2e0byBb͍rI2x^ҥKSKM6Y'ʕ+sm̚5 SLſ_]6zzztЁ 6~ze}$ 6,pVXӧOҥ aaa4n}}}7nLXX]t/ر#aaak׎eҩS'֬YcT,Z:uꠧG2eܹr2m۶ZlYM8SKDD4nܘ>Ν;ʁxEO:&GXnڵL2*U֭[n:Ν;6 "$͛gקaÆl޼nݺ?zje73hҤ ԬYxШQ|?ի7hЀкukJ*EFزe ]t!))Ih@Aۻ0HʊYfѠAԔ#G**槞2SȈ%J`hhHΝ PYbʔ)ԬY%J@Ϟ=)[,GUѣн{xFB[RR9rd^xݻwTf2sLy/^ÇڵYf$R(̞=Sۧ| ͛IHH ##4^իڵkIIIQ[)ITTiii4kLm[fHKKS{S,B!B!B/?ɓbРADGGSn]m%D;[nw^m"ņK,! Ǐk;!D1s7|q@!##={2|8;;knnNff&nR={ x*}⢶b21j?~ZYVxJyZӧAAAm7&33B-^?-ZĹs2e M4AGGOraϟOnݨ_hsrm2335 駟oS~IVnnn,YRLGGSԩZ裏rOCCCJ͜92eʨe߲+](*Ň}ҷozL<ɓ'ZGC\rK||2)>>3g0vXʖ-BQ0۶m/L0+n:""" sl߾{QZ5&L 7n`Ŋܽ{[2dkԨ\2aaaq }tuuI&dddp|||ꫯ U,ͪVZOzs:ӧ011QkTYGFB!"\2OV۴iqC!Bf޽pׯgСI|iذ!;vdK!D1x`V^ͷ~ˉ' Q\t ##\)fё۷oSN6l@%T_~ chhǏINN*mg(WĖ]:uʕ+B+F"::h8nݺ_W__heׯq<~bbbȑ#ܸq={I-^OV7oŋp Μ9@fff)^,,,4+V5|"y e|?rNTҔW\\\]Q~ T`Ĉڵ \\\WaggB'!5%SN0j(GGGeRBLL jVzP~}ƌ;'NPK=zͯ0RRRK.Mjj*v޽{jJcfn|'[@b5jmSe@!B!D|>|XDBOӓ7WB!Dڵ#F`ddɓ'iРC@6l?v8BQl,_͛秜]^*Chh(˗g۶mTXQӧOXQNbccuy'##CeU,9 X/_~I9?$ LMMٻwr7-7~YUPAeUVqYΞ=ƍ߿?_~%JSձ+$)) _R)!ĻLppB!(Çk,3gdʤy+++f̘MԖ B!3Xh#F`ʕ񾰲bĉ0{lm#F&Mpvv~T\Y! r?~$._ĉ!00Fxd>}ctЁXٓWҲeKkW?~LRR*URG1-ѿݻwٿ?DFFrUnܸw} =\?RRR?sM-_<5hkkkj׮M*UhذrڂDGGG,---&Yt*K eoݻwԩS+bӦMtM+](r Zh3/`񘙙i9*!ʾrbeEv߰WͲʚa84mڔ'Or\BHH;wƆݻ9nնIjZ"UQU! b„ XZZj; !Bc }ޗ x ߟhccÆ Y !B㱱ɓYѣGk;$!ޘ>0c ߬By̙3Yj"GuԩS;v޽{y4 Bjj*sΥo߾9555̙3\pvڽ!Cl2~W&M?'[lɓ'*cPLMM?W^j8qBY03j(FΝ;9t[ ?رclٲo6׺pwwˋcǪlKHHP˗i˗/IMMUƍkٳgرZcǀWIu o,^///Fr9s???tuuX?k׮jǏ^U_oBޮ;V[[[,Y’%Kv8B!A1ݻwsWZ5TϫVZХK͛ׯO>eڵjO!ٳg2M}k5Qd>+JQ&B!B>x!f͢k׮m;;;݋ڪΩX}Ҷm[kVZEzzN}LHH-[T~6^:愄h|&B!^4oޜD%B+$$$cm"Ŋ! ,ۛ3gh;!rqP}hh(:::G|ʔ);wNnntڶ .CڵeZ{$&&2x`RSSsl޽{,^]]]L,ٳ'-R$˗IsZ ?j׮/^TfaaVV99spܹ\۟?>۷W)W;6lZ[K.U;xX"111j4ƝkŋT)PEonԩ7|CZZzz8Czz:ƍqU%Kx 0`m]]u!BӼysvލ?}˗'((ի|rZlI||D@}uL022ʵMCCCؿ?ݻwÇ oMǮ]6>jWBwmk; !ÀxwRRRppp޽{ʲ8\]]ꦧĥKe q<==skϞ=jA‚ݫB!˗/qwwgΜ9XYYメP}///pX>|8^^^L0Ç̾/DQʕ+W^xxxдiS ! %Kg\MnݺƍcժUSzu&NH9~8&LM˖-ٽ{7M4￧wsnܸݻYdrқ37+VpAN:\~WWW:Dٲeoذ`aaaARS&d0c+5j;vVZ1fLӦM)W'OLJ-[PdI̙r\ Ғ/_1ׯ_gʔ)TR8.\?zHeڃW۶m͉gʕ믅rMdС|\xSrqU Yh/^$"" СCi޼9}?ԩSDFF;w֘T3g&&&1m4"##OVdW_o$\!BիWqvvV6p@lj'HHHP722*Ֆx{{㣖ѨQ#I'Njժ\26[lIDDׯWo֬Z[ӱ̈*q2 "֖ m!BU :Ta|Ђw5jয়~aÆ?3f\t&Mꊩ)wÃoߎ%U-l@Æ PEEEI…B!Dqa~B"DQDqG! <==iݺ5֭cĈI55k$88~ZnVgժU*cJΝ s000ȱmd@ueժU8880|J m۶e*ekV޾vZK(Y>cf۶ml۶Me{R O>1ضm/_Gjk֬?Vb++Y$;wdȐ!ݻOOO<==5ohh/m۶U)oѢZY j3g$$$k׮qI_3سgׯ_C*mz{{w~IV+VΎ 6WZ-[P|yeYA:ڵiӦb XDL8 RT eرW^m۶QtiܮIvE.}Y&E!-Zy䉶B>}pUzzz|Gtޝ{2n87n19|0{B ӠA͛{4i҄M^022BOOҥKS^=ƎӧՒ-kN:FFF,Y###ӧw?4144$::~FQLJ,I:u9}[~@Ŋٳg;Æ ~-[]]] iݺ5sի**ӫW/X?1cưvZ5kFR044/`Ν̝;3g啧wQQQۗ+O֭ٹs'cƌ)k0tPBCCiԨzzz|nj7gbffR }}}-[F\\ӧOCCCJ(A hݺ53fڵk/&[X[[s!͕㿦NvM4)ʯM' ךW̠hF]B}lݺUBq ##e˖0i$s۸q#aaaXjժ[ $3 ,[}*-Y(fVhBQ\}AAAj|dD6 9vRiiio^ڵͱ-CCC8q6mڐv,t,,,#::p:'BEff&/Օ={H ܹsYbj)BGQ~}FŋB )>$o& z[M !B"_~9r;w*sܹUW_i9:!yuׯFmܿ@?Nbbr6N:1d )ڢEӀ BM7#:=}46uuVi:B!xnj5;wO?"yoIBʕ+3o<&N=5vHB!($pakk+ʄBּ:W.]3|k<?ڥB =~W^e̘1(ӹr W\aǎ*WCsi<==ٸq# ̅ 033S… 2SB!DO<͛73k,fΜn:}4͛7/NGGL /:uŊ+B;mڴaL2/ej;$!BBOPB삃:tB!">|ˉٳg2|p\]]DT|}}ٷo7UTw888H aҥ;wRJ_wߩh62e SΎ~6mD\\iiiTRΝ;LٲefmGѯRJ_<|Pp`lll399+2ݻXYYQfMmۖs7ѣGTR]舁ZX`C__3aTKٻw/ ajjJ~udBBBhٲ%ժUSW^sssBBB6l˗G!Dh#L2x{{ɑ#Gx)&&&1k,g燏+W333S)SXr%гgO~=U8z(tt\!p}o-B!B!x{eqqqdqrrҥKʲpj';$ؼy3/_ͭm.]d____:Νc{|qttoߞ&Mn| aaaDEEaooߨM6l`DEEq1V^ TgU>]1cЩS'ƍGhhrfnCRlYTP$\t!_jݺuܼy5k*/5jjdDGGG5>:aaaXh9ظqZ/** (SBQtusss~7dYb...888;ݺu ƍ8LQG!KO?qE _9郇KE!uԩSڵ+(+deeKJJ ;w^z̙32d۷';;ڶm˪UT.Ҳ6͛8V\ɓ'O  0L1*O(xyy7( "޽g5zRN +WdG!$KKKƍǬY_*e!(IxN(,^;;; eѢElڴ mmB $00Pa!xMOWVZBŌ)$444jEWXt) ڵcȐ!ѣGJX ăHHH(1TSSggg”+@4k֌>h222HLLT,E2+ԡ[t gl۶'OJdd$3f࣏>*ܕ=Nq-_- !([y?zCQu)By!'$$?6lȒ%KHMM_~\Ο??3ḺҴiS""">|8NNNٳggg޽͛ק˖-TEu҅uod,Y?L'33N:E޽INNVY}CAq̎; 6App$\JqwwO>ߟM;!ڵ+| /Vw8&::Za!D jy]ׯ3ae=111]5F'BODDD_!^&β sj׮'OwU|ooo FXX,J!jIÆ o5jvʪVIKKwީsWˈ#((hu!jCZb˖-e:9P ə3g011!::GG޽III!>>T1ÇЭ[1(uB!BOrikk5M6A/uuu ׯCeݺuZ&+++ԤG|WL6 wӶm[ׯ\ٱϞ=J˖-W3}t6l؀~5j+qƬ]qѪU+ׯOVgݺu8& ҠAZj+WT|tMMM455122b|geSF%Αr!ϒ!,ȧr!aذaXp֮][j!.]*.\Hzz:}0Q;w*ɥgʔ):t>O<==ܶx}*BQF gѣG5~~r'R6|#S[^ fff*/kKM!77(233:t(vvv٩۽{7l6mϖƆllllܹs;Sn8ƍW1Sc.]ҥKٱcsO133cܽ{OrE \.͛`ҤIjH=iݺ5QQQʲ( pttTcdB!B!=yO?+++;Va]v%))>}l2n߾ɓiܸ16l@CC7n3oe˖tE9beۨ;͐!CԤ_~Vt===6lC r6atttWw(BQ[?ʕ+G!V(>ۣbe\Ѣ8+++ٶm'O$44Hf̘G}P"W,_à IDAT]^|E,-BXt)52cǔr1,,,uMM/Ͳe(,,dҵkWu&+kȑl޼???T뫬!nʟ=zQmmm|||={2$>> Bd8}$/2!rÇD@@ ,X@駟x"/WWWZj_iZΝ;M1cF3W !N Q=2BWQbb"hjjr!儎UO`` e։&::DNNN &0a„jeOXZVXr b݌1B!DC@@?~g*eeeKJJ ;w^z̙32d۷';;ڶm˪UT.Rximeii͛7g\'O<[!ldff*ݻ%oQnLrr2wӳ=(+##=zlWfѼy8p@c< !66BƏ1{VCBJI3f fffl޼<:w1zx{{sY_hu,[e˖aoopB!B(,,믿fذaۗǏW+BQuݺuc\Rݡ!DgccgϞw%--Gqi?Lxx8M6%""wē'Opvvݻ[}}}BeHMM_~QT.]8:uĹI;vÇܻwOT\C)* ?xŋcggmڴ֖ExVpvv???T9r߿SNROKKs*u}v (1#A !?(?N~~>ڵc̘1|9rnB__AG$]*|>}---ϊl ӧ 4---O?ľ}S[^̕u혘"""ؿ? 4`Ĉ?\V^_4h@>}:u*ڵr}*vdd$Wȑ#hhh`jjĉWֽ> ЬY3ey-ѣ =ZeW!B!B!xܼygggvҥK3g$ ΤI|2oB:Mȑ#wޝuѩS'"##quu`ذa̝;Hrr2gf嶡x[oUn tsdnݺܯoժU <+V0j(sr8}4666L>ӧWDLg<88:֮]UV'&&k*p%mmmOڵ+hjjbddɓϔtuu ׯCeݺu*S̟?mR~} 1b:::$&&!11]]]{ڿ`!z~)$77ǏsE=zg}ƶm۸~:??`˖-Çeݬ_i޼97nT>Tɓ'ܸq~իW{UmP?tP+z߿̥̋\~5kְcg,Y$>|9x k׮v_@T'֬Y7^էxNxyС=L<ccck=8q"seݫWNFF!!!ԫW<;w$// ,,,HIIa׮]|G5xuBׁ|3%?!** jN!x5xy\\)))L6 cccٲ?rgVf̘13gXbԩS100>`˖-ܿ<`X~=dffHVVPou aÆ޽{KM(sU\]] F"66ӦMFͦMTVw[S[qOL6/eŋTLQGMtt4=RwB!DڴiCx蕜ˣXݰxb߇lmmYh6mB[[E)Bݻwquue˖-_ۻSBcڴi,]۷;! 9w˗/gܹ=zUT.\Hzz:}رc$00Rm\tI>yuܙӧOsA)˓Kϔ)S2e ǎ믿S.^ xؐ۵m˖-cffƣGHHHϏXeEdd$gΜĄ bddDVV ^>=|0ݺu+Òp!.ᢶ^/aL4 xz!u͕+W_JtU>RϥXv)nԨ:CuΩXJ?믿&33D3AW߿ѣGXn֭+Q?%%+p*^UʟT^7TmBV\^Tv kהeYGRO'B!^ cǎUw/^ Xv-ׯ_W&nڵkPctB!x8qqq=vލCBxw־bBz-Fʕ+%B!jч~lޫW/Oζm KKK ޽{ dݻÇ|rFͰaÀg_^XX(*ppp 88~7nlٲmT=-͍ 6`nnNZZnnn%Ihh(Ջ .+iڧHѣG۵MGG\dСaggRo*'ر#>>>ؐM;w#ŸE!..ӧ0c LMMBr*>]1x[β,&Qty:g3̤uۗvѩS'<===wU?tP 8tVVV\V-WZXX_^e=Lh6ڸ5:W~}mEbH~Q\Ci}(e5k,f͚0B(SصkW^ECC###ƌ25kְ{nZhᅬTɌt=6nH߾}n3336nȤIhܸqtB!D1aaa̞=sssiݺCBlllضm[^$www>C~7BI^^^lݺ$ebRD-[۷*m*ϟOZZC QۗȾ`(1GcE)>ҥKYtr//jw9sgLrr2+V7WZ[[zENYnݺPNEBTKpQfsppAa!uJ֭9w={T޲eK\w}G˖-k8ɓ'/O<)1ؾ:۷xh޽K?q۷DEqUws~~w 9r$_}򝼇; Y|9dggRAOO矫 ~ؼy3we̘1Znnnhhhw)muԉ>O>9zOj߿?g۶m>>XZZҸqcׯ!1eLGG/QFѲeKի.}eɒ%՚&9e,,,BCCN:1g~IYUVԫWGUǏ..7!CШQ#yqNNNXYY{ݽ~ڸbO055~4k kkkoEC!BQ5s̡aÆ$'' '??ZLQ$Y(r-7n\bLQG!&=zٳg3~x&O%B XZZ_sE Ѝ=Oʛo̙3gggA۶mKMtjEYZZ?͛7Yr%sU}6 `ڵܼy|/q.///[ y1$''B!e2e }e֬Ye8!DU  )@OOO!W\BB?8A+g VVVXYY5j֬YCNNm۶ '''N憎N/B*9B!Kn^&L'OFOOOf%//---f̘}??طo4lP:ɓ'ܹgggtttعsCATӒ%Kسg̙3x=ʮ]iӦ w/v~ <`a``@6meѢE}׮]#FߒJ{Z!KMM壏>O!DE 9w.]C_p!ۗcǎm2:wӧ9x ߔɥgʔ)L21cp<==%B!^iӦٳey!O>xxx3JLF#UQk B!B!^]$ӧOӡCmfȿ>\|YYvRΙ3geW^%<< BBBW1nnnʙy)>g@@S_bk֬Qs56nݻw]:WGEPPrss:ur0+LˋÇĪUg3ZTvuگ*~X~=mVy5kЦM.\uC ȑ#ӦM\BddNUrÆ Ю];"""믿h?|M_^(:}\ѽW033+S\MV>|nݺ8^Q#/gggZnMTT,** B!^U8::2sL>~'I ///vJRR}ACCaٲeܾ}ɓ'Ӹqc6l؀4ngggn߾ @˖-ҥrʶQw!;;!CI~Jyxxx2Đ!CܶB4h&Mӓ((qƕ:tZU*kvi劁޼hhh`jjʢEJwbllLر#>>>$$$8foߞ 2qD޽\tttTa;vݻO?|rkTꨨrCiիrXbv? ~m Eҗ`…Cy?K]PP@/`ZXXU2ꨉɹsgI#)F(!^%o=U\zWZZZO!gbnnΎ;HHH>U0xU4i҄$̙ChР͚5ƆdyYNppy!\|/_NǎiР>RUѰaCiٲ% 6dܸq Zrssc͚5m۶~JBQ}_5oOݡ!ȋVZZ!!!RXX166wM!P|||͊-ft֍bccٸq##FqaooO~<>**ZKFEE̴2(+qX[[z .(322RӧO뜊 7n`ܹ/%6_~}ع:W>u7.qLQG!^}f̘QB!ٴi...уǏ}TL__@ˬMtttr'''&L`„ j~WZVXE*)h۶-7o˫ܘBԜ{?(!쓙43Of1Q990d|0̊DD-9D9YdDޟTyvu߯z_׻z̙֭31c#FwuHB!D# %ӧOSSSό3 H !9svv{C!J&cJ%+Wi&6mIJe077gРA 4KKBȾp[X;بǫUȚPX}fxb.^;vܜw~뭷xIIImܹUS<>9Ѷ?x1T$^]Bg֝=39UVŢe˖ܹs}1bĈ߷oҾ 8}~&̉*^m:VX$([_899(IV!"==iӦĉYd *TuXB7ȈÇzjܤBA&MbڵL:-[:Qmܸt]!.EhHRR bӦM*e'B!uXXX0k,f͚oFpp0֭c޼y|899iy0339s>H9~dz}v%1uF111PzuZw "##>|ШQ|_C~އԫW7oriJ-OP>===233y @UΞ=Ve~Pa*?siffF\\NZ9~yܹ@:tu~\?]ϋqQ*̉ $''cbbdB!B 888pW9B...,]]vѻwo]#e˖-GDDD`U۶m%BQ9::I5b[b߾}q%ҨY&:ubTTIiw}VXy)]toQߺu {{{_~矄cffqUt ӓgRBuƔ)S*L_\#GB 6lzR#55"##{.5kO?eԨQj;s>~ڴiCڵuʊJ*U^B!ٶmBR [[|N#{][lI˖-Yx1фhѢQ:~Ӻtޝׯw1w\{=.^w}Ѷsά[wwwhܸ1>$(( 6`eeŪU5~ay1uF\\~rѣGԨQCI2uC~޽;^rѳgOjԨٷok׮gϞUy}rҩS'֭[ǂ ;w.\pACJիn݊=wa…9=|%sα`z衴\ٓ+W`[7o˗?~!{&""QF1fv튉 c޽YOҳgOou(M^I&$&&XB!B.22cbbKlS!Dᰰk׮b_!xlllpttՕnݺI1aҤIL4Ia!DT,.|}}W;HHH=R<~#G_y~XYYqIΞ=K͕sgϞ%!!mbffqUƎZ?̋/pww8;;s=5׮]c̙<}3\\\pΝ;py|||Rw&55UmKkkkN8={4 !:ckkBbׯ닂>;vcǎx{{{n6mDʕlagg/@_͛Z6d5|p8{,C Q;WbE\]]=~a٩S'v̙39sK.amm$SSSoߞ8p111,[e˖iէOgggϭ[9rڹԩ,]Kh\FqYZZg)3C a߾}\|1c({,ݝ{鉧FΝ;QgRa|eNڷo9}tJ~\x???|||8t)))4lؐ!C0sLjvZ8@RR+WUVШQ|^}~Wdddiܸ1C ƍyf6m+V 11gϞ{rʱl2_~:ӬY3O=!'UT!00իW111k׮=U*m]]]'22Obee~־===5k111ԨQ={jT\ٷoO!ʺuV]!BQݿaÆҥK8qCB#z|UBׯә?>CuHB6nHzzBRO/ |fqFUS-vŋ\rWrIɓ 08|}}iӦǑ#G}][lje\z*WݺuUV=v4X K^x8 ~WΞ= 9gϞ_I]CUB̟?3tP\]]y!3gd/?l?gРA?P*T@޽Yx1Ɠ¼y %!!SSS+VTک5={ԩSY~=˗gĉ|ܿ &}v ej;wg2uT(WUVeɒ%-ŋ̞=͛7sM i֬O>TO dzh"@wOQ4}ŕ3L255eȐ!:Nҧ(VB!/Jǎ VZIQL=yO?I"Ba+VӓBtБI&1i$]!eKȾ;`n.^/bffFǎ177L=|yٱc]taΝcggqUT wvLW6ѧF;ձ+Boƾϟzj4ݹs;wpLB֭_#ڒٳgvѣG5&LPY0? =-f{ IDAT.^ƍdڴi4h@*'`֬Y(}||_>AAAJӧOزe2'NdݺuÇ9uիW:fZZ;wV->> p vޭĉ*}sΥaÆ\Rʏ?>sܿ VZsӧ+ϟ={FLL 111@!D|ʕ+ʁBQĊe…ʱc֭qqq|GQF zʕ+Y`~-͛7̟?_k=zÇPq}d,X@j&44333lܑuݺuFƍy!AAAlذ+++VZ\LH||FbE||F!(Td!y&0k,7nvKKK<==!22.ĸtؘso+z->>CMơCB.[nK9֫W/v֭[8p+>̙T1b^^^1o<6l,͓'O4^rJaccCxxx Û6mw^~Xn5"((H# bŊ4o)S0sL>}իW+̙T(X˗/qƔ+W{:$''ƍcܸqjqrr:V\߇HԥQ(SGGG]!BQjӴiSN:Euҥ M4ח+W:!(s7n+fbРAʦvB!(|2SNܹ3g2sL.]ښ!Co>._̘1c=zPСk&)) 333>W#F 22gҧOsGV>(Ξ=ː!CUXWWWy9x ϟ1QU٣CyQ!(o9uiiiѯ_?.] 'j?s1ӧ1ch8?Է˱ϬV,7+W ݗ8}4cǎYBz;v௿vL4 ryZ۷su7nѣ_>'~~~;wеkWԫW &`bbBxx8?~\y }1o<"Ν;5j:uBOOW2{l9W>wȱbC^d}۶mܾ}[m_˹u[neԩS|74k֌8IJJbǎJ۷Dݺu4i 4ʕ+h/*N:Z^=ͮ sV%kW)Ƚ9U ͕6X;FΝiӦ9A\|7o;i|orի3bFɓ'YhSNťHB!x/j~LJ͛7g}f!B݋&&&?~\BDj4h7N!D4h VZ_|ѣG56B!+ "wDDD2dG'BLN ĉ˄H ,--qqqHt===233y)*Urp}>V׭[۷osEZhzFݻs!lB֭_F7om6vZ"?s\ ^˜TV{c?Vڔ%ׯ_xyyi311aǎA^57^&*fdM.377\y~uƌB!Dq[(Z>>>tΝ;ǹsx ٳg( \@@ϟ'dݻw9kkkN8={/īB!DI닫+;v$((H@ !/gG!ʤjժ`ƍѣi֬CB!JjW! R-.Yf W^\r5kh裏Xl7nŋ<|  9s=~ayiӹqvd?0k,y&餧smvޭ|*mڴϏ+W(vZeQ3$۷4n߾f{zEh߾}_UϏ .s]U&MOHKKc̙DDDp}?ν{ؾ};f"--?{@k{O_gRay j^uLF:~8aaaC!(raaao|r;ơC߱6oެ … 4k֌7rqBCCiѢ'NPccc*Ϗ9h:j#B+33C2qDصk$[!ÇgչVBQFI-4iCB!Jp!Bw>|smkggɓ'r _ڹ * Ϗ>Cハ7nТE VJrr2'OVZX"9r믿m6eOvueԨQ_~?~;w0c s?Qiӆ{a6lVi"zK=577O?UKLL 7odk]?vX9vׯgm>#\\\ 4qQ*{=/sҪU+bccvjm]$Q8990B"qFBYdeeY [JHII!888:uꄃj"##X`aÆ̞=;;;v܉]㨪:;T *? !lcǎj !DQsqqaҥڵ޽{:!(Y|9ք됄BRA*\!X %44T빊+2{l:wL*U(W 4`ĉ(m+Uܹsݻ7jB__cccڴi k~a9bX"4jԈ)S(xڵKiWvmIOO/1EѣGahh>5jԠM6L2KҼyW^reϟOϞ=QO^?>JaÆacc1h"dVZQ\9jԨ-f*{j``9sjժ`mmرco``+nnnnݚ*UOժU͍iӦahhX3E0IX"5^J*)mB!(nLB ˋ>ݺu [[[TId-韷9QU(ʊ}te˗ءL:SJRBbjժϏ?3=Sicjj ۉOl5Ω!B!.bjjիuBijbΜ9xyyqU]#Bx_$>c>V!B'g֮]ڵkUZUY_QrTX &wѷo_8QBG'D_r7adffgaaaA-xB!DnGBBӧOW^4nܘ?]ΝYnѸqc>|HPP6lʊUV/+dffr-ׯ@&MHLL$>>^#">>^i#B!R|yFʕ+3gI!ʬdzf\]]6B!Dz…dB$33S!!J#GbjjJtt4 cbb‡~5juBd0h ,--uO>e73c 022za !rvvf,\ kժxDEEqY ֮bŊ*ϫW΃߿?l߾sAΟ?/СCa_ !ȍU'GfO899:!(ʗ/7]t!""޽{:$!* ТZQBmܸ[: !D)O޽RX/ Bc bӦM2~xF'8EŦS|yBCČ:쬴366& kr\2ZŅF)mMƊ+HLLٳgуKͰabؘݻ+B|!"+(~mlmm !бΝ;舫+ݺuB I!(,ѱBPB!(s/V\ɑ#GHIIA 6^z{1~~~߿SfMt˜1c066VYYY'gϞB t֍)S(^x{Ν;`nnN~S+X屟ӦM‚UVJW]wNc?~͵ IDATڴiCڵ6uʊJ*UF!E[[WZ*SNeԩFXʕqrrߟ~m8}4cǎRJ!D BUWƍ{;w>@!Dd,--Yx1G!(uB!BQZ=xggg"""x!\v3g}v]JJ 96m⯿"##D6nȨQHMM{ر8q4TvZnݺEzz:;wy===yZCa^6w&55kkks֤gϞ|#Bԭ[W`LMM2d#B!Bڵ+:!(ׯό3Xp!nu8B!DTd.^WRR='N =='''֭[,|N;pBCC]v 0 _c !B!(=֯_Ͻ{d޼yԬY///lB@@}$..~sҴiS.\׮]cݺu?^wyٳgSN ˱"==;wpB6oժYhݷaÆ`hhXםGiӦ9r{{|$lZY03eddٳ+W2i$]#B2M1뮣mڴ^&fdOP= ӓgRBuƔ)S_ سgwsss뇝F<,\(*VHϞ=0a*TDŽЦMj׮nݺXYYСC)B5l۶Ma!D六(Vtu:u?v9W7oӴmV9~ib~UZ[[[lmmpcxzz)";===233y \vM]^[INND\rrF!B!DٔHݺuu(!~Bڴip!@ƍquuÃIB!D˄;v"--/^?<==sq9G//6nHFFnnn޽T5Ʋĉٳ@YBҨ|xyy: !( ] ’%KX`ժUۛPزe :ubݺu,XsbiiɅ СCׯ L>^zѸqc@:Fff&nݢ~Yzu|say9kצm۶1޽{:7BU/33P~:۷+WcTBƍ޽;9 6,1ߔ5kq3rX\9TB ڵ+&Lw͵׽]Ev/^ߟp5}:֭u8B!DP,.U;ٳSp.X|9VVVsN.\͛5.oBCC155eŊp%ȑ#4mTc,ձ#GH…BՋ^z: !($1b={>}=zٙs-F֮~ٙ pBVZ)UIԔ۷gNgҥ,]@ynmcoߞry8Ο?,E!D4(IJJbڵK۷}6?3zbӦMTZUQ\nnn s>ɓ~WΞ= @ٳg&(;իǂ ^M6*v[[\&Oc1}t贵ukr899OBBo6 >}cRR\BQȂ8!(FA\\;vd֭TVM|&M1cvvvtЁj*&NK777xwزe -[T;_F uFnݘ8q"ٵkj"<<\#"f͚pB\\\XfFťKػw/wޥVZtwww7nueffrIk;FŊС3f࣏>xիWػw/FFFckkɓ^C~cM~޿ׯパwI&ݺuk"""裏Xj&MR{M9&:ϬUqכgJ˩S駟}xr,117?L^էsX\\-bϞ=DGGSrepqqӦÇX"'ׯ租~wq5_e2Q~ &#Y&cMenXom$fHP%F1ƾDHha-1~sw:t'|TSNDEEuV\]]B>#<<0xHҫW/7nűzjpssŋXYY)e+7oSN`ժUJ71b/^`ȑL<*Upq ™3g?>gȑ#tܹ@PD ,SuܹsUW^ӓÅ \FC cǎ?ƍsIʵko_ƍ#99aÆ1i$WNFF'N`\pYf) ^5 ݻ=zh~U\]]Yr%ө]Qŭ<,+6o9+eKYr%M4А8ƍGtt4s-FǣGpssSx{{ckkoĉ5jֱ8󆿾}rJ~W.N}5ڵk?!22pU*VX/ҥ >{~B!חC2|p5jBbbx{{ハڵ,hѢ$>>^}||<Z*|B!Bll,iiiTZnݺw5ؼy3<|eҪU+z쩶Rc…]+W`bbB8pډں9sL.\Hՙ1cǏg׮]ܸqgϞQlY6mJ~(Y6g?*eʔ᯿V6\XYY)}ၥ%=J{1vX*UNsʦM8q=lٲ4k֌޽{cnnqׯgժUDGGcbbB&M߿Z L6mıc?166ZjiӆO?4y+hThРک*T~DEEѵkWF!M2Q_nnnV!B\ɷ~;88K,ajs;~8^^^L8Qw={F߾}YbRުU+6l@ӦM ֭[Zl)))iӆ-[(]կ_-[Ю];ߏjݺZ| ?m۶رcjuO> /}h4hЀe1ƖSa˗/+? !!AvE7UժU `ݺujC7k֌uQV-Ν;W1 3?|XÇ)]4۷/ЅB<}///:DJJ ܸqClϙ5kܿ OlقmfΜy)))ٳG-5za\v-J60/^oƓ'Ox<`ǎjj={T+$$/oRر#Ƈdu'O0}t믿_Rҫzj8y!ONJJR'44͛7s=222HKKʕ+\۷9wmwQҴѨQ#^CU!B!BAuW_};wγǏӯ_?222Ϙ7o_deeq ӧ.]Z;Qg&MvP!'O`ǎZۏ=Z? ))I\SNWV~}sNŖSa?f/?'UTiSug]献T<{,z;YYYZ 2m۶0yd6JTSLŦzN&''k>}t9c#5Fw;lذAߡ! XlGg!"6ÅڰQz...RJիsUTΝ;1bӛBjoAJ3f 5kʕ+,]ݶm׮]VZ 6jժq]9i"##}cu5-[ƭ[غu+j}2uT4hYt)gJ]Շz:tUVsNtSvgΜf͚}(udSB!o`bbbx9nnny. -._Lڵso8)w,9+Բ8 !^ .ٙM:^Çckkˆ 022Rʈ:e,--y:/jsn5j>dԩ|DGG d駟ҵkWt邩iŖSaҥKc?~REE9fauu&3Rmre߈ĉUx6mM͵=ޜ'h-Wwҥ[q~]RbE2dB3d&M+mB!Ezp}Ծ+?ؘz1rH~UV;5j`hh￯6-CСCTY٥KeSd߸q#666>|Qlɓ|0;::l2|||pss~$$$ĤI*Kٙ].55K.affeszFTe9OiB!BMIMM%==;$?; XΜ9'Nd„ ,[LSZp,,,lٲuT GYй36x;[}/زeZVY={R^=_^dT*S«(LܺγsFWUG> ...|ٳ?S1 3+!rsRΝ;@Z^PrU|9$8=z4օB藷7̝;Wߡ!Np133c̙;':/]'Bݽ{u몕޽{@ީoݺQJ O*L?8￙?M^v"YaWY&5k֤{<[i&?~<]veΝ4jԈ8233iԨi/?@Scu}sXNAٳg9{,7n?sΔ(Q"/hǏSN2U!DJhhB!D1Zķb >#ż5rgPex-^ ӧO>}:zRN9ѣӦMcӦMָ]$%%#I\xcb``59Frr2)))UV:u]vѬYֿr M6k׮_^#?~Lr4ڨN~Na||ܺu{r9•+Wv_5E[asI~' |93攕ZYZZZ ͳsFΦlpS{FTPu* 0j#Sz}g m6mAZ%Uym)#ĻƍBUXٳg3~x|N/BpQ>#<</?3IIIjpibbbСFU&Uƣjժ__}vv,/ ˪U9r$ 4PJƍqvv7k&>>~~АL&?Ӆ1b5<2s*|4hЀ#Gpi5;; .$ Cj<'#((CCC ӧi۶F܇8.૯byfByxxرcٵkB!u:ssscѢE,ZHI!Bʕ+pe?C) ˗/'<<\+,,LOoj<ŋ"f͚޽[k_l!?ڵ+&&&ܹ8 iܸK+J*p%rϪ{ʕ+ZRtiڴiÜ9s7oݺu#%%kSAکNBSIU* !B! ___<233:_5j~M㚶lE[a> &3:uDllZs#/_N=z4;vٳgk׎֭[/[,111Yܺ3tQy]L6~ [[[to݄qmO>}鸅?Ei֖4:t[111tԉtFgEixгgOkRcffƪUBXt){a֭G!(6$ÅB!:lܸ~ԬYk׮)'uV{JIJJbDFFR~}f̘Eg\\͛7۬XB)WF700 ++{aee:u… >??? 2e gΜח&MPreRJL:_Uvɓ'Z :::ݻ5C !˗/ѣtooo6lHtt4aaaYccc6,_ nݺf5jSreƎǏg̘1|jmiڴ);vA?;RZ5?εkرc-xyyV?j(-[ѣڒԩS9p|W<LJ޽{H%t钲 $ E[a?>8880p@Gƍiڴ)ϟˋ@1lْNpp0ܹs˗q:ϐ}Jrr2[l{dff晱M>g򓑑ɩScݘz|˄ L03fPB.]zF6 3Æ c޽޽{3glmmz*ӦM#:::|}}_qttߟƍSL=zDll,M*QQQۗYfQfM.]ɓ9r|Ff|)s_wC aŊL4YzB9Z_~|7tЁ%K;$!BdÅB!:;wȑ#ܹsOO|vޝ'Or&OvD %J1bj+W&""Bc1`q}/ĻhԨQ,Y۷pBK:uX`ӧOw8B!q!BK,Y3gҦM,,,022Fʇ&&&J]sss̙CΝX".]M2wBe)(>K)Y$&&&_+رCWR% yy0`4mڔrahh 5j,--5b433SW-UR;w.:tP.W:ubܹZ?LLL011jժtޝѣGOA۵hт%Kri>N>vB!WzY6ӓ bbb‡~ԩSطoFh=yʔ)ԪU SSSxSNz(QcݡC8y$cǎ|̟?Q7vvvQfMfΜ dҴiS܈eС>}WWW숹]|@BDߪ:B׫SN1dV eʔ}޽QFտvc 2ұcG{=LMMSsaZOAmРϟ'00:`ee %KΎ#Fp*]v%66`ee1VVV 43gн{wCKKKCz033[[[Əϙ3g4^26]?###f̘7oiժ˗Ȉ%KRzu:w… 1c%J`޼ylR{{{= e˖Ԕf͚}v VdqfСJؘ?߹|SϙSB9s& wذa]FQD ,--i׮۷ogⷀWhѢqqq 4HiS~}~'\]]4d.]Ĕ)S###{=5kƴiӸz*}]-źؘ5k2qDΟ?i/ϗU-r,BzmeBU 9\A[VGGG9z"gϞѢE LMM9vZcǎmPCUF*0n8.^sUE<{,vjK4h ԬYk8881?111!:::8D+Ox{W_~ݼy___.\l@B׮];;׺R5D^Qeڻr G!DϞ=aÆԫWY!ze:V-B!(j "-- www177ݻ^:u9·[ZZiiiO@^DݺuСCl߾]Y}v*VH.]Ԯ]IOOC$%%aiiΝ;-^}޽9~8'5Ի _&<<Ǐ v%!DA~)#QyhРA5jԠM6zFJ,ѣ7od?yij{!( ǏE`$%%āq-^uί\°aHMMUӹ|2/_f۶m^Zɜ$̙3qF۷/vvv4jHߡ !߉YfJy\\VVV=Ն ʖ- וVVVܼym۶螖/_NBB… 7ۦ|$&&cʗ/vJ!B5kƁ۷/ .Yf(Q7nj*,Xmehhȑ#?>sL! !ٙݻ3vXΝ;CB!7\9rKuBwHxxCB Ν;<ҫW/ʕ+jʕ#%%3bĈ.WЯ_?ի稄,"""oNddlU|ѢEҬY3ƎK͚5ŋ,ZK.ٳ(U6=}ӧӫW/ep=6m6mڅk֬ۛ9s( ͛@VX,Ѵk׎ׯ{Dr000 ++[nQZ5nӦ k֬ӓI&QV-=z' 'OJFà 6ЪU+6m 5ڵk͛756VܼyS#BâEO9s ~u|||prrz !̜9B\/^LzXt)Ǐw8B!^[ni&z]] !r6mwBw!;ws坤Z` y\B9y$e˖e9r;wʹs;w.4noooz#GbquuUdž C ! ̾}uC QVZ5ɉ(Ҹ~%)[,>GXYYm۶_pΝ;ǀԮ,Yq̙3177g3sLnj3ظq#ZciѢ$>>&M ,B!x5M4,Z]vs*WL˖-;v,SP}_6\!D10qDfϞM\CB!޸"pR\ !'44; !B!?ѣPF  DN:2a씍jO}0rH?~9t&&&nݚI&QT)ς΅j0/^̹s(Qڵ믿,9$00ݻws=LLL[nt]˕+ *ժUS=99.]PlYl٢M֭[у*UQU d߾}_TPm2|pJ.qGLJ={`jjJ˖-3f *TPs?mLXXM6RJJyʕ',,baaBDa2XXXʕ+9p˗O?eذaXZZHM8 >̋/U ƍqF0a˗/'11t(]4AAAZs}J*E&M9r$0k,fҤI̘1Ν5>Ç3h >Lҥi߾}̘B!ޔ%K5,/xFE͉yG!D.SLaڵL7oLff&yǺu딟?9-,,۷/AAA8;;+:@֭u=%%CruMbb"ZWNFOKK#**8֯_,. 3ovɓ'8::jHLL wgϞZB]!L2L4I&[XXÕeLKKK<̝;sj-RJիsUTΝ;1bB!&M/.277gwww`*UB!Bwڵkyc˖-:t? 5kѣs4h@hh('N <J:uJYqN<ݻwVZaggݻw\BBB}6M``ZFŠgÆ ;v#G'kyf=,4UmF(\T^-[pQ \dٲeDGGs!LƍҒk2l0>FExxIܜ5k֨V;u~:UTQիWcmmMBB8GaҥDGG5weJ]?;z(uCU#0x`*WLHHR cdB!Blф;!Zٓ3f^xpB7Ԯ][CuVIJJ‚GlٲǶ u @)}sDB!5QQQo^a!#G0qDUg ={޴0sLwNTTݻwŋرWrI ;KA~tP6ѭ[7,X@DDM6}{zzҰaC4i´i3fL1̔)S nnn3NJ8p JIǷo|椤qrrW^Kk}i/^ĉrN>}P|y}$B Z>w\Kҥqwwm۶_?~ك g.拧O2}tzꅕ=z`ڴilڴI!BWK,aɒ%C!Pc``Sܛ_Q"g ___.^HժUO~.8;w$===Ϟ=cΝY:l [VVr=8J(06bM[>888p"""8{,_ѣGӳgOZjӧORJj}2x`ʕgϞÇ"vCIM9羠[A۩E*O!ŃnnnCw$Bwȑ#Yd ۷o[nG!^^^0}tBFmÅdQ}o:#** KKKN,W:uWWWի 6_Qpp0U)ׯ6l 88=F']vZ/֚,իsVVVܼym۶)ZpDٲe~r=##Cc!|aƉ`ҥ8::j\?vcƌ!22RcEnUVڵk={?X)?{F]HMMU2gh6tWeii ...\p{vZ/^l[.l޼YkmU O?ƍ9vhB߉YfJy\\9:wę3gtm Ү|$&&c?~B!ZlIxxB!(R8;;/.LJC2l0B!Ļ("xyyѶm[7o΀ؽ{7}$ =zмysZn͗_~֭[5 iӦj'qV\{{{HNN:?B!.TB>QQQܽ{WaUT .\Hbb"iiiboomڴӓK. /{9rFhӹtӦMSʓT{-ǹz*/^>yZcn޼9իW… \z5k߾=#>> Ο?ϼy46lݺOru|||0 3/mλu놽=?OB# ŻSNT^+VBQL|rYnwHB!kU,3\pm "::@ *ׯ… 4hЀPN8Axx85"&&HN:9u U1ܿ_ڑ#GXt)ammݻwY~}cn:_N*U^z5$$$f6իWg˖-=zTY<~-[Xl:t)S-8z(uCU#B!3Pn]ϟON^ھt憕FFFXZZĚ5kN8q"ΘajjJ7oÇڄ R FFFJǂsv؁%;w7.]`aa;xy Ņ{ccc={6JqѫW/ʔ))L>s+]漠y=N:bjj _|fRk?vXprrbŊbjjFT2TT) ӧ*UȈJ*ѯ_?֬Y@ŋӲeKT " @s*h> sss>Ç)]aE!|ɗ|%mYYY%_%_ ?=Bv55k֐pBQF >)S(ۅBwpaoo_SN)<==iذ!}i4NܳgX[[PfMfΜI݉{yƥZ@alTdee7)S(u6lOOOFȷߜ~ԩSꫯطoF&MZj@AAAjBHHׯ_ɉ^zѫW/~._ doȭFjuB!]dmmwu@`ii7|7|oyt쌳㘘w|cP133S۴+WN##ǽ{PZSLQ6w^cR^9/hy͹ ...CꪨoV^5kc~eʔaҤIL4@)SFBܿx ҮTRzjܹC*Usqqq1B#ۧ)<<\!Vk֬CB! ̙3 -Pb!1gÙ={6 .w8B!kS,3\ܼyƍ7iD[/&|ReP-˝;w{!EF}v}5Ƈ~VwjQWV}ה(QǏdKPPiiij<|^LUG!o^}O>\G/9TP޽{PB|̙3Kr%}#B6o,EU-;=3w6 me)%%%7޽{ݻ/2zA-"ɝCаh@qvvٳgg=={LR !o/N\Bqq$N:ũSpttdjByx",Z,&L@5SCԩS'~'|||QVlYZn|oggG׮]1sLƎl:dҥ,YR ! $.ī MaQ\|ڵkÕ@!X888p1B< >@ƌݻBZ Z*׮]ٳ|Jٳg5ZYYqMmۆcٓ(iժu\ºu9;wN@p>b{\$T9̡LK5r!Zhc5_9J6kv Fq!ILäHJu} ^z_﫫z=_/K.\@bbމ>x󅓓NNN$''k.VZE@@*ܜt077W;?++KUG!Vqɩr &..CCCH.][.=zH/UĉՇLJ$_@^^}ԫWw}@dM&Mؾ};ϟ4hЀ~wwwAկ_,jժIgTm9uS!M& Q>>Sݫފ;!! ֮]B`СjՊ:<8B]ҥ |p!XժU ޞu1x`]$BTق  pqƍ,Ykkk<<}dӦM:L痞...۷xnʌ3$ DEE~,YLQRe7|7|uB!xݻ}]f͚ŠAgggfΜݻwqB!DzCBQ]2zhMFnnB!*\bĈ\p1c[w̘1ٳcǎ1rHcL2E)8;;caa֭[cΝOݙ5k4l۶-C-\]]ٽ{7.]MX&M4J???W;VpȮ]w^?ǁ%|!O͛7 ȑ#ѸqcŋU_9pboo -bժU>}}}}~mF*~Yۜ;w.-iӦ̙38Ο?Ͻ{055s >*mQq(}(nrrrXn֭[ҥKacPT0/_΁קSN1SSS2n_~СCܻw.]0~x1ܽ{7NNN4jH8שS~Ν;UILJq*ޗGye999DEEѮ];Tu7 **~QV'!B!yܡ2& uddd0l0U_ S !1tPMu8B!O֭/u8B!DŭXfMqrrĄjժѪU+/ի TZcccuʕ+ve6m5jժܿ_U^zu 055kjԨ5ހVgOjqwAk&44!CP~}VJ>|8+Wظ9;;3{l^uWNi޼9cƌa޼yz{АX6bcc122R<"Bݻw={6111ܹspy$7o7o&##p56l؀?=8gܹ$%%q=ܹÎ;Xzj*rrr077`͚5?Ç~:۷og^y/ơ,qe֬YDEEq 8l۶ٳgWWX={T0k,ܹSa̙DGGsٷoK,ԩS&5Ezĉr_}yT4c~LסC믿ZB!(L@N#666ՋSo߾ҷuEt!22S2uTDz!ċի̙3w} FTTFl틍 |(Ξ='vvvٓ #~'yqpp`رj;^|,ȑ#:b*uwvv6k֬sj5lkkk֬YCvvv1#.Bj֬رc G!D16lٳ9B!*T1{l4>)#Ewpp̱֩S؉E-N:x{{]l{E%h+wrrɩjժ +V 55Usjj*L0Amc!B~7^J4iO&00L[nܹsl IDATْqѤI\Bpp0IIIDGGӳgOs,--ܜ 6a<ɠ +t Qxܖ&$$ki߼ysUI&B!O?e޽޻wٳgk.7oCLL zb޼yjTDߕMBBAAA]BСCiժDi!/33WWW_*;{,gÇ;wGNDD񄄄h|65a#ՊAAA $%%DNN#F(1*?V㘭- 8L1 !/0`BQI&|rNƍuBQa*e…L>GGGx" .cǎ:ʶmW%|caaȑ#uB]Q GFFiժm۶???O>Q}H+_bcc5&yGXXXarrr>@dzj )))rE?<ޑ8>^fYƲhBuƍcj}+w4V? e43%miDZ2YQA~~B!u:޽{k.pwwG4hЀ,駟رcݝ+ʖ|q]f͚ŠAT39s&֭f͚:PQVZi۶-_~%uoeƍV^MJJ 5biӆdf͚ٳgYr%jm7mڔsҠAVXAHHѪ 6wammM~~>QQQvZFÇU;Kh{| )s2\i@6m4U߿_.B7oN޽%B!*9}}},YB޽ytBQ!*eE>}X~=355XYWT5k2w\>F<^900G'Bϕ+W } !kĉZۺtFYƍU_תU G=Qݣn޼… VS6ǣGxS+q(}P*<W^'hk###ȶJҾ}{_uJJeKeʤ±dnnεk׸vjB6333+WQ1.+FdV1Qr exuE-B*&>KDEEallLHH͚5S333O>0l04ho~BCCPK>|8 OFBQ8::̤IHJJFI!xb2Bf9<ߧA۸y _QzVVVxzzh"N:8*!/]81J*%)Ln*͂:BÆ ܹ37EL:ضڵ"33SP׮]S+q(*h_?eMZ6 &e,ڍp?8ڵk~\\~yōqi}Y禰Ҍ2^m,kۤI4iB!D%suٷowޥy=3fsssYb;vڵkԭ[wwwD܂ &{l޼ѣG%[dffȑ# dƍvߏ?;vz1i$֭[lڒN fܸquңGƏF;k֬! cǎQF }]&OڍѣG>͛7j+fggf:waÆX[[fF5\ɐ ʝԼysƀ{'Of޼yǏ?...^\풔G,SB!^F>*?K,u8B!JҦMpB'V).pqqEסK׫=- !x5lؐSNGUmzq{իW!WDG/T}8|ej__y믿nZu,rR~}RSS9yj'OP*UP({.@">}7xC͂le,6l˗9q:tP9sFn^aƍXYYi .ezYK,c4UMiĄׯ٪:7b"""tB̔* ''www^'OT%[t}<<Hmt"""'$$DKi„ _yj V^ORRIII0brssՈ֖xOXBgWI:(nyh͛9z(?aaaxyy- .r}OrݲBQ<===NŋuBRhҤ ӧOחQF3n! B!űeڵ,[k;we˖i}뭷شiK,ᣏ>iӦܾ}~-[o0gΜ2_m&&&ose~GU;wS* -- 2okk?Cdd$zzzabb­[_O)܇tܙT4i͛7ܹs,_x1YYYڵ^zquU=m.]еkWUk֬!88'ҺukΟ?j֭[@LL gf`bb͛79pׯ'//;;;^rů$c4UMiƤYf\~+Wh$V(W,JOxxB!6mڤ0 \|KKKKڴiCRR3g$##CnXXɴk׎3fмys.^lٲr7NWzW4طotܙ'N0sL\BXXE]իIIIQF̟?6mڐ̬Y8{,+WS휦M2w\4h+ !::Zpa;'** ֮]Jؿ?}W?e%BL 9{,m۶-%.\ 11.]CbllNNN$''k.VZE@@@ov:{F^6椧1e\r!eBg}Fhh(yB$\!ӷo_GjjjGQ C8}4ڱ5jZ+ͷz 8~y:tJ2OW}ٳ'=z04zw֭[)rҿ>}вeK_@sw BKJԔL6mIKK߿׭֭[ŋ5++ynݺ!B!b``@@@xxx5BQYB!EFFsΥ{Ԯ]UҬY3&Nxjhh۷/COO###:woV6ǎ-ӢE &O}vU룧G~~~裏hݺ5:t///TRq(}(JZ ===ӧUF#FFFӡC">s:uDժU133ɉ9s񡯯ϼyptt}}}lmm0a2e XYYQvm066oooMFǓT?7]b``qjҚB!x('vQSNu/]֪$ m+T~}ҊV  /_.͢h߾Zr%7o\1=RM<5jǷ~СC !//OLj׮чLYG;v,;v>;;;|ƍSsuuI&憍 nnn\r&MV澕 :oZ󽩩)ׯ6 ,^{{{ fY[[;?ѮL&' !B!7 W^L4:!ܞ噴'BQZu?V+v-puu-q Æ SM}TGFF<;dȐ M!σy~,8QН;w=ޱcGصkcǎ-ޮ]T +;2AObQ*@QXU sttƆ={yf=?@XX^^^ 8P\m1+!g7ya愆āˣE3={ծ]P.]ʞ={qٓqƕoygggUFdd$)))4n{{{#MOzzjWL2===vݻwf֬Yj;n庵ݻwo/^Lll,GVk766###zUB!B] } !ϋ O(ZB!0zhpwwCCC\Š+xuAOLL *~^z|:NOٻwMwgVMTy&111ЫW/͛W;":}4ƍ#''GUv}N:ũSغu++Vкj.!! ֮]B`СjJzB7n̹s8z(oѣu-,,x"[n¢} 8(BCCEuN>իT;gt1턄^}2biiɅ HLLK.Dr]< '''ٵkV" @pannNzz:YYY#xXZZWb:u퍷wz\TGGGKG͚5>}"溵]V-\\\Xb4jxKQbb"&LаvBd!dΞZn͔)S3gÇ^zI!(Y K!rpp`帹1l0N?}^^^DGGs5]Ftt4ԨQC Qj{}6|}}ٵkFFFL<-[pAO ,--ٱc_~eyg7ߐC.] ..}l2^u zq“ݻw5k KKK9s&wޭHP `?΃HJJbuw'OÇܸq%K`mmrK.ڷoO߾}ݝ^ʃHOO',,qq]{= o8Tx ^ºuǎ;vLu%^!ښדK˖-iժ;r/j,ݼEJÆ Wcaaȑ#uBlݺu[Na!KKEeCZ1cCB!ʥwطo#Ff/B9777,,,%55|i߾= L!>ʬm۶ 6PN۶muVbb"QQQBfTӧ666 6( ěoرc &&&TV;燳3eRhh( 6LU6|p~BCCatB1 .0f̘b3={p1IL2E)8;;caa֭[$߻w;w@@@F?ݻwg֬YZc033ӈm۶ :ľ ruue\tIR&M4J???W;֩S']ve޽?~\@>O:I;߽(jܹ֬sO5j[l!00G'% Ea!K)""BJȈQF1a„T!xZ*ߏ?;vz1i$֭Vpc&88ݻws ֭K=?~RMT^sDD񄄄`hhv΄ R믿#|||Tue|Ѯ];ڪ]6C%$$;w:hSIJ3AAA^ZuN~~>III$%%Sd{i?V [[[?8p~ygffٳ:u*SN-MGGG-988X:ux2-N:x{{]l{E%h+wrrɩjժ +V 55FJbb"&L})x6d'EVB<  I*U ښ+W2vX]$B^UB!B۪U~:m۶eÆ Q^c}( IDAT䈰0i׮9p!!!XZZrŠիWBF XKKKΞ=ʕ+5iڴ)6l`V?cccVZŸqxw㏉T[{ĈjL/S幦`Æ |w8pOڵkoYۿ?mڴhCY#/Y~=oѣG?yбcGGbruuaÆñ`ȑ:L!B<-֭cݺuC!^X<+W:tÃ?[n:!^.N:E֭uOdd$qqqB!BQ ۷>&M㣶BA#Gzw㤌s2`0`xBĉl߾3gp! )K?djՊP(/h骝|M|||"::OOb-HÛo @1c'Ndy{{ň# m+W>qȑ#$$$p!"##WuaРA^ [[[9z(jբSNeF-[ @ !B%<<\vB$""ݐK"##3gK,u8B!D4 ~){}[$OBBAAA]BСCiժ:tx1 !͖-[VB!kkk2k+W:I_91]ҥK899iYׯ_gʔ)8qBS~}HKKS%h_ׯ/_.6΢h߾Zr%qԼysڴiC6m3f gʕ_W_e֮]Khh(_<|~[#1sMɓ'3o<∋ b'Lj׮цLYG!^4d۷sy߿O xqwwPT +++<==Yh^^^ɎB!B!gbb1ƍ]vI!(Kpw^s]f͚ŠAٙ3gn:j֬LB!tՕ^z: !xIbwTRL n%'Jܹ⫯ĉ4nܘwy͛ojIJӱcGصkcǎ-]vV8 ApWbQ\kmޞw1Az긺ěljUUsMOGGGlllسg7oѣ]ע*  ===\\\**O{ ]y^./u:!B'ҥK4iwu8B!Dtp\YEP2226ll/BgC2 X!NAᅵ={mۖm .^֭[UIEQ&`jj @JJ4&旧Ehh(hBӧYz5 8~1AxW0KKK.\@bb"]tQ'&&ڴiÑ#G_5_xL,5jׯgڵ_ѵk2_k*x䄓ڵUVPdEi377'==,RB![&Mm۶%99 w(9r!!!ܹs?&Mp}97|իGY~= cbbRsMӧ-[w(nݚt.^XLRiݺuC!B<[vvv: !ⅳo>VZž}x">~t҅cҷo_]L(w@-iҰcǎtؑ!Cо}"=zHHHH --֊=jժԮ]f͚ѳgO\ئƿ"cE8q+V ::gϒCڵiժzݝf͚Ux˖-ۛ7ojlc$ƆѣG3m4}9B!DeS).ܹG}gzz:apBn @^^QQQ$&&1[[[?]R!xmٲ-[: !Hcǎeǎ;v>@ظqJ<̘1ٳcǎ1rHcL2E}n݈b̞=['Obkk)8;;caa֭[cΝ_ݙ5k233c̘1jem۶eСY+wҥKkҤFYitޝQF?vZ֮]QiӦL4I|Ѭ[(_J3ZN:Oiڵ+{?~(!Bg'""#F: !:"+^FFcƌa._˗_ӧK%yY}voΉ'ذaCu̙͛atÇu$&&߳l2y_Dyyy|O> 2yd]|Ə_am !*… yU-h%BTF2bդШQ#ϟO6mHNNf֬Y={+WY >Qx xBA`` ;wĉ̜9+WV!߿6mh,ۿ$\!x!M2?Pa!KO[sssBCC ѢE ƌCϞ=K<Ȉ/_Ntt4ԪUN:A-Tu?3e˖9ڵkeڴi|s2Pzuaƍ$%%qmԩ C9š{wwwW-j׮Mhh(K.eϞ=ܸqssszɸq'|B.]X~=7nܠZjUuZj.Sk*x:;;SZ5"##U Q4n{{{\]]ݛŋѣڈȈ^zkLi1b6muBQDFF:!PDDM\z[[[RRRdԩTZϳn:+prrbΝTV)TZ-Z`ǎܿ_0%1M6U<+͛7IJJbɒ%lݺѣGcjjsʋT{޽{gȑЩS'LMM}6G!<<EqQmVIEyZQ 7o޸ѲeK]$Bh^)J*?|08|Mڷoό38q"w.S"DIO|oプϩS+k OYG!xagg0BYZZWlmIJL:SFڵJ[8::ʕXPN/^\lmqj:u퍷w5E`ccSl=zx:^+wKO'''-ҜWV-\\\Xb4jT0aB**eAiҤOקgϞٓ? &p9jԨ)Sw^7oκuرq333}]}]&N;v?׺cɲe˘2e B!$t_rB+<1Cm!+Bff&xRMa2e!B!B<ܽ{e˖0j(Gl۶pUHxx89R !vB!//իW133Gжm[UT)«>| ghܸ1f̙Ʋ}vիdž 4- j׮xxxl2'O`vɵkרWzGR(:t?```=3g䭷8̙3|7ܹ5kҼysO^CYc+NY2b۶mZ.mۦW\Ey?eiS(Ǒ#GX~=#F(<~G6n?CVV&&&t҅>nݺ<&?>v֭[k׎yѯ_?18q`رcj1YYYm6z-8qڂ9 믿̙3УGf͚Ev4k^gQ2< Vуm۶i#BTzϪÇ+\Gy/SlyT9S:vȡC裏8w_~Z”)Sfܸqx{{ӴiS9Rl=9oҥKԩzzz$&&2e8/|A}1q-\\\TѢE >3>c}ٳxz,ҥKٹs'g̘ /C;wc˖-93gP^w)i< [hmڴaƍ 6L!O(<<\QJaeew}R899)=W=zTѯ_?oUիJ?+rssΝS5J}kkkŋ1 0@W9rDѷo_bԩSVVV[ZoO?-q̄xޕB!B(\\\^!xQ"B<^*s\E߾}K,Q(}! !3QY ???Δ)S=tEj>S;C8СC @ѬY3UYZ"++>3c ޽Çj>|޽Cݻw+ZPܼySǏ+eVPy_~6GP(k4/8Cit^A999 @ahhXlM6UZ9sFkERW_}=z8'??_acc/,a$ݸqC}QѮ]; _8E?UBqD?l#++ÇcjLYFKelDɾ%ISHe~-R|-eK"H0C:Ϸ9i!u]:s?ysJ:dһwo.\v899ikiiIvv6W\Q=za!D]hh(Δ) L2̜9}i=ҤImm۶ 55UƆ/XӦM&%%b˩$^?~Wvs1~aePO<ɷߟIvv |rAسg3g8\rJTZjUbSR iiiZϞ=+{qB ׯ_!!ZZELLֲFFF_PV-ʖ-KZ/U~ DժUקcǎ̞=[3fԔe˒vlٲetܙrQV-'%2aDDF mB!B!BZa'$$`iiI߾}ʷoVVҒ"SUx]?>cfڴiX[[ӱcGF sJBIg`` ӧOg@dBΎ:еkWFݻ5rJlmmСg͚5j2--[ҦMjժ]6lݺ,BQx hڴ)/^ƍ?ܜ-[PlY.]?)vBjQunvUlkU\@"ǏgСaffƨQعszƖSI~u֥iӦDDD(s~};ֹ( 3/ÇA$))={0{l111 ;;[Pm&ۦMtAk{Jݻw;O>ڮܹs/WY!Br@^V3X"...N氶:ϱ~6NʕqttˋLMMHII!..+B!B!BQ4iBƍILL$22]ĉܸqkqT7|Ñ#G4 Z3g8s ̙3G^9uVb۶mdgg@Æ iѢEi&DZ ???ӧO9{,gϞÇ 2 &L,qdzj*]sڿ?tQXǎ 8N!DqZE ~6l 00###VFW077'&&sѮ];̤\-Q-&k###sSU9ȑ#Z*?_닯/ 4ō-~*666Obgg_233nQf_U:l۶ WWW5fQ9+1wްaCbccIHH3)C3g`aax5S(WY!ga IDATBW6M3bK@@D C-B!ͳ}v Pa!/K;F^3p@~GvޝoŮ]{*9͍~Zj1qD:uDժUٹs'~~~̜9 6G#fϞ͠Ah5k۷obŊųsNV\%O>%$$mۦ$\@fpuuA$''Ntt4 0EΟqǎq1ڎ;& B \2ܻw/yILLdʔ)O&M#--4F.]a:%\\p6mп ݻ1668G[jϹz*aaaDFFɅ |2SN%88c+뗓*b޽ٱw^e_X驵=~D]MAeP~!ԫW5jиqc6lEyOιwիW8ZDz"66B%\ѳgOVŞW.^gQ4'Nի-Ю];J; ! " /IŊ;w.SLaذabŊW2^!B!^wO>/(0Bi[[{f;v۷okEݻ=z *Я_W/22VZʱ:u0yd*UĚ5kXf k׮-h5p-}lٲJ1:S,E&gUT@RRݺucРA 4H_hh(ԩS3w\ @HHHIϟnݺիG!īܜX~7y?f?`kkg G||<:u*ڃf]iӦ)e׮]-oР'O$::>}hs _I233cԨQ5 _+K:~uܙ*Uo>ٿ?+WO>y)L2deežgX~=Ϸ djnܸ|o0({YfDFFrI5w˱cDzd<==3f ͚5768)S GɓC=** ?q*?Yb7o.0oK; ! o٢Dn''',Y’%K4iB!B lyC<ޚ(ڂ㬬,<==CtڕQF{nOŊׯϞ=cϞ=Z믿8+,, &Э[7ڷoϧ~ʒ%KHOO9>;w?гgO:vȬYFatRĸl2>===%1W͛888kXd eʔaƌJ{޽Xx1YYYjdeed ^zhرF[IV/raeeō7ؼy3nݢGuUZ55y{{f?ܜO?4߾ߋTl2G8nQ9K.DvvrNnL<ǏӧO~!##'YcҥmϞ=cѢET RWWYcNǛB QZQ؄B![bBvUV<)gϞٳ<|!Cjȑ#v k׮XuMnJjj*?N񥥥1zhRRRI EƌCRRZ7o&::ooo*UvβeHKKHpXx1;wToذSSSoNBB|G{RL{xtY|w,\2cƌ)}f3220ar|YoooYjIOO׺xcǎDGGsBB*g_%]Gִoߞp8}4k֬ߟI&)$hKIX.Ϊ?B.'''V^ͱcӧ4oޜǏuV|}})W9^͛W'f|||]6SLȈ(kFviӦ Yf|7333>}˗ٷoK.̙3Gm'rJ>s%\]] RJL}'OU]ի4nܘ&Me\\\߿?zzz={s|6m"%% rYeAR^rWPɹϏ$LMM?>7&!!ٳgs%|}}qrrR;~x{{k,Jg˖-ԭ[???<==aÆرڵkqF֯_ѣGtܹis[a0OBB͚5Օ ;+;;v ƍk\Gv1I*U`kk- qF-[MLLHNNfϞ=zիsM߿OՎݿ_#ajj͛4haaakNϚ5khѢ\{ *‚5k0vX.\… uz)/^Be2g͛{=AAA700 u;HLLZ㸩)? (_^틞 KEIYf~.]D.]Ԏ_q{~AAlڴ]3ƍcڵgf֭\x33|U۶mwر;vѣ)[xݻ3grJ|}}[ٲe2e -ʷI`` Ǐ|vQB֭[yA^ɿ('NP !Dqk׮woIB!BFrtt$ B!^͛7+o rkٽRJݻծk.s=Jvv6۷OY֞Sxx84mVZw1eÇpssy|Ǹ2yd:p1tP;`aa<ӓ'O,@6lׯW@Clٲ<{v߅PݩS\\\‚ +w/nnn8;;caa{gӦMXZZ <_իW 5͛$''k$V$''+}BZlllcѢErM*UD۶moRB1zh,,,Xp!Ǐ'==3d[ 5kٳgcΝ>}۷oSlY޽;&M⣏>:fe…?PfMpqqњp[UT,Z \ӧOW}3gΤf͚/,¾~y111e˖ҴiS+رc̛7>|H˖-={6+0"r;аaC0aN~W|r100m۶L:~aaa)y(N: QF̛7FQ)QE__˗3i$8x .\ÇѨQ#=z4|AӨQ#ƌC\\fff 8gggjԨLJ''O]vOQYߊ+ؼysi!xXa!^IB!B!B֙:u*#**(֮]ѣqtt W^,]CJ*UHMM%,, CCCz}6ӦM#11Qc̼vڵkJbJ˖-=O[rIn)))dm׾~9dr.fιA+V88TV-]ƍ7_><|Pmwa_3X.]|}]T}x]1www<<%I%ΎrHRR>]taĈJ?CCCٰa֭[T\VZ1a͕3f`ܼy z?LDDÈ Ktk!BUJx.\P*aZnCB!(Of̰/ !A!BΟ?sNGeǓ}vvvtܹ."ԢeQ{PueȐ!ׯH. DEEо}{B!^*U`kk- qF-[pi~TV @Yp SORRtQiW^u?$..v)qqq@S߾} ! ՎgeeIvv6S- s߅ٳgOsWzun޼^ڱ+}xݩ~J*L>ӧkkkʕ+舗)))ϫ1~x;B!hڵkGxx8,Yva``+Wذa?ƍ+HB!x1J}Jڵܹ3SL}oH6nHdd$<{ZjѮ];FUջ_&z,ߋ c5֒Gm\tbddDÆ bرԫWD_pvv8U!JZ'\K !^S7ofݥB!*..T.} < DK/හ13f $$ė>nbJۗ_~ɖ-[$,\rO.]1bsuFչst]Ϸ~Kff&<{ ʕ+WX~=۶m)B__׳rJx!| nnn߿_֭[Gxx8wܡzٓ*Uuf̘իy&r zzzoߞҶmbwaooo6lÇu+WUVL0ssso޽矉`j׉++f! [ɉ%K0iҤ<B!DѼ{,_TcŸmu-Fɾ}4]vk׮clllزeKx<~oOOOԎݻwhYhSN]kʹB^X…5SIB!۷ob "##y 4`ʎ9pEhh(5jԠo߾;}}}@}X1110|}ztttˋLMMHII!..SR%GzӪ9::Xa!B!Dر#IIIԩSӧӷo_>ʖ-˕+Wؾ};/&$$[[[
IDATX!B!KkרS?~׫UP'!!f͚yfN8A`` -Z ::`@}ؘ?I1{RѹEFFb ?7u;vn~~~$%%ajjܷuҥKjT֭Ν;9vGTM9OAs_|mڴw8peW_zLLL裏xqqqL2;vǏ@۶mTǎqqT}BX"s%887np ;w.*T(B!BVrr2ƍ LLL2d Z-&&{{{j֬> 4Ņ`߾}ԬY;w;wf_~8~9F)2rH.^}у+RZ5lmmzŋ0a;кuk͛ǽ{SSׯ0?~ڵ+իW\rԨQ~^۶mYf3f̘~_CCCBCCiٲenݺRbŊ$%%I&/_5k|r{߳B[S;oȑ#L@1ZhѣYn^^^,X@O?̪UW׮]bO_ѣZߓ'OxbygJݺu#00f͚,^/nl9뗗6kk׮~~~XXXp̙jܸ1;vʊ0֭[ǬYJ/^@&M u .\~ȡCM4i®]ӓٳgjLW=+xJ'vvvtЁ]2j(&Yr%tЁf222>9wT0|pd:t(vҸΓ'O7o;wG̙3۷o8vίUXt)}|,[LcaK.DΝٳ' .T+C Fvv6jpdmmMVV7nÇnR;wǬ[NCJFl6U!B!ylyC[xC<^Cծ'Oz|رy>_~%ݛ ?x ֍---ʕ+Jja!D]hh(>)S3go>O4Im۶>k! K9ִiS%[N%?$;;[-BE9TuilǷ~ Vu!/EGkTj}^kFgϞ$[|tY!ۭ*\gժUOrYΞ=Ç2dL07&>>UV廓jL׮]sάXBYӢEسg?&$$8xtƌCRRvM6oLtt4TTI+ /?~̎; }Ozz:;vԈcǎDGGseQB!Bi|2 .d|$&&j1{憳3ܻw6mڄ%?fggsÙ׏{2vXƍGϞ=^:o/}ТE ,XyYݻGعu ___ݙ?>M4!!!A.]obuT9͋.s1cJJJ ׯgƌʱSϞ=Yd ;vP.:uIJeزerMFqM5+T;X5jHB!B!x 0@mWo!%gZ7.EZXFTڌ7?ssslBٲeՎ6RU(H*Uwiii^0;^ՂTymfnnVre@ +F?~xgϞߟO?}}-xtAtt4'NP6/)VۺL6mXEJ*Bݻ'hmWϹst+W=+x ;wӧm۔Dh֬4hЀd݉&88Tȹ?{[4EFFb ڴiCbb"fع)ϧq$$$0{l.]Fvkݺu;w..^^^x{{sa%By>xy/B*۾};˗/0Tm߾CB y]޾B7ӧO%ᢄ6l@`` FFFQZ5><tXannNLL Ν]v:VIAU FFF<|P *4.|Xk>|xylVWWW&O̡C4R:duϟ?<_[z!B!ěbŊ_UVqQ[qٲe|DEEallL>};vZy>>>[pܹCٳ'_}U*7Ga4/IAZj_|mXt)V ?Oh$Itܙ[ҽ{w޽{?ՎEDD`hh!oݻwb![/(B!SreRSSw^I$&&2eI&yƃtK.İ~..\@6m߿?7ݻ1668GU@U>s>s^JXXDFFr._ԩS .؊b~!ԫW5jиqc6ly{|w (heeEll,JP%S񒸟Y!핬p1uT͛GTTQQQ]ѣG訖vUlmm^rRjƍܸqCI\&%%E[՞hk׮;v^c.ֲeK_qN U;r=u޽ //'UB!B3gZۍ7͝ZTә>}z״:]v7"/UV.*/VZggg^^UsW1>i^eNtooz}] ;;;3f0c ʕ+舗)))VTR !ěLf8B6qttѱB!Kry5jʍHTT|$ !oѷo_{1&==~!u|_\\tԩk<˗vZMum_Nv"55===1O}eXYYi }Ϙ13g`ggǬY{契pG}Dݺu?h۶-VVV<7gvK,L2j?ݛ'Oxbfeed JQ^=iӦj:vѿ$c++Æ 8l2Gidt颶333 yo8SQܜɓ'Ӈݻw+vfddd0iҤ<+w,]T#ٳg窪u*zzzdgg癐ӫBJ&+R xyyLj#HMMU={񈎎w vWNy`N{-,]zܿ_|UB!BlllGm{{{bbbVƍ+śbŊ̝;`gpp0sUR)o۷+}vbB!x9rѣG̞=AabbB:uŏ=zQ !Dsrrjժ;v>}Ǐ9|0Ǐgذa+Wwww`V^M+pC'b``ߤs! ȑ#Y~=5j`߾}4k֌իWDff&=">>~ KKKn߾ͬYԪ L8###> dddTTɓ'{K ÇdffTlY$c+WfffgϞ0|p%Ν;ZϏdȐ!? N:E>}̌1cƼ{\x1z"%%;2j(<ȝ;w8p#GҩS'nܸAnXtis̙3|DFFhT044`Νdff_*\g}FJJ ...`aa_Wu޽;쌅ߟM6aii',W*~׏{2vXƍGϞ=^:o/}h_`?ٳ?X{Vh;nݺ닻;ϧI&$$$(Y]tyaبQ#n޼IrrFbErrG!B!TΘo>\BFF.:t`رԪUCo֭[q~ҤIjժBWG@@ABwRZ!*˥QAnRE[l''RN!Ԕ͛73h h׮F5k֨6k k֬aر,\PYҡC\\\ի,ٻOO(a&x=ml9yԔ%jene&Xfzg[)!["v-ܔPt ϑ|>Ŭ>k̠2K-!"""""rhڴ)F/Kkl>㏼$''SPP@׮]>}e\|l¡Ct۷fV k͉#] Zlɣ>ʂ pttƍlٲrZhAn?~<~~~V}sZZ-[xbz͛LZU^Ѽ;??xluc;b0gڴiZ,*"Ґ;t^}Uɉx%K0zhW1ѣWr}Ȓ%Kl.r۷/'Od޽f͚C=h IDAThsqW_zj>3~'nFMhh({&&"))_;C~ϊȝn -y$$$ @Ν1bAAAvDGG;pa.^H֭bܹtvlڴ\-??ɓ\r6m????FYfk׮_$%%{3gP7sqq!&&z#Gp%xG5k?|njúuHJJbV}$%%\ {|2AAAϖ,VX7n<3őJtt4NNNV}ϙ3<qaaalܸ۷[_v'OrI^J```]*[yr .UMjj*TDvӳgO*?O1bDo~ܲeKf͚ŬY5f>}xw+ݾ*oӦ DDD%~U{VM6iӦĉvxn5cmR/Tۮ]'ج+k =+"w/suueѢE,Zv>>>619rdeiӦ ֭ضNڴiCHH!!!WV<[h4ENN:u ''t̙SӧOӫW/SloUv%!!ʤIe\l۶>}/Ӿ}{^u>C-۷o';;N:j*zͩSX|9YYYlݺyYݥKù{"::Ç[.v 0 \vΝ; }GttM@kke `nd%\HHDǎԩSH /T.8ĉlܸ%Kxb6l@zz`0Xׅcǎd<==qrr",,4>CKÇF~hѢgҥ:tT!!!xzzҲeK+W,]ƒHKKc޽ԩUNʼm9}4{BɭvjFDDDDD1j;\Hrtt$<<޲޽{Y~=Zst""""""UG\\Fѣ8EQQ˗/' wwwYlvѱ^)*ܥ.''[8Ъu򳫫+7nܰ-X+WBJJ o&J0'8TUem˗pqq)Ug.3ip! b޼yYeDDDDDDaH=3  ZLL /^䩧M2{͛gDDDDnN{&Mjt jѢE0tP9G}_o;?>&Lpv0L1KJJJds6mZ\VUDDDDD!SE55?FF!C0d{!""" L~~>[lСC\t3{l-Zusʓ$11s=Gkec۶m]R jҤ &WZ>deejWyFnn.yyyYYڈHf2ݔN?id xgx%..3gRXXX۷b\ ʕ+:NM̙3T>`ڵΝ˶m8<׮]… DGG`8pBKMaa!yeذaY\y0 <䓖vF "" ~7222Xz5#FǏ`0SXXH=ٳ'`{wsUgvسgEEEdggYyW^={T2sH """"""(ѤI:tС>_r}vԩ?~(<<}X|9ϲeصk^h4b4؉dw"""RV….ktl` >|ŋ4.nܸ-[8x .\Et֍X_ܚgk.[i߾=Fb޼y899Ye/^LϞ=.n3??xL,m:v` >>iӦRϊHök.&NhHDD'㏼$''SPP@׮]>}:cǎjϖ-[8t.]}<̞=gggK;x֮]KFF-[Gej^:͛7yCn[ͻSDDKLظq#۷o>+WK/Yխ]|pou .UMjj*d„ Hhw""R\\\j a4ҥ |̘1={Wq\| ~gKYVV+V7y-rss#55hM̙C^^|ܸq0f5*&E:ED4w޽}+Ǐ Ν; Ǵ4{P[o@RRR>y饗399޽{:\fn#""""""""""/00&MСT,33}|\׶m?L>}ؽ{79O>$іv۷o';;N:ljÃ,nZ.]{n Ö4yfm[:EDmu*<-- ''' %;;QF@@@@|L&>Μ9×_~ ySPnO~U׮]ڈHJHHw"!C;͜`q|\׎;%K ,,̲ 9Q",,~п.]ʳ>ˡC7o9!!!>ʕ+4:L[*;o[SDDK%\Tł Xr%)))ohUV?pBRu&T?\|Ru2ss>}^z٬KHH %%C2iҤ MDD*p _{5^{5UVj*R:ԦYNN{hͺuf7nXj^Ӭ20mmѺN/pqņBECȑ#|G|׼ر3a„2yoܹ3ӟ֭X+UiriӦeږ[DDDDDDDDDnhAHb4H /pQ_N8ƍٹs'&ɓ'ӳgO `HED!Û(nբErǩzMaL&˘%%%k2oR___ؾ};AAA\rkז{,XqYoU Ru2s}L&:tTuQEEE,_ߟe˖QTTTQHYزe }%-- 6EZZKsYYY ==ݪݽZWwfỶW^ٚ[emuH}i Ǐ`0SXXH=ٳ'`;ܹs8~8%%%|,[R~ʕrǶէygϖjo.ΖV""""""""""""""""w"`wu&&&/SOYʦLŋcd""rL [ðaXf ka0x'-F@DDodddzjFQkcڵk={(**";;j[:EDĞdEPP9|0Q3,+VۛSrη~[ض4n:*""""""""""""""""R?nܸ͛dȑ<ٳTB6l؀/> ƍ7ޠ愑H󉏏gtҦcǎ ϯÙHU4~777222IHH`֬YvAAAxzzCpp0C%88IpppǮzͲq[#F0i$ڶm[yk]ؓ.fߟ+V?֭3f`ʕv/SN4kvɒ%舃?Wfܹܸl9fHJJ*>)) gggF]ADDDDDDDDDDn_O8\Xh-q\DDDDظq#ΝڵkrI^~ebcc-JJJ;w.۶m\v .͂ qF8pBKy{{SXXk}~""R=&Xz\܈ZhA޽yW;v 111L4:ЬY3:t)Sغu+U&5˲pBhӦ x{{|j[:EDĞ;[n|||\\\ޜta֭[c4"''N:Czz:sɩ9H /p2oƩi,'N`ƍܹɓٳ' C"""""޽ 6`0v'22;w;8u}eҥt֍gAjj*{Ϗ4KݻT d&LPٿO?.qxxxfmڴ!$$rە],k GGGBCC }eu""bO 6ᢡ ?&66r;SNst""""""""""Rߎ=jqn㴭E^ @QQ˗/' www#.[]vXqHWl5ƒͨQ ]bb"xxxp}ϯq̻uҥT]׮]ڈHi]%\T#&Ld2OAAAM4iӦe6"""R3Z)""uM Ud41CDDDDDDDDDDgylKYnn.qqqSꫯo>ٿ?رWWZ"%%%̝;SNY.\@tt4lܸMʉ'xcǎIXtU8@aa!ޥ꼽IMMk"""""ꊯ/:u>m۶vZ5;gϞe߾}Wy 777rssͪ.//FDDn/ډҺNKSEDDDDDDDDDD۷MNDEEAVV[nR7=--܏;9~8xxxpyvQTdǎ:u}_|ABB 55{ʕ+z*+Vƍ\zp\]]ˌ%99޽{\fn#""""bOǏ`0SXXH=ٳ'-Z{衇wo~:.]+sZښw8wW^={T 2siؔp!""""""""""%`uW~aSEߟKpСZ-44oooZhA~ $&&AϞ=i֬w߿vF+ySO;ӧҥK]Z "##9r$>Znƌtڕ Nʐ!C3f .Zb…ڵߟq0l0233K`.1bDPDDDDDDj[s{ """"""""""Rrrr߿U8|7tP ~q*bˮ,/ɓw}wc\|}[mDDDDD̪{[my$$$ @Ν1b%ٙhy>ŋiݺ5^^^̝;ݻ[.^M6KII cƌaݺu$%%1}tpvvf0c:K0o(""""""""""Rn]e͢* nܸQfd|m^8UY͛[niOdLr j<9v[s3o""""P|3WWW-ZĢEmハUY֭1DEECNә3gNNN՟T[\\qqqCDDDn#p1|pj[j'==!CXpww5iիWqvvJQ K'{kusYg5Dzlڴ,éSظqc %//777۵k˗ݝ}Y.f̘aվO>L899K/ѼysÙ2e /";wf,Æ ѣdffe5Fff&`"""""r4hc͚5̟?5ԟg0Dh4; 'pa"r8%dHIKK+Ņz-9¥KpssGa֬YZ.\MHQQ˗[xb6mDnn.%%%Vuk׮_$%%{3gPTٙhy>ŋiݺ5^^^̝;ݻɏ?ʕ+Ã^|E"##yWl2f֭[GRRӧO?)) gggF]EDDDUuh4j1Hb4w"r3,"w:KiHڴiCHH!!!stt$44P[~SX]+rι+-bѢEeyWxWJ?<ҺukF#QQQЩS'rrrHOOgΜ9899HC w_|Νw"@yzz2d{!""ULll%%66wwwNjv"7]ߋL vvizNHH %%C2iҤ MDDDDDg0DҖ""GGGy6m{ejNDno9%\ /ѣGc8q7sNL&'OgϞ 0K .DĖ@{ ""4h ͛ǚ5k?>^^^vJ }~=^DDDLkEDDj.ѣ n좢"/_N@@l2vڅc})"""""""""r[ vшhw""""bG ADDDAڢ ŋyꩧ,eSL#&&y1:i Ν;g0DN<==2dۄ>?4l';1 @㽱TMNy7HNN]2}tƎk.??-[p!.]Dyᇙ={6Ζv x֮]KFF-[Ge7ne<ȅ hѢݺucYuiiilŋٳ'7o."VyE.k|˴oߞ_?hKΦSNZ޽{s)/_NVV[n-EV.]{!**h>lIؽ{76l`0p5Odd$;w$00j7 [[w}DGG[2Nkse @޽Kk.KNNŔԻ@c0DDDDD ADDW5;vԩSۗKҭ[7Ξ=KDDݻ???E6رc,YOOO,IftR}Y:T*"$$g`` \rRDAAdgg3j(tSNrT~޶>}=V]vj#"""""""""R_&NhDn['N@DDD.z]DDnwĄmXTvabb"xxx9<<???ߏ_hHK… 8<z*]NN{hͺuf7nX,Xʕ+III!%%7|`F#ZT拗m|2...e6"""""""w\:vh0DDxc4f]sx9|}}mU8Z#(""RuM@]iҤIο9V-Z(w>VXA)..7`ر|-L&璒R5ܦMK-e5}^EDDDDDcǎ1k,z-[s;zӤI:7߰xb D6mh޼9ڵcȐ!,[xmn u72o0?? bŊ""tQg_cwfmQգb9RPWs4P57zT^pQs=&++>}Ãt b)OOOݽZ㻺닯/N>c۶m] &T&M`2z*΀l777rssͪμ8r;ŋ1c|I~~>c{YvI)..^`ͥn/Jjj** , ""& 55{Z>]~_~]vB6mHa4mgҤIAg9DDDDDʬ9twwٳ۷ZkFPDD% F||S7okUqq1=GEL:шڵʕ+|Wfk>ZM(KEw/~:?# oeժUYԞ'2qD{!"ReP7ʺ&7߉\DDDDDʮ9|衇غu+aaaУG~vヒ``̀ԖB5O?4nnnddd?ÇY,킂$''`Jpp0ϟӓ*@dd$#G$,, ///KvϸqsԨQ[#F0i$ڶm[y{ذadff\V駟&;;?O^O>8::@^Xli۶-G|-[p!G[n|l۶1cpwӼys.}Qbbb/ 11/5kܹ݀?}v{HDDDD3fеkW222:u*C a̘1jՊ ZjHh nnnニ -Zw޼꫌;Ņ&MDh֬:t`ʔ)lݺZb ?ݺucƌ\ntԉf͚QRRRn .$ 6m7˗/m=fHJJ*oRRΌ=χHm8{,gӓ-[N`` NپI&JKKcĉ}ݖrXKJJO>f6k١.UDNpppSN̘13gΔ;//~GGGڵk/6;s sΥ{lْm2h V\/bV~| [lٙDXn߃ l~zo~bbbӧ-Zfɥp{P}5tPrss*""ӧ0zh,YBVVVƓEXhN鳃~vb^uF͛7}<9rwI߾}qpps<3u>|z]DDDDrHKK1+ٙhF#4k WWWF֭[vAZbXXn/o`0~K?`2 ~ێIc/DDDDh4Fco矛\]]M@UV?9|8::ZߺuYf68TPP`i;{l`Z|y[\\lJJJ2Vkrtt9f֭Me+%%ԪUR988[jrqq)ڵ)77bUU_%Kŋ+͙3.]j3^{f -[4۷T{[Gys׳my-qU(^g{ "wѢEA; ӠA=Fi:{lW_Fa:)''4|pӉ'e;d2UzAJϫ1v\b*snM4\pkofk=TߍʖLEקiH4DjR=AAAtؑXKYll,L:Վȝ… 9s EEE?~3gRRRh… VLRGDDwޱw1g_ܹs())?{8qW_}رc<C˖->|8\z)?~|VwGil7g__ɓ'駟ڬ_t)C%%%b˯ԩS駟cv{,zV_̙3e *QU=i=jʘFjj*}իVDu˗;l2||sÝ١*"##ꫯ9vƒ>dW^) _SNORTTĔ)S,'j{LkEDD5c}Ewת!5f6lϛ f?aﰤ""""bV; EDDSXX [ovqSMiɒ%Vu!!!&4yR}嗥vhݺ 0;fy.]jL=Vuׯ_7=C6ܹsKy!`j۶UnJiLͪm._l\tqTXXX|^z SV]}7WcԨQ Pzt1j!|G|;qӠAL>>>uǦML6㏖ .lڴiSSCR}v>;Rul.]LͿy9s4]vͪƍGyVZU'sPu۪|&04$}}ژ;jH5DjÅ2h ͛ǚ5kXf a?gY?sLϟ?T<+W;w رcd2Skݬ:96mT\]]ϷY|rn9׳29r$o6jժ}TEm5`૯fܹB6{tTO@$S؀A-喱-ڂH5&`"Ap!"-T1 "D.-r  x K 1L$_kegLfw,]KO=/_"cܺuK+VP\\zGyD=6lN6\~[կԳgO=Zx^ZVM6nI%;~5kVhhu2LZfM}7)w'Ofs>I;3gΔS~HrociF銈p$&>PW/^+W8&iHb IDAT„ 2I:sRSSe˖9&/[LVrԻq>Ç@{U3Fyyyg*==]JMMUPPPZ'OJZlYݽ~ ջw21[ںu V@E;yc?eggk:xkW\}I?'=ZiU9ǚ"_jFCA.^ݻW>PFFqFhѢLݽc}’'􋋋^\ބ`IRAA< ))Iׯw3iʕ2dz!ՍQQQ4999VZ,?=Q+>>>{OGU\\nܸQ㺫SKڵӞ={4|IիuuIڿ222ԵkWeggkӦM}ׯ_/IZtݫ]vW_$]VxZ׫VR^^"""{*%%E:qV\YXGk&Iիc'1s-$iϞ=|L,2{;ycO]V۷Www}WqCy充*XC@ձw^-V͛7{QKիCU؆'_e˖JHHp']t>5c_/t)jю _}LR+U0`$)##ãs̔$p~>͛7,OHH>#*;;[gϮtWw~GVs a2~*+geeI,X(]v;w$i˖-;X,eddСCeZ+]}vI̙3չsgK.1c$i۶meN]={jԨQ:rBBB4f̘*8~$M6ej۶S;ycOgQ^^x͞=[)))ڸqJDUα&cͣ_gyF7obbbʭ۾}{IRnn[~g$Iwt'|˗/ǧL.ٿS{N))):~6o,IڱcGVoرӊ+~O}4i$I[oUnLY՝x+ڬ`owiIRLL„XI҉'uLFi߾}zw4bP-??_R[noFc- 4Hiii R^/JaRvI2{;ycO뒤+Vѣ?F'ܺurYnSNPs!ukÇCw<1h 믿y(#F'piӔ8͚56}Q}̙3߿~rcٲeFcǎH999z'{nw}eaU\\r];BBB[oI2u+Iz{n]~]_}N˗{^Ug'ib1s-Zl)ItRe:@u1v3t{ͷ~7o}YIpwޭx?~\EEE:p })22Rcƌs:p[=چ ww[5Y,[ ;lr_?֤I$?˗/w]nݒ?%|||\իڵke:|ﯰ ???ۜ9s\צMl5r_Fl|I}\^K.|rc8XÕݻW?=CjҤաC+:pڴiSkywѱcꫯd2)$$D~~~j޼zYf}]5jԨ8 ;vd2_ڵo~>|X?OOKKӠAt]w_{oǯ_k…NL&٣hBѣ?jܸqUm*''G ѣq}Gjܸ^~eGDI… #W^9s${z-Z$驧r|=Z={ѣ7(22̓kUxעO>2W;TO};cܸqյkW5jH!!!/??fϞ-IZb㕑zH{ /СCN{u"_f٪st-̔$UkP||$b| $I=\blv*L&$jve}ھ}Ν;Kݻwׄ ԡC6mRFF$I?կ_?%&&*88XuV:{7oO?Tt%vء .e˖/~q)$$c8q7FwEAAx uQqS[lQPP13yk @$>>^mP233ݿuIBJ@]hAHHNSVzTXg8p`f͚i>}zB OcܻwufRRRIR~~<dzԊ=zƍF6l"##@-}o߾5 h`?Iűb(,,L Gɓ'kF9_ W&M4w\mڴIgΜљ3gi&͝;W76:<rU ĉh"IҤIԽ{w .@fٌH22F6_6,(XP2335l0Pdff* 0x w"@C WqㆆntKmذA6l0: ^.`8"btKl6CE? .[dvmt ҷo_M<0x1bTɓշo_è1F~ؿ233233~233*ӧF1~=p fY0ԱxCPOX,fCD~'\‚ Rʜ;wNV׿U_bcc-[W8~:vumgddh߾}={ju~p׮]ڰarssue?Էood2IVk]P?T+IO~SNM^yܹӐɕ}-[Lk׮fӈ#]yԆ?3g>3^vҮ]4`͛7O5ڶ!?TLyt;w]ٳgkС Sxx4k,]vvԴiSM2E6muV͟?_~mjz C?XpJ&j*IRrrL&֬Y#IZtL&aiii:wyGȑ#u9!F:xl٢\R n cݺuK3gΔ$-[qw^Iq>Ç@ƿxb]riRM+~ ջw2zlmݺUC (&~H&LБ#GuϜ9Tjٲe-ч$=#ӷo_-YD(6o,veԆ^ 77WԩS'kۇ~롇k.׿$:ZJyyyPjjݫĉZreciFׯמ={4zhIׯ_/IZtݫ]vW_$]VeVk;϶kN{kk{Ϟ=|qe:wUIZz9N:)==]WFFvlmڴIRw}'I 0{Wtܹ2vޭ%Kh޽JMMUxx^¶K3*?`&~򓟸|]v-Io~Hi̙ZOfΜΝ;+ @]tь3$I۶m+sӧ+22R5rmˎ;Z,eddСCeZ=+Z;oW?.ڶmT0LN?ۯfeeI,X(]v;w$i˖-TTT$I6%ݻԹsgKNv $\}}bЛo)ɤ/^{7H:vXa|IR.]ʻutڷo=$$Dt-Gٔ)SԨQ#۷OFT]~}$I-ٳguu2L׿I҂ b7ސ$㶟~iL&[N%mO>]1[h!Iڰa]<-\;JN:U2{ꊻ꣏>*I9s;7o… zd24aG]W`.]_J;vߪXgϞի5n8]vM R׮]+77W:p,X Iz+l4#iW0aSVVrrrO:m7nDm۶MOѣEFF)sGbb,X Խ{w-Zŋ0m޼c_ׯ׻ᆱw}WԻw2=oWmG;wTnnSaU&PIIIڱcrrrTqzgΜ?_ŋ>ٳgVRRSC=#FTvs6*?!?0~.^NB˖-*88Xz^v VZZP)44T#Gʕ+qqqq3gx*00P۷WRR͛7m4EDDOEEE_СCլY3w.'{{ޮ~?q?s5mT zP*rjӦM*٬0)$$Dʕ+^^ jׯ7o.___hB</^zK5rŋշo_+44T>VXJ.|F`jf/IX,._[bRRRaEDDHkرGoW|||dXd6]nL&$h! D}py@C.@KLLT֭&X,)!!@]!./x+wi FhҤΝ^zIF$mڴIK,Qƍ x+އ ފVDGGkĉZh$iҤI޽QD>]|_F,s IDAT{I{ff^h9 MC5|_6FmXpP .J7:$)==]Ff񊏏7: ^А`ɓ'+660$22x C=zBbx7{4$FmxpK,Qzza0lb1: ^lFkEf0j !a+0!3zMꊏ! w"@CktކwǏcǎ.eddh߾}={juwb2ʔYfڵƏ:Y<&?@cxWsNY28e˖iڵl1bԵkW"RQm[nە>H. `<` _NsN׮]ٳ5tP)<<\qqq5k]VQp)6judggkƍ֭ z&w``0L2LFQt9=3#GܹsJKK302_C|}}z&wgjΟ?%Khݺvڷog}V3f̐tvJIIQVV;jJO<Ǝ@Irejʕ+Zf~a:nZ&Ik֬ѨQ\ @r-%''k֭:sԾ}{=ӊT~-IW\Qrrmۦ .UVzϫiӦeKNNִi+VHr/OpG6m$I.]r*w7Fwx&?@ *PPPc믿v;v1Y"M0AGq9sFղe[&?Swe[ںu Rͳγl2Zƍ:|>ǗիW5f9Ξ=tegg+55UAAAN,^XW\qLnPɓ'%I-[R^ rsaqpX,TݻWNw[z9N:)==]WFFvlmڴImV={$I>`cug֯_/IZtݫ]vW_$]VR}UG>p8qB+W,^vg͟?_yBEuQk^z9y;B"74,<L&[%' մ۷KfΜΝ;KwYfiNu$I ,Pxxۓ-ΝXmٲEuqIR6mlk۶SYWb(//OСC5tJqtE3fЋ/m۶iĉN$$$(00yBy1cT)Fw ?3 5TԩSnݺ9w޽LӧOKbbb\ĉuEIRpppm2{)Sh޼yڷoۧ?=zf7n\n/`(ɞ|7eOvn I~~~q),,J1{-M1~Uγ;icٜ^TZWV~L__2eNI0p@S;vƍu!-_\W֤I4dۿuVJ>BzPݼU^ r7~gj .c}W:t~9:TnXXN:͛7;q]-[ٳgu%liۥKu@Մ(&&F111:r>3}Zxq .uIs5mq\PVEB\\̙x@ T핔y9ꣃÇ+44T~~~ ȑ#rJT'yBUxׂ>P]<J}ݚ3gSٙ3g$IZr* ԩS5u 9p@ 8Щ쮻lVJJ!I5~xUtc(&&:hIj֬OӧWE*;Ny܍QrZUp@yL&[N/_֭[qnݺh{jݺ,b(,,L 5@eM0~'\=T+ǵZH5چ+*fVkNsN׮]ٳ5tP)<<\qqq5k]VQGžXVeggkƍ֭ X;hXXp7L&L&ax4;wNX&V{wUV)//OJMM޽{p8qB+W:z^{5IR^<9}?P0~'\drdWb(//OСC5t*ʒ$-X@ᒤviܹՖ-[<Ǐ$iӦ̶m:n}vI̙3չsgIR.]4c ڶm&Nv!!!3fLn@i<L2E5Ҿ};hĈJMM׫TӒL&Ǐ}ԉ'*ŋ2e:Y~~ۋ J֭$o~~~{4h ),,Jw7wP *}Yi򎴥 8P={Ԏ;qF:tH˗/ի5i$ 2ģz6^ZvIo{*uhhj+WuG1~`5'\ࡐ(%%EVRbb._ŋ{\~͛7jή0-[J.]Tf^8 $2~jڴ PWQQ VZZP)44T#Gʕ+Rx<9}?>xp5A+&&F111nM:USN5p@KfY)))WDD$)??_Ti 4tQIj֬OӧWzLwc OKLLT֭eXeEaaaJHH002PT!>pL&S*w6B&M4w\K5j$iӦMZd7nlptp .h5qD-ZH4i$u@mP|{Xp˝dkll6: PGap!P9_6,(XP2335l0Pdff* 0x w"@C WqㆆntKmذA6l0: ^.`8"btKl6CE? .[dvmt ҷo_M<0x1bTɓշo_è1F~ؿ233233~233*ӧF1~=p fY0ԱxCPOX,fCD~'\‚ R;ձcG222o>IRϞ=5|o^C,8\j: ڄ_+baeLYHVT/@H9t$J*dK@ NiVB߶>W\{~l=y.ٟ=\guF1Ɓ( u]YlY3{,Z(MMMK3jԨp Ei:3ym5>@-v_lٲvǷmۖ?&MJYYY3a„w}ٶm~:3ym5@hǨLeeeg͛~)]vYa/O?y13ݧơXS;vؑ9sw?gȑ 3~[nܹsdɒ9CrkMIII!7o… s)dᅘ;,Ypa 2d?ȏ?g}6˗/ϖ-[rG+y"n͙3gN.]_5rH<\,/rN8ᄬX"ofOnhh(|_|y裏nCXs P/B~s1Yxq>\tEIB܋/kfĈ'|s禼;_df֎81@q|I{'I3fBѬQbƌ9$|r-Ytin?3mڴ9kkkSWW~p5Ig&O]u5N-GtoC=O?4~i{L:55550`@!nݺuIvϳf͚Nq$ɐ!CkkX/7Q'$=ztqׯON<#G>&IvQjMҙMػnEVM+f7n\N=|y7_gK/o9'NL455uz-[tzO>I}9<Ǟ-gEk:]I:- ܾ}{{2o5CZ NuuuV^{//Bf͚U0UVVo6oVvÆ ˆ iӦ 6űM6bfzhY&sLqorTUUW\$U+$]IvE>}Ԕ=NS:q/~=>( SYYW_}5[n5*I'QqI3f䫯_5O=T*++s7b4nݺѣ$~mǚc8Ӓ$On[7\@'L3gGmN*|ꪫ1'On7`q|p6nܘ &,oVN;,[,Vjq$YjUۜ =W_}u,YƜ-]wuSLҥKnݺL:E\EEEjM^ёcfy'O&Iƌ&V@o  ihhدל0aBxuQ)))IIIIF*=P!nKMMMү_fر?~}1bD۷'I9 4(}Q> <8g}0СaÆe޼y7n\ 裏c=;7dȐ̛7/\rI~e3ZWgt;ȤI2tДd̘1w{j թ׸y睹;;7n\ƍb젃JMMMΝgĈIgʕ2hРݟWg̙7tL6-ӦM4w&i3}ʼ8@ ML2%vX,XP[`A2y"f8o=oǫ'inW\$y7Og5zK]Лqnz O>97tSx$7ߜN:Y5-}K=^o{tMMMjjjH.M8t{ `[ hhb'IR__bAMMMSz; ~K}@o[3~bIEEES_~)@VUUU*&.(^ {.bt#/}@w E}}}닝 ;Mmmm d}-}Y֭[W4n"UUUN(W^y)@VUUbeг4\ҷ t7.ZpЊ V4\ hhE@+$It'ݶ&IENDB`ConsoleTools-1.7.5/design/console.png000066400000000000000000000404051470000742400175710ustar00rootroot00000000000000PNG  IHDRHg 3X IDATxˑ6ФA1t@1AȄƁ!8l?A3?fx`Ӷ jvo7+5M=S4McXK`TO`TO`Tov`4MCkvtj8&<.դ>k1XJw ' šBKAomqpj MIg$s863ּ U|7MLE}?)e xVuL6sOgueΥQ \J"s))ʏ.ZcfEf.%}]my MӸA<g6+cQ \ӳ5D5pY"24Q \/|_^.@LE'''''''''''''''q`_ j߶mg8*γU1` nya.ol2I~vae7}&r#gIKw o+ij`gg%c㢑~JSцJ5k)) `&Hڔ/Ih{\@6 dgŵ23^9LM۶MӴm{HHiܠ P76@6@v l&w u|d6V(:ŋ3g:b*ZmwB#(3P'Hf(el {u8]&Y/?ZUv2ؾt/uAvu0&h6kǼP]m5O0:2Sф(%g]gY| ve&HK$'Y]II} AmFm~}ivL_״m4M۶g`iܠޖMrM5_ݿ+ Xi垇XV&&3Y[9Hd 1v@}0bbQ TgMG򂖮Yؼ qf`xYy$D;kbxWPWE8qG`TO`TO`TO`Toq.>Yqd\zM3qz__-~ @E6lS+c̰V|fr^e8mxܟ(tO)<|ll-DXzvvn |׫ l?-;]ϛLEIn .O~XNr67̔/iV+\U8LWjO S2Ԓ@4g gZ;ޠ↧x,~ieX"g֜) ϳSњ?9VlLùC}Y&$WiwcVaA~S鶓Ǫύ(D5 2|%x$2Vd`??w_~[BS2 sISG5+%T4r&}\E`}SU)l'_Bi۶i5-!/_̪4 p81b080X}l lꙊE9{mY`= V.o'V ~^*p`;,| 4 6!G+L\+{Ҕ ~5MTL ؖN{1 K3՝ 8>L^?Qu'px<8Yߤ8BiZPjVxXk}a#񔶹KuTJ`՟oxڝ%kN\3ʽw=ٗ#6Lz [ZD5p$ <3a29O,^0`ٹ0_~-.Ό!`ha48LӶm4VЇi, g>ʀ+[y4 p81""'V%cߓ[#`FY3Gc`4~dֺ7c9{l%ȽY|rfiۙxIRhӱ/w K!ۙU3k(O &(ö7˚ps/y&Iłg^cbAf{wllye7#n> i398*6yؕ|m}.%N֍0+O܁Cxcdd0s+|>H.B`ԡ$Gyiֵ.9 1I>Hxd=8ߤ8ro܏d8xJ۬eXF`T`x.I,e62+ߗ|W 31}+yK|b ^\8#Gņ3LZ+Kɗ_ca;d25<3-Bt-lU='à%yP`0`0L*Ɩ+⠫|}vG.xvrǚ2|6M#c/N~l c~9{k lt6ovVfWT 2]syVPg6n{5^Iv'y8$[V=f,¸g%p6IlIdz㻪w.x/>= i"1<{_LUWp$ @_ayֽ)Qvf],>?pTC<+dQɗf]2 Y}# ł| GD*cCJ;o2ՁzP`_bdr#~$wX|?sl,' &X c.x †Yٞp@ؼRE@a*/|iY_؂ed˶e ҀzV`\X^?p aL8M۶MӴm}M6a&gkv?>5Mu3ۚjXil嗳U^2;k0_M:WO693;H ,dT7qGY#mxmKpY6ZS{H 咼l&d#ȴ̺HfxGX!a*Tzx|E_$WAZ38lU׮q!%PB`+賦Q%= N LΧM>k.1`"6P x~;glw\keiu|k%CZ@ fHV Ww\_f0ޟĥpmۦiڶݾ馉WE&o_)$([e 5|6MsC>S):%Q>\6֔̌ߚqn*uY=? [63 l]}U4`CoܔĪheT4zzzzzfMI p_/R^/dS[ߣYXVvo`cMI6wbtxp&7;9k?~υs _{ ΃ϧAj5"鰑V;g+D6u+|tp'Y%ݣ"=qr|9egr:p0-'dI46%3ھIMc㟜ӏX̺!MU&?72;X0vA/9o^V{DR:䧮[*/ۏ(똀{ߙ71/'ʼnWJbdlю\4zNNZVq8xbXGɄRJV N&dyzc# ڴ&*ZƤ9eb_m֜?*tJI{ ]U*ڮ%ISc9}$:?\pc'~]j` [~/䒋}GW%JH/>f ;ހۻ򉾅X"&8'+ &jb +yE+6yla/x8.Yd,s7 uOgc%we/n o@\| L2D%%ǐMxnf$LL |’IqfJ->3+4& ;u?pSp¬ign7Oa.OpW>ޑ_{:roPӗ{sfqSwcv0+{Tm/\9R#F7HɩceJJ I6!ɫd㗒1\P9gPrO-Q4V+x&{86xbWͿE+X0mwl'%Lìgc)L8VZpr'cZ2\_ SX],KL -GI -%fHPˤ#JI~V\aUY"oa򙱮'ԏʥvÁőXXTJ"x$2fe ꫢH~L2okwUұe#9KSWE{?Jcx>U~~DY{]iw{xaVmRZ~U0G IDATTS Q rt`ۯugW2Hq-u]vPLyC.H0+T'; ;.Y`FlK//8|XᰅȲ܃#PFcjr`c;ɰpr os.&@ql~eKsddTg^^e#plUO%R k-Ϫ.99:v<Ґw1Wr{S )>Â:T♙Xd#GO\md>-_pYaeil' \՗{N~]ĕvcw]yW~`ӗ{wnN ҕC{~F|/r<4^kqijL*˻۰vVv+;y||#>ҩT ?B~J63oT/Zlphn ,XsgؚdF"6hsK&_*Y19ahlm8zIVLvZ򮓽ۉ i'7h}UIɗƞ| ;}UY]BZ\,rU}36o'dBBakC%2Żs;6}z_ &NtfS~땜T0q8Z88Wc`Ա ɎZ1 ?7;ԩ({X MT##F}&ǡ2_dqO~~9{iF @Ew^Z◾X#hn 00JN*+l '-1K.ȁs_sx6W;&]=޷Q8sha426)^*Z&<k5Q \kC%|~H_f M<{pi_!Ul. ϯ!SѸ .nP_^p 8 W6R7г\ߺ뛊K7;Z*z*жiiQPsƒ$_7^jm%]  qdqA$0,m闸x)Nɽۈc m &iby.J[ttXXc#3`'džً|aEHװVm)YBut• lO@KtLLW,,86ʽ6T;s&>ƶ'IyN ᔰ` bc'e˜x[__X8hLj^&9M`C!Zrj.g=΁MX6ܐE̹=9u v1􁞌 d9n$2Bp3Z̙qi_.nry9.JgF򹔠䜴Cqɖ33c`̆&=f6"?w%6{ /ټm*"6c 3}kD2hqdfтsFJ5NxzG;ࣛן+9}QgYuA@k?,/t& 6>"83q 4M?ޚ*};`486жgʧk#͈mk# l`KbQk6/p{?kܵ8vх4n/c|pؗ/v\[!pcs p?l[Q p؝.[(F`Gb P;s A/86@D5 lPbF]YT\& 8KnZHpN8;xׯ|@LK3:8dF8Q P)% ,> l*,'ȇP;SZLK'n@`#SKT܀HllࢺFxD \WM؜ \ؐj@6&\Tܘ V :}lGIw^kWO !LE!*mIQ6P+ 0ƚ*f9 &IݒBjmπMmɺFT<Cl$QtCTб*Mw\nϢC26pO ]I6pgbk:q \8 i2dlR7P5Q @BlLm:.($g@E PN`68Y6D%` <.E5 l6p5k=|}l N`= z}F c8;Miip0Q V6O6pQ 6@Hl{3\c$XN#؃ LC%؉VL?ؕ FT 0MlkH@`b %8UрRə4(diA#%\Tp0 0$6'@5`>q! dlKn%8XKl l mx,\؆؆3ult[qo `{qc\؅؆[,  7#2;cTa p}`_v^*4*%8؆t7eB`GlC- P p.pe55GpMJ lsmTM`FluvLb@ܠ8Y8x=N+}krw"N!fZ,ޮv}EMG~CZ=gxP=Ɨ_ yfJ+=x{ן5F`D肄a#x&E #|;s;,=np 8`8ədzI|#Q6K )\3 ldQɗSkIL.D&,%٘LJJ^Ó/ Wдm4M۶gi>eo>/_:k}l lt 5sN`T|OFk, ^`'ţx*RS8垁Ki K&kUr}d;iLd0 P$6(Ȑ̒-36 \$)c׷.^X؜Q2|&OLF. c h`&?s@HӶm4m۞=4MsNGm4Mcg['1?>}v_~9u,4=36{73vd>JlrNP/_l8tS26ny/u8ydX~Q+SM, P= P= P= P= P= P=u͹ Ƭp3zӼtK}ɒ Po?+p?/+Y@-6(1,{TO~;xO0[8{؜&yeE5_~ힹY]e){vqz}Ա_47ؼYOiJj]q,CC 69JR \߿Q !ad#qF"hyV|dAWY%cT\26#5]HsX6~?*6GzT4j%1M6~E8@o[] Wx_I5 ]ٝ0*sbƦ\tޭu9e-p_LEvܡrx;n;f$wUngwi|-No@tY~ ap PҰpKIY%'cg}~$y2Y%h6$w٣øbĵF CF•K뵬͗TKn)3 g`܉/36]}/׏m¡qo/_:kFm/ nHHO@6@6@\coeu˫[WnIjx3y^p#6dlŷެ3()|Hb'y_α8(ҟ=mE{kf^ "|G,MG}HjgEb X#ެ6, p8%)- N424:6 n#?(Y+Q|UL> C䔹ngiڶmm۳G\K4?Ql,ygSpM%7ulg-A'PJIDAT= P= P= P=?@ sG0T4zzM۶MsN4 P= P= P= P= P= P= P= P= P= P= P= P= P= P= Pm*> ̺vIENDB`ConsoleTools-1.7.5/design/console.xml000066400000000000000000005652251470000742400176210ustar00rootroot00000000000000 Enterprise Architect 2.5 Package ConsoleTools-1.7.5/design/console_parameters.png000066400000000000000000000206361470000742400220200ustar00rootroot00000000000000PNG  IHDR IDATxݱՆ:םA`GvT!L$/&I8`O.4,@ԩ%X6@q hl*^^]mA]V.+Pz^ƓU(k,Ē6Շ{=Nrgh_69?3H.%VKrHQi A]Xl *fJL%8mLu?("*|؃#9][Rvhڏ uEatVի:+npR>o%Wz)UZKr3J1%B,]cC |AZ{Hk{Wע ' z]/+H9? @]V.+P `uX@]V.+P `uXX]SМ\_q h@[asTwE^&~{~fJly*m59=cWpnvkK|ee_<|Gva/kpB `uXhJkϛ?74^4Გ "sEsKnT]Az0+<bz<>rċ31W8+^ )_`sL5TY{/ .^p@]V.+P2j<0r1!5YqHqZ a>j_zW>^V5%>EUәF|ޫѽr_٫x՜N(ݞ{?yov 荢ʖ^1iH'u9f߸"{ҪDT]1CuPj#vn?Q.Kfv`oj+5t}P=({EXҪޫ:u9x A]V.+P .*w̾gU.^ɞwEoϪB GKziX;.VZ\ UsxwiooCzxysyq6du5(9\x;.Txg"<\ ].+P `uX@]V.+P `uX@]V.+P fo }^BC ZFۧ5ٙX1{燿}[i^.+P &g0f\w\ح<_F]0 o>p[.3{*HYZ'N|54┩\͚0@%oǜ#oIsS `F'`Y)`h&NR@]0zG\'fLi.6wX`&C]V.+P `wrJxRև_fFn^nVX]-~ v[4.積ɌUg^i.^EX`EjK\3z酭 .rLQ_UX\tAp:/-͈mr#0.ާ\bK];@]V.+P `uXQx/lF `uXx~&ˍ2[Sun"ꚶx~], k"t]*nq_q~8S*E8|) T5պ$%ĢTG ㆴ}]XQ;vt燿}ؚ\`uX@]V.+P &3ڴux}=eze!ݮsU.Q(az9ãPKrN7aOuţ=C.d8<+b1Plc\~UCT!=RQeP3FXmե'⮢럢LΜE%2RċxcθJ]5/ګ'焙ۼ>>>^׽_? ov~d+P `uX oXl `uX|W~>ROSŶL L{uw]@`.ecił-BRpL;3t#gT_?}>~2/8qco3}jBŀJw^1"-#F/.x+M\.-W5Bjuā̙KO/A82PsK%XP !I aCu%IwluX1wBh@ xWaINe0nƤ3\}ic#' \,/ҡdT:t֮<맼p:8t奋|/L.^X \ L¤6uX{Hb^-C,S\bD<- M]<hM/ZO=}祐A.n<.ˌ >AIg͹}~{̥d< oiqЇxf9kܬyӠxa%p/L†BK0 OYuXDP*eR: {4nF#f[}oQ3JَDP`$ONBTmz1{թ5WבxH0R2 #oal! F#ĩΔ2ޮ!WGxƕ+'2.ʼnkj=3 L3C'9y/~:p W3K/Gj}e̉<AqsS ]9]9b78spst<u\Sxcdj){DS\]ˢu#zҺ̞:bEqbalMsx3^%X<ܤ' }nw7">gȯClc>3xK2.i8v]/W9C|RV %B_6?Uqvpfa*CxFvyZ]:Fk^yXK]y5uXXL]}i\rE oT-zdiV[ t;< 2ޫ r u4<8J]2=rs6rlL?6-o ƿcH.S\n˥4։ 93MCb.-MZrfARA./H xғg>&@\>-޿doЇɡzGxvT=28[ӛ٢Y %NҰ+!ZNu̷4;:2 b-u-X`bCuzƷ5{/`e.d1o5W|_E{xwE*twhHU4-qhb}978>oh$ҟ)\1E6V]KWŐzH]siBMMY*As9lbT379bɱf/P帜Z׭IMOꌟRj/O q2~eM5Qw]RǮTv2}AlrŘ*+n=;汱 w&?zP]9Ow[:[.x]p[:&uXnxŔ^nuY\?n(j gj %d ݺ8kW(ȭv-p ݸn]n03RL]<-@]n ک{{XPXَvfwBP]j%}mQ0vql`\x^[飭RNf& \swź"]B jV=Ū-?GWW;T[,1ni9v9Ld- rD|w+p_B_ .0U]bE +|]JUxSv+|/hPz.ހylE52+FȿBtfFkjR[92ڎw~pB]<9Ʌ<5 Nň}#Ea삕` % fbEX.>T.ͶK0ޫ!|Ut3WLD ́slhS7u{y{YůGRJq5ReX+*ǷP)v|~P|ɌiX|$E>ڇ375DģGx'uiG :Pw1NYR7?v('@gPkweŶ eevVd={ƛwja/L1]1㺴ަ._8fp}+s~|,UdWl1X~Ȥ.^ef%WҗNҚw|r3l,{G]J뮢]zze nzb} Pw' +0- ;;&qfh .8UP+XC]/_;LK)wl L^Jٍ?sqMRF̻h9vQ4ɝ-~? D"8K 8$ĥ)+,8Hksξ2u2ܕ)f1JPw7f%osoњ3[ggVxv)Ʈל"l "&`WK\%2AAJ W*-bn*Ou8H*lz.^Qf6"z.&鏦|>\|Lq`Hf'YIDATcԥ[WR >P8fR'T}j%i%.|8& % 5HE$\L 8o2였!)GKޭSw~/D[cPD9@*/hN}Mra#[5H*0u^,cDf>S c:x! u\Y0 N.yTN2:x+ Yk쪽zIUJ]n{l.]A]VTj@)VvuX@]V.+P `uX@]V.+P 0BfQeIENDB`ConsoleTools-1.7.5/design/design-1.3.txt000066400000000000000000000522171470000742400177360ustar00rootroot00000000000000eZ component: ConsoleTools, Design, 1.3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :Author: Tobias Schlitt :Revision: $Rev$ :Date: $Date$ :Status: Draft .. contents:: ===== Scope ===== The scope of this document is to describe the enhancements of the ConsoleTools component for version 1.3 of the component. The following features are part of this design document: - Basic dialog system to interact with a user through STDIN. - Enhanced handling of arguments (including help output). This document describes a new range of classes, which will be added to ConsoleTools, to fulfill this needs. ============= Dialog system ============= Design overview =============== The following section gives a brief introduction of the concept of a simple dialog system that enables the developer of a console based application to interact with the user through STDIN. Clarification of terms ---------------------- User ^^^^ The "user" is the person using a program and interacting with it during runtime. The user has no knowledge about the internals and must be guided through the dialog system he is interacting with. The term "user" in case of this document is _not_ the developer using the described component, but the user who interacts with the final program. Developer ^^^^^^^^^ In contrast to the "user", the term "developer" in this document refers to the person who creates a program, using the described component. Dialog ^^^^^^ The basic idea of a new mechanism to interact with the user through STDIN in this document is called a "dialog". A dialog is presented to the user by a certain amount of output it generates and a certain amount of input it requests afterwards. Basic design ------------ The core of the new feature described in this document is a dialog. The dialog is an object, must have the following capabilities: - Presents itself to the user on the command line - Requests data from the user through STDIN. - Validate the result for its purpose. - Return the result to the developer. A dialog is usually used in a loop, which displays the dialog over and over again until it received a valid result. Class design ============ The following classes will realize the idea of dialogs in ConsoleTools. ezcConsoleDialog ---------------- This interface describes the external API, which is commonly used by developers and other classes to interact with a dialog object. Each dialog class must implement this interface. __construct( ezcConsoleOutput $output, ezcConsoleDialogOptions $options ) The constructor of a dialog receives an instance of ezcConsoleOutput, which it must use for presenting its output, when requested. In addition, it can receive an option object, which must be an instance or subclass instance of ezcConsoleDialogOptions. hasValidResult() This method must return a boolean value, which indicates, if the dialog already received a result from the user, which is valid in its context. getResult() After the hasValidResult() method returned true, this method will return the value received from the dialog. display() Using this method, the developer can instruct the dialog object to display itself to the user. The dialog must use the instance of eczConsoleOutput it received in the constructor for displaying. reset() This method resets the dialog internally to the state it had directly after construction, so that it can be reused by the developer. In addition to this interface, an implementation of ezcConsoleDialog can contain a set of static factory methods, which return a dialog in a pre-configured state (for example a standard yes/no question, where only the text needs to be set). ezcConsoleDialogOptions ----------------------- This is the base option class, which must be accepted by a dialog. It contains options, which must be valid for each dialog class. A dialog class may request, that the options it receives are instances of a subclass, if it expects additional options. Each of the options defined in ezcConsoleDialogOptions must also be available in this subclass. Every option defined must have a sensible default value, so that there is no need for the developer to change it. The base option class provides the following options: format The name of the format this dialog uses to be displayed. The format identifier must be defined in the ezcConsoleOutput instance submitted to the constructor of the dialog. validator An instance of ezcConsoleDialogValidator. This validator is responsible for validation and manipulation of the result a dialog received from the user. An implementation of ezcConsoleDialog may require a specific sub-class of ezcConsoleDialogValidator here (e.g. for a menu dialog, which requires a special validator object to work). ezcConsoleDialogViewer ---------------------- The ezcConsoleDialogViewer class provides a set of static convenience methods that can be used by a developer that works with dialogs and by a developer that creates new dialogs. These methods are: displayDialog( ezcConsoleDialog $dialog ) [static] This method is recommended to be used for displaying dialogs. It performs the typical loop: Display the dialog over and over again until it received a valid result. When this state is reached, it returns the dialogs value. readLine( $trim = true ) [static] The readLine() method is commonly used in a dialog implementation to read a line from standard in. It returns the input provided by a user, optionally trimmed of leading and trailing white spaces. ezcConsoleDialogValidator ------------------------- The ezcConsoleValidator interface provides signatures for methods that can be used by a dialog to validate the result it retrieved. The validator is configured by the user and submitted to the dialog via an option (if a dialog supports this mechanism). __construct( ... ) The signature of the constructor is left to the validator implementation to retrieve settings (e.g. the valid results). validate( mixed $result ) This method is responsible for validating a given result. The dialog will submit the result it received to this method which will indicate if the result was valid by a boolean value. To manipulate / fix a retrieved result, the dialog implementation can call the fixup() method. fixup( mixed $result ) A validator can try to fix a given result to be valid. This manipulation can be omitted by simply returning the result again. An example for a fixup would be to convert a date information into a Unix timestamp using strtotime(). The validate method would then expect an integer value > 0. The dialog implementation is responsible for calling fixup() as it thinks is appropriate (e.g. always before calling validate(), only if validate() fails, never). Dialog implementations ====================== The new feature set comes with a collection of basic dialog implementations, which will be described in this section. ezcConsoleQuestionDialog ------------------------ The ezcConsoleQuestionDialog is the most basic imaginable dialog. It asks the user a simply question and expects a certain answer. A typical output from an ezcConsoleQuestionDialog object could look like this: :: Do you want to continue? (y/n) This dialog implementation provides a set of rudimentary options, which can be used to customize its appearance and enhance its capabilities. For this purpose, it comes with a custom options class ezcConsoleQuestionDialogOptions, that accepts the following options in addition to those provided by ezcConsoleDialogOptions: text This option defines the main "question" text, displayed to the user. validator The validator is an instance of ezcConsoleQuestionDialogValidator. For implementation details, see further below. showResults If this boolean option is set to true, the dialog will display the possible result values behind the question text itself (retrieved from the validator). If a default value is provided, it will also indicate, which one this is. For example: :: Do you want to continue? (y/n) [y] While here "y" and "n" are valid results and "y" is the default result, which is selected when simply skipping this question by hitting just . ezcConsoleQuestionDialogValidator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The interface if ezcConsoleQuestionValidator inherits from ezcConsoleDialogValidator and adds the following methods: getResultString() This method returns the result string to be displayed if the option "showResults" is true in the ezcConsoleQuestionDialogOptions object. Concrete implementations of these interface are: ezcConsoleQuestionDialogTypeValidator This validator checks the result to be of a given type (e.g. int, float). It optionally checks if the result is in a given range (e.g. [0-100]). ezcConsoleQuestionDialogCollectionValidator This validator checks the result against a given collection of pre-defined values (e.g. "y" and "n"). Beside that, it can perform basic operations on the result before checking it (like strtolower()). ezcConsoleQuestionDialogRegexValidator This validator checks the result against a given regex (e.g. "/([0-2]?[0-9])[:.]([0-6]?[0-9])/" for validation of a time value). In addition it can perform a manipulation using this regex with a given (e.g. "\1:\2" to unify the time value given). ezcConsoleMenuDialog -------------------- The second dialog implementation shipped with ConsoleTools is the menu dialog, which is an enhanced version of the question dialog. The menu dialog will display an ordered set of options and let the user select one of these. A typical menu can look like this: :: You can choose one of the following actions. 1) Create something new 2) Edit something 3) Delete something 4) Do something else Please choose an action: The menu dialog also comes with its own set of options, the ezcConsoleMenuDialogOptions: text The text displayed before the menu. selectionText The text displayed after the menu to indicate to the user that he should make a selection. markerChar The marker character used to divide the marker of a menu entry (e.g. the number) from the menu value (the text of a menu entry). validator The validator must be an instance of ezcConsoleMenuDialogValidator. ezcConsoleMenuDialogValidator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In contrast to ezcConsoleQuestionDialogValidator, this is not an interface, but a concrete implementation of ezcConsoleDialogValidator. The menu validator offers 2 properties to determine the menu entries: $entries An array of entries shown as the menu. The keys of this array represent the markers of the menu entries, the values represent the text shown. In addition, the validator can be configured to perform a manipulation on the result like the ezcConsoleQuestionDialogCollectionValidator (e.g. strtolower()). ============================== Enhanced handling of arguments ============================== Design overview =============== The current functionality for handling options and arguments for a console based application in eZ Components (mainly the class ezcConsoleInput) has only rudimentary support for dealing with arguments. The current possibility is to either switch arguments on or off and does not allow to specify the following things: - How many arguments are possible - Which arguments are mandatory/optional - Names and types for the arguments. The goal of this design section is to provide these 3 features for argument handling. Clarification of terms ---------------------- Parameter ^^^^^^^^^ The term parameter is a generic term, which covers options and arguments together. Option ^^^^^^ An option for a console based application is specified using a short or a long name and can (optionally) carry a value of a given type. Short names are built using "-" syntax, long names use "--". Options are already handled by the ezcConsoleInput class, using objects of ezcConsoleOption. Example: :: foo.php --save "bar.txt" -a "--save" is a long name and the parameter carries a string value "bar.txt". "-a" is a short name of an option, while this option does not carry any value. Argument ^^^^^^^^ In contrast to an option, an argument is not specified using a specific name, but by by the order of submission to the program. Arguments can only be given to a program, after all options have been specified. Example: :: foo.php --save "bar.txt" -a -- "baz" 23 "baz" is the value of the first argument, 23 the value of the second one. Arguments separator ^^^^^^^^^^^^^^^^^^^ In some cases the semantic for specifying parameters on the console is not deterministic. Since the algorithm used to parse parameters cannot handle nondeterministic semantics, a separator must be used to determine the border between options and arguments. The separator used is "-- ", which indicates that anything following it is an argument and does not belong to the option before it. The seperator is not mandatory and must only be used in cases where the parsing would not be deterministic. Example: :: foo.php --save "bar.txt" -a "baz" 23 foo.php --save "bar.txt" -a -- "baz" 23 In the first line, it is not possible to determine if "baz" is the value for the parameter "-a" or if it is an argument. The second line clarifies this. Basic design ------------ The core of the new functionality is built upon 2 classes: - ezcConsoleArguments - ezcConsoleArgument The first one is a collection class, which takes care of holding all arguments. The second one is a struct class, which handles 1 specific argument. Class design ============ ezcConsoleArgument ------------------ This struct class represents a single argument and defines the following properties: name A descriptive name for the argument. This name is used for 2 purposes: - Displaying it in the help text generated by ezcConsoleInput. - Identifying and retrieving the object from the argument collection. This property is mandatory and may not be left out. type The data type of the argument. The type is used for validation purposes when the parameter string of the console application is parsed and is indicated to the user in the help text generated by ezcConsoleInput. The default for this property will be ezcConsoleInput::TYPE_STRING. ezcConsoleInput::TYPE_NONE will not be allowed. shorthelp A short help text, which is used by ezcConsoleInput when generating a short help output. A sensible default will be set for this property. longhelp A long help text, which is used by ezcConsoleInput when generating a long help output. A sensible default will be set for this property. mandatory This boolean flag specifies if an argument is mandatory or optional. Since ezcConsoleArguments is an ordered collection, all arguments following an optional argument will be handled as optional, too. The default value for this property is true. default This property carries the default value for optional arguments. If these are not submitted, the default value will be used as the value property. If no default value was explicitly defined, it is null and therefore the value property will be null, if the argument was not submitted. multiple This booloan property determines, if an argument can carry multiple values. The default here is false. If this is set to true, no more arguments may follow the current argument, since multiple defines the number of values to be undefined. If arguments would follow, the parameter string would be non-deterministic. Therefore all following arguments are silently ignored. For arguments with this property true, the returned value is an array. The default value may be an array, too. value This property is not meant to be set by the developer directly, but by ezcConsoleInput while parsing the parameter string of the application. It will contain the value submitted for the argument after parsing. If this argument was not submitted, the value of the default property will be set here. ezcConsoleArguments ------------------- This collection class carries the arguments for an application. It keeps track of the order of the ezcConsoleArgument objects and offers 2 access methods for them: - By their order (using numeric identification) - By their name For these purpose, the ArrayAccess and Iterator interfaces provided by PHP will be implemented in this class. ArrayAccess will be used for both variants of access, while Iterator will iterate of the numeric order of the arguments. Argument names must be unique. The registration of new arguments is only allowed by number. The retrival is also allowed by name. The class does not offer any additional methods to deal with arguments and purely relies on the given interfaces. Integration aspects =================== The current design of ezcConsoleInput is not designed to handle arguments in the planned way. Therefore its API must be changed slightly. Naturally this will be done maintaining BC. The following changes are planned: Property $argumentDefinition ---------------------------- Since ezcConsoleInput already has a property $arguments, which carries the values of submitted arguments in an array, a new property will be introduced to carry the instance of ezcConsoleArguments. By default, this property will be null, which indicates, that the old manor of handling arguments is used. If ezcConsoleArguments is used, the preferred way of retrieving argument values is, to use the ezcConsoleArgument struct of the specific argument. The $arguments property of ezcConsoleInput will still be set and contain the values of the provided arguments. Switching arguments on/off -------------------------- The ezcConsoleOption class allows to switch arguments on or off for a console call to the application using its property $arguments. This behaviour will not be changed. If an option defines that arguments are not allowed, if it is submitted, all defined arguments will not be allowed. It is possible, that this behaviour will be enhanced in the future, so that options can define a filter rule for arguments instead of a boolean on/off switch. But this feature is not in scope of the design given here. Usage example ============= The following example shows the basic of the newly created API. Using arguments --------------- :: $input = new ezcConsoleInput(); $input->argumentDefinition = new ezcConsoleArguments(); // First argument $input->argumentDefinition[] = new ezcConsoleArgument( "infile", ezcConsoleInput::TYPE_STRING, "The file to read from.", "The input file, from which the content to process is read.", ); // Second argument $input->argumentDefinition[1] = new ezcConsoleArgument( "outfile" ); $input->argumentDefinition[1]->shorthelp = "Output file."; $input->argumentDefinition[1]->longhelp = "File to output generated content to. Output will be printed to STDOUT if left out."; $input->argumentDefinition[1]->mandatory = false; $input->argumentDefinition[1]->default = "php://output; try { $input->process(); } catch ( ezcConsoleMissingArgumentException $e ) { // Only the first argument will possibly trigger this die( "Argument {$e->argument->name} missing!" ); } // The first argument is always present if no exception occured $inFile = fopen( $input->argumentDefinition["infile"]->value, "r" ); // The second one is optional, but has a default $outFile = fopen( $input->argumentDefinition["infile"]->value, "w" ); Generating help --------------- :: $input = new ezcConsoleInput(); $input->argumentDefinition = new ezcConsoleArguments(); // First argument $input->argumentDefinition[] = new ezcConsoleArgument( "file", ezcConsoleInput::TYPE_STRING, "Output file.", "File to output generated ); // Second argument $input->argumentDefinition[] = new ezcConsoleArgument( "bytes", ezcConsoleInput::TYPE_INT, "Bytes to write.", "The number of bytes written to the output file. If left out, everything will be written.", false, -1 ); echo $input->getHelpiText( "Some example program." ); Generates: :: Usage: test.php [--] [] Some example program Arguments: Output file. = -1 Bytes to write. Open issues =========== - The access of arguments by number and name is comfortable, but (as described above) not reliable, if 2 parameters have the same name. It also makes implementation more complex (if a parameter is removed, namesake has to come in place. So, do we want this or not? - The property name $argumentsDefinition is OK for the definition part, but does actually not make sense, if you access it later to retrieve the value. Any better naming solution? - It is sometimes sensible to allow that for 1 argument multiple values are submitted (e.g. if you expect an undefined number of file names). To resolve this, we need to invent a property "multiple" for the ezcConsoleArgument struct. If multiple is defined for 1 argument, no more arguments must follow, since the algorithm could not determine easily where the multiple values for the argument end. Do we need this functionality (now)? .. Local Variables: mode: rst fill-column: 79 End: vim: et syn=rst tw=79 ConsoleTools-1.7.5/design/design.txt000066400000000000000000000066271470000742400174430ustar00rootroot00000000000000eZ publish Enterprise Component: ConsoleTools, Requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Introduction ============ Purpose of the ConsoleTools package ----------------------------------- The ConsoleTools package will contain several classes to support comfortable development of console based applications. These classes shall be designed as independant as possible from each other to let the user flexibly choose, which ones he likes to use and which not. Aimed features are: - Comfortable handling of options and parameters (long, short, values,...) - Formatting assistance for console output (placing, colors,...) - Convenience functionality to deal with Windoze and Unix based systems Current implementation ---------------------- Included classes ^^^^^^^^^^^^^^^^ 2 classes are directly involved in the CLI handling. eZScript General initiallization and shutdown script functionality. Very eZ publish specific. Handles initialization of database connection for example, but also some CLI related actions like shutting down every thing cleanly and returning an exit code. eZCLI Handles general console output, formating and option parsing. External classes ^^^^^^^^^^^^^^^^ Beside that, there are dependencies to log/debug/whatever and the following ones. eZTextCodec Base clase for drivers that convert text between different output formats. Currently has 1 extending class: eZCodePageCodec. Part of the I18N package. General involved classes: eZPublishSDK, eZDebug, eZINI, eZDebugSetting, eZExecution, eZExtension, eZDB, eZSession, eZUser, eZModule General notes ------------- The functionality for console handling seems to be widly spread over the current eZ publish classes. Beside that, it is not quite feature complete and mixed around with web handling classes. Requirenments ============= Design goals ------------ The command line handling classes should be redesigned completly, since it's currently messed up with web related stuff and fully integrated with eZ publish. Following functionality will be included: - General in and output handling functionality - Output strings - Style output (color, underline?, ...) - Position change - Error/notice/warning support - Handling of options - Long / short options - Dealing with help output - Optional and required stuff - Dependencies and exclusion of options - Handling of advanced output - Progress bars (single, keep multiple in mind) - Tables All parts should be as independant as possible. Some parts depend quite hardly on the output itself, this is ok. Option parsing and handling should _not_ depend on the output class. Design ====== The design for this package has been splitted into 3 categories of classes: Output This type of classes provide low level output functionality for console based applications (like styling text, line breaking,...). Generation This type of classes provide more advanced outputing features (like generating structures of text automatically, providing advances tools,...). This type of classes depends on the Output classes described above, since they output data. Handling This type of classes is independant of both above shown categories and provides features for handling common functions in console based applications. Since they do not have to output directly, there is no need to depend on output functionality. ConsoleTools-1.7.5/docs/000077500000000000000000000000001470000742400150755ustar00rootroot00000000000000ConsoleTools-1.7.5/docs/example_input.php000066400000000000000000000066141470000742400204670ustar00rootroot00000000000000registerOption( new ezcConsoleOption( 'h', 'help' ) ); // Register complex parameter -f/--file $file = new ezcConsoleOption( 'f', 'file', ezcConsoleInput::TYPE_STRING, null, false, 'Process a file.', 'Processes a single file.' ); $optionHandler->registerOption( $file ); // Manipulate parameter -f/--file after registration $file->multiple = true; // Register another complex parameter that depends on -f and excludes -h $dir = new ezcConsoleOption( 'd', 'dir', ezcConsoleInput::TYPE_STRING, null, true, 'Process a directory.', 'Processes a complete directory.', array( new ezcConsoleOptionRule( $optionHandler->getOption( 'f' ) ) ), array( new ezcConsoleOptionRule( $optionHandler->getOption( 'h' ) ) ) ); $optionHandler->registerOption( $dir ); // Register an alias for this parameter $optionHandler->registerAlias( 'e', 'extended-dir', $dir ); // Process registered parameters and handle errors try { $optionHandler->process( array( 'example_input.php', '-h' ) ); } catch ( ezcConsoleOptionException $e ) { echo $e->getMessage(); exit( 1 ); } // Process a single parameter $file = $optionHandler->getOption( 'f' ); if ( $file->value === false ) { echo "Parameter -{$file->short}/--{$file->long} was not submitted.\n"; } elseif ( $file->value === true ) { echo "Parameter -{$file->short}/--{$file->long} was submitted without value.\n"; } else { echo "Parameter -{$file->short}/--{$file->long} was submitted with value <".var_export($file->value, true).">.\n"; } // Process all parameters at once: foreach ( $optionHandler->getOptionValues() as $paramShort => $val ) { switch ( true ) { case $val === false: echo "Parameter $paramShort was not submitted.\n"; break; case $val === true: echo "Parameter $paramShort was submitted without a value.\n"; break; case is_array( $val ): echo "Parameter $paramShort was submitted multiple times with value: <".implode(', ', $val).">.\n"; break; default: echo "Parameter $paramShort was submitted with value: <$val>.\n"; break; } } ?> ConsoleTools-1.7.5/docs/example_menu_dialog_full.php000066400000000000000000000011601470000742400226240ustar00rootroot00000000000000text = "Please choose a possibility:\n"; $opts->validator = new ezcConsoleMenuDialogDefaultValidator( array( "A" => "Selection A", "B" => "Selection B", "C" => "Selection C", "D" => "Selection D", "Z" => "Selection Z", ), "Z" ); $dialog = new ezcConsoleMenuDialog( $out, $opts ); $res = ezcConsoleDialogViewer::displayDialog( $dialog ); echo "User seletced $res\n"; ?> ConsoleTools-1.7.5/docs/example_output.php000066400000000000000000000054661470000742400206740ustar00rootroot00000000000000options->verbosityLevel = 10; // Enable auto wrapping of lines after 40 characters $out->options->autobreak = 40; // Set the color of the default output format to green $out->formats->default->color = 'green'; // Set the color of the output format named 'success' to white $out->formats->success->color = 'white'; // Set the style of the output format named 'success' to bold $out->formats->success->style = array( 'bold' ); // Set the color of the output format named 'failure' to red $out->formats->failure->color = 'red'; // Set the style of the output format named 'failure' to bold $out->formats->failure->style = array( 'bold' ); // Set the background color of the output format named 'failure' to blue $out->formats->failure->bgcolor = 'blue'; // Output text with default format $out->outputText( 'This is default text ' ); // Output text with format 'success' $out->outputText( 'including success message', 'success' ); // Some more output with default output. $out->outputText( "and a manual linebreak.\n" ); // Manipulate the later output $out->formats->success->color = 'green'; $out->formats->default->color = 'blue'; // This is visible, since we set verboseLevel to 10, and printed in default format (now blue) $out->outputText( "Some verbose output.\n", null, 10 ); // This is not visible, since we set verboseLevel to 10 $out->outputText( "Some more verbose output.\n", null, 20 ); // This is visible, since we set verboseLevel to 10, and printed in format 'failure' $out->outputText( "And some not so verbose, failure output.\n", 'failure', 5 ); ?> ConsoleTools-1.7.5/docs/example_progressbar.php000066400000000000000000000101551470000742400216540ustar00rootroot00000000000000formats->red->color = "red"; // Create progress bar itself $progress = new ezcConsoleProgressbar( $out, 100, array( 'step' => 5 ) ); $progress->options->emptyChar = '-'; $progress->options->progressChar = $out->formatText('>', "red"); $progress->options->formatString = "Uploading file : %act%/%max% kb [%bar%]"; // Perform actions $i = 0; while( $i++ < 20 ) { // Do whatever you want to indicate progress for usleep( mt_rand( 20000, 2000000 ) ); // Advance the progressbar by one step ( uploading 5k per run ) $progress->advance(); } // Finish progress bar and jump to next line. $progress->finish(); $out->outputText( "Successfully uploaded .\n", 'success' ); /* OUTPUT: (sequential, will be printed into 1 line and updated in reallife) Uploading file : 5/100 kb [++#----------------------------------------------] Uploading file : 10/100 kb [+++++#-------------------------------------------] Uploading file : 15/100 kb [++++++++#----------------------------------------] Uploading file : 20/100 kb [+++++++++++#-------------------------------------] Uploading file : 25/100 kb [++++++++++++++#----------------------------------] Uploading file : 30/100 kb [+++++++++++++++++#-------------------------------] Uploading file : 35/100 kb [++++++++++++++++++++#----------------------------] Uploading file : 40/100 kb [+++++++++++++++++++++++#-------------------------] Uploading file : 45/100 kb [++++++++++++++++++++++++++#----------------------] Uploading file : 50/100 kb [+++++++++++++++++++++++++++++#-------------------] Uploading file : 55/100 kb [++++++++++++++++++++++++++++++++#----------------] Uploading file : 60/100 kb [+++++++++++++++++++++++++++++++++++#-------------] Uploading file : 65/100 kb [++++++++++++++++++++++++++++++++++++++#----------] Uploading file : 70/100 kb [+++++++++++++++++++++++++++++++++++++++++#-------] Uploading file : 75/100 kb [++++++++++++++++++++++++++++++++++++++++++++#----] Uploading file : 80/100 kb [+++++++++++++++++++++++++++++++++++++++++++++++#-] Uploading file : 85/100 kb [++++++++++++++++++++++++++++++++++++++++++++++++#] Uploading file : 90/100 kb [++++++++++++++++++++++++++++++++++++++++++++++++#] Uploading file : 95/100 kb [++++++++++++++++++++++++++++++++++++++++++++++++#] Uploading file : 100/100 kb [++++++++++++++++++++++++++++++++++++++++++++++++#] Uploading file : 100/100 kb [++++++++++++++++++++++++++++++++++++++++++++++++#]Successfully uploaded . */ ?> ConsoleTools-1.7.5/docs/example_progressmonitor.php000066400000000000000000000035061470000742400226010ustar00rootroot00000000000000addEntry( 'ACTION', "Performed action #{$i}." ); } $out->outputLine(); /* OUTPUT: 14.3% ACTION Performed action #1. 28.6% ACTION Performed action #2. 42.9% ACTION Performed action #3. 57.1% ACTION Performed action #4. 71.4% ACTION Performed action #5. 85.7% ACTION Performed action #6. 100.0% ACTION Performed action #7. */ ?> ConsoleTools-1.7.5/docs/example_question_dialog_collection_full.php000066400000000000000000000011451470000742400257450ustar00rootroot00000000000000text = "Do you want to proceed?"; $opts->showResults = true; $opts->validator = new ezcConsoleQuestionDialogCollectionValidator( array( "y", "n" ), "n", ezcConsoleQuestionDialogCollectionValidator::CONVERT_LOWER ); $dialog = new ezcConsoleQuestionDialog( $out, $opts ); echo "The user decided to " . ( ezcConsoleDialogViewer::displayDialog( $dialog ) === "n" ? "not " : "" ) . "proceed.\n"; ?> ConsoleTools-1.7.5/docs/example_question_dialog_factory_yesno.php000066400000000000000000000007151470000742400254560ustar00rootroot00000000000000 ConsoleTools-1.7.5/docs/example_question_dialog_type_full.php000066400000000000000000000011661470000742400245760ustar00rootroot00000000000000text = "How old are you?"; $opts->showResults = true; $opts->validator = new ezcConsoleQuestionDialogTypeValidator( ezcConsoleQuestionDialogTypeValidator::TYPE_INT ); $dialog = new ezcConsoleQuestionDialog( $out, $opts ); if ( ( $res = ezcConsoleDialogViewer::displayDialog( $dialog ) ) < 8 ) { echo "Sorry, I can not believe that you are $res years old!\n"; } else { echo "Hey, you're still young! :)\n"; } ?> ConsoleTools-1.7.5/docs/example_statusbar.php000066400000000000000000000033261470000742400213350ustar00rootroot00000000000000add( (bool)mt_rand( 0, 1 ) ); } $out->outputLine(); // Print statistics $out->outputLine( $status->getSuccessCount() . ' operations succeeded, ' . $status->getFailureCount() . ' failed.' ); /* OUTPUT: +-++++-++++-++-+--+- 13 operations succeeded, 7 failed. */ ?> ConsoleTools-1.7.5/docs/example_table.php000066400000000000000000000055311470000742400204140ustar00rootroot00000000000000formats->headline->color = 'red'; $out->formats->headline->style = array( 'bold' ); // Define a new format "sum" $out->formats->sum->color = 'blue'; $out->formats->sum->style = array( 'negative' ); // Create a new table $table = new ezcConsoleTable( $out, 60 ); // Create first row and in it the first cell $table[0][0]->content = 'Headline 1'; // Create 3 more cells in row 0 for ( $i = 2; $i < 5; $i++ ) { $table[0][]->content = "Headline $i"; } $data = array( 1, 2, 3, 4 ); // Create some more data in the table... foreach ( $data as $value ) { // Create a new row each time and set it's contents to the actual value $table[][0]->content = "$value"; } // Set another border format for our headline row $table[0]->borderFormat = 'headline'; // Set the content format for all cells of the 3rd row to "sum" $table[2]->format = 'sum'; $table->outputTable(); echo "\n"; /* RESULT (without color): +------------+------------+------------+------------+ // | Headline 1 | Headline 2 | Headline 3 | Headline 4 | // Red bordered line +------------+------------+------------+------------+ // | 1 | | | | +------------+------------+------------+------------+ | 2 | | | | // Content printed in white on blue +------------+------------+------------+------------+ | 3 | | | | +------------+------------+------------+------------+ | 4 | | | | +------------+------------+------------+------------+ */ ?> ConsoleTools-1.7.5/docs/example_table_2.php000066400000000000000000000047151470000742400206400ustar00rootroot00000000000000formats->evenRow->color = 'red'; $out->formats->evenRow->style = array( 'bold' ); $out->formats->oddRow->color = 'blue'; $out->formats->oddRow->style = array( 'bold' ); // Define format schemes for even and odd cells $out->formats->evenCell->color = 'red'; $out->formats->evenCell->style = array( 'negative' ); $out->formats->oddCell->color = 'blue'; $out->formats->oddCell->style = array( 'negative' ); // Create a new table with a width of 60 chars $table = new ezcConsoleTable( $out, 60 ); // Set global cell content align $table->options->defaultAlign = ezcConsoleTable::ALIGN_CENTER; for ( $i = 0; $i < 5; $i ++ ) { for ( $j = 0; $j < 5; $j++ ) { // Fill each table cell with ## $table[$i][$j]->content = '##'; if ( $i === $j ) { // On diagonal line set explicit cell format for even/odd $table[$i][$j]->format = $j % 2 == 0 ? 'evenCell' : 'oddCell'; } } // Set global format for even/odd rows $table[$i]->format = $i % 2 == 0 ? 'evenRow' : 'oddRow'; // Set border format for even/odd rows $table[$i]->borderFormat = $i % 2 == 0 ? 'evenRow' : 'oddRow'; } $table->outputTable(); echo "\n"; ?> ConsoleTools-1.7.5/docs/img/000077500000000000000000000000001470000742400156515ustar00rootroot00000000000000ConsoleTools-1.7.5/docs/img/consoletools_tutorial_example_06.png000066400000000000000000000007511470000742400250500ustar00rootroot00000000000000PNG  IHDR um@bKGD pHYs  tIME 2AvIDATx1r0p.|äH2E!h|;}Z ?_o Uvo~g@~}!T+Ys쵚_#r~K#l Tl1w1լ>{vs8Η6wkf"|TƞKj W~ERRVM. Y]t&sU|E!]LhH>֘T⒝ʽv@v3wmlT{{^|[\ȭpٹ{tݎu7\:Q@ X5r{^.Ao(2Jґ#H{)*$nyi[/IENDB`ConsoleTools-1.7.5/docs/img/consoletools_tutorial_example_07.png000066400000000000000000000012371470000742400250510ustar00rootroot00000000000000PNG  IHDRj[bKGD pHYs  tIME#R,IDATx휱0 !Xx8{8;{)(NbITR;u*q|M8Ovc\ƛ+[B@t9^!ʝS/|X%yŒM&K5' D/) CbN]BGٰIt5Fޔ-Uq='LP/՜MYp[|ͨ;SEp\@p=Z̀~lsԗmO5/#|Lͱ)xMeosZi6O^}iW>ޑm]QD%Za)@pSN9S)6 }''F"zTUDQD``ϭt:+јŏ~iKݱWOdkzjɰ/!~Q휉‰`3:p^N+Ď~3VO{c2nR66Z7~uI'Cs<%L'c4$ $cM)~3<^*j."*`xCLE;"]NDseOEWd/ "|~iyGس0BDkpr{j9/{=rw,SZ8!fB&CR ).HkqdZM~OQh/*#PQ@T'}aG*#׻1M9SNy<*۰vo?HZP*   fGO/ڥO/syzǽhhIʟ>t_IjxkO0w# Acʰ/\P:>{=Is ,ob!efokxKIMAs3O&ba?L絶ǫ! ߷U[5%zE;~H7F.Y>Axf ֢9z4{HUz)!4T#D8QlnLi`K{FKx3I!jiݿKj-SUhP[O.*c>|a7طpA{ jU*ݰLuY)Xsk;][F$KcPka._Yn=^:hYzr?G*  !<_'cF 9UESN9唧ˣ k6 ~IH"UDQ@TQCTU|z~ksbjm-֏/[3r oHv>7m{b~.ѱqU\zig|~6hsqIk%tlO#&#H [|k[?qf& uN/ְi퓺ޞVJQMo;HIZړ4%yFyt묩rMۂZk0KdPr=;ZM1zsy~b]vSռtjnMTC#\i1jf52JJJfqM?E+xѮ[<=/XJX?u~[׳dn[_!g9oo_yS3{<˒Gf{Z[*hGҦUړZ֮ٔ\o/h&$:whyGGG5Kd3'֮K^byD F?' SQ}Q PQ@T'GyG*#׻RN9SyTTa-ʭG?@!R@TUDU!ܼջ ÇlGn~~%Ǧdk>٫\F[Xmؗ*̶+%9Pj62skCH)5d`o)ϭ !2));{+kҵ͒_n"Z#YKl0a Sk"W.9"ӈft3+E=#w58c9O/ר̪&7g p\lxN͒@xmy˵=-h"ϘԋgYy>7SyL4 *7UDxʣ*k~2nD\[_*K9SN7T: ~IH"UDQ@TQ͛O_fϙ_,~95Iʟ!--5U:WSVm7[1u<&1!vpdi\4$)3&!/kp̰ۙ+OSOl95Oǫ3G%5Z?>+]Qkv,ى3~/>ܡQ:o=Dr&be#`!/Ykmϭ^fM`HnҬo"z_k~x>bB`DҞ:OR_8#Bk\m]T5!嘽k)^D#-ߎ7.YTH5&kI  鼹omۈACڵv3\~"-3NhW|N `]ߧJ$*U* p?~8y8{\Sޤ%Z?Zka'O_/jiOx;-[mQ460uKw\mwݪKr"D* zhn?C|nK: %iײ%Տ+m'ĢuIQ`-飚 {qnqR8*ϭh M.-[ kx 9rY@}{m[ s#{V!,kmֈb5KT2jI!M$/EsہzBwiT:ޓxA]7)Kka?Kn\*KTj+nt>R}gf? Tk * *DG^?d݈T>y)SN9QQ݆|~?HZP* ,DIENDB`ConsoleTools-1.7.5/docs/img/consoletools_tutorial_example_10.png000066400000000000000000000056241470000742400250470ustar00rootroot00000000000000PNG  IHDRVbKGD pHYs  tIME -5 qI !IDATxAH`O&rHąAj:c{};X^_G6_J+V@` V X@+V@`5PWC]|^ݱvǺl۪竢ϵ_ESkW;=U<6ƿzf3^r=_ZzP-3mEt7H:c5XN-NƟ^?~mֺOQ_XGt 7gxc2 ?zQ7(:++ X@` V X + X@` V X stS|9U˩d;XޟꟽY?žѵWMqo>+ סg>3Z3߽/}Fһ,X3ܛz]M4[FzfR`;%dL=^l(Ӎ5{FzfRkhxcjn{ߠoYwӳm =5kFzfRX 3 + X@`+V X + X@` V X';XvT|x^?P֟D}g)G6_6g,>Cx u&g1Rf={]wKMrVM.K.ݟ7ť3{e_Ywu*XN KNKKUcL7hMo{b]>ۦ7Opk=~xcҟQoQ3{e_Ywu7Vm@` V X +V@` V X@+l_Ɵ/\n׻3|e(?[jS6_Ɵ;TK:hCzFu)gi2\n/Kh'W7O9϶'&5|z77(㉶?dgyy7VM7V@`  X@+V@`  X@+/XϗSuu;j[_7k?\m^mj|/سm(Ph_o*\o$j>twYNn U{?\uD7v qb4fu3uՍv DlpN=Mv_SX_oj~s'on` + X@`+V` + X@`+Vc]uyn˩:_Nj+w[/xǏmg,xn׻DC|\k<9QzTS ՟ߙo^ɻ' ֱ'ӕϷ%o+Z[/xYn϶? v ih@pc:_zƓw=N+l`  X@+V`  X@`+V/9_Nr*>m.>XWc]|^mDe?g}>XC}?=U/Al7zV?ˎ|Yһ}ZCnm~h^Ϥ:vX5vz^uvk^?{}ϲ㟳>Xo{nݣn϶u 7֬mDe?g}&m@` V X +V@` V X@+Iu;YC]uyn˩:_N袍?{f;g|tmC϶Iƿlue}]m3{?d߹3rQK4/wKmwYB`g}~P:vzt"5eNF2P?{f;g>ۦ7>_=>P#Xo{fFw +d6 X@`+V` + X@`+V X 6˩:_N絉c]u9:k:C]uy0u}>zC}'h}cxMz7_ӆPhÌ[릲_yBn0u}~c5X}i; 70u}|c]}Apcmoo|Ưt71ō5k?L]ctc+V X + X@`+V X + X@`}XWc]|T/stS'?kZ=#G6_гm^ġ u{-O?VϨyї޽xwY6wgmZz?kZ=#W`;=D=v t⚺>ٍy#?Dsk]!o϶7.c=~ÚƪrDzFp@fn+V@` V X@+V@`,(h7$IENDB`ConsoleTools-1.7.5/docs/tutorial.txt000066400000000000000000000576541470000742400175220ustar00rootroot00000000000000eZ Components - ConsoleTools ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. contents:: Table of Contents Introduction ============ The ConsoleTools component provides several useful tools to build applications that run on a computer console (sometimes also called shell or command line). For example, eZ Publish includes several shell scripts that perform tasks like clearing caches. .. note:: From version 1.5.2 on, all necessary string operations in ConsoleTools are performed using `ext/iconv`__. This makes the component binary safe and allows you to use arbitrary unicode characters in your texts. Please make sure to convert your text to UTF-8 before submitting it to a method in ConsoleTools, if you use a different, non ASCII-compatible encoding. .. __: http://php.net/iconv Class overview ============== The ConsoleTools component offers several (mostly independent) classes to perform different tasks. The main classes are: ezcConsoleOutput ezcConsoleOutput is responsible for printing text to the console. It allows you to print text in different colors with different background colors. It can also apply other styling information to the text, making it bold or underlined for example. It can automatically wrap text after a certain number of characters are printed (keeping words intact) and handle output of different verbosity levels. ATTENTION: Windows does not support the styling of text. ezcConsoleInput Using this little tool, you can handle the options and arguments provided to your shell application. It is capable of handling and validating three types of option datatypes (string, int and none) and can handle optional and mandatory options as well as rules to define relations between them. Rules can include dependencies and exclusions between options. ezcConsoleProgressbar Most often you will use a console application in favor of a web application when it comes to processing time-consuming tasks. To indicate the current progress of a task, a kind of "status indicator" will be used, which is most commonly a progress bar. ezcConsoleProgressbar gives you an easy-to-use interface to display this. It will keep track of re-drawing the bar as needed, showing current and maximum values, as well as percentage completion. It is fully configurable regarding its visual appearance. ezcConsoleStatusbar ezcConsoleStatusbar is the "little brother" of ezcConsoleProgressbar. It also allows you to display the progress of a time-consuming action, but does not use a fixed bar-like appearance. Instead, it indicates successful and failed operations by displaying specific characters and keeps a count of successes and failures. This allows you to indicate the progress of a process where you don't initially know the number of actions to be performed. ezcConsoleProgressMonitor Sometimes you need to display the progress of several actions and don't want to use a progress bar to do so. In this case you need the status indicator. It allows you to display a status entry for each action and generates the percentage completion of the current step. ezcConsoleTable This class lets you easily create tables to be displayed on the console. It has a very convenient interface to create a table and manage the data it contains. It is highly configurable regarding the table's appearance (for example, different color and style information for content and borders on a per-cell basis, character selection for borders, variable width of the table and so on). ezcConsoleTable will also take care of measuring the best width for table columns (to make your content fit best), automatically wrapping content and aligning the content in the cells as indicated. ezcConsoleDialog This interface provides a common API for user interaction elements. A console dialog can request information from a user and react on this information interactively. For you as a user of ezcConsoleDialog, a dialog is displayed to the user and returns a result value to you, which was provided by the user of your application in some way. Currently 2 implementations of ezcConsoleDialog exist: ezcConsoleQuestionDialog is a dialog for asking a simply question and retrieving the answer from the user of an application. Instances of the ezcConsoleMenuDialog class display a menu to the user and retrieves his choice of a menu item. Usage ===== Printing text to the console ---------------------------- As mentioned, the class ezcConsoleOutput is used to print text to the console. Let's look at a basic example: .. include:: tutorial_example_01_output_basic.php :literal: The ezcConsoleOutput object is simply instantiated. You can optionally submit options and predefined formatting options to its constructor, but this can also be done later. In line 7, you can see how format is defined. Formats are created on the fly, as soon as you access them (for reading or writing) through the $output->formats attribute. There, we create a format called "info" and assign the color value "blue" to it. This will make all text printed with this format blue. In line 9, you can see how the format is applied to some text at printing time. The second example shows some more advanced code: .. include:: tutorial_example_02_output_advanced.php :literal: In this example, two more formats are defined: "error" and "fatal". These formats have an additional style attribute set, which makes them both appear bold. The "fatal" format will also underline the text and give it a black background color. The difference between ezcConsoleOutput->outputText() and ezcConsoleOutput->outputLine() is that the latter automatically adds a newline value to your text. The newline sequence used here is adjusted based on the operating system. The use of ezcConsoleOutput->outputLine() is recommended over the direct output of, for example, "\n". If you leave the second parameter of ezcConsoleOutput::outputText() and ezcConsoleOutput::outputLine() out, the "default" format is used. The default is set to your console's default setting, but you can also change this as for any other format you define. A third variant to format text is ezcConsoleOutput->formatText(), which returns the formatted string instead of printing it. This example shows some of the options ezcConsoleOutput supports: .. include:: tutorial_example_03_output_options.php :literal: autobreak Will wrap lines automatically after the set amount of characters, keeping word boundaries intact. verbosityLevel Allows you to specify a third parameter to ezcConsoleOutput->outputLine() and ezcConsoleOutput->outputText() to indicate a verbosity level for when the text should be printed. By setting the "verbosityLevel" option for ezcConsoleOutput, you define which texts will and will not be printed. In our example, the call on line 23 would not print out text with the "verbosityLevel" option set to 3, but the call on line 25 would. The last example shows how to change the target of a format, which allows you to print text e.g. to STDERR. .. include:: tutorial_example_output_targets.php :literal: The error message 'Unable to connect to database' will be printed in bold, with a red foreground color to STDOUT. The default target is ezcConsoleOutput::TARGET_OUTPUT, which prints to STDOUT and has standar output buffering in place. If you want to switch out the standard output bufferung, use ezcConsoleOutput::TARGET_STDOUT. Although this feature was originally not designed for that purpose, you can also use any other arbitrary PHP stream definition as a target. For example 'file:///var/log/my.log' to get the messages redirected to a log file instead of displaying them. Mastering options and arguments ------------------------------- Below is a simple example for ezcConsoleInput: .. include:: tutorial_example_04_input_basic.php :literal: After instantiating a new ezcConsoleInput object to handle the options, an option is registered on lines 7-12. This option will be available as "-h" and "--help". The ezcConsoleInput->process() call makes ezcConsoleInput respond to the options submitted by the user. If any error occurs with the submitted user data, the method will throw an exception of type ezcConsoleOptionException. By default, all options are registered with the value type ezcConsoleInput::TYPE_NONE, which indicates that they don't expect a value from the user. If a value is submitted anyway, ezcConsoleInput->process() will throw a ezcConsoleOptionTypeViolationException. On line 23, a check is performed to see whether an option was submitted. If an option was not submitted, its $value property will contain bool *false*. Depending on the $type set, it can contain different value types if it was submitted. If you use the (not shown here) ezcConsoleOption->$multiple property, the value will be an array containing the specified value types. The next example is more advanced: .. include:: tutorial_example_05_input_advanced.php :literal: Two options are registered here: "-i" / "--input" and "-o" / "--output". For the first one, additional properties for the ezcConsoleOption object are submitted through the constructor. For the second ezcConsoleOption object, you see how to provide additional properties after construction. We change the type of both options to expect a string value from the user (lines 13 and 20). In lines 25 and 28 we make both parameters depend on each other. If one of them is submitted without the other, ezcConsoleInput->process() will throw an ezcConsoleOptionDependencyViolationException. Aside from dependency rules, you can also define exclusion rules using ezcConsoleOption->addExclusion(). On line 43, the method ezcConsoleInput->getSynopsis() is used to retrieve a synopsis string for the program. The synopsis for our example would look like this: :: $ ./tutorial_example_05_input_advanced.php [-h] [-i [-o ] ] [[--] ] The synopsis will indicate the option value types, whether they are optional, the inter-option dependencies and default values (if set). On line 46, the property ezcConsoleOption->$shorthelp is accessed, where you can store some short help information. It has a reasonable default value set. On line 49, the submission of the "-o" option is checked. Because this has a dependency on the "-i" option, a check for that is not necessary. Line 52 shows how you can access the arguments submitted to the program. ezcConsoleInput->getArguments() always returns an array (which is empty if no arguments are submitted). A more advanced way of handling arguments is explained further below. Here is an example of how the defined program would be called: :: $ ./tutorial_example_05_input_advanced.php -i /var/www -o /tmp foo bar The program would respond by printing the following: :: Input: /var/www, Output: /tmp Arguments: foo, bar As you can see, this example does not define, which arguments are expected and therefore, the program simply accepts any number of arguments and provides them through the ezcConsoleInput->getArguments() method. The following example shows, how specific arguments can be defined: .. include:: tutorial_example_12_input_arguments.php :literal: As seen before, a help option is registered. In addition, 3 arguments are registered: The first one with the name "source" is a standard argument. It is mandatory for the user to submit a value here. The second argument, "destination" is optional and a default value is assigned, which will be used, if the user does not provide a value for it. The third one ("iterations") is not of type string, but an integer. Because the second argument is optional, this third one is automatically optional, too. Since no default value is assigned, it will be null, if the user does not submit it. If a value is provided, it must be an integer. Argument definitions are processed as usual inside the ezcConsoleInput->process() method, but will throw an ezcConsoleArgumentException if something goes wrong. If desired, exceptions about options and arguments can be caught together using ezcConsoleException or be handled on their own. The value of an argument can be fetched from its definition, using its value property. As can be seen at the very end of the example, for reading purpose, arguments can be accessed by their name. This does not work for writing purpose, since a specific order must be given there. If the --help option is set, mandatory arguments don't need to be submitted and are silently ignored. The help text generated by ezcConsoleInput for this example looks like this: :: Usage: $ tutorial_example_12_input_arguments.php [-h] [--] [] [] A simple text program -h / --help No help available. Arguments: The source directory. No help available. Number of iterations. For further information, please refer to the API documentation of ezcConsoleInput. Progress indication ------------------- This example defines a simple progress bar: .. include:: tutorial_example_06_progressbar_basic.php :literal: The created progressbar will count to a maximum value of 15, submitted to ezcConsoleProgressbar->__construct() in line 7. ezcConsoleProgressbar utilizes ezcConsoleOutput to print the generated progress bar. The call to ezcConsoleProgressbar->advance() pushes the progress bar one step further on each call and redraws it (line 11). Calling ezcConsoleProgressbar->finish() will set the progress bar to 100% immediately. The progress bar generated by the example will look like this: .. image:: img/consoletools_tutorial_example_06.png The next example performs more customization on the progress bar appearance: .. include:: tutorial_example_07_progressbar_advanced.php :literal: The defined options array demonstrates only a small subset of options. For detailed information, see the API documentation on ezcConsoleProgressbarOptions. The "emptyChar" value defines the character to prefill the bar, the "barChar" option defines the character to fill the bar with when calling ezcConsoleProgressbar->advance(). Using the "formatString" option, you define the appearance of the whole bar. Here the substitution of several placeholders (like "%fraction%" and "%bar%") is permitted. "formatString" must contain the "%bar%" placeholder, while all other values are optional. Any other printable character is permitted. Formatting options are allowed in the "formatString" option, but not in any other option. "redrawFrequency" defines how often the progressbar will be redrawn. In the example this will be every 50th call to ezcConsoleProgressbar->advance(). The resulting progress bar looks like this: .. image:: img/consoletools_tutorial_example_07.png With ezcConsoleStatusbar, you can indicate the progress of a time-consuming action in a simpler way. Here is an example: .. include:: tutorial_example_08_statusbar.php :literal: This variant of indicating progress only displays success or failure indicators for an action and allows you to run any number of actions, without specifying in advance how many you will perform. The "successChar" and "failureChar" options indicate which string to print on a successful or failed action. Lines 11 and 12 format these strings. Indicating a status is done using ezcConsoleStatusbar->add(), which expects *true* for a succeeded action and *false* for a failed one (line 20). You can access the number of successes and failures through ezcConsoleStatusbar->getSuccessCount() and ezcConsoleStatusbar->getFailureCount(). To make ezcConsoleStatusbar wrap a line after a certain amount of statuses, you can use ezcConsoleOutput->$autobreak. Here the result of the example: .. image:: img/consoletools_tutorial_example_08.png Finally, ezcConsoleProgressMonitor can indicate progress, but does not use a bar-like interface. It simply prints status information about each action you perform and shows the current progress as a percentage value in relation to the number of actions you plan to perform overall. .. include:: tutorial_example_11_progressmonitor.php :literal: Line 7 creates a new status indicator, which will iterate over 7 actions. Inside the while loop, we simulate some actions. The call to $status->addEntry() adds a status entry and causes the indicator to print the entry. Every entry consists of a tag (first parameter) and a message. The result of the example is as follows: :: 14.3% ACTION Performed action #1. 28.6% ACTION Performed action #2. 42.9% ACTION Performed action #3. 57.1% ACTION Performed action #4. 71.4% ACTION Performed action #5. 85.7% ACTION Performed action #6. 100.0% ACTION Performed action #7. More information on these classes can be found in the API documentation of ezcConsoleProgressbar, ezcConsoleStatusbar and ezcConsoleProgressMonitor. Large data served in a table ---------------------------- This is the result of a table generated by ezcConsoleTable: .. image:: img/consoletools_tutorial_example_09.png Here is its corresponding source code: .. include:: tutorial_example_09_table_basic.php :literal: ezcConsoleTable (like ezcConsoleStatusbar and ezcConsoleProgressbar) uses the ezcConsoleOutput class to print to the console. To create a table, you just need to submit the maximum width of the table to its constructor: ezcConsoleTable->__construct(). Options for table formatting are inherited from the table itself to the table rows and from there to the table cells. On each inheritance level, options can be overridden individually. The "defaultBorderFormat" option sets the global format value for all borders (line 24). This is overridden in line 26 for the first row of the table. Table rows are accessed like an array in PHP (this is achieved by implementing the ArrayAccess_ interface from SPL_). ezcConsoleTable implements the Iterator interface (SPL_, too) to allow iteration over table rows using foreach. Each table row is represented by an object of type ezcConsoleTableRow, which also implements the ArrayAccess_ and Iterator interfaces to access cells contained in the rows in the same way. Each of the named classes allows the access of its properties as usual, in addition to access to its contained objects through the array interface. ezcConsoleTableRow and ezcConsoleTableCell have a $format setting to define the format of the contained text. All cells (as described above) will inherit the setting of their parent ezcConsoleTableRow, as long as this has not been explicitly overridden. The same applies to ezcConsoleTableRow->$align and ezcConsoleTableCell->$align. Possible align values are: - ezcConsoleTable::ALIGN_DEFAULT (inherit from parent) - ezcConsoleTable::ALIGN_LEFT - ezcConsoleTable::ALIGN_RIGHT - ezcConsoleTable::ALIGN_CENTER The content of a cell is stored in the ezcConsoleTableCell->$content property (line 34). The usage of formatted text in a cell is possible, but not recommended. If you want to define the format of cell content, use the ezcConsoleTableCell->$format property. .. _SPL: http://php.net/spl .. _ArrayAccess: http://www.php.net/~helly/php/ext/spl/interfaceArrayAccess.html Below is a more advanced (but in a way useless) example to show the handling of tables: .. include:: tutorial_example_10_table_advanced.php :literal: The "corner", "lineHorizontal" and "lineVertical" options define which characters to use for the borders of the table. These options must be exactly one character long and cannot contain formatting information. To style the borders, use the ezcConsoleTable->$defaultBorderFormat and ezcConsoleTableRow->$borderFormat properties. The random format and alignment options selected above create the following table: .. image:: img/consoletools_tutorial_example_10.png More information on the handling of tables on the shell can be found in the API documentation of ezcConsoleTable, ezcConsoleTableRow and ezcConsoleTableCell. Interacting with the user ------------------------- Implementations of the interface ezcConsoleDialog are generic building blocks, which allow to interact with the user of a shell application using STDIN. A dialog is initialized, displayed and will return a value provided by the user. What exactly happens inside a specific dialog may vary. Commonly, the dialog validates and possibly manipulates the provided value before returning it. The most basic dialog is the ezcConsoleQuestionDialog, which prints out some text and retrieves a single value back. .. include:: tutorial_example_13_dialog_question.php :literal: Every dialog expects an instance of ezcConsoleOutput which is used to display it. The question dialog here will display the text "Do you want to proceed?" and expects an answer from the user. The $showResults option indicates, that the possible values the user may provide will be indicated, as well as the default value, if one is set. The mechanism for validating the answer is defined by an instance of ezcConsoleQuestionDialogValidator. In the example, a collection validator is used, which defines a collection of valid values. Beside that, it performs a case conversion on the user provided result before validating it, if desired. Displaying a dialog can either be done directly, by calling ezcConsoleDialog->display(), or the more convenient way, shown in the example. The ezcConsoleDialogViewer::displayDialog() method displays the dialog in a loop, until the user provided a valid value, so that the program can rely on this. In the example, the user is asked after every performed action, if he still wants to proceed. If the answer is "n" or "N", the program stops. An example run of this application could look like this: :: Some action performed... Do you want to proceed? (y/n) [y] y Some action performed... Do you want to proceed? (y/n) [y] Some action performed... Do you want to proceed? (y/n) [y] n Goodbye! A very similar yes/no question can be created through convenience method very easily: .. include:: tutorial_example_14_dialog_yesnoquestion.php :literal: The created yes/no question dialog contains a custom question and defaults to "y", if nothing is selected. In contrast to the last example, the dialog created here also accepts "yes" and "no" as answers. Both phrases can be used in any typing, e.g. like "yEs" or "NO". This is made possibly by the ezcConsoleQuestionDialogMappingValidator, which extends ezcConsoleQuestionDialogCollectionValidator. The mapping validator allows to define an arbitrary mapping between the user typed answers and expected ones. Therefore the dialog still only returns either "y" or "n". The second dialog type, provided with ConsoleTools, is the ezcConsoleMenuDialog. Similar to the ezcConsoleQuestionDialog, it displays a list of menu items to the user and requires him to choose one of these. An example for this class looks like this: .. include:: tutorial_example_15_dialog_menu.php :literal: Again the dialog is instantiated and some options are tweaked to get the desired behaviour. The validator in this case receives an array of possible menu items, while the key represents the identifier and the value contains the text to be displayed for the item. The second argument is the default value, chosen if the user simply presses . An example run of this program could look like this: :: Please choose a possibility: 1) Perform some more actions 2) Perform another action 0) Quit Select: [0] 1 Performing some more actions... Please choose a possibility: 1) Perform some more actions 2) Perform another action 0) Quit Select: [0] 2 Performing some other actions! Please choose a possibility: 1) Perform some more actions 2) Perform another action 0) Quit Select: [0] The character used to divide the identifier and text, as well as the text indicating that a selection must be done, can be tweaked, too. Further information about dialogs can be found in the API documentation of ezcConsoleDialog, ezcConsoleQuestionDialog and ezcConsoleMenuDialog. .. Local Variables: mode: rst fill-column: 79 End: vim: et syn=rst tw=79 ConsoleTools-1.7.5/docs/tutorial_autoload.php000066400000000000000000000007571470000742400213520ustar00rootroot00000000000000 ConsoleTools-1.7.5/docs/tutorial_example_01_output_basic.php000066400000000000000000000002701470000742400242440ustar00rootroot00000000000000formats->info->color = 'blue'; $output->outputText( 'Test text in color blue', 'info' ); ?> ConsoleTools-1.7.5/docs/tutorial_example_02_output_advanced.php000066400000000000000000000012211470000742400247260ustar00rootroot00000000000000formats->info->color = 'blue'; $output->formats->error->color = 'red'; $output->formats->error->style = array( 'bold' ); $output->formats->fatal->color = 'red'; $output->formats->fatal->style = array( 'bold', 'underlined' ); $output->formats->fatal->bgcolor = 'black'; $output->outputText( 'This is some standard text ' ); $output->outputText( 'including some error', 'error' ); $output->outputText( ' wrapped in standard text.' ); $output->outputText( "\n" ); $output->outputLine( 'This is a fatal error message.', 'fatal' ); $output->outputText( 'Test' ); ?> ConsoleTools-1.7.5/docs/tutorial_example_03_output_options.php000066400000000000000000000013631470000742400246640ustar00rootroot00000000000000formats->info->color = 'blue'; $output->formats->info->style = array( 'bold' ); $output->setOptions( array( 'autobreak' => 78, 'verbosityLevel' => 3 ) ); $output->outputLine( 'This is a very very long info text. It has so much information in '. 'it, that it will definitly not fit into 1 line. Therefore, '. 'ezcConsoleOutput will automatically wrap the line for us.', 'info' ); $output->outputLine(); $output->outputLine( 'This verbose information will currently not be displayed.', 'info', 10 ); $output->outputLine( 'But this verbose information will be displayed.', 'info', 2 ); ?> ConsoleTools-1.7.5/docs/tutorial_example_04_input_basic.php000066400000000000000000000006171470000742400240530ustar00rootroot00000000000000registerOption( new ezcConsoleOption( 'h', 'help' ) ); try { $input->process(); } catch ( ezcConsoleOptionException $e ) { die( $e->getMessage() ); } if ( $helpOption->value !== false ) { echo "Help requested."; } else { echo "No help requested."; } ?> ConsoleTools-1.7.5/docs/tutorial_example_05_input_advanced.php000066400000000000000000000021551470000742400245370ustar00rootroot00000000000000registerOption( new ezcConsoleOption( 'h', 'help' ) ); $inputOption = $input->registerOption( new ezcConsoleOption( 'i', 'input', ezcConsoleInput::TYPE_STRING ) ); $outputOption = $input->registerOption( new ezcConsoleOption( 'o', 'output' ) ); $outputOption->type = ezcConsoleInput::TYPE_STRING; $inputOption->addDependency( new ezcConsoleOptionRule( $outputOption ) ); $outputOption->addDependency( new ezcConsoleOptionRule( $inputOption ) ); try { $input->process(); } catch ( ezcConsoleOptionException $e ) { die( $e->getMessage() ); } if ( $helpOption->value === true ) { echo $input->getSynopsis() . "\n"; foreach ( $input->getOptions() as $option ) { echo "-{$option->short}/{$option->long}: {$option->shorthelp}\n"; } } elseif ( $outputOption->value !== false ) { echo "Input: {$inputOption->value}, Output: {$outputOption->value}\n"; echo "Arguments: " . implode( ", ", $input->getArguments() ) . "\n"; } ?> ConsoleTools-1.7.5/docs/tutorial_example_06_progressbar_basic.php000066400000000000000000000004171470000742400252450ustar00rootroot00000000000000advance(); usleep( mt_rand( 2000, 200000 ) ); } $bar->finish(); $output->outputLine(); ?> ConsoleTools-1.7.5/docs/tutorial_example_07_progressbar_advanced.php000066400000000000000000000011241470000742400257260ustar00rootroot00000000000000formats->bar->color = 'blue'; $output->formats->bar->style = array( 'bold' ); $options = array( 'emptyChar' => ' ', 'barChar' => '-', 'formatString' => '%fraction%% <' . $output->formatText( '%bar%', 'bar' ) . '> Uploaded %act% / %max% kb', 'redrawFrequency' => 50, ); $bar = new ezcConsoleProgressbar( $output, 1024, $options ); for ( $i = 0; $i < 1024; $i++ ) { $bar->advance(); usleep( mt_rand( 200, 2000 ) ); } $bar->finish(); $output->outputLine(); ?> ConsoleTools-1.7.5/docs/tutorial_example_08_statusbar.php000066400000000000000000000012201470000742400235560ustar00rootroot00000000000000formats->success->color = 'green'; $output->formats->failure->color = 'red'; $options = array( 'successChar' => $output->formatText( '+', 'success' ), 'failureChar' => $output->formatText( '-', 'failure' ), ); $status = new ezcConsoleStatusbar( $output, $options ); for ( $i = 0; $i < 120; $i++ ) { $nextStatus = ( bool )mt_rand( 0,1 ); $status->add( $nextStatus ); usleep( mt_rand( 200, 2000 ) ); } $output->outputLine(); $output->outputLine( 'Successes: ' . $status->getSuccessCount() . ', Failures: ' . $status->getFailureCount() ); ?> ConsoleTools-1.7.5/docs/tutorial_example_09_table_basic.php000066400000000000000000000021001470000742400237750ustar00rootroot00000000000000formats->headBorder->color = 'blue'; $output->formats->normalBorder->color = 'gray'; $output->formats->headContent->color = 'blue'; $output->formats->headContent->style = array( 'bold' ); $table = new ezcConsoleTable( $output, 78 ); $table->options->defaultBorderFormat = 'normalBorder'; $table[0]->borderFormat = 'headBorder'; $table[0]->format = 'headContent'; $table[0]->align = ezcConsoleTable::ALIGN_CENTER; foreach ( $data as $row => $cells ) { foreach ( $cells as $cell ) { $table[$row][]->content = $cell; } } $output->outputLine( 'eZ components team:' ); $table->outputTable(); $output->outputLine(); ?> ConsoleTools-1.7.5/docs/tutorial_example_10_table_advanced.php000066400000000000000000000020001470000742400244500ustar00rootroot00000000000000formats->blue->color = 'blue'; $output->formats->blue->style = array( 'bold' ); $output->formats->red->color = 'red'; $output->formats->red->style = array( 'bold' ); $output->formats->green->color = 'green'; $output->formats->green->style = array( 'bold' ); $colors = array( 'red', 'blue', 'green' ); $aligns = array( ezcConsoleTable::ALIGN_LEFT, ezcConsoleTable::ALIGN_CENTER, ezcConsoleTable::ALIGN_RIGHT ); $table = new ezcConsoleTable( $output, 78 ); $table->options->corner = ' '; $table->options->lineHorizontal = ' '; $table->options->lineVertical = ' '; $table->options->widthType = ezcConsoleTable::WIDTH_FIXED; for ( $i = 0; $i < 10; $i++ ) { for ( $j = 0; $j < 10; $j++ ) { $table[$i][$j]->content = '*'; $table[$i][$j]->format = $colors[array_rand( $colors )]; $table[$i][$j]->align = $aligns[array_rand( $aligns )]; } } $table->outputTable(); $output->outputLine(); ?> ConsoleTools-1.7.5/docs/tutorial_example_11_progressmonitor.php000066400000000000000000000004531470000742400250230ustar00rootroot00000000000000addEntry( 'ACTION', "Performed action #{$i}." ); } $output->outputLine(); ?> ConsoleTools-1.7.5/docs/tutorial_example_12_input_arguments.php000066400000000000000000000026421470000742400247760ustar00rootroot00000000000000registerOption( new ezcConsoleOption( 'h', 'help' ) ); $helpOption->isHelpOption = true; $input->argumentDefinition = new ezcConsoleArguments(); $input->argumentDefinition[0] = new ezcConsoleArgument( "source" ); $input->argumentDefinition[0]->shorthelp = "The source directory."; $input->argumentDefinition[1] = new ezcConsoleArgument( "destination" ); $input->argumentDefinition[1]->mandatory = false; $input->argumentDefinition[1]->default = './'; $input->argumentDefinition[2] = new ezcConsoleArgument( "iterations" ); $input->argumentDefinition[2]->type = ezcConsoleInput::TYPE_INT; $input->argumentDefinition[2]->shorthelp = "Number of iterations."; $input->argumentDefinition[2]->longhelp = "The number of iterations to perform."; try { $input->process(); } catch ( ezcConsoleException $e ) { die( $e->getMessage() ); } if ( $helpOption->value === true ) { echo $input->getHelpText( "A simple text program" ); } else { echo "Source: {$input->argumentDefinition["source"]->value}\n"; echo "Destination: {$input->argumentDefinition["destination"]->value}\n"; echo "Iterations: " . ( $input->argumentDefinition["iterations"]->value === null ? "not set" : $input->argumentDefinition["iterations"]->value ); } ?> ConsoleTools-1.7.5/docs/tutorial_example_13_dialog_question.php000066400000000000000000000010501470000742400247310ustar00rootroot00000000000000options->text = "Do you want to proceed?"; $question->options->showResults = true; $question->options->validator = new ezcConsoleQuestionDialogCollectionValidator( array( "y", "n" ), "y", ezcConsoleQuestionDialogCollectionValidator::CONVERT_LOWER ); do { echo "\nSome action performed...\n\n"; } while ( ezcConsoleDialogViewer::displayDialog( $question ) !== "n" ); echo "Goodbye!\n"; ?> ConsoleTools-1.7.5/docs/tutorial_example_14_dialog_yesnoquestion.php000066400000000000000000000005161470000742400260160ustar00rootroot00000000000000 ConsoleTools-1.7.5/docs/tutorial_example_15_dialog_menu.php000066400000000000000000000013431470000742400240350ustar00rootroot00000000000000options = new ezcConsoleMenuDialogOptions(); $menu->options->text = "Please choose a possibility:\n"; $menu->options->validator = new ezcConsoleMenuDialogDefaultValidator( array( "1" => "Perform some more actions", "2" => "Perform another action", "0" => "Quit", ), "0" ); while ( ( $choice = ezcConsoleDialogViewer::displayDialog( $menu ) ) != 0 ) { switch ( $choice ) { case 1: echo "Performing some more actions...\n"; break; case 2: echo "Performing some other actions!\n"; break; } } ?> ConsoleTools-1.7.5/docs/tutorial_example_output_targets.php000066400000000000000000000004641470000742400243410ustar00rootroot00000000000000formats->error->color = 'red'; $output->formats->error->style = array( 'bold' ); $output->formats->error->target = ezcConsoleOutput::TARGET_STDERR; $output->outputLine( 'Unable to connect to database', 'error' ); ?> ConsoleTools-1.7.5/phpunit.xml.dist000066400000000000000000000010231470000742400173140ustar00rootroot00000000000000 ./src ./tests ConsoleTools-1.7.5/review-1.4.txt000066400000000000000000000007601470000742400165120ustar00rootroot00000000000000Review Alexandru 2008-05-08 =========================== [X] Regarding #10765 (new MappingValidator for yes/no dialogs) - It is not documented or mentioned in the tutorial. [X] Why is $this->properties['map'] = array(); in the constructor? $this-map is assigned on the following line anyway. [X] The @property tag for ezcConsoleDialogQuestionMappingValidator contain a wrong description for $conversion - TYPE_* instead of CONVERT_* constants. Same for other validator classes. ConsoleTools-1.7.5/review-1.6.txt000066400000000000000000000016171470000742400165160ustar00rootroot00000000000000Review Alexandru 2009-08-03 =========================== [x] issue #14916 (extract help) doesn't have any tests Inherently tested by ezcConsoleInputTest, since this already has tests for help generation. These also ensure that no regression turns up during the refactoring. Extracting the very same tests into a new test case would not make sense. --ts [x] ezcConsoleStandardInputHelpGenerator - shouldn't it be named ezcConsoleInputStandardHelpGenerator? [x] some method signatures don't reflect the doc blocks, eg. generateGroupedOptionHelp() [x] @return array(int=>array(int=>string)) can be written easier as @return array(array(string)) as we do in other components [x] for issue #15010 the file testTableWithoutFormatting.dat does not exist in the windows folder. There are actually 15 files or so which are missing from the windows folder and prevent testing on Windows. ConsoleTools-1.7.5/review.txt000066400000000000000000000112721470000742400162120ustar00rootroot00000000000000> Comments by: Raymond Bosman (12-12-2005) > ---------------------------------------- > General: > - Check directory structure, if you add more structures to Struct directory > then there is no real separaration between the different tools. > Parameter: > - Should use struct. Done. > - What does excludes dO? Exclude another parameter, if this one is used. Hope this is clearer now, since I updated lots of docs while refactoring. > Output: > - Should use struct instead of the options array. Done. > Progressbar: > - Should use struct instead of the options array. Done. > StatusBar: > - For sake of compatibility, use struct instead of the options array. > StatusBar: > - Use struct instead of the options array. # Looks like the same to me... I changed that to properties to not use a class for 2 simple types. Please object if I should really make a struct. Comments by Jan Borsodi (15-12-2005) ------------------------------------ > General: > - Check the coding standard, spacing is not correct in many places. Thanks for the hint. I tried my best to fix it. Can you point me to extreme cases where I maybe missed it? > - Why have a 'break' after a 'return' in switch/case statements? Just for completness. I learned it that way "no case without break" and that rule was quite usefull in many cases. If it disturbs you, let me know and I will remove it. Update 20-12-2005: Removed the break. > ezcConsoleParameter::getValues > - I think maybe the name should be getResult, getOptionResult or getOptionValues > - Alternatively the process() method could return a result object which has the > options and arguments stored. > $result = $p->process(); > if ( $result->options['help'] ); > var_dump( $result->arguments ); > > This removes the need for the class to both handle registration/processing > as well as being a storage device. I don't think this is a good idea, please take a look how the new ezcConsoleInput. About the renaming of getValues() I'm quite open, since usually it is intended that you to "$input->getParam('h')->value;". > ezcConsoleParameter::getParam > - I think the name is misleading, when I first saw it I though it returned > the input value. I think it should be more clearer that the option definition > is returned by changing the name. This for all the other param methods too. This is already implemented by the move to use of ezcConsoleOption. > ezcConsoleParameter::fromString > - This should get a different name, I first though that this returned the > parameter definitions but it actually registers them. > I suggest registerOptionString(). Like before, the naming does not really matter to me. Please let me know, if I should finally change that. Update 20-12-2005: Renamed the method. > ezcConsoleStatusbar > I'm not sure what the purpose of this is. I think we should have a class > which outputs status in similar way which is done in eZ publish (the block > based output). This not only breaks at a given column but can also show the > progress on each line if the total value is known. > > e.g. > > ............................................................ 40% > ...................x........................................ 80% > ............................ 100% The statusbar is implemented as I understood your defintion by now. I'm sure I can implement it in the way you want it, but I'm not sure this will work for beta2. ezcConsoleTable - Coding standard is not followed all places. - The doc for the class needs more work, it doesn't really explain how to use all of its features. > ezcConsoleTable::__get/__set: > - The property handling is inconsistent, one of them is a protected variable while > the others are handled in an array. > - The property array should be named properties not settings. Fixed by the whole refactoring. > ezcConsoleTable::setOptions: > - Why is this present when you can set them with a member variable, either only have the method > or use the property. Also the method sets them as array entries while $options is now an object. Fixed by the whole refactoring. > ezcConsoleTable::setSettings: > - Similar issue as setOptions, also no documentation of which settings one can set. Fixed by the whole refactoring. Comments by Derick ================== ezcConsoleTableOptions::__construct has no good documentation for the parameters. ezcConsoleOutputFormats should not be in the structs directory as it is no struct. ezcConsoleOptionRule, ezcConsoleOutputFormat and ezcConsoleProgressbarOptions are not real structs so they should not go into the structs directory. They are not real structs because they have additional checks for value's ranges. Also change the word "struct" in its docs to "class". ConsoleTools-1.7.5/src/000077500000000000000000000000001470000742400147345ustar00rootroot00000000000000ConsoleTools-1.7.5/src/console_autoload.php000066400000000000000000000161621470000742400210050ustar00rootroot00000000000000 'ConsoleTools/exceptions/exception.php', 'ezcConsoleArgumentException' => 'ConsoleTools/exceptions/argument.php', 'ezcConsoleOptionException' => 'ConsoleTools/exceptions/option.php', 'ezcConsoleArgumentAlreadyRegisteredException' => 'ConsoleTools/exceptions/argument_already_registered.php', 'ezcConsoleArgumentMandatoryViolationException' => 'ConsoleTools/exceptions/argument_mandatory_violation.php', 'ezcConsoleArgumentTypeViolationException' => 'ConsoleTools/exceptions/argument_type_violation.php', 'ezcConsoleDialogAbortException' => 'ConsoleTools/exceptions/dialog_abort.php', 'ezcConsoleInvalidOptionNameException' => 'ConsoleTools/exceptions/invalid_option_name.php', 'ezcConsoleInvalidOutputTargetException' => 'ConsoleTools/exceptions/invalid_output_target.php', 'ezcConsoleNoPositionStoredException' => 'ConsoleTools/exceptions/no_position_stored.php', 'ezcConsoleNoValidDialogResultException' => 'ConsoleTools/exceptions/no_valid_dialog_result.php', 'ezcConsoleOptionAlreadyRegisteredException' => 'ConsoleTools/exceptions/option_already_registered.php', 'ezcConsoleOptionArgumentsViolationException' => 'ConsoleTools/exceptions/option_arguments_violation.php', 'ezcConsoleOptionDependencyViolationException' => 'ConsoleTools/exceptions/option_dependency_violation.php', 'ezcConsoleOptionExclusionViolationException' => 'ConsoleTools/exceptions/option_exclusion_violation.php', 'ezcConsoleOptionMandatoryViolationException' => 'ConsoleTools/exceptions/option_mandatory_violation.php', 'ezcConsoleOptionMissingValueException' => 'ConsoleTools/exceptions/option_missing_value.php', 'ezcConsoleOptionNoAliasException' => 'ConsoleTools/exceptions/option_no_alias.php', 'ezcConsoleOptionNotExistsException' => 'ConsoleTools/exceptions/option_not_exists.php', 'ezcConsoleOptionStringNotWellformedException' => 'ConsoleTools/exceptions/option_string_not_wellformed.php', 'ezcConsoleOptionTooManyValuesException' => 'ConsoleTools/exceptions/option_too_many_values.php', 'ezcConsoleOptionTypeViolationException' => 'ConsoleTools/exceptions/option_type_violation.php', 'ezcConsoleTooManyArgumentsException' => 'ConsoleTools/exceptions/argument_too_many.php', 'ezcConsoleDialogValidator' => 'ConsoleTools/interfaces/dialog_validator.php', 'ezcConsoleQuestionDialogValidator' => 'ConsoleTools/interfaces/question_dialog_validator.php', 'ezcConsoleDialog' => 'ConsoleTools/interfaces/dialog.php', 'ezcConsoleDialogOptions' => 'ConsoleTools/options/dialog.php', 'ezcConsoleInputHelpGenerator' => 'ConsoleTools/interfaces/input_help_generator.php', 'ezcConsoleInputValidator' => 'ConsoleTools/interfaces/input_validator.php', 'ezcConsoleMenuDialogValidator' => 'ConsoleTools/interfaces/menu_dialog_validator.php', 'ezcConsoleQuestionDialogCollectionValidator' => 'ConsoleTools/dialog/validators/question_dialog_collection.php', 'ezcConsoleArgument' => 'ConsoleTools/input/argument.php', 'ezcConsoleArguments' => 'ConsoleTools/input/arguments.php', 'ezcConsoleDialogViewer' => 'ConsoleTools/dialog_viewer.php', 'ezcConsoleInput' => 'ConsoleTools/input.php', 'ezcConsoleInputStandardHelpGenerator' => 'ConsoleTools/input/help_generators/standard.php', 'ezcConsoleMenuDialog' => 'ConsoleTools/dialog/menu_dialog.php', 'ezcConsoleMenuDialogDefaultValidator' => 'ConsoleTools/dialog/validators/menu_dialog_default.php', 'ezcConsoleMenuDialogOptions' => 'ConsoleTools/options/menu_dialog.php', 'ezcConsoleOption' => 'ConsoleTools/input/option.php', 'ezcConsoleOptionRule' => 'ConsoleTools/structs/option_rule.php', 'ezcConsoleOutput' => 'ConsoleTools/output.php', 'ezcConsoleOutputFormat' => 'ConsoleTools/structs/output_format.php', 'ezcConsoleOutputFormats' => 'ConsoleTools/structs/output_formats.php', 'ezcConsoleOutputOptions' => 'ConsoleTools/options/output.php', 'ezcConsoleProgressMonitor' => 'ConsoleTools/progressmonitor.php', 'ezcConsoleProgressMonitorOptions' => 'ConsoleTools/options/progressmonitor.php', 'ezcConsoleProgressbar' => 'ConsoleTools/progressbar.php', 'ezcConsoleProgressbarOptions' => 'ConsoleTools/options/progressbar.php', 'ezcConsoleQuestionDialog' => 'ConsoleTools/dialog/question_dialog.php', 'ezcConsoleQuestionDialogMappingValidator' => 'ConsoleTools/dialog/validators/question_dialog_mapping.php', 'ezcConsoleQuestionDialogOptions' => 'ConsoleTools/options/question_dialog.php', 'ezcConsoleQuestionDialogRegexValidator' => 'ConsoleTools/dialog/validators/question_dialog_regex.php', 'ezcConsoleQuestionDialogTypeValidator' => 'ConsoleTools/dialog/validators/question_dialog_type.php', 'ezcConsoleStandardInputValidator' => 'ConsoleTools/input/validators/standard.php', 'ezcConsoleStatusbar' => 'ConsoleTools/statusbar.php', 'ezcConsoleStatusbarOptions' => 'ConsoleTools/options/statusbar.php', 'ezcConsoleStringTool' => 'ConsoleTools/tools/string.php', 'ezcConsoleTable' => 'ConsoleTools/table.php', 'ezcConsoleTableCell' => 'ConsoleTools/table/cell.php', 'ezcConsoleTableOptions' => 'ConsoleTools/options/table.php', 'ezcConsoleTableRow' => 'ConsoleTools/table/row.php', ); ?> ConsoleTools-1.7.5/src/dialog/000077500000000000000000000000001470000742400161735ustar00rootroot00000000000000ConsoleTools-1.7.5/src/dialog/menu_dialog.php000066400000000000000000000163261470000742400211770ustar00rootroot00000000000000 null, "output" => null, ); /** * Creates a new menu dialog. * Creates a new question dialog to be displayed to the user. All behaviour is * defined through the $options parameter. The $output parameter is used to * display the dialog in the {@link display()} method. * * @param ezcConsoleOutput $output Output object. * @param ezcConsoleMenuDialogOptions $options The options. * @return void */ public function __construct( ezcConsoleOutput $output, ?ezcConsoleMenuDialogOptions $options = null ) { $this->output = $output; $this->options = $options === null ? new ezcConsoleMenuDialogOptions() : $options; } /** * Returns if the dialog retrieved a valid result. * If a valid result has already been received, this method returns true, * otherwise false. * * @return bool If a valid result was retrieved. */ public function hasValidResult() { return ( $this->result !== null ); } /** * Returns the result retrieved. * If no valid result was retreived, yet, this method throws an * ezcConsoleNoValidDialogResultException. Use {@link hasValidResult()} to * avoid this. * * @return mixed The retreived result. * * @throws ezcDialogNoValidResultException * if this method is called without a valid result being retrieved * by the object. Use {@link hasValidResult()} to avoid this * exception. */ public function getResult() { if ( $this->result === null ) { throw new ezcConsoleNoValidDialogResultException(); } return $this->result; } /** * Displays the dialog and retreives a value from the user. * Displays the dialog and retreives the desired answer from the user. If * the a valid result is retrieved, it can be obtained using {@link * getResult()}. The method {@link hasValidResult()} can be used to check * if a valid result is available. * * @return void * @throws ezcConsoleDialogAbortException * if the user closes STDIN using -D. */ public function display() { $this->reset(); $text = "{$this->options->text}\n"; foreach ( $this->options->validator->getElements() as $key => $entry ) { $text .= sprintf( $this->options->formatString, $key, $entry ); } $text .= "\n{$this->options->selectText}{$this->options->validator->getResultString()} "; $this->output->outputText( $text, $this->options->format ); $result = $this->options->validator->fixup( ezcConsoleDialogViewer::readLine() ); if ( $this->options->validator->validate( $result ) ) { $this->result = $result; } } /** * Reset the dialog. * Resets a possibly received result and all changes made to the dialog * during {@link display()}. After that, the dialog can be re-used. All * option values are kept. * * @return void */ public function reset() { $this->result = null; } /** * Property read access. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * * @param string $propertyName Name of the property. * @return mixed Value of the property or null. * @ignore */ public function __get( $propertyName ) { if ( array_key_exists( $propertyName, $this->properties ) ) { return $this->properties[$propertyName]; } throw new ezcBasePropertyNotFoundException( $propertyName ); } /** * Property write access. * * @param string $propertyName Name of the property. * @param mixed $propertyValue The value for the property. * * @throws ezcBasePropertyPermissionException * If the property you try to access is read-only. * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * @ignore */ public function __set( $propertyName, $propertyValue ) { switch ( $propertyName ) { case "options": if ( ( $propertyValue instanceof ezcConsoleMenuDialogOptions ) === false ) { throw new ezcBaseValueException( $propertyName, ( is_object( $propertyValue ) ? get_class( $propertyValue ) : gettype( $propertyValue ) ), "instance of ezcConsoleMenuDialogOptions" ); } break; case "output": if ( ( $propertyValue instanceof ezcConsoleOutput ) === false ) { throw new ezcBaseValueException( $propertyName, ( is_object( $propertyValue ) ? get_class( $propertyValue ) : gettype( $propertyValue ) ), "instance of ezcConsoleOutput" ); } break; default: throw new ezcBasePropertyNotFoundException( $propertyName ); } $this->properties[$propertyName] = $propertyValue; } /** * Property isset access. * * @param string $propertyName Name of the property to check. * @return bool If the property exists or not. * @ignore */ public function __isset( $propertyName ) { return array_key_exists( $propertyName, $this->properties ); } } ?> ConsoleTools-1.7.5/src/dialog/question_dialog.php000066400000000000000000000213471470000742400221010ustar00rootroot00000000000000 null, "output" => null, ); /** * Creates a new question dialog. * Creates a new menu dialog to be displayed to the user. All behaviour is * defined through the $options parameter. The $output parameter is used to * display the dialog in the {@link display()} method. * * @param ezcConsoleOutput $output Output object. * @param ezcConsoleQuestionDialogOptions $options Options. * @return void */ public function __construct( ezcConsoleOutput $output, ?ezcConsoleQuestionDialogOptions $options = null ) { $this->output = $output; $this->options = $options === null ? new ezcConsoleQuestionDialogOptions() : $options; } /** * Returns if the dialog retrieved a valid result. * If a valid result has already been received, this method returns true, * otherwise false. * * @return bool If a valid result was retrieved. */ public function hasValidResult() { return $this->result !== null; } /** * Returns the result retrieved. * If no valid result was retreived, yet, this method should throw an * ezcConsoleNoValidDialogResultException. * * If no valid result was retreived, yet, this method throws an * ezcConsoleNoValidDialogResultException. Use {@link hasValidResult()} to * avoid this. * * @return mixed The retreived result. * * @throws ezcDialogNoValidResultException * if this method is called without a valid result being retrieved * by the object. Use {@link hasValidResult()} to avoid this * exception. */ public function getResult() { if ( $this->result === null ) { throw new ezcConsoleNoValidDialogResultException(); } return $this->result; } /** * Displays the dialog and retreives a value from the user. * Displays the dialog and retreives the desired answer from the user. If * the a valid result is retrieved, it can be obtained using {@link * getResult()}. The method {@link hasValidResult()} can be used to check * if a valid result is available. * * @return void * @throws ezcConsoleDialogAbortException * if the user closes STDIN using -D. */ public function display() { $this->reset(); $this->output->outputText( $this->options->text . ( $this->options->showResults === true ? " " . $this->options->validator->getResultString() : "" ) . " ", $this->options->format ); $result = $this->options->validator->fixup( ezcConsoleDialogViewer::readLine() ); if ( $this->options->validator->validate( $result ) ) { $this->result = $result; } } /** * Reset the dialog. * Resets a possibly received result and all changes made to the dialog * during {@link display()}. After that, the dialog can be re-used. All * option values are kept. * * @return void */ public function reset() { $this->result = null; } /** * Returns a ready to use yes/no question dialog. * Returns a question dialog, which requests the answers "y" for "yes" or * "n" for "no" from the user. The answer is converted to lower-case. * * * // Would you like to proceed? (y/n) * $dialog = ezcConsoleDialog( $out, "Would you like to proceed?" ); * * // Would you like to proceed? (y/n) [n] * $dialog = ezcConsoleDialog( $out, "Would you like to proceed?", "n" ); * * * @param ezcConsoleOutput $out Output object. * @param string $questionString Question string. * @param string $default "y" or "n", if default value is desired. * @return ezcConsoleQuestionDialog The created dialog. */ public static function YesNoQuestion( ezcConsoleOutput $out, $questionString, $default = null ) { $opts = new ezcConsoleQuestionDialogOptions(); $opts->text = $questionString; $opts->showResults = true; $opts->validator = new ezcConsoleQuestionDialogMappingValidator( array( "y", "n" ), $default, ezcConsoleQuestionDialogCollectionValidator::CONVERT_LOWER, array( 'yes' => 'y', 'no' => 'n', ) ); return new ezcConsoleQuestionDialog( $out, $opts ); } /** * Property read access. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * * @param string $propertyName Name of the property. * @return mixed Value of the property or null. * @ignore */ public function __get( $propertyName ) { if ( array_key_exists( $propertyName, $this->properties ) ) { return $this->properties[$propertyName]; } throw new ezcBasePropertyNotFoundException( $propertyName ); } /** * Property write access. * * @param string $propertyName Name of the property. * @param mixed $propertyValue The value for the property. * * @throws ezcBasePropertyPermissionException * If the property you try to access is read-only. * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * @ignore */ public function __set( $propertyName, $propertyValue ) { switch ( $propertyName ) { case "options": if ( ( $propertyValue instanceof ezcConsoleQuestionDialogOptions ) === false ) { throw new ezcBaseValueException( $propertyName, ( is_object( $propertyValue ) ? get_class( $propertyValue ) : gettype( $propertyValue ) ), "instance of ezcConsoleQuestionDialogOptions" ); } break; case "output": if ( ( $propertyValue instanceof ezcConsoleOutput ) === false ) { throw new ezcBaseValueException( $propertyName, ( is_object( $propertyValue ) ? get_class( $propertyValue ) : gettype( $propertyValue ) ), "instance of ezcConsoleOutput" ); } break; default: throw new ezcBasePropertyNotFoundException( $propertyName ); } $this->properties[$propertyName] = $propertyValue; } /** * Property isset access. * * @param string $propertyName Name of the property to check. * @return bool If the property exists or not. * @ignore */ public function __isset( $propertyName ) { return array_key_exists( $propertyName, $this->properties ); } } ?> ConsoleTools-1.7.5/src/dialog/validators/000077500000000000000000000000001470000742400203435ustar00rootroot00000000000000ConsoleTools-1.7.5/src/dialog/validators/menu_dialog_default.php000066400000000000000000000162051470000742400250470ustar00rootroot00000000000000 array(), "default" => null, "conversion" => self::CONVERT_NONE, ); /** * Creates a new menu default validator. * Creates a validator specified by the given parameters. The $elements * array specifies the possible menu items to select from. The item * identifier (the key of the array) is used to validate the result. The * assigned text is displayed as the menu item text. If no result is * provided and an optionally provided default value is used. The * $conversion parameter can be used to get a conversion applied to the * result before validating it. * * @param array $elements The elements of the menu. * @param mixed $default The default value. * @param int $conversion The conversion to apply. * @return void */ public function __construct( array $elements = array(), $default = null, $conversion = self::CONVERT_NONE ) { $this->elements = $elements; $this->default = $default; $this->conversion = $conversion; } /** * Returns if the given result is valid. * Checks if the given result is a valid key in the $elements property. * * @param mixed $result The received result. * @return bool If the result is valid. */ public function validate( $result ) { return isset( $this->elements[$result] ); } /** * Returns a fixed version of the result, if possible. * Converts the given result according to the conversion defined in the * $conversion property. * * @param mixed $result The received result. * @return mixed The manipulated result. */ public function fixup( $result ) { if ( $result === "" && $this->default !== null ) { return $this->default; } switch ( $this->conversion ) { case self::CONVERT_LOWER: return strtolower( $result ); case self::CONVERT_UPPER: return strtoupper( $result ); case self::CONVERT_NONE: default: return $result; } } /** * Returns a string representing the default value. * For example "[y]" to indicate that "y" is the preselected result and * will be chosen if no result is provided. * * @return string The result string. */ public function getResultString() { return $this->default === null ? "" : "[{$this->default}]"; } /** * Returns an array of the elements to display. * * @return array(string=>string) Elements to display. */ public function getElements() { return $this->elements; } /** * Property read access. * * @param string $propertyName Name of the property. * @return mixed Value of the property or null. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * @ignore */ public function __get( $propertyName ) { if ( isset( $this->$propertyName ) ) { return $this->properties[$propertyName]; } throw new ezcBasePropertyNotFoundException( $propertyName ); } /** * Property write access. * * @param string $propertyName Name of the property. * @param mixed $propertyValue The value for the property. * * @throws ezcBasePropertyNotFoundException * If a the value for the property options is not an instance of * @throws ezcBaseValueException * If a the value for a property is out of range. * @ignore */ public function __set( $propertyName, $propertyValue ) { switch ( $propertyName ) { case "elements": if ( is_array( $propertyValue ) === false ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "array" ); } break; case "default": if ( is_scalar( $propertyValue ) === false && $propertyValue !== null ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "scalar" ); } break; case "conversion": if ( $propertyValue !== self::CONVERT_NONE && $propertyValue !== self::CONVERT_UPPER && $propertyValue !== self::CONVERT_LOWER ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "ezcConsoleMenuDialogDefaultValidator::CONVERT_*" ); } break; default: throw new ezcBasePropertyNotFoundException( $propertyName ); } $this->properties[$propertyName] = $propertyValue; } /** * Property isset access. * * @param string $propertyName Name of the property. * @return bool True is the property is set, otherwise false. * @ignore */ public function __isset( $propertyName ) { return array_key_exists( $propertyName, $this->properties ); } } ?> ConsoleTools-1.7.5/src/dialog/validators/question_dialog_collection.php000066400000000000000000000153401470000742400264600ustar00rootroot00000000000000 array(), "default" => null, "conversion" => self::CONVERT_NONE, ); /** * Creates a new question dialog collection validator. * Creates a new question dialog collection validator, which validates the * result specified by the user against an array of valid results * ($collection). If not value is provided by the user a possibly set * $default value is used instead. The $conversion parameter can optionally * define a conversion to be performed on the result before validating it. * Valid conversions are defined by the CONVERT_* constants in this class. * * @param array $collection The collection of valid results. * @param mixed $default Optional default value. * @param int $conversion CONVERT_* constant. * @return void */ public function __construct( array $collection, $default = null, $conversion = self::CONVERT_NONE ) { $this->collection = $collection; $this->default = $default; $this->conversion = $conversion; } /** * Returns if the given result is valid. * Returns if the result is in the $collection array. * * @param mixed $result The received result. * @return bool If the result is valid. */ public function validate( $result ) { return in_array( $result, $this->collection ); } /** * Returns a fixed version of the result, if possible. * Converts the given result according to the conversion defined in the * $conversion property. * * @param mixed $result The received result. * @return mixed The manipulated result. */ public function fixup( $result ) { if ( $result === "" && $this->default !== null ) { return $this->default; } switch ( $this->conversion ) { case self::CONVERT_UPPER: return strtoupper( $result ); case self::CONVERT_LOWER: return strtolower( $result ); default: return $result; } } /** * Returns a string representing valid results. * Returns the string that will be displayed with the question to * indicate valid results to the user and a possibly set default, if * available. * * @return string */ public function getResultString() { return "(" . implode( "/", $this->collection ) . ")" . ( $this->default !== null ? " [{$this->default}]" : "" ); } /** * Property read access. * * @param string $propertyName Name of the property. * @return mixed Value of the property or null. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * @ignore */ public function __get( $propertyName ) { if ( isset( $this->$propertyName ) ) { return $this->properties[$propertyName]; } throw new ezcBasePropertyNotFoundException( $propertyName ); } /** * Property write access. * * @param string $propertyName Name of the property. * @param mixed $propertyValue The value for the property. * * @throws ezcBasePropertyNotFoundException * If a the value for the property options is not an instance of * @throws ezcBaseValueException * If a the value for a property is out of range. * @ignore */ public function __set( $propertyName, $propertyValue ) { switch ( $propertyName ) { case "collection": if ( is_array( $propertyValue ) === false ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "array" ); } break; case "default": if ( is_scalar( $propertyValue ) === false && $propertyValue !== null ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "scalar" ); } break; case "conversion": if ( $propertyValue !== self::CONVERT_NONE && $propertyValue !== self::CONVERT_UPPER && $propertyValue !== self::CONVERT_LOWER ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "ezcConsoleQuestionDialogCollectionValidator::CONVERT_*" ); } break; default: throw new ezcBasePropertyNotFoundException( $propertyName ); } $this->properties[$propertyName] = $propertyValue; } /** * Property isset access. * * @param string $propertyName Name of the property. * @return bool True is the property is set, otherwise false. * @ignore */ public function __isset( $propertyName ) { return array_key_exists( $propertyName, $this->properties ); } } ?> ConsoleTools-1.7.5/src/dialog/validators/question_dialog_mapping.php000066400000000000000000000134241470000742400257610ustar00rootroot00000000000000 * array( * 'yes' => 'y', * 'no' => 'n', * '1' => 'y', * '0' => 'n' * ) * * While the corresponding collection of valid answers would look like * this: * * array( * 'y', 'n' * ) * * If the answer 'yes' is received by the validator, it is mapped to 'y', * therefore considered valid and 'y' is returned as the received value. * * @package ConsoleTools * @version //autogen// * * @property array(string) $collection * The collection of valid answers. * @property mixed $default * Default value. * @property int $conversion * ezcConsoleDialogValidator::CONVERT_NONE (default) or * ezcConsoleDialogValidator::CONVERT_LOWER or * ezcConsoleDialogValidator::CONVERT_UPPER. * @property array(string=>string) $map * Mapping of answers to valid answers (e.g. array('yes' => 'y') to * map 'yes' to 'y' while 'y' must be in $collection). */ class ezcConsoleQuestionDialogMappingValidator extends ezcConsoleQuestionDialogCollectionValidator { /** * Creates a new question dialog mapping validator. * Creates a new question dialog mapping validator, which validates the * result specified by the user against an array of valid results * ($collection). If not value is provided by the user a possibly set * $default value is used instead. The $conversion parameter can optionally * define a conversion to be performed on the result before validating it. * Valid conversions are defined by the CONVERT_* constants in this class. * * While this functionality is already provided by {@link * ezcConsoleQuestionDialogCollectionValidator}, the additional $map * paramater allows the sepcification of a map of result values. These * mapping is then checked for matches, before a received answer is * validated against the collection. * * @param array(string) $collection The collection of valid results. * @param mixed $default Optional default value. * @param int $conversion CONVERT_* constant. * @param array(string=>string) $map * @return void */ public function __construct( array $collection, $default = null, $conversion = self::CONVERT_NONE, array $map = array() ) { // Initialize additional property $this->properties['map'] = $map; parent::__construct( $collection, $default, $conversion ); } /** * Returns a fixed version of the result, if possible. * Converts the given result according to the conversion defined in the * $conversion property. * * @param mixed $result The received result. * @return mixed The manipulated result. */ public function fixup( $result ) { if ( $result === "" && $this->default !== null ) { return $this->default; } switch ( $this->conversion ) { case self::CONVERT_UPPER: $result = strtoupper( $result ); break; case self::CONVERT_LOWER: $result = strtolower( $result ); break; } return ( isset( $this->map[$result] ) ? $this->map[$result] : $result ); } /** * Property write access. * * @param string $propertyName Name of the property. * @param mixed $propertyValue The value for the property. * * @throws ezcBasePropertyNotFoundException * If a the value for the property options is not an instance of * @throws ezcBaseValueException * If a the value for a property is out of range. * @ignore */ public function __set( $propertyName, $propertyValue ) { switch ( $propertyName ) { case "map": if ( is_array( $propertyValue ) === false ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "array" ); } break; default: return parent::__set( $propertyName, $propertyValue ); } $this->properties[$propertyName] = $propertyValue; } } ?> ConsoleTools-1.7.5/src/dialog/validators/question_dialog_regex.php000066400000000000000000000133121470000742400254340ustar00rootroot00000000000000mixed) */ protected $properties = array( "pattern" => null, "default" => null, ); /** * Create a new question dialog regex validator. * Create a new question dialog regex validator, which validates the result * specified against a given regular expression. The delimiters and * eventual modifiers must be included in the pattern. If not value is * provided by the user a possibly set $default value is used instead. * * * @param string $pattern Pattern to validate against. * @param mixed $default Default value. * @return void */ public function __construct( $pattern, $default = null ) { $this->pattern = $pattern; $this->default = $default; } /** * Returns if the given result is valid. * Returns if the result matches the regular expression. * * @param mixed $result The received result. * @return bool If the result is valid. */ public function validate( $result ) { if ( $result === "" ) { return $this->default !== null; } return preg_match( $this->pattern, $result ) > 0; } /** * Returns a fixed version of the result, if possible. * If no result was provided by the user, the default value will be * returned, if set. * * @param mixed $result The received result. * @return mixed The manipulated result. */ public function fixup( $result ) { if ( $result === "" && $this->default !== null ) { return $this->default; } return $result; } /** * Returns a string representing valid results. * Returns the string that will be displayed with the question to * indicate valid results to the user and a possibly set default, if * available. * * @return string */ public function getResultString() { return "(match {$this->pattern})" . ( $this->default !== null ? " [{$this->default}]" : "" ); } /** * Property read access. * * @param string $propertyName Name of the property. * @return mixed Value of the property or null. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * @ignore */ public function __get( $propertyName ) { if ( $this->__isset( $propertyName ) ) { return $this->properties[$propertyName]; } throw new ezcBasePropertyNotFoundException( $propertyName ); } /** * Property write access. * * @param string $propertyName Name of the property. * @param mixed $propertyValue The value for the property. * * @throws ezcBasePropertyNotFoundException * If a the value for the property options is not an instance of * @throws ezcBaseValueException * If a the value for a property is out of range. * @ignore */ public function __set( $propertyName, $propertyValue ) { switch ( $propertyName ) { case "pattern": if ( is_string( $propertyValue ) === false || strlen( $propertyValue ) < 2 ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "string, length > 1" ); } break; case "default": if ( is_scalar( $propertyValue ) === false && $propertyValue !== null ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "scalar" ); } break; default: throw new ezcBasePropertyNotFoundException( $propertyName ); } $this->properties[$propertyName] = $propertyValue; } /** * Property isset access. * * @param string $propertyName Name of the property. * @return bool True is the property is set, otherwise false. * @ignore */ public function __isset( $propertyName ) { return array_key_exists( $propertyName, $this->properties ); } } ?> ConsoleTools-1.7.5/src/dialog/validators/question_dialog_type.php000066400000000000000000000171031470000742400253050ustar00rootroot00000000000000 self::TYPE_STRING, "default" => null, ); /** * Creates a new question dialog type validator. * Creates a new question dialog type validator, which validates the result * specified to be of a certaon type. The $type must be one of the TYPE_* * constants in this class. If no value is provided by the user a possibly * set $default value is used instead. * * @param int $type One of ezcConsoleQuestionDialogTypeValidator::TYPE_*. * @param mixed $default Default value according to $type. * @return void */ public function __construct( $type = self::TYPE_STRING, $default = null ) { $this->type = $type; $this->default = $default; } /** * Returns if the given result is valid. * Returns if the result is of the given type. * * @param mixed $result The result to check. * @return bool True if the result is valid. Otherwise false. */ public function validate( $result ) { if ( $result === "" ) { return $this->default !== null; } switch ( $this->type ) { case self::TYPE_INT: return is_int( $result ); case self::TYPE_FLOAT: return is_float( $result ); case self::TYPE_BOOL: return is_bool( $result ); case self::TYPE_STRING: default: return is_string( $result ); } } /** * Returns a fixed version of the result, if possible. * Returns the value casted into the correct type or the default value, if * it exists and the result is empty. * * @param mixed $result The result received. * @return mixed The manipulated result. */ public function fixup( $result ) { if ( $result === "" && $this->default !== null ) { return $this->default; } switch ( $this->type ) { case self::TYPE_INT: return ( preg_match( "/^[0-9\-]+$/", $result ) !== 0 ) ? (int) $result : $result; case self::TYPE_FLOAT: return ( preg_match( "/^[0-9.E\-]+$/i", $result ) !== 0 ) ? (float) $result : $result; case self::TYPE_BOOL: switch ( $result ) { case "1": case "true": return true; case "0": case "false": return false; } case self::TYPE_STRING: default: return $result; } } /** * Returns a string representing valid results. * Returns the string that can will be displayed with the question to * indicate valid results to the user and a possibly set default, if * available. * * @return string */ public function getResultString() { $res = "(<%s>)" . ( $this->default !== null ? " [{$this->default}]" : "" ); switch ( $this->type ) { case self::TYPE_INT: return sprintf( $res, "int" ); case self::TYPE_FLOAT: return sprintf( $res, "float" ); case self::TYPE_BOOL: return sprintf( $res, "bool" ); case self::TYPE_STRING: default: return sprintf( $res, "string" ); } } /** * Property read access. * * @param string $propertyName Name of the property. * @return mixed Value of the property or null. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * @ignore */ public function __get( $propertyName ) { if ( isset( $this->$propertyName ) ) { return $this->properties[$propertyName]; } throw new ezcBasePropertyNotFoundException( $propertyName ); } /** * Property write access. * * @param string $propertyName Name of the property. * @param mixed $propertyValue The value for the property. * * @throws ezcBasePropertyNotFoundException * If a the value for the property options is not an instance of * @throws ezcBaseValueException * If a the value for a property is out of range. * @ignore */ public function __set( $propertyName, $propertyValue ) { switch ( $propertyName ) { case "type": if ( $propertyValue !== self::TYPE_STRING && $propertyValue !== self::TYPE_INT && $propertyValue !== self::TYPE_FLOAT && $propertyValue !== self::TYPE_BOOL ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "ezcConsoleQuestionDialogTypeValidator::TYPE_*" ); } break; case "default": if ( is_scalar( $propertyValue ) === false && $propertyValue !== null ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "scalar" ); } break; default: throw new ezcBasePropertyNotFoundException( $propertyName ); } $this->properties[$propertyName] = $propertyValue; } /** * Property isset access. * * @param string $propertyName Name of the property. * @return bool True is the property is set, otherwise false. * @ignore */ public function __isset( $propertyName ) { return array_key_exists( $propertyName, $this->properties ); } } ?> ConsoleTools-1.7.5/src/dialog_viewer.php000066400000000000000000000050421470000742400202660ustar00rootroot00000000000000 * // Instatiate dialog in $dialog ... * ezcConsoleDialogViewer::displayDialog( $dialog ); * * * For implementing a custom dialog, the method {@link readLine()} method can be * used to read a line of input from the user. * * @package ConsoleTools * @version //autogen// */ class ezcConsoleDialogViewer { /** * Displays a dialog and returns a valid result from it. * This methods displays a dialog in a loop, until it received a valid * result from it and returns this result. * * @param ezcConsoleDialog $dialog The dialog to display. * @return mixed The result from this dialog. */ public static function displayDialog( ezcConsoleDialog $dialog ) { do { $dialog->display(); } while ( $dialog->hasValidResult() === false ); return $dialog->getResult(); } /** * Returns a line from STDIN. * The returned line is fully trimmed. * * @return string * @throws ezcConsoleDialogAbortException * if the user closes STDIN using -D. */ public static function readLine() { $res = trim( fgets( STDIN ) ); if ( feof( STDIN ) ) { throw new ezcConsoleDialogAbortException(); } return $res; } } ?> ConsoleTools-1.7.5/src/exceptions/000077500000000000000000000000001470000742400171155ustar00rootroot00000000000000ConsoleTools-1.7.5/src/exceptions/argument.php000066400000000000000000000026121470000742400214510ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/argument_already_registered.php000066400000000000000000000045051470000742400253720ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/argument_mandatory_violation.php000066400000000000000000000032051470000742400256120ustar00rootroot00000000000000name}' is mandatory but was not submitted." ); } } ?> ConsoleTools-1.7.5/src/exceptions/argument_too_many.php000066400000000000000000000032661470000742400233640ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/argument_type_violation.php000066400000000000000000000035631470000742400246040ustar00rootroot00000000000000type ) { case ezcConsoleInput::TYPE_INT: $typeName = 'int'; break; } parent::__construct( "The argument '{$arg->name}' expects a value of type '{$typeName}', but received the value '{$value}'." ); } } ?> ConsoleTools-1.7.5/src/exceptions/dialog_abort.php000066400000000000000000000026671470000742400222670ustar00rootroot00000000000000-D to a dialog instead of a valid answer. * * @package ConsoleTools * @version //autogen// */ class ezcConsoleDialogAbortException extends ezcConsoleException { /** * Creates a new exception object. * * @param string $name Name of the already existing option. * @return void */ public function __construct() { parent::__construct( "User send EOF." ); } } ?> ConsoleTools-1.7.5/src/exceptions/exception.php000066400000000000000000000022541470000742400216270ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/invalid_option_name.php000066400000000000000000000030321470000742400236420ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/invalid_output_target.php000066400000000000000000000027571470000742400242550ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/no_position_stored.php000066400000000000000000000026541470000742400235550ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/no_valid_dialog_result.php000066400000000000000000000027041470000742400243410ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/option.php000066400000000000000000000026011470000742400211350ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/option_already_registered.php000066400000000000000000000027511470000742400250610ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/option_arguments_violation.php000066400000000000000000000032371470000742400253140ustar00rootroot00000000000000long}' excludes the usage of arguments, but arguments have been submitted." ); } } ?> ConsoleTools-1.7.5/src/exceptions/option_dependency_violation.php000066400000000000000000000040211470000742400254150ustar00rootroot00000000000000long}' depends on the option '{$dependantOption->long}' "; if ( $valueRange !== null ) { $message .= "to have a value in '{$valueRange}' "; } $message .= "but this one was not submitted."; parent::__construct( $message ); } } ?> ConsoleTools-1.7.5/src/exceptions/option_exclusion_violation.php000066400000000000000000000040021470000742400253070ustar00rootroot00000000000000long}' excludes the option '{$excludedOption->long}'"; if ( $valueRange !== null ) { $message .= " to have a value in '{$valueRange}'"; } $message .= " but this one was submitted."; parent::__construct( $message ); } } ?> ConsoleTools-1.7.5/src/exceptions/option_mandatory_violation.php000066400000000000000000000031501470000742400252770ustar00rootroot00000000000000long}' is mandatory but was not submitted." ); } } ?> ConsoleTools-1.7.5/src/exceptions/option_missing_value.php000066400000000000000000000032061470000742400240640ustar00rootroot00000000000000long}' expects a value, but none was submitted." ); } } ?> ConsoleTools-1.7.5/src/exceptions/option_no_alias.php000066400000000000000000000030131470000742400230000ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/option_not_exists.php000066400000000000000000000030441470000742400234160ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/option_string_not_wellformed.php000066400000000000000000000030141470000742400256220ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/exceptions/option_too_many_values.php000066400000000000000000000031671470000742400244310ustar00rootroot00000000000000long}' expects a single value, but multiple were submitted." ); } } ?> ConsoleTools-1.7.5/src/exceptions/option_type_violation.php000066400000000000000000000040001470000742400242550ustar00rootroot00000000000000type ) { case ezcConsoleInput::TYPE_NONE: $typeName = 'none'; break; case ezcConsoleInput::TYPE_INT: $typeName = 'int'; break; } parent::__construct( "The option '{$option->long}' expects a value of type '{$typeName}', but received the value '{$value}'." ); } } ?> ConsoleTools-1.7.5/src/input.php000066400000000000000000001316501470000742400166120ustar00rootroot00000000000000 * $optionHandler = new ezcConsoleInput(); * * // Register simple parameter -h/--help * $optionHandler->registerOption( new ezcConsoleOption( 'h', 'help' ) ); * * // Register complex parameter -f/--file * $file = new ezcConsoleOption( * 'f', * 'file', * ezcConsoleInput::TYPE_STRING, * null, * false, * 'Process a file.', * 'Processes a single file.' * ); * $optionHandler->registerOption( $file ); * * // Manipulate parameter -f/--file after registration * $file->multiple = true; * * // Register another complex parameter that depends on -f and excludes -h * $dir = new ezcConsoleOption( * 'd', * 'dir', * ezcConsoleInput::TYPE_STRING, * null, * true, * 'Process a directory.', * 'Processes a complete directory.', * array( new ezcConsoleOptionRule( $optionHandler->getOption( 'f' ) ) ), * array( new ezcConsoleOptionRule( $optionHandler->getOption( 'h' ) ) ) * ); * $optionHandler->registerOption( $dir ); * * // Register an alias for this parameter * $optionHandler->registerAlias( 'e', 'extended-dir', $dir ); * * // Process registered parameters and handle errors * try * { * $optionHandler->process( array( 'example_input.php', '-h' ) ); * } * catch ( ezcConsoleOptionException $e ) * { * echo $e->getMessage(); * exit( 1 ); * } * * // Process a single parameter * $file = $optionHandler->getOption( 'f' ); * if ( $file->value === false ) * { * echo "Parameter -{$file->short}/--{$file->long} was not submitted.\n"; * } * elseif ( $file->value === true ) * { * echo "Parameter -{$file->short}/--{$file->long} was submitted without value.\n"; * } * else * { * echo "Parameter -{$file->short}/--{$file->long} was submitted with value '".var_export($file->value, true)."'.\n"; * } * * // Process all parameters at once: * foreach ( $optionHandler->getOptionValues() as $paramShort => $val ) * { * switch ( true ) * { * case $val === false: * echo "Parameter $paramShort was not submitted.\n"; * break; * case $val === true: * echo "Parameter $paramShort was submitted without a value.\n"; * break; * case is_array( $val ): * echo "Parameter $paramShort was submitted multiple times with value: '".implode(', ', $val)."'.\n"; * break; * default: * echo "Parameter $paramShort was submitted with value: '$val'.\n"; * break; * } * } * * * @package ConsoleTools * @version //autogen// * @mainclass * * @property ezcConsoleArguments $argumentDefinition Optional argument definition. */ class ezcConsoleInput { /** * Option does not carry a value. */ const TYPE_NONE = 1; /** * Option takes an integer value. */ const TYPE_INT = 2; /** * Option takes a string value. */ const TYPE_STRING = 3; /** * Array of option definitions, indexed by number. * * This array stores the ezcConsoleOption objects representing * the options. * * For lookup of an option after its short or long values the attributes * {@link ezcConsoleInput::$optionShort} * {@link ezcConsoleInput::$optionLong} * are used. * * @var array(array) */ private $options = array(); /** * Short option names. * * Each references a key in {@link ezcConsoleInput::$options}. * * @var array(string=>int) */ private $optionShort = array(); /** * Long option names. * * Each references a key in {@link ezcConsoleInput::$options}. * * @var array(string=>int) */ private $optionLong = array(); /** * Arguments, if submitted, are stored here. * * @var array(string) */ private $arguments = array(); /** * Wether the process() method has already been called. * * @var bool */ private $processed = false; /** * Indicates if an option was submitted, that has the isHelpOption flag set. * * @var bool */ private $helpOptionSet = false; /** * Tool object for multi-byte encoding safe string operations. * * @var ezcConsoleStringTool */ private $stringTool; /** * Input validator. * * @var ezcConsoleInputValidator */ private $validator; /** * Help generator. * * @var ezcConsoleInputHelpGenerator */ private $helpGenerator; /** * Collection of properties. * * @var array(string=>mixed) */ protected $properties = array(); /** * Creates an input handler. */ public function __construct() { $this->argumentDefinition = null; $this->stringTool = new ezcConsoleStringTool(); // @TODO Verify interface and make plugable $this->validator = new ezcConsoleStandardInputValidator(); $this->helpGenerator = new ezcConsoleInputStandardHelpGenerator( $this ); } /** * Registers the new option $option. * * This method adds the new option $option to your option collection. If * already an option with the assigned short or long value exists, an * exception will be thrown. * * @see ezcConsoleInput::unregisterOption() * * @param ezcConsoleOption $option * * @return ezcConsoleOption The recently registered option. */ public function registerOption( ezcConsoleOption $option ) { foreach ( $this->optionShort as $short => $ref ) { if ( $short === $option->short ) { throw new ezcConsoleOptionAlreadyRegisteredException( $short ); } } foreach ( $this->optionLong as $long => $ref ) { if ( $long === $option->long ) { throw new ezcConsoleOptionAlreadyRegisteredException( $long ); } } $this->options[] = $option; $this->optionLong[$option->long] = $option; if ( $option->short !== "" ) { $this->optionShort[$option->short] = $option; } return $option; } /** * Registers an alias for an option. * * Registers a new alias for an existing option. Aliases can * be used as if they were a normal option. * * The alias is registered with the short option name $short and the * long option name $long. The alias references to the existing * option $option. * * @see ezcConsoleInput::unregisterAlias() * * @param string $short * @param string $long * @param ezcConsoleOption $option * * * @throws ezcConsoleOptionNotExistsException * If the referenced option is not registered. * @throws ezcConsoleOptionAlreadyRegisteredException * If another option/alias has taken the provided short or long name. * @return void */ public function registerAlias( $short, $long, ezcConsoleOption $option ) { if ( !isset( $this->optionShort[$option->short] ) || !isset( $this->optionLong[$option->long] ) ) { throw new ezcConsoleOptionNotExistsException( $option->long ); } if ( isset( $this->optionShort[$short] ) || isset( $this->optionLong[$long] ) ) { throw new ezcConsoleOptionAlreadyRegisteredException( isset( $this->optionShort[$short] ) ? "-$short" : "--$long" ); } $this->optionShort[$short] = $option; $this->optionLong[$long] = $option; } /** * Registers options according to a string specification. * * Accepts a string to define parameters and registers all parameters as * options accordingly. String definition, specified in $optionDef, looks * like this: * * * [s:|size:][u:|user:][a:|all:] * * * This string registers 3 parameters: * -s / --size * -u / --user * -a / --all * * @param string $optionDef * @return void * * @throws ezcConsoleOptionStringNotWellformedException * If provided string does not have the correct format. */ public function registerOptionString( $optionDef ) { $regex = '\[([a-z0-9-]+)([:?*+])?([^|]*)\|([a-z0-9-]+)([:?*+])?\]'; // Check string for wellformedness if ( preg_match( "/^($regex)+$/", $optionDef ) == 0 ) { throw new ezcConsoleOptionStringNotWellformedException( "Option definition not wellformed: \"$optionDef\"" ); } if ( preg_match_all( "/$regex/", $optionDef, $matches ) ) { foreach ( $matches[1] as $id => $short ) { $option = null; $option = new ezcConsoleOption( $short, $matches[4][$id] ); if ( !empty( $matches[2][$id] ) || !empty( $matches[5][$id] ) ) { switch ( !empty( $matches[2][$id] ) ? $matches[2][$id] : $matches[5][$id] ) { case '*': // Allows 0 or more occurances $option->multiple = true; break; case '+': // Allows 1 or more occurances $option->multiple = true; $option->type = self::TYPE_STRING; break; case '?': $option->type = self::TYPE_STRING; $option->default = ''; break; default: break; } } if ( !empty( $matches[3][$id] ) ) { $option->default = $matches[3][$id]; } $this->registerOption( $option ); } } } /** * Removes an option. * * This function removes an option. All dependencies to that * specific option are removed completely from every other registered * option. * * @see ezcConsoleInput::registerOption() * * @param ezcConsoleOption $option The option object to unregister. * * @throws ezcConsoleOptionNotExistsException * If requesting a not registered option. * @return void */ public function unregisterOption( ezcConsoleOption $option ) { $found = false; foreach ( $this->options as $id => $existParam ) { if ( $existParam === $option ) { $found = true; unset( $this->options[$id] ); continue; } $existParam->removeAllExclusions( $option ); $existParam->removeAllDependencies( $option ); } if ( $found === false ) { throw new ezcConsoleOptionNotExistsException( $option->long ); } foreach ( $this->optionLong as $name => $existParam ) { if ( $existParam === $option ) { unset( $this->optionLong[$name] ); } } foreach ( $this->optionShort as $name => $existParam ) { if ( $existParam === $option ) { unset( $this->optionShort[$name] ); } } } /** * Removes an alias to an option. * * This function removes an alias with the short name $short and long * name $long. * * @see ezcConsoleInput::registerAlias() * * @throws ezcConsoleOptionNoAliasException * If the requested short/long name belongs to a real parameter instead. * * @param string $short * @param string $long * @return void * * @todo Check if $short and $long refer to the same option! */ public function unregisterAlias( $short, $long ) { foreach ( $this->options as $id => $option ) { if ( $option->short === $short ) { throw new ezcConsoleOptionNoAliasException( $short ); } if ( $option->long === $long ) { throw new ezcConsoleOptionNoAliasException( $long ); } } if ( isset( $this->optionShort[$short] ) ) { unset( $this->optionShort[$short] ); } if ( isset( $this->optionLong[$long] ) ) { unset( $this->optionLong[$long] ); } } /** * Returns the definition object for the option with the name $name. * * This method receives the long or short name of an option and * returns the ezcConsoleOption object. * * @param string $name Short or long name of the option (without - or --). * @return ezcConsoleOption * * @throws ezcConsoleOptionNotExistsException * If requesting a not registered parameter. */ public function getOption( $name ) { $name = $name; if ( isset( $this->optionShort[$name] ) ) { return $this->optionShort[$name]; } if ( isset( $this->optionLong[$name] ) ) { return $this->optionLong[$name]; } throw new ezcConsoleOptionNotExistsException( $name ); } /** * Process the input parameters. * * Actually process the input options and arguments according to the actual * settings. * * Per default this method uses $argc and $argv for processing. You can * override this setting with your own input, if necessary, using the * parameters of this method. (Attention, first argument is always the pro * gram name itself!) * * All exceptions thrown by this method contain an additional attribute "option" * which specifies the parameter on which the error occurred. * * @param array(string) $args The arguments * @return void * * @throws ezcConsoleOptionNotExistsException * If an option that was submitted does not exist. * @throws ezcConsoleOptionDependencyViolationException * If a dependency rule was violated. * @throws ezcConsoleOptionExclusionViolationException * If an exclusion rule was violated. * @throws ezcConsoleOptionTypeViolationException * If the type of a submitted value violates the options type rule. * @throws ezcConsoleOptionArgumentsViolationException * If arguments are passed although a parameter disallowed them. * * @see ezcConsoleOptionException */ public function process( ?array $args = null ) { if ( $this->processed ) { $this->reset(); } $this->processed = true; if ( !isset( $args ) ) { $args = isset( $_SERVER['argv'] ) ? $_SERVER['argv'] : array(); } $nextIndex = $this->processOptions( $args ); if ( $this->helpOptionSet() ) { // No need to parse arguments return; } $this->processArguments( $args, $nextIndex ); $this->checkRules(); $this->setOptionDefaults(); } /** * Sets defaults for options that have not been submitted. * * Checks all options if they have been submited. If not and a default * values is present, this is set as the options value. */ private function setOptionDefaults() { foreach ( $this->options as $option ) { if ( $option->value === false || $option->value === array() ) { // Default value to set? if ( $option->default !== null ) { $option->value = $option->default; } } } } /** * Reads the submitted options from $args array. * * Returns the next index to check for arguments. * * @param array(string) $args * @returns int * * @throws ezcConsoleOptionNotExistsException * if a submitted option does not exist. * @throws ezcConsoleOptionTooManyValuesException * if an option that expects only a single value was submitted * with multiple values. * @throws ezcConsoleOptionTypeViolationException * if an option was submitted with a value of the wrong type. * @throws ezcConsoleOptionMissingValueException * if an option thats expects a value was submitted without. */ private function processOptions( array $args ) { $numArgs = count( $args ); $i = 1; while ( $i < $numArgs ) { if ( $args[$i] === '--' ) { break; } // Equalize parameter handling (long params with =) if ( iconv_substr( $args[$i], 0, 2, 'UTF-8' ) == '--' ) { $this->preprocessLongOption( $args, $i ); // Update number of args, changed by preprocessLongOption() $numArgs = count( $args ); } // Check for parameter if ( iconv_substr( $args[$i], 0, 1, 'UTF-8' ) === '-' ) { if ( !$this->hasOption( preg_replace( '/^-*/', '', $args[$i] ) ) ) { throw new ezcConsoleOptionNotExistsException( $args[$i] ); } $this->processOption( $args, $i ); } // Must be the arguments else { break; } } // Move pointer over argument sign isset( $args[$i] ) && $args[$i] == '--' ? ++$i : $i; return $i; } /** * Resets all option and argument values. * * This method is called automatically by {@link process()}, if this method * is called twice or more, and may also be used to manually reset the * values of all registered {@ezcConsoleOption} and {@link * ezcConsoleArgument} objects. */ public function reset() { foreach ( $this->options as $option ) { $option->value = false; } if ( $this->argumentDefinition !== null ) { foreach ( $this->argumentDefinition as $argument ) { $argument->value = null; } } $this->arguments = array(); } /** * Returns true if an option with the given name exists, otherwise false. * * Checks if an option with the given name is registered. * * @param string $name Short or long name of the option. * @return bool True if option exists, otherwise false. */ public function hasOption( $name ) { try { $param = $this->getOption( $name ); } catch ( ezcConsoleOptionNotExistsException $e ) { return false; } return true; } /** * Returns an array of all registered options. * * Returns an array of all registered options in the following format: * * array( * 0 => ezcConsoleOption, * 1 => ezcConsoleOption, * 2 => ezcConsoleOption, * ... * ); * * * @return array(string=>ezcConsoleOption) Registered options. */ public function getOptions() { return $this->options; } /** * Returns the values of all submitted options. * * Returns an array of all values submitted to the options. The array is * indexed by the parameters short name (excluding the '-' prefix). The array * does not contain any parameter, which value is 'false' (meaning: the * parameter was not submitted). * * @param bool $longnames Wheather to use longnames for indexing. * @return array(string=>mixed) */ public function getOptionValues( $longnames = false ) { $res = array(); foreach ( $this->options as $param ) { if ( $param->value !== false ) { $res[( $longnames === true ) ? $param->long : $param->short] = $param->value; } } return $res; } /** * Returns arguments provided to the program. * * This method returns all arguments provided to a program in an * int indexed array. Arguments are sorted in the way * they are submitted to the program. You can disable arguments * through the 'arguments' flag of a parameter, if you want * to disallow arguments. * * Arguments are either the last part of the program call (if the * last parameter is not a 'multiple' one) or divided via the '--' * method which is commonly used on Unix (if the last parameter * accepts multiple values this is required). * * @return array(string) Arguments. */ public function getArguments() { return $this->arguments; } /** * Get help information for your options. * * This method returns an array of help information for your options, * indexed by int. Each help info has 2 fields: * * 0 => The options names (" / ") * 1 => The help text (depending on the $long parameter) * * The $long options determines if you want to get the short or long help * texts. The array returned can be used by {@link ezcConsoleTable}. * * If using the second options, you can filter the options shown in the * help output (e.g. to show short help for related options). Provide * as simple number indexed array of short and/or long values to set a filter. * * The $paramGrouping option can be used to group options in the help * output. The structure of this array parameter is as follows: * * * array( * 'First section' => array( * 'input', * 'output' * 'overwrite', * ), * 'Second section' => array( * 'v', * 'h', * ), * ) * * * As can be seen, short option names are possible as well as long ones. * The key of the first array level is the name of the section, which is * assigned to an array of options to group under this section. The $params * parameter still influences if an option is displayed at all. * * @param bool $long * @param array(string) $params * @param array(string=>array(string)) $paramGrouping * @return array(array(string)) Table structure as explained. * * @apichange In future versions, the default values of $params will change * to null instead of an empty array. Giving an empty array for * these will then be taken literally. */ public function getHelp( $long = false, array $params = array(), ?array $paramGrouping = null ) { // New handling $params = ( $params === array() || $params === null ? null : $params ); $help = array(); if ( $paramGrouping === null ) { // Original handling $help = $this->getOptionHelpWithoutGrouping( $long, $params ); } else { $help = $this->getOptionHelpWithGrouping( $long, $params, $paramGrouping ); } if ( $this->argumentDefinition !== null ) { $help[] = array( "Arguments:", '' ); $argumentsHelp = $this->helpGenerator->generateArgumentHelp( $long ); if ( $argumentsHelp === array() ) { $help[] = array( '', "No arguments available." ); } else { $help = array_merge( $help, $argumentsHelp ); } } return $help; } /** * Creates the option help array in the original, ungrouped way. * * Creates the original help array generated by {@link getHelp()}. The * $long and $params options are the same as they are for this method. * * @param bool $long * @param array $params * @return array */ private function getOptionHelpWithoutGrouping( $long, $params ) { return $this->helpGenerator->generateUngroupedOptionHelp( $long, $params ); } /** * Generates options helo array with ordering and grouping. * * @param mixed $long * @param mixed $params * @param mixed $paramGrouping * @return array() */ private function getOptionHelpWithGrouping( $long, $params, $paramGrouping ) { $rawHelp = $this->helpGenerator->generateGroupedOptionHelp( $paramGrouping, $long, $params ); $help = array(); $first = true; foreach ( $rawHelp as $category => $optionsHelp ) { if ( !$first ) { $help[] = array( '', '' ); } else { $first = false; } $help[] = array( $category, '' ); $help = array_merge( $help, $optionsHelp ); } return $help; } /** * Get help information for your options as a table. * * This method provides the information returned by * {@link ezcConsoleInput::getHelp()} in a table. * * The $paramGrouping option can be used to group options in the help * output. The structure of this array parameter is as follows: * * * array( * 'First section' => array( * 'input', * 'output' * 'overwrite', * ), * 'Second section' => array( * 'v', * 'h', * ), * ) * * * As can be seen, short option names are possible as well as long ones. * The key of the first array level is the name of the section, which is * assigned to an array of options to group under this section. The $params * parameter still influences if an option as displayed at all. * * @param ezcConsoleTable $table The table object to fill. * @param bool $long Set this to true for getting the * long help version. * @param array(string) $params Set of option names to generate help * for, default is all. * @param array(string=>array(string)) $paramGrouping * @return ezcConsoleTable The filled table. */ public function getHelpTable( ezcConsoleTable $table, $long = false, array $params = array(), $paramGrouping = null ) { $help = $this->getHelp( $long, $params, $paramGrouping ); $i = 0; foreach ( $help as $row ) { $table[$i][0]->content = $row[0]; $table[$i++][1]->content = $row[1]; } return $table; } /** * Returns a standard help output for your program. * * This method generates a help text as it's commonly known from Unix * command line programs. The output will contain the synopsis, your * provided program description and the selected parameter help * as also provided by {@link ezcConsoleInput::getHelp()}. The returned * string can directly be printed to the console. * * The $paramGrouping option can be used to group options in the help * output. The structure of this array parameter is as follows: * * * array( * 'First section' => array( * 'input', * 'output' * 'overwrite', * ), * 'Second section' => array( * 'v', * 'h', * ), * ) * * * As can be seen, short option names are possible as well as long ones. * The key of the first array level is the name of the section, which is * assigned to an array of options to group under this section. The $params * parameter still influences if an option as displayed at all. * * @param string $programDesc The description of your program. * @param int $width The width to adjust the output text to. * @param bool $long Set this to true for getting the long * help version. * @param array(string) $params Set of option names to generate help * for, default is all. * @param array(string=>array(string)) $paramGrouping * @return string The generated help text. */ public function getHelpText( $programDesc, $width = 80, $long = false, ?array $params = null, $paramGrouping = null ) { $help = $this->getHelp( $long, ( $params == null ? array() : $params ), $paramGrouping ); // Determine max length of first column text. $maxLength = 0; foreach ( $help as $row ) { $maxLength = max( $maxLength, iconv_strlen( $row[0], 'UTF-8' ) ); } // Width of left column $leftColWidth = $maxLength + 2; // Width of righ column $rightColWidth = $width - $leftColWidth; $res = 'Usage: ' . $this->getSynopsis( $params ) . PHP_EOL; $res .= $this->stringTool->wordwrap( $programDesc, $width, PHP_EOL ); $res .= PHP_EOL . PHP_EOL; foreach ( $help as $row ) { $rowParts = explode( "\n", $this->stringTool->wordwrap( $row[1], $rightColWidth ) ); $res .= $this->stringTool->strPad( $row[0], $leftColWidth, ' ' ); $res .= $rowParts[0] . PHP_EOL; // @TODO: Fix function call in loop header for ( $i = 1; $i < sizeof( $rowParts ); $i++ ) { $res .= str_repeat( ' ', $leftColWidth ) . $rowParts[$i] . PHP_EOL; } } return $res; } /** * Returns the synopsis string for the program. * * This gives you a synopsis definition for the options and arguments * defined with this instance of ezcConsoleInput. You can filter the * options named in the synopsis by submitting their short names in an * array as the parameter of this method. If the parameter $optionNames * is set, only those options are listed in the synopsis. * * @param array(string) $optionNames * @return string */ public function getSynopsis( ?array $optionNames = null ) { return $this->helpGenerator->generateSynopsis( $optionNames ); } /** * Returns if a help option was set. * This method returns if an option was submitted, which was defined to be * a help option, using the isHelpOption flag. * * @return bool If a help option was set. */ public function helpOptionSet() { return $this->helpOptionSet; } /** * Property read access. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * * @param string $propertyName Name of the property. * @return mixed Value of the property or null. * @ignore */ public function __get( $propertyName ) { if ( !isset( $this->$propertyName ) ) { throw new ezcBasePropertyNotFoundException( $propertyName ); } return $this->properties[$propertyName]; } /** * Property set access. * * @param string $propertyName * @param string $propertyValue * @ignore * @return void */ public function __set( $propertyName, $propertyValue ) { switch ( $propertyName ) { case "argumentDefinition": if ( ( $propertyValue instanceof ezcConsoleArguments ) === false && $propertyValue !== null ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "ezcConsoleArguments" ); } break; default: throw new ezcBasePropertyNotFoundException( $propertyName ); } $this->properties[$propertyName] = $propertyValue; } /** * Property isset access. * * @param string $propertyName Name of the property. * @return bool True if the property is set, otherwise false. * @ignore */ public function __isset( $propertyName ) { return array_key_exists( $propertyName, $this->properties ); } /** * Returns the synopsis string for a single option and its dependencies. * * This method returns a part of the program synopsis, specifically for a * certain parameter. The method recursively adds depending parameters up * to the 2nd depth level to the synopsis. The second parameter is used * to store the short names of all options that have already been used in * the synopsis (to avoid adding an option twice). The 3rd parameter * determines the actual deps in the option dependency recursion to * terminate that after 2 recursions. * * @param ezcConsoleOption $option The option to include. * @param array(string) $usedOptions Array of used option short names. * @param int $depth Current recursion depth. * @return string The synopsis for this parameter. * * @apichange This method is deprecates. Implement your own {@link * ezcConsoleInputHelpGenerator} instead, as soon as the * interface is made public. */ protected function createOptionSynopsis( ezcConsoleOption $option, &$usedOptions, $depth = 0 ) { $synopsis = ''; // Break after a nesting level of 2 if ( $depth++ > 2 || ( in_array( $option->short, $usedOptions['short'] ) && in_array( $option->long, $usedOptions['long'] ) ) ) return $synopsis; $usedOptions['short'][] = $option->short; $usedOptions['long'][] = $option->long; $synopsis .= $option->short !== "" ? "-{$option->short}" : "--{$option->long}"; if ( isset( $option->default ) ) { $synopsis .= " " . ( $option->type === ezcConsoleInput::TYPE_STRING ? '"' : '' ) . $option->default . ( $option->type === ezcConsoleInput::TYPE_STRING ? '"' : '' ); } else if ( $option->type !== ezcConsoleInput::TYPE_NONE ) { $synopsis .= " "; switch ( $option->type ) { case ezcConsoleInput::TYPE_STRING: $synopsis .= ""; break; case ezcConsoleInput::TYPE_INT: $synopsis .= ""; break; } } foreach ( $option->getDependencies() as $rule ) { $deeperSynopsis = $this->createOptionSynopsis( $rule->option, $usedOptions, $depth ); $synopsis .= ( iconv_strlen( trim( $deeperSynopsis ), 'UTF-8' ) > 0 ? ' ' . $deeperSynopsis : '' ); } if ( $option->arguments === false ) { $allowsArgs = false; } // Make the whole thing optional? if ( $option->mandatory === false ) { $synopsis = "[$synopsis]"; } return $synopsis . ' '; } /** * Process an option. * * This method does the processing of a single option. * * @param array(string) $args The arguments array. * @param int $i The current position in the arguments array. * @return void * * @throws ezcConsoleOptionTooManyValuesException * If an option that expects only a single value was submitted * with multiple values. * @throws ezcConsoleOptionTypeViolationException * If an option was submitted with a value of the wrong type. * @throws ezcConsoleOptionMissingValueException * If an option thats expects a value was submitted without. */ private function processOption( array $args, &$i ) { $option = $this->getOption( preg_replace( '/^-+/', '', $args[$i++] ) ); // Is the actual option a help option? if ( $option->isHelpOption === true ) { $this->helpOptionSet = true; } // No value expected if ( $option->type === ezcConsoleInput::TYPE_NONE ) { // No value expected if ( isset( $args[$i] ) && iconv_substr( $args[$i], 0, 1, 'UTF-8' ) !== '-' && sizeof( $args ) > ( $i + 1 ) ) { // But one found throw new ezcConsoleOptionTypeViolationException( $option, $args[$i] ); } // Multiple occurance possible if ( $option->multiple === true ) { if ( ! is_array( $option->value ) ) { $option->value = array(); } $option->value[] = true; } else { $option->value = true; } // Everything fine, nothing to do return $i; } // Value expected, check for it if ( isset( $args[$i] ) && iconv_substr( $args[$i], 0, 1, 'UTF-8' ) !== '-' ) { // Type check if ( $this->isCorrectType( $option->type, $args[$i] ) === false ) { throw new ezcConsoleOptionTypeViolationException( $option, $args[$i] ); } // Multiple values possible if ( $option->multiple === true ) { if ( ! is_array( $option->value ) ) { $option->value = array(); } $option->value[] = $args[$i]; } // Only single value expected, check for multiple elseif ( isset( $option->value ) && $option->value !== false ) { throw new ezcConsoleOptionTooManyValuesException( $option ); } else { $option->value = $args[$i]; } $i++; } // Value found? If not, use default, if available if ( !isset( $option->value ) || $option->value === false || ( is_array( $option->value ) && count( $option->value ) === 0) ) { throw new ezcConsoleOptionMissingValueException( $option ); } } /** * Process arguments given to the program. * * @param array(string) $args The arguments array. * @param int $i Current index in arguments array. * @return void */ private function processArguments( array $args, &$i ) { $numArgs = count( $args ); if ( $this->argumentDefinition === null || $this->argumentsAllowed() === false ) { // Old argument handling, also used of a set option sets disallowing arguments while ( $i < $numArgs ) { $this->arguments[] = $args[$i++]; } } else { $mandatory = true; foreach ( $this->argumentDefinition as $arg ) { // Check if all followinga arguments are optional if ( $arg->mandatory === false ) { $mandatory = false; } // Check if the current argument is present and mandatory if ( $mandatory === true ) { if ( !isset( $args[$i] ) ) { throw new ezcConsoleArgumentMandatoryViolationException( $arg ); } } else { // Arguments are optional, if no more left: return. if ( !isset( $args[$i] ) ) { // Optional and no more arguments left, assign default $arg->value = $arg->default; continue; } } if ( $arg->multiple === true ) { $arg->value = array(); for ( $i = $i; $i < $numArgs; ++$i ) { if ( $this->isCorrectType( $arg->type, $args[$i] ) === false ) { throw new ezcConsoleArgumentTypeViolationException( $arg, $args[$i] ); } $arg->value = array_merge( $arg->value, array( $args[$i] ) ); // Keep old handling, too $this->arguments[] = $args[$i]; } return; } else { if ( $this->isCorrectType( $arg->type, $args[$i] ) === false ) { throw new ezcConsoleArgumentTypeViolationException( $arg, $args[$i] ); } $arg->value = $args[$i]; // Keep old handling, too $this->arguments[] = $args[$i]; } ++$i; } if ( $i < $numArgs ) { throw new ezcConsoleTooManyArgumentsException( $args, $i ); } } } /** * Returns if arguments are allowed with the current option submition. * * @return bool If arguments allowed. */ protected function argumentsAllowed() { foreach ( $this->options as $id => $option ) { if ( $option->value !== false && $option->arguments === false ) { return false; } } return true; } /** * Check the rules that may be associated with an option. * * Options are allowed to have rules associated for dependencies to other * options and exclusion of other options or arguments. This method * processes the checks. * * @throws ezcConsoleException * in case validation fails. */ private function checkRules() { // If a help option is set, skip rule checking if ( $this->helpOptionSet === true ) { return; } $this->validator->validateOptions( $this->options, ( $this->arguments !== array() ) ); } /** * Checks if a value is of a given type. Converts the value to the * correct PHP type on success. * * @param int $type The type to check for. One of self::TYPE_*. * @param string $val The value to check. Will possibly altered! * @return bool True on succesful check, otherwise false. */ private function isCorrectType( $type, &$val ) { $res = false; switch ( $type ) { case ezcConsoleInput::TYPE_STRING: $res = true; $val = preg_replace( '/^(["\'])(.*)\1$/', '\2', $val ); break; case ezcConsoleInput::TYPE_INT: $res = preg_match( '/^[0-9]+$/', $val ) ? true : false; if ( $res ) { $val = ( int ) $val; } break; } return $res; } /** * Split parameter and value for long option names. * * This method checks for long options, if the value is passed using =. If * this is the case parameter and value get split and replaced in the * arguments array. * * @param array(string) $args The arguments array * @param int $i Current arguments array position * @return void */ private function preprocessLongOption( array &$args, $i ) { // Value given? if ( preg_match( '/^--\w+\=[^ ]/i', $args[$i] ) ) { // Split param and value and replace current param $parts = explode( '=', $args[$i], 2 ); array_splice( $args, $i, 1, $parts ); } } } ?> ConsoleTools-1.7.5/src/input/000077500000000000000000000000001470000742400160735ustar00rootroot00000000000000ConsoleTools-1.7.5/src/input/argument.php000066400000000000000000000163541470000742400204370ustar00rootroot00000000000000 null, "type" => ezcConsoleInput::TYPE_STRING, "shorthelp" => "No help available.", "longhelp" => "There is no help for this argument available.", "mandatory" => true, "multiple" => false, "default" => null, "value" => null, ); /** * Creates a new console argument object. * Creates a new console argument object, which represents a single * argument on the shell. Arguments are stored insiede * {@link ezcConsoleArguments} which is used with {@link ezcConsoleInput}. * * For the type property see {@link ezcConsoleInput::TYPE_STRING} and * {@link ezcConsoleInput::TYPE_INT}. If 1 argument is defined as optional * ($mandatory = false), all following arguments are autolamtically * considered optional, too. * * @param string $name The name for the argument. Must be unique. * @param int $type The value type. * @param string $shorthelp A short help text. * @param string $longhelp A long help text- * @param bool $mandatory Whether the argument is mandatory. * @param bool $multiple Whether the argument accepts multiple values. * @param mixed $default A default value, if not mandatory. * @return void */ public function __construct( $name = null, $type = ezcConsoleInput::TYPE_STRING, $shorthelp = "No help available.", $longhelp = "There is no help for this argument available.", $mandatory = true, $multiple = false, $default = null ) { if ( !is_string( $name ) || strlen( $name ) < 1 ) { throw new ezcBaseValueException( "name", $name, "string, length > 0" ); } $this->properties["name"] = $name; $this->type = $type; $this->shorthelp = $shorthelp; $this->longhelp = $longhelp; $this->mandatory = $mandatory; $this->multiple = $multiple; $this->default = $default; } /** * Property set access. * * @param string $propertyName Name of the property. * @param string $propertyValue Valze for the property. * @return void * @ignore */ public function __set( $propertyName, $propertyValue ) { switch ( $propertyName ) { case "name": throw new ezcBasePropertyPermissionException( $propertyName, ezcBasePropertyPermissionException::READ ); break; case "type": if ( $propertyValue !== ezcConsoleInput::TYPE_INT && $propertyValue !== ezcConsoleInput::TYPE_STRING ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "string, length > 0" ); } break; case "shorthelp": case "longhelp": if ( is_string( $propertyValue ) === false ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "string" ); } break; case "mandatory": case "multiple": if ( is_bool( $propertyValue ) === false ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "bool" ); } break; case "default": if ( is_scalar( $propertyValue ) === false && is_array( $propertyValue ) === false && $propertyValue !== null ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "array, scalar or null" ); } break; case "value": if ( is_scalar( $propertyValue ) === false && is_array( $propertyValue ) === false && $propertyValue !== null ) { throw new ezcBaseValueException( $propertyName, $propertyValue, "string or null" ); } break; default: throw new ezcBasePropertyNotFoundException( $propertyName ); } $this->properties[$propertyName] = $propertyValue; } /** * Property read access. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * * @param string $propertyName Name of the property. * @return mixed Value of the property or null. * @ignore */ public function __get( $propertyName ) { if ( isset( $this->$propertyName ) ) { return $this->properties[$propertyName]; } throw new ezcBasePropertyNotFoundException( $propertyName ); } /** * Property isset access. * * @param string $propertyName Name of the property. * @return bool True is the property is set, otherwise false. * @ignore */ public function __isset( $propertyName ) { return array_key_exists( $propertyName, $this->properties ); } } ?> ConsoleTools-1.7.5/src/input/arguments.php000066400000000000000000000211641470000742400206150ustar00rootroot00000000000000ezcConsoleArgument) */ protected $named = array(); /** * Returns if the given offset exists. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. Valid offsets are integers or * strings. If an integer is used, it refers to the position in the command * line. A string refers to the arguments name property. * * @param mixed $offset The offset to check. * @return bool True when the offset exists, otherwise false. * * @throws ezcBaseValueException * If the provided offset is neither an integer, nor a string. */ #[ReturnTypeWillChange] public function offsetExists( $offset ) { switch ( gettype( $offset ) ) { case "string": return array_key_exists( $offset, $this->named ); case "integer": return array_key_exists( $offset, $this->ordered ); default: throw new ezcBaseValueException( "offset", $offset, "string or int" ); } } /** * Returns the element with the given offset. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. Valid offsets are integers or * strings. If an integer is used, it refers to the position in the command * line. A string refers to the arguments name property. * * @param string|integer $offset The offset to check. * @return ezcConsoleArgument * * @throws ezcBaseValueException * If the provided offset is neither an integer, nor a string. */ #[ReturnTypeWillChange] public function offsetGet( $offset ) { switch ( gettype( $offset ) ) { case "string": if ( isset( $this[$offset] ) ) { return $this->named[$offset]; } break; case "integer": if ( isset( $this[$offset] ) ) { return $this->ordered[$offset]; } break; default: throw new ezcBaseValueException( "offset", $offset, "string or int" ); } throw new ezcBasePropertyNotFoundException( $offset ); } /** * Set the element with the given offset. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. In contrast to the other * ArrayAccess implementations of this class, this method allows only integer * keys. * * @param int $offset The offset to assign an item to. * @param ezcConsoleArgument $value The argument object to register. * @return void * * @throws ezcBaseValueException * If a non integer offset is provided. * @throws ezcBaseValueException * If the provided value is not of type {@ling ezcConsoleTableRow}. * @throws ezcConsoleArgumentAlreadyRegisteredException * If an argument with the given offset or name is already registered. */ #[ReturnTypeWillChange] public function offsetSet( $offset, $value ) { // Determine key if not set (using $obj[] = ...) if ( $offset === null ) { $offset = count( $this->ordered ) === 0 ? 0 : max( array_keys( $this->ordered ) ) + 1; } // Set access only allowed with integer values if ( !is_int( $offset ) ) { throw new ezcBaseValueException( "offset", $offset, "int" ); } switch ( true ) { case ( $value instanceof ezcConsoleArgument ): if ( isset( $this->ordered[$offset] ) ) { throw new ezcConsoleArgumentAlreadyRegisteredException( $offset, ezcConsoleArgumentAlreadyRegisteredException::ORDERED ); } if ( isset( $this->named[$value->name] ) ) { throw new ezcConsoleArgumentAlreadyRegisteredException( $value->name, ezcConsoleArgumentAlreadyRegisteredException::NAMED ); } $this->named[$value->name] = $value; $this->ordered[$offset] = $value; break; case ( $value === null ): // Aliasing unset() with assignement to null unset( $this->named[$this->ordered[$offset]->name] ); unset( $this->ordered[$offset] ); break; default: throw new ezcBaseValueException( "value", $value, "ezcConsoleArgument or null" ); } } /** * Unset the element with the given offset. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. In contrast to the other * ArrayAccess implementations of this class, this method allows only integer * keys. * * @param int $offset The offset to unset the value for. * @return void * * @throws ezcBaseValueException * If a non numeric row offset is used. */ #[ReturnTypeWillChange] public function offsetUnset( $offset ) { // Set access only allowed with integer values if ( is_int( $offset ) === false ) { throw new ezcBaseValueException( "offset", $offset, "int" ); } unset( $this->named[$this->ordered[$offset]->name] ); unset( $this->ordered[$offset] ); } /** * Returns the currently selected argument from the list. * Used by foreach-Loops. * * @return ezcConsoleArgument */ #[ReturnTypeWillChange] public function current() { return current( $this->ordered ); } /** * Returns the key of the currently selected argument from the list. * Used by foreach-Loops. In contrast to the iteration direction, which is * defined by the ordered list of arguments, this is the name of the * argument. * * @return string */ #[ReturnTypeWillChange] public function key() { return key( $this->ordered ); } /** * Advances the internal pointer to the next argument and returns it. * Used by foreach-Loops. * * @return ezcConsoleArgument */ #[ReturnTypeWillChange] public function next() { return next( $this->ordered ); } /** * Rewinds the internal pointer to the first argument and returns it. * Used by foreach-Loops. * * @return ezcConsoleArgument */ #[ReturnTypeWillChange] public function rewind() { // Called before foreach ksort( $this->ordered ); return reset( $this->ordered ); } /** * Checks if the current position is valid. * Used by foreach-Loops. * * @return bool */ #[ReturnTypeWillChange] public function valid() { return ( current( $this->ordered ) !== false ); } /** * Returns the number of registered arguments. * * @return int */ #[ReturnTypeWillChange] public function count() { return count( $this->ordered ); } } ?> ConsoleTools-1.7.5/src/input/help_generators/000077500000000000000000000000001470000742400212545ustar00rootroot00000000000000ConsoleTools-1.7.5/src/input/help_generators/standard.php000066400000000000000000000342461470000742400235760ustar00rootroot00000000000000input = $input; } /** * Generates help information as a multidimensional array. * * This method generates a tabular view on the help information of a * program. The returned array has the following structure: * * * array( * 0 => ' * * Each row of the array represents the help information for a single option. * The first cell of a row contains the option name (maybe short, long or * both), the second cell contains the help text of the option. * * The returned array is used by {@link ezcConsoleInput} for different * purposes. * For example, the user can retrieve it raw through the * {@link ezcConsoleInput::getHelp()} method, he can generate a help * {@link ezcConsoleTable} through {@link ezcConsoleInput::getHelpTable()} * are can generate a printable help text through {@link * ezcConsoleInput::getHelpText()}. * * The parameter $long defines if the long or short help text of the * options should be used in the second cell of the returned array. The * $optionsFilter parameter is used to restrict the generated help to a certain * sub-set of options. It consists of an array of short or long names of * the options to include. * * @param bool $long * @param array(string) $optionsFilter * @return array(array(string)) */ public function generateUngroupedOptionHelp( $long = false, ?array $optionsFilter = null ) { $help = array(); foreach ( $this->input->getOptions() as $id => $param ) { if ( $optionsFilter === null || in_array( $param->short, $optionsFilter ) || in_array( $param->long, $optionsFilter ) ) { $help[] = $this->getOptionHelpRow( $long, $param ); } } return $help; } /** * Generates help information as a multidimensional array, grouped in categories. * * This method behaves similar to {@link generateUngroupedOptionHelp()}. In * contrast to the latter one, this method returns an array with 1 * dimension more, grouping options into categories. The $groups parameter * defines the categories to generate. Each category may contain an * arbitrary number of options, options might occur in different * categories. * * The returned array has the follorwing format: * * * ' => array( * 0 => array( * 0 => ' * * The $long parameter, as in {@link generateUngroupedOptionHelp()} * determines if the options short or long help is to be used. The * $params array can in addition be used to determine if a parameter * is displayed at all. If $optionsFilter is submitted and is not null, * only options listed in it will be shown in the help at all. * * @param array(string=>array(string)) $groups * @param bool $long * @param array(string) $params * @return array(string=>array(array(string))) */ public function generateGroupedOptionHelp( array $groups, $long = false, ?array $optionsFilter = null ) { $help = array(); foreach ( $groups as $groupName => $groupOptions ) { foreach ( $groupOptions as $optionName ) { $option = $this->input->getOption( $optionName ); if ( $optionsFilter === null || in_array( $option->short, $optionsFilter ) || in_array( $option->long, $optionsFilter ) ) { $help[$groupName][] = $this->getOptionHelpRow( $long, $option ); } } } return $help; } /** * Generates help information as a multi-dimensonal array for the given $argumentDefinition. * * This method generates a tabular help information for the given * $argumentDefinition in the following format: * * * array( * 0 => '', * 1 => '' * ), * 1 => array( * 0 => '', * 1 => '' * ), * // ... * ) * ?> * * * The $long parameter defines if the long of short help text should be * used. * * @param bool $long * @return array(array(string)) */ public function generateArgumentHelp( $long = false ) { $help = array(); if ( $this->input->argumentDefinition !== null ) { foreach ( $this->input->argumentDefinition as $arg ) { $argSynopsis = "<%s:%s>"; switch ( $arg->type ) { case ezcConsoleInput::TYPE_INT: $type = "int"; break; case ezcConsoleInput::TYPE_STRING: $type = "string"; break; } $argSynopsis = sprintf( $argSynopsis, $type, $arg->name ); $help[] = ( $long === true ) ? array( $argSynopsis, $arg->longhelp . ( $arg->mandatory === false ? ' (optional' . ( $arg->default !== null ? ', default = ' . ( is_array( $arg->default ) ? "'" . implode( "' '", $arg->default ) . "'" : "'$arg->default'" ) : '' ) . ')' : '' ) ) : array( $argSynopsis, $arg->shorthelp ); } } return $help; } /** * Creates 1 text row for displaying options help. * * Returns a single array entry for the {@link getOptionHelpRow()} method. * * @param bool $long * @param ezcConsoleOption $param * @return string */ private function getOptionHelpRow( $long, ezcConsoleOption $param ) { return array( ( $param->short !== "" ? '-' . $param->short . ' / ' : "" ) . '--' . $param->long, $long == false ? $param->shorthelp : $param->longhelp, ); } /** * Generates a command line synopsis for the options and arguments. * * This method generates a synopsis string that lists the options and * parameters available, indicating their usage. If $optionsFilter is * submitted, only the options named in this array (short or long variant) * will be included in the synopsis. * * @param array(string) $optionsFilter * @return string */ public function generateSynopsis( ?array $optionFilter = null ) { $usedOptions = array( 'short' => array(), 'long' => array() ); $synopsis = '$ ' . ( isset( $argv ) && sizeof( $argv ) > 0 ? $argv[0] : $_SERVER['argv'][0] ) . ' '; foreach ( $this->input->getOptions() as $option ) { if ( $optionFilter === null || in_array( $option->short, $optionFilter ) || in_array( $option->long, $optionFilter ) ) { $synopsis .= $this->createOptionSynopsis( $option, $usedOptions ); } } if ( $this->input->argumentDefinition === null ) { // Old handling $synopsis .= " [[--] ]"; } else { $synopsis .= "[--] " . $this->createArgumentsSynopsis(); } return $synopsis; } /** * Returns the synopsis string for a single option and its dependencies. * * This method returns a part of the program synopsis, specifically for a * certain parameter. The method recursively adds depending parameters up * to the 2nd depth level to the synopsis. The second parameter is used * to store the short names of all options that have already been used in * the synopsis (to avoid adding an option twice). The 3rd parameter * determines the actual deps in the option dependency recursion to * terminate that after 2 recursions. * * @param ezcConsoleOption $option The option to include. * @param array(string) $usedOptions Array of used option short names. * @param int $depth Current recursion depth. * @return string The synopsis for this parameter. */ private function createOptionSynopsis( ezcConsoleOption $option, &$usedOptions, $depth = 0 ) { $synopsis = ''; // Break after a nesting level of 2 if ( $depth++ > 2 || ( in_array( $option->short, $usedOptions['short'] ) && in_array( $option->long, $usedOptions['long'] ) ) ) return $synopsis; $usedOptions['short'][] = $option->short; $usedOptions['long'][] = $option->long; $synopsis .= $option->short !== "" ? "-{$option->short}" : "--{$option->long}"; if ( isset( $option->default ) ) { $synopsis .= " " . ( $option->type === ezcConsoleInput::TYPE_STRING ? '"' : '' ) . $option->default . ( $option->type === ezcConsoleInput::TYPE_STRING ? '"' : '' ); } else if ( $option->type !== ezcConsoleInput::TYPE_NONE ) { $synopsis .= " "; switch ( $option->type ) { case ezcConsoleInput::TYPE_STRING: $synopsis .= ""; break; case ezcConsoleInput::TYPE_INT: $synopsis .= ""; break; } } foreach ( $option->getDependencies() as $rule ) { $deeperSynopsis = $this->createOptionSynopsis( $rule->option, $usedOptions, $depth ); $synopsis .= ( iconv_strlen( trim( $deeperSynopsis ), 'UTF-8' ) > 0 ? ' ' . $deeperSynopsis : '' ); } if ( $option->arguments === false ) { $allowsArgs = false; } // Make the whole thing optional? if ( $option->mandatory === false ) { $synopsis = "[$synopsis]"; } return $synopsis . ' '; } /** * Generate synopsis for arguments. * * @return string The synopsis string. */ private function createArgumentsSynopsis() { $mandatory = true; $synopsises = array(); foreach ( $this->input->argumentDefinition as $arg ) { $argSynopsis = ""; if ( $arg->mandatory === false ) { $mandatory = false; } $argSynopsis .= "<%s:%s>"; switch ( $arg->type ) { case ezcConsoleInput::TYPE_INT: $type = "int"; break; case ezcConsoleInput::TYPE_STRING: $type = "string"; break; } $argSynopsis = sprintf( $argSynopsis, $type, $arg->name ); $synopsises[] = $mandatory === false ? "[$argSynopsis]" : $argSynopsis; if ( $arg->multiple === true ) { $synopsises[] = "[$argSynopsis ...]"; break; } } return implode( " ", $synopsises ); } } ?> ConsoleTools-1.7.5/src/input/option.php000066400000000000000000000475121470000742400201250ustar00rootroot00000000000000mixed) */ protected $properties; /** * Dependency rules of this parameter. * * @see ezcConsoleOption::addDependency() * @see ezcConsoleOption::removeDependency() * @see ezcConsoleOption::hasDependency() * @see ezcConsoleOption::getDependencies() * @see ezcConsoleOption::resetDependencies() * * @var array(string=>ezcConsoleParamemterRule) */ protected $dependencies = array(); /** * Exclusion rules of this parameter. * * @see ezcConsoleOption::addExclusion() * @see ezcConsoleOption::removeExclusion() * @see ezcConsoleOption::hasExclusion() * @see ezcConsoleOption::getExclusions() * @see ezcConsoleOption::resetExclusions() * * @var array(string=>ezcConsoleParamemterRule) */ protected $exclusions = array(); /** * The value the parameter was assigned to when being submitted. * Boolean false indicates the parameter was not submitted, boolean * true means the parameter was submitted, but did not have a value. * In any other case, this caries the submitted value. * * @var mixed */ public $value = false; /** * Create a new parameter struct. * Creates a new basic parameter struct with the base information "$short" * (the short name of the parameter) and "$long" (the long version). You * simply apply these parameters as strings (without '-' or '--'). So * * * $param = new ezcConsoleOption( 'f', 'file' ); * * * will result in a parameter that can be accessed using * * * $ mytool -f * * * or * * * $ mytool --file * * . * * The newly created parameter contains only it's 2 names and each other * attribute is set to it's default value. You can simply manipulate * those attributes by accessing them directly. * * @param string $short Short name of the parameter without '-' (eg. 'f'). * @param string $long Long name of the parameter without '--' (eg. 'file'). * @param int $type Value type of the parameter. One of ezcConsoleInput::TYPE_*. * @param mixed $default Default value the parameter holds if not submitted. * @param bool $multiple If the parameter may be submitted multiple times. * @param string $shorthelp Short help text. * @param string $longhelp Long help text. * @param array(ezcConsoleOptionRule) $dependencies Dependency rules. * @param array(ezcConsoleOptionRule) $exclusions Exclusion rules. * @param bool $arguments Whether supplying arguments is allowed when this parameter is set. * @param bool $mandatory Whether the parameter must be always submitted. * @param bool $isHelpOption Indicates that the given parameter is a help * option. If a help option is set, all rule * checking is skipped (dependency/exclusion/ * mandatory). * * @throws ezcConsoleInvalidOptionNameException If the option names start with a "-" * sign or contain whitespaces. */ public function __construct( $short = '', $long = '', $type = ezcConsoleInput::TYPE_NONE, $default = null, $multiple = false, $shorthelp = 'No help available.', $longhelp = 'Sorry, there is no help text available for this parameter.', array $dependencies = array(), array $exclusions = array(), $arguments = true, $mandatory = false, $isHelpOption = false ) { $this->properties['short'] = ''; $this->properties['long'] = ''; $this->properties['arguments'] = $arguments; if ( !self::validateOptionName( $short ) ) { throw new ezcConsoleInvalidOptionNameException( $short ); } $this->properties['short'] = $short; if ( !self::validateOptionName( $long ) ) { throw new ezcConsoleInvalidOptionNameException( $long ); } $this->properties['long'] = $long; $this->__set( "type", $type !== null ? $type : ezcConsoleInput::TYPE_NONE ); $this->__set( "multiple", $multiple !== null ? $multiple : false ); $this->__set( "default", $default !== null ? $default : null ); $this->__set( "shorthelp", $shorthelp !== null ? $shorthelp : 'No help available.' ); $this->__set( "longhelp", $longhelp !== null ? $longhelp : 'Sorry, there is no help text available for this parameter.' ); $dependencies = $dependencies !== null && is_array( $dependencies ) ? $dependencies : array(); foreach ( $dependencies as $dep ) { $this->addDependency( $dep ); } $exclusions = $exclusions !== null && is_array( $exclusions ) ? $exclusions : array(); foreach ( $exclusions as $exc ) { $this->addExclusion( $exc ); } $this->__set( "mandatory", $mandatory !== null ? $mandatory : false ); $this->__set( "isHelpOption", $isHelpOption !== null ? $isHelpOption : false ); } /** * Add a new dependency for a parameter. * This registeres a new dependency rule with the parameter. If you try * to add an already registered rule it will simply be ignored. Else, * the submitted rule will be added to the parameter as a dependency. * * @param ezcConsoleOptionRule $rule The rule to add. * @return void */ public function addDependency( ezcConsoleOptionRule $rule ) { foreach ( $this->dependencies as $existRule ) { if ( $rule == $existRule ) { return; } } $this->dependencies[] = $rule; } /** * Remove a dependency rule from a parameter. * This removes a given rule from a parameter, if it exists. If the rule is * not registered with the parameter, the method call will simply be ignored. * * @param ezcConsoleOptionRule $rule The rule to be removed. * @return void */ public function removeDependency( ezcConsoleOptionRule $rule ) { foreach ( $this->dependencies as $id => $existRule ) { if ( $rule == $existRule ) { unset( $this->dependencies[$id] ); } } } /** * Remove all dependency rule referring to a parameter. * This removes all dependency rules from a parameter, that refer to as specific * parameter. If no rule is registered with this parameter as reference, the * method call will simply be ignored. * * @param ezcConsoleOption $param The param to be check for rules. * @return void */ public function removeAllDependencies( ezcConsoleOption $param ) { foreach ( $this->dependencies as $id => $rule ) { if ( $rule->option == $param ) { unset( $this->dependencies[$id] ); } } } /** * Returns if a dependency to the given option exists. * Returns true if a dependency rule to the given option is registered, * otherwise false. * * @param ezcConsoleOption $param The param to check if a dependency exists to. * @return bool True if rule is registered, otherwise false. */ public function hasDependency( ezcConsoleOption $param ) { foreach ( $this->dependencies as $id => $rule ) { if ( $rule->option == $param ) { return true; } } return false; } /** * Returns the dependency rules registered with this parameter. * Returns an array of registered dependencies. * * For example: * * array( * 0 => ezcConsoleOptionRule, * 1 => ezcConsoleOptionRule, * 2 => ezcConsoleOptionRule, * ); * * * @return array(ezcConsoleOptionRule) Dependency definition. */ public function getDependencies() { return $this->dependencies; } /** * Reset existing dependency rules. * Deletes all registered dependency rules from the option definition. * * @return void */ public function resetDependencies() { $this->dependencies = array(); } /** * Add a new exclusion for an option. * This registeres a new exclusion rule with the option. If you try * to add an already registered rule it will simply be ignored. Else, * the submitted rule will be added to the option as a exclusion. * * @param ezcConsoleOptionRule $rule The rule to add. * @return void */ public function addExclusion( ezcConsoleOptionRule $rule ) { foreach ( $this->exclusions as $existRule ) { if ( $rule == $existRule ) { return; } } $this->exclusions[] = $rule; } /** * Remove a exclusion rule from a option. * This removes a given rule from a option, if it exists. If the rule is * not registered with the option, the method call will simply be ignored. * * @param ezcConsoleOptionRule $rule The rule to be removed. * @return void */ public function removeExclusion( ezcConsoleOptionRule $rule ) { foreach ( $this->exclusions as $id => $existRule ) { if ( $rule == $existRule ) { unset( $this->exclusions[$id] ); } } } /** * Remove all exclusion rule referring to a option. * This removes all exclusion rules from a option, that refer to as specific * option. If no rule is registered with this option as reference, the * method call will simply be ignored. * * @param ezcConsoleOption $param The option to remove rule for. * @return void */ public function removeAllExclusions( ezcConsoleOption $param ) { foreach ( $this->exclusions as $id => $rule ) { if ( $rule->option == $param ) { unset( $this->exclusions[$id] ); } } } /** * Returns if a given exclusion rule is registered with the option. * Returns true if a exclusion rule to the given option is registered, * otherwise false. * * @param ezcConsoleOption $param The param to check if exclusions exist for. * @return bool True if rule is registered, otherwise false. */ public function hasExclusion( ezcConsoleOption $param ) { foreach ( $this->exclusions as $id => $rule ) { if ( $rule->option == $param ) { return true; } } return false; } /** * Returns the exclusion rules registered with this parameter. * Returns an array of registered exclusions. * * For example: * * array( * 0 => ezcConsoleOptionRule, * 1 => ezcConsoleOptionRule, * 2 => ezcConsoleOptionRule, * ); * * * @return array(ezcConsoleOptionRule) Exclusions definition. */ public function getExclusions() { return $this->exclusions; } /** * Reset existing exclusion rules. * Deletes all registered exclusion rules from the option definition. * * @return void */ public function resetExclusions() { $this->exclusions = array(); } /** * Property read access. * Provides read access to the properties of the object. * * @param string $key The name of the property. * @return mixed The value if property exists and isset, otherwise null. * @ignore */ public function __get( $key ) { switch ( $key ) { case 'short': case 'long': case 'type': case 'default': case 'multiple': case 'shorthelp': case 'longhelp': case 'arguments': case 'isHelpOption': case 'mandatory': return $this->properties[$key]; case 'dependencies': default: throw new ezcBasePropertyNotFoundException( $key ); } } /** * Property write access. * * @param string $key Name of the property. * @param mixed $val The value for the property. * * @throws ezcBasePropertyPermissionException * If the property you try to access is read-only. * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * @ignore */ public function __set( $key, $val ) { switch ( $key ) { case 'type': if ( $val !== ezcConsoleInput::TYPE_NONE && $val !== ezcConsoleInput::TYPE_INT && $val !== ezcConsoleInput::TYPE_STRING ) { throw new ezcBaseValueException( $key, $val, 'ezcConsoleInput::TYPE_STRING, ezcConsoleInput::TYPE_INT or ezcConsoleInput::TYPE_NONE' ); } break; case 'default': if ( ( is_scalar( $val ) === false && $val !== null ) ) { // Newly allow arrays, if multiple is true if ( $this->multiple === true && is_array( $val ) === true ) { break; } throw new ezcBaseValueException( $key, $val, 'a string or a number, if multiple == true also an array' ); } break; case 'multiple': if ( !is_bool( $val ) ) { throw new ezcBaseValueException( $key, $val, 'bool' ); } break; case 'shorthelp': if ( !is_string( $val ) ) { throw new ezcBaseValueException( $key, $val, 'string' ); } break; case 'longhelp': if ( !is_string( $val ) ) { throw new ezcBaseValueException( $key, $val, 'string' ); } break; case 'arguments': if ( !is_bool( $val ) ) { throw new ezcBaseValueException( $key, $val, 'bool' ); } break; case 'mandatory': if ( !is_bool( $val ) ) { throw new ezcBaseValueException( $key, $val, 'bool' ); } break; case 'isHelpOption': if ( !is_bool( $val ) ) { throw new ezcBaseValueException( $key, $val, 'bool' ); } break; case 'long': case 'short': throw new ezcBasePropertyPermissionException( $key, ezcBasePropertyPermissionException::READ ); break; default: throw new ezcBasePropertyNotFoundException( $key ); break; } $this->properties[$key] = $val; } /** * Property isset access. * * @param string $key Name of the property. * @return bool True is the property is set, otherwise false. * @ignore */ public function __isset( $key ) { switch ( $key ) { case 'short': case 'long': case 'type': case 'default': case 'multiple': case 'shorthelp': case 'longhelp': case 'arguments': case 'isHelpOption': case 'mandatory': return ( $this->properties[$key] !== null ); } return false; } /** * Returns if a given name if valid for use as a parameter name a parameter. * Checks if a given parameter name is generally valid for use. It checks a) * that the name does not start with '-' or '--' and b) if it contains * whitespaces. Note, that this method does not check any conflicts with already * used parameter names. * * @param string $name The name to check. * @return bool True if the name is valid, otherwise false. */ public static function validateOptionName( $name ) { if ( substr( $name, 0, 1 ) === '-' || strpos( $name, ' ' ) !== false ) { return false; } return true; } } ?> ConsoleTools-1.7.5/src/input/validators/000077500000000000000000000000001470000742400202435ustar00rootroot00000000000000ConsoleTools-1.7.5/src/input/validators/standard.php000066400000000000000000000137371470000742400225670ustar00rootroot00000000000000 $option ) { if ( $option->mandatory === true && $option->value === false ) { throw new ezcConsoleOptionMandatoryViolationException( $option ); } $this->validateDependencies( $option ); $this->validateExclusions( $option ); if ( $option->arguments === false && $option->value !== false && $hasArguments ) { throw new ezcConsoleOptionArgumentsViolationException( $option ); } } } /** * Validated option dependencies. * * Validates dependencies by $option. * * @param ezcConsoleOption $option. */ private function validateDependencies( ezcConsoleOption $option ) { $optSet = ( $option->value !== false && ( !is_array( $option->value ) || $option->value !== array() ) ); foreach ( $option->getDependencies() as $dep ) { if ( $dep->ifSet === $optSet ) { $this->validateDependency( $option, $dep ); } } } /** * Validates a single dependency. * * Validates the dependency $dep, which is set in the $srcOpt. * * @param ezcConsoleOption $srcOpt * @param ezcConsoleOptionRule $dep */ private function validateDependency( ezcConsoleOption $srcOpt, ezcConsoleOptionRule $dep ) { $optValue = $dep->option->value; if ( $optValue === false || $optValue === array() ) { throw new ezcConsoleOptionDependencyViolationException( $srcOpt, $dep->option ); } if ( $dep->values !== array() ) { $optVals = ( is_array( $optValue ) ? $optValue : array( $optValue) ); $unrecognizedVals = array_diff( $optVals, $dep->values ); if ( $unrecognizedVals !== array() ) { throw new ezcConsoleOptionDependencyViolationException( $srcOpt, $dep->option, implode( ', ', $dep->values ) ); } } } /** * Validated option exclusions. * * Validates exclusions by $option. * * @param ezcConsoleOption $option. */ private function validateExclusions( ezcConsoleOption $option ) { $optSet = ( $option->value !== false && ( !is_array( $option->value ) || $option->value !== array() ) ); foreach ( $option->getExclusions() as $excl ) { if ( $excl->ifSet === $optSet ) { $this->validateExclusion( $option, $excl ); } } } /** * Validates a single exclusion. * * Validates the exclusion $excl, which is set in the $srcOpt. * * @param ezcConsoleOption $srcOpt * @param ezcConsoleOptionRule $excl */ private function validateExclusion( ezcConsoleOption $srcOpt, ezcConsoleOptionRule $excl ) { $optValue = $excl->option->value; if ( $optValue !== false && $optValue !== array() && $excl->values === array() ) { throw new ezcConsoleOptionExclusionViolationException( $srcOpt, $excl->option ); } $optVals = ( is_array( $optValue ) ? $optValue : array( $optValue ) ); $forbiddenVals = array_intersect( $optVals, $excl->values ); if ( $forbiddenVals !== array() ) { throw new ezcConsoleOptionExclusionViolationException( $srcOpt, $excl->option, implode( ', ', $excl->values ) ); } } } ?> ConsoleTools-1.7.5/src/interfaces/000077500000000000000000000000001470000742400170575ustar00rootroot00000000000000ConsoleTools-1.7.5/src/interfaces/dialog.php000066400000000000000000000065241470000742400210360ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/interfaces/dialog_validator.php000066400000000000000000000042551470000742400231020ustar00rootroot00000000000000 ConsoleTools-1.7.5/src/interfaces/input_help_generator.php000066400000000000000000000143561470000742400240160ustar00rootroot00000000000000 * array( * 0 => '
ezcConsoleOption, contains the parameter that this rule refers to. * - values array(string), contains a list of values that are accepted. * * @see ezcConsoleOption * * @package ConsoleTools * @version //autogen// */ class ezcConsoleOptionRule { /** * Properties. * * @var array */ protected $properties = array( 'option' => null, 'values' => array(), 'ifSet' => true ); /** * Creates a new option rule. * * Creates a new option rule. Per default the $values parameter * is an empty array, which determines that the option may accept any * value. To indicate that a option may only have certain values, * place them inside tha $values array. For example to indicate an option * may have the values 'a', 'b' and 'c' use: * * * $rule = new ezcConsoleOptionRule( $option, array( 'a', 'b', 'c' ) ); * * * If you want to allow only 1 specific value for an option, you do not * need to wrap this into an array, when creating the rule. Simply use * * * $rule = new ezcConsoleOptionRule( $option, 'a' ); * * * to create a rule, that allows the desired option only to accept the * value 'a'. * * The $ifSet parameter determines, if the rule is validated when its option * is set or left out. If $ifSet is true, the rule is validated when the * option is set. Otherwise the rule is validated if the option was not set * by the user. * * @param ezcConsoleOption $option The option to refer to. * @param mixed $values The affected values. * @param bool $ifSet */ public function __construct( ezcConsoleOption $option, array $values = array(), $ifSet = true ) { $this->__set( 'option', $option ); $this->__set( 'values', $values ); $this->__set( 'ifSet', $ifSet ); } /** * Property read access. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * * @param string $propertyName Name of the property. * @return mixed Value of the property or null. * @ignore */ public function __get( $propertyName ) { switch ( $propertyName ) { case 'option': return $this->properties['option']; case 'values': return $this->properties['values']; case 'ifSet': return $this->properties['ifSet']; } throw new ezcBasePropertyNotFoundException( $propertyName ); } /** * Property write access. * * @param string $propertyName Name of the property. * @param mixed $propertyValue The value for the property. * * @throws ezcBasePropertyPermissionException * If the property you try to access is read-only. * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * @ignore */ public function __set( $propertyName, $propertyValue ) { switch ( $propertyName ) { case 'option': if ( !( $propertyValue instanceof ezcConsoleOption ) ) { throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcConsoleOption' ); } $this->properties['option'] = $propertyValue; return; case 'values': if ( !is_array( $propertyValue ) ) { throw new ezcBaseValueException( $propertyName, $propertyValue, 'array' ); } $this->properties['values'] = $propertyValue; return; case 'ifSet': if ( !is_bool( $propertyValue ) ) { throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' ); } $this->properties['ifSet'] = $propertyValue; return; } throw new ezcBasePropertyNotFoundException( $propertyName ); } /** * Property isset access. * * @param string $propertyName Name of the property to check. * @return bool If the property exists or not. * @ignore */ public function __isset( $propertyName ) { switch ( $propertyName ) { case 'option': case 'values': case 'ifSet': return true; } return false; } } ?> ConsoleTools-1.7.5/src/structs/output_format.php000066400000000000000000000141331470000742400220660ustar00rootroot00000000000000mixed) */ protected $properties = array( 'color' => 'default', 'style' => array( 'default' ), 'bgcolor' => 'default', 'target' => ezcConsoleOutput::TARGET_OUTPUT, ); /** * Create a new ezcConsoleOutputFormat object. * Creates a new object of this class. * * @param string $color Name of a color value. * @param array(string) $style Names of style values. * @param string $bgcolor Name of a bgcolor value. * @param string $target Target output stream. */ public function __construct( $color = 'default', ?array $style = null, $bgcolor = 'default', $target = ezcConsoleOutput::TARGET_OUTPUT ) { $this->__set( 'color', $color ); $this->__set( 'style', isset( $style ) ? $style : array( 'default' ) ); $this->__set( 'bgcolor', $bgcolor ); $this->__set( 'target', $target ); } /** * Overloaded __get() method to gain read-only access to some attributes. * * @param string $propertyName Name of the property to read. * @return mixed Desired value if exists, otherwise null. * @ignore */ public function __get( $propertyName ) { switch ( $propertyName ) { case 'style': return (array) $this->properties[$propertyName]; case 'color': case 'bgcolor': case 'target': return $this->properties[$propertyName]; default: throw new ezcBasePropertyNotFoundException( $propertyName ); } } /** * Overloaded __set() method to gain read-only access to properties. * It also performs checks on setting others. * * @throws ezcBasePropertyNotFoundException * If the setting you try to access does not exists * @throws ezcBaseValueException * If trying to set an invalid value for a setting. * * @param string $propertyName Name of the attrinbute to access. * @param string $val The value to set. * @ignore */ public function __set( $propertyName, $val ) { if ( !isset( $this->properties[$propertyName] ) ) { throw new ezcBasePropertyNotFoundException( $propertyName ); } // Extry handling of multi styles if ( $propertyName === 'style' ) { if ( !is_array( $val ) ) $val = array( $val ); foreach ( $val as $style ) { if ( !ezcConsoleOutput::isValidFormatCode( $propertyName, $style ) ) { throw new ezcBaseValueException( $propertyName, $style, 'valid ezcConsoleOutput format code' ); } } $this->properties['style'] = $val; return; } // Continue normal handling if ( ( $propertyName === "color" || $propertyName === "bgcolor" ) && !ezcConsoleOutput::isValidFormatCode( $propertyName, $val ) ) { throw new ezcBaseValueException( $propertyName, $val, 'valid ezcConsoleOutput format code' ); } $this->properties[$propertyName] = $val; } /** * Property isset access. * * @param string $propertyName Name of the property. * @return bool True is the property is set, otherwise false. * @ignore */ public function __isset( $propertyName ) { return isset( $this->properties[$propertyName] ); } } ?> ConsoleTools-1.7.5/src/structs/output_formats.php000066400000000000000000000127231470000742400222540ustar00rootroot00000000000000 * // New ezcConsoleOutput * // $output->format is instance of ezcConsoleOutputFormats. * $output = new ezcConsoleOutput(); * * // Default format - color = blue * $output->formats->default->color = 'blue'; * // Default format - weight = bold * $output->formats->default->style = array( 'bold' ); * * // New format "important" - color = red * $output->formats->important->color = 'red'; * // Format "important" - background color = black * $output->formats->important->bgcolor = 'black'; * * * @package ConsoleTools * @version //autogen// */ class ezcConsoleOutputFormats implements Iterator, Countable { /** * Array of ezcConsoleOutputFormat. * * @var array(ezcConsoleOutputFormat) */ protected $formats = array(); /** * Create a new ezcConsoleOutputFormats object. * * Creates a new, empty object of this class. It also adds a default * format. */ public function __construct() { $this->formats['default'] = new ezcConsoleOutputFormat(); $this->formats['success'] = new ezcConsoleOutputFormat(); $this->formats['success']->color = 'green'; $this->formats['success']->style = array( 'bold' ); $this->formats['failure'] = new ezcConsoleOutputFormat(); $this->formats['failure']->color = 'red'; $this->formats['failure']->style = array( 'bold' ); } /** * Returns the current Iterator value. * * Implementation of {@link Iterator::current()}. * * @return ezcConsoleOutputFormat */ #[ReturnTypeWillChange] public function current() { return current( $this->formats ); } /** * Advances the Iterator to the next element. * * Implementation of {@link Iterator::next()}. * * @return ezcConsoleOutputFormat|bool */ #[ReturnTypeWillChange] public function next() { return next( $this->formats ); } /** * Returns the current Iterator key. * * Implementation of {@link Iterator::key()}. * * @return string */ #[ReturnTypeWillChange] public function key() { return key( $this->formats ); } /** * Resets the Iterator to the first element. * * Implementation of {@link Iterator::rewind()}. * * @return ezcConsoleOutputFormat */ #[ReturnTypeWillChange] public function rewind() { return reset( $this->formats ); } /** * Checks if the current Iterator position is still valid. * * Implementation of {@link Iterator::valid()}. * * @return bool */ #[ReturnTypeWillChange] public function valid() { return ( current( $this->formats ) !== false ); } /** * Returns the number of registered formats. * * Implementation of {@link Countable::count()}. * * @return void */ #[ReturnTypeWillChange] public function count() { return count( $this->formats ); } /** * Read access to the formats. * * Formats are accessed directly like properties of this object. If a * format does not exist, it is created on the fly (using default values), * * @param string $formatName * @return ezcConsoleOutputFormat The format. */ public function __get( $formatName ) { if ( !isset( $this->formats[$formatName] ) ) { $this->formats[$formatName] = new ezcConsoleOutputFormat(); } return $this->formats[$formatName]; } /** * Write access to the formats. * * Formats are accessed directly like properties of this object. If a * format does not exist, it is created on the fly (using default values), * * @param string $formatName * @param ezcConsoleOutputFormat $val The format defintion. * @return void */ public function __set( $formatName, ezcConsoleOutputFormat $val ) { $this->formats[$formatName] = $val; } /** * Property isset access. * * @param string $formatName Name of the property. * @return bool True is the property is set, otherwise false. */ public function __isset( $formatName ) { return isset( $this->formats[$formatName] ); } } ?> ConsoleTools-1.7.5/src/table.php000066400000000000000000000721021470000742400165360ustar00rootroot00000000000000format. The second dimension gives * you direct access to the cells of your table, like $table[0][0] accesses the * first cell in the first row of your table. You can access its properties * diretly here, too. This works like e.g. $table[0][0]->format. Table row and * cell objects are created on the fly, when you access them for the first time. * You can also create them as if you simply create new array elements. E.g. * $table[] creates a new row in the table. * * * // Initialize the console output handler * $out = new ezcConsoleOutput(); * // Define a new format "headline" * $out->formats->headline->color = 'red'; * $out->formats->headline->style = array( 'bold' ); * // Define a new format "sum" * $out->formats->sum->color = 'blue'; * $out->formats->sum->style = array( 'negative' ); * * // Create a new table * $table = new ezcConsoleTable( $out, 60 ); * * // Create first row and in it the first cell * $table[0][0]->content = 'Headline 1'; * * // Create 3 more cells in row 0 * for ( $i = 2; $i < 5; $i++ ) * { * $table[0][]->content = "Headline $i"; * } * * $data = array( 1, 2, 3, 4 ); * * // Create some more data in the table... * foreach ( $data as $value ) * { * // Create a new row each time and set it's contents to the actual value * $table[][0]->content = $value; * } * * // Set another border format for our headline row * $table[0]->borderFormat = 'headline'; * * // Set the content format for all cells of the 3rd row to "sum" * $table[2]->format = 'sum'; * * $table->outputTable(); * * * @property ezcConsoleTableOptions $options * Contains the options for this class. * @property int $width * Width of the table. * * @see ezcConsoleOutput * @package ConsoleTools * @version //autogen// * @mainclass */ class ezcConsoleTable implements Countable, Iterator, ArrayAccess { /** * Automatically wrap text to fit into a column. * @see ezcConsoleTable::$options */ const WRAP_AUTO = 1; /** * Do not wrap text. Columns will be extended to fit the largest text. * ATTENTION: This is risky! * @see ezcConsoleTable::$options */ const WRAP_NONE = 2; /** * Text will be cut to fit into a column. * @see ezcConsoleTable::$options */ const WRAP_CUT = 3; /** * Align text in the default direction. */ const ALIGN_DEFAULT = -1; /** * Align text in cells to the right. */ const ALIGN_LEFT = STR_PAD_RIGHT; /** * Align text in cells to the left. */ const ALIGN_RIGHT = STR_PAD_LEFT; /** * Align text in cells to the center. */ const ALIGN_CENTER = STR_PAD_BOTH; /** * The width given by settings must be used even if the data allows it smaller. */ const WIDTH_FIXED = 1; /** * The width given by settings is a maximum value, if data allows it, the table gets smaller. */ const WIDTH_MAX = 2; /** * Container to hold the properties * * @var array(string=>mixed) */ protected $properties; /** * The ezcConsoleOutput object to use. * * @var ezcConsoleOutput */ protected $outputHandler; /** * Collection of the rows that are contained in the table. * * @var array(ezcConsoleTableRow) */ protected $rows; /** * Tool object for multi-byte encoding safe string operations. * * @var ezcConsoleStringTool */ private $stringTool; /** * Creates a new table. * * @param ezcConsoleOutput $outHandler Output handler to utilize * @param int $width Overall width of the table (chars). * @param array $options Options * * @see ezcConsoleTable::$options * * @throws ezcBaseValueException On an invalid setting. */ public function __construct( ezcConsoleOutput $outHandler, $width, $options = array() ) { $this->rows = array(); $this->outputHandler = $outHandler; $this->stringTool = new ezcConsoleStringTool(); $this->__set( 'width', $width ); if ( $options instanceof ezcConsoleTableOptions ) { $this->properties['options'] = $options; } else if ( is_array( $options ) ) { $this->properties['options'] = new ezcConsoleTableOptions( $options ); } else { throw new ezcBaseValueException( "options", $options, "array" ); } } /** * Set new options. * This method allows you to change the options of the table. * * @param ezcConsoleTableOptions $options The options to set. * * @throws ezcBaseSettingNotFoundException * If you tried to set a non-existent option value. * @throws ezcBaseSettingValueException * If the value is not valid for the desired option. * @throws ezcBaseValueException * If you submit neither an array nor an instance of * ezcConsoleTableOptions. */ public function setOptions( $options = array() ) { if ( is_array( $options ) ) { $this->properties['options']->merge( $options ); } else if ( $options instanceof ezcConsoleTableOptions ) { $this->properties['options'] = $options; } else { throw new ezcBaseValueException( "options", $options, "instance of ezcConsoleTableOptions" ); } } /** * Returns the current options. * Returns the options currently set for this table. * * @return ezcConsoleTableOptions The current options. */ public function getOptions() { return $this->properties['options']; } /** * Returns the table in an array. * * Returns the entire table as an array of printable lines. Each element of * the array represents a physical line of the drawn table, including all * borders and stuff, so you can simply print the table using * * echo implode( "\n" , $table->getTable() ): * * which is basically what {@link ezcConsoleTable::outputTable()} does. * * @return array An array representation of the table. */ public function getTable() { return $this->generateTable(); } /** * Output the table. * Prints the complete table to the console. * * @return void */ public function outputTable() { echo implode( PHP_EOL, $this->generateTable() ); } /** * Returns the table in a string. * * @return string */ public function __toString() { return implode( PHP_EOL, $this->generateTable() ); } /** * Returns if the given offset exists. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. * * @param int $offset The offset to check. * @return bool True when the offset exists, otherwise false. * * @throws ezcBaseValueException * If a non numeric row ID is requested. */ #[ReturnTypeWillChange] public function offsetExists( $offset ) { if ( !is_int( $offset ) || $offset < 0 ) { throw new ezcBaseValueException( 'offset', $offset, 'int >= 0' ); } return isset( $this->rows[$offset] ); } // From here only interface method implementations follow, which are not intended for direct usage /** * Returns the element with the given offset. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. In case of the * ezcConsoleTable class this method always returns a valid row object * since it creates them on the fly, if a given item does not exist. * * @param int $offset The offset to check. * @return ezcConsoleTableCell * * @throws ezcBaseValueException * If a non numeric row ID is requested. */ #[ReturnTypeWillChange] public function offsetGet( $offset ) { $offset = ( $offset === null ) ? count( $this->rows ) : $offset; if ( !is_int( $offset ) || $offset < 0 ) { throw new ezcBaseValueException( 'offset', $offset, 'int >= 0 or null' ); } if ( !isset( $this->rows[$offset] ) ) { $this->rows[$offset] = new ezcConsoleTableRow(); } return $this->rows[$offset]; } /** * Set the element with the given offset. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. * * @param int $offset The offset to assign an item to. * @param ezcConsoleTableRow $value The row to assign. * @return void * * @throws ezcBaseValueException * If a non numeric row ID is requested. * @throws ezcBaseValueException * If the provided value is not of type {@link ezcConsoleTableRow}. */ #[ReturnTypeWillChange] public function offsetSet( $offset, $value ) { if ( !( $value instanceof ezcConsoleTableRow ) ) { throw new ezcBaseValueException( 'value', $value, 'ezcConsoleTableRow' ); } if ( !isset( $offset ) ) { $offset = count( $this ); } if ( !is_int( $offset ) || $offset < 0 ) { throw new ezcBaseValueException( 'offset', $offset, 'int >= 0' ); } $this->rows[$offset] = $value; } /** * Unset the element with the given offset. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. * * @param int $offset The offset to unset the value for. * @return void * * @throws ezcBaseValueException * If a non numeric row ID is requested. */ #[ReturnTypeWillChange] public function offsetUnset( $offset ) { if ( !is_int( $offset ) || $offset < 0 ) { throw new ezcBaseValueException( 'offset', $offset, 'int >= 0' ); } if ( isset( $this->rows[$offset] ) ) { unset( $this->rows[$offset] ); } } /** * Returns the number of cells in the row. * This method is part of the Countable interface to allow the usage of * PHP's count() function to check how many cells this row has. * * @return int Number of cells in this row. */ #[ReturnTypeWillChange] public function count() { $keys = array_keys( $this->rows ); return count( $keys ) > 0 ? ( end( $keys ) + 1 ) : 0; } /** * Returns the currently selected cell. * This method is part of the Iterator interface to allow access to the * cells of this row by iterating over it like an array (e.g. using * foreach). * * @return ezcConsoleTableCell The currently selected cell. */ #[ReturnTypeWillChange] public function current() { return current( $this->rows ); } /** * Returns the key of the currently selected cell. * This method is part of the Iterator interface to allow access to the * cells of this row by iterating over it like an array (e.g. using * foreach). * * @return int The key of the currently selected cell. */ #[ReturnTypeWillChange] public function key() { return key( $this->rows ); } /** * Returns the next cell and selects it or false on the last cell. * This method is part of the Iterator interface to allow access to the * cells of this row by iterating over it like an array (e.g. using * foreach). * * @return mixed ezcConsoleTableCell if the next cell exists, or false. */ #[ReturnTypeWillChange] public function next() { return next( $this->rows ); } /** * Selects the very first cell and returns it. * This method is part of the Iterator interface to allow access to the * cells of this row by iterating over it like an array (e.g. using * foreach). * * @return ezcConsoleTableCell The very first cell of this row. */ #[ReturnTypeWillChange] public function rewind() { return reset( $this->rows ); } /** * Returns if the current cell is valid. * This method is part of the Iterator interface to allow access to the * cells of this row by iterating over it like an array (e.g. using * foreach). * * @return ezcConsoleTableCell The very first cell of this row. */ #[ReturnTypeWillChange] public function valid() { return current( $this->rows ) !== false; } /** * Property read access. * * @param string $key Name of the property. * @return mixed Value of the property or null. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * @ignore */ public function __get( $key ) { switch ( $key ) { case 'options': case 'width': return $this->properties[$key]; default: break; } throw new ezcBasePropertyNotFoundException( $key ); } /** * Property write access. * * @param string $key Name of the property. * @param mixed $val The value for the property. * * @throws ezcBasePropertyNotFoundException * If a the value for the property options is not an instance of * @throws ezcBaseValueException * If a the value for a property is out of range. * @ignore */ public function __set( $key, $val ) { switch ( $key ) { case 'options': if ( !( $val instanceof ezcConsoleTableOptions ) ) { throw new ezcBaseValueException( $key, $val, 'ezcConsoleTableOptions' ); } $this->properties['options'] = $val; return; case 'width': if ( !is_int( $val ) || $val < 1 ) { throw new ezcBaseValueException( $key, $val, 'int > 0' ); } $this->properties[$key] = $val; return; default: break; } throw new ezcBasePropertyNotFoundException( $key ); } /** * Property isset access. * * @param string $key Name of the property. * @return bool True is the property is set, otherwise false. * @ignore */ public function __isset( $key ) { switch ( $key ) { case 'options': case 'width': case 'cols': return true; } return false; } /** * Generate the complete table as an array. * * @return array(string) The table. */ private function generateTable() { $colWidth = $this->getColWidths(); $table = array(); if ( $this->options->lineVertical !== null ) { $table[] = $this->generateBorder( $colWidth, ( isset( $this[0] ) ? $this[0]->borderFormat : 'default' ) ); } // Rows submitted by the user for ( $i = 0; $i < count( $this->rows ); $i++ ) { // Auto broken rows foreach ( $this->breakRows( $this->rows[$i], $colWidth ) as $brkRow => $brkCells ) { $table[] = $this->generateRow( $brkCells, $colWidth, $this->rows[$i] ); } $afterBorderFormat = isset( $this->rows[$i + 1] ) && $this->rows[$i + 1]->borderFormat != 'default' ? $this->rows[$i + 1]->borderFormat : $this->rows[$i]->borderFormat; if ( $this->options->lineVertical !== null ) { $table[] = $this->generateBorder( $colWidth, $afterBorderFormat ); } } // Empty tables need closing border if ( $this->options->lineVertical !== null && count( $this->rows ) == null ) { $table[] = $this->generateBorder( $colWidth, 'default' ); } return $table; } /** * Generate top/bottom borders of rows. * * @param array(int) $colWidth Array of column width. * @param string $format Format name. * @return string The Border string. */ private function generateBorder( $colWidth, $format ) { $border = ''; foreach ( $colWidth as $col => $width ) { $border .= ( $this->options->lineHorizontal !== null ? $this->properties['options']->corner : '' ) . str_repeat( $this->properties['options']->lineVertical, $width + ( 2 * iconv_strlen( $this->properties['options']->colPadding, 'UTF-8' ) ) ); } $border .= ( $this->options->lineHorizontal !== null ? $this->properties['options']->corner : '' ); return $this->formatText( $border, $format ); } /** * Generate a single physical row. * This method generates the string for a single physical table row. * * @param array(string) $cells Cells of the row. * @param array(int) $colWidth Calculated columns widths. * @param ezcConsoleTableRow $row The row to generate. * @return string The row. */ private function generateRow( $cells, $colWidth, $row ) { $rowData = ''; for ( $cell = 0; $cell < count( $colWidth ); $cell++ ) { $align = $this->determineAlign( $row, $cell ); $format = $this->determineFormat( $row, $cell ); $borderFormat = $this->determineBorderFormat( $row ); $data = isset( $cells[$cell] ) ? $cells[$cell] : ''; $rowData .= $this->formatText( $this->properties['options']->lineHorizontal, $borderFormat ); $rowData .= $this->properties['options']->colPadding; $rowData .= $this->formatText( $this->stringTool->strPad( $data, $colWidth[$cell], ' ', $align ), $format ); $rowData .= $this->properties['options']->colPadding; } $rowData .= $this->formatText( $this->properties['options']->lineHorizontal, $row->borderFormat ); return $rowData; } /** * Determine the alignment of a cell. * Walks the inheritance path upwards to determine the alignment of a * cell. Checks first, if the cell has it's own alignment (apart from * ezcConsoleTable::ALIGN_DEFAULT). If not, checks the row for an * alignment setting and uses the default alignment if not found. * * @param ezcConsoleTableRow $row The row this cell belongs to. * @param int $cellId Index of the desired cell. * @return int An alignement constant (ezcConsoleTable::ALIGN_*). */ private function determineAlign( $row, $cellId = 0 ) { return ( $row[$cellId]->align !== ezcConsoleTable::ALIGN_DEFAULT ? $row[$cellId]->align : ( $row->align !== ezcConsoleTable::ALIGN_DEFAULT ? $row->align : ( $this->properties['options']->defaultAlign !== ezcConsoleTable::ALIGN_DEFAULT ? $this->properties['options']->defaultAlign : ezcConsoleTable::ALIGN_LEFT ) ) ); } /** * Determine the format of a cells content. * Walks the inheritance path upwards to determine the format of a * cells content. Checks first, if the cell has it's own format (apart * from 'default'). If not, checks the row for a format setting and * uses the default format if not found. * * @param ezcConsoleTableRow $row The row this cell belongs to. * @param int $cellId Index of the desired cell. * @return string A format name. */ private function determineFormat( $row, $cellId ) { return ( $row[$cellId]->format != 'default' ? $row[$cellId]->format : ( $row->format !== 'default' ? $row->format : $this->properties['options']->defaultFormat ) ); } /** * Determine the format of a rows border. * Walks the inheritance path upwards to determine the format of a * rows border. Checks first, if the row has it's own format (apart * from 'default'). If not, uses the default format. * * @param ezcConsoleTableRow $row The row this cell belongs to. * @return string A format name. */ private function determineBorderFormat( $row ) { return $row->borderFormat !== 'default' ? $row->borderFormat : $this->properties['options']->defaultBorderFormat; } /** * Returns auto broken rows from an array of cells. * The data provided by a user may not fit into a cell calculated by the * class. In this case, the data can be automatically wrapped. The table * row then spans over multiple physical console lines. * * @param array(string) $cells Array of cells in one row. * @param array(int) $colWidth Columns widths array. * @return array(string) Physical rows generated out of this row. */ private function breakRows( $cells, $colWidth ) { $rows = array(); // Iterate through cells of the row foreach ( $colWidth as $cell => $width ) { $data = $cells[$cell]->content; // Physical row id, start with 0 for each row $row = 0; // Split into multiple physical rows if manual breaks exist $dataLines = explode( "\n", $data ); foreach ( $dataLines as $dataLine ) { // Does the physical row fit? if ( iconv_strlen( $dataLine, 'UTF-8' ) > ( $colWidth[$cell] ) ) { switch ( $this->properties['options']->colWrap ) { case ezcConsoleTable::WRAP_AUTO: $subLines = explode( "\n", $this->stringTool->wordwrap( $dataLine, $colWidth[$cell], "\n", true ) ); foreach ( $subLines as $lineNo => $line ) { $rows[$row++][$cell] = $line; } break; case ezcConsoleTable::WRAP_CUT: $rows[$row++][$cell] = iconv_substr( $dataLine, 0, $colWidth[$cell], 'UTF-8' ); break; case ezcConsoleTable::WRAP_NONE: default: $rows[$row++][$cell] = $dataLine; break; } } else { $rows[$row++][$cell] = $dataLine; } } } return $rows; } /** * Determine width of each single column. * * @return void */ private function getColWidths() { if ( is_array( $this->properties['options']->colWidth ) ) { return $this->properties['options']->colWidth; } // Determine number of columns: $colCount = 0; foreach ( $this->rows as $row ) { $colCount = max( sizeof( $row ), $colCount ); } if ( $colCount === 0 ) { return array( $this->width ); } $borderWidth = 0; if ( is_string( $this->properties['options']->lineHorizontal ) ) { $borderWidth = iconv_strlen( $this->properties['options']->lineHorizontal, 'UTF-8' ); } // Subtract border and padding chars from global width $globalWidth = $this->width - ( // Per column: 2 * border padding + 1 border $colCount * ( 2 * iconv_strlen( $this->properties['options']->colPadding, 'UTF-8' ) + $borderWidth ) // 1 Additional border ) - $borderWidth; // Width of a column if each is made equal $colNormWidth = round( $globalWidth / $colCount ); $colMaxWidth = array(); // Determine the longest data for each column foreach ( $this->rows as $row => $cells ) { foreach ( $cells as $col => $cell ) { $contentLength = 0; foreach ( explode( PHP_EOL, $cell->content ) as $contentRow ) { $contentLength = max( $contentLength, iconv_strlen( $contentRow, 'UTF-8' ) ); } $colMaxWidth[$col] = isset( $colMaxWidth[$col] ) ? max( $colMaxWidth[$col], $contentLength ) : $contentLength; } } $colWidth = array(); $colWidthOverflow = array(); $spareWidth = 0; // Make columns best fit foreach ( $colMaxWidth as $col => $maxWidth ) { // Does the largest data of the column fit into the average size // + what we have in spare from earlier columns? if ( $maxWidth <= ( $colNormWidth + $spareWidth ) ) { // We fit in, make the column as large as necessary $colWidth[$col] = $maxWidth; $spareWidth += ( $colNormWidth - $maxWidth ); } else { // Does not fit, use maximal possible width $colWidth[$col] = $colNormWidth + $spareWidth; $spareWidth = 0; // Store overflow for second processing step $colWidthOverflow[$col] = $maxWidth - $colWidth[$col]; } } // Do we have spare to give to the columns again? if ( $spareWidth > 0 ) { // Second processing step if ( count( $colWidthOverflow ) > 0 ) { $overflowSum = array_sum( $colWidthOverflow ); foreach ( $colWidthOverflow as $col => $overflow ); { $colWidth[$col] += floor( $overflow / $overflowSum * $spareWidth ); } } elseif ( $this->properties['options']->widthType === ezcConsoleTable::WIDTH_FIXED ) { $widthSum = array_sum( $colWidth ); foreach ( $colWidth as $col => $width ) { $colWidth[$col] += floor( $width / $widthSum * $spareWidth ); } } } // Finally sanitize values from rounding issues, if necessary if ( ( $colSum = array_sum( $colWidth ) ) != $globalWidth && $this->properties['options']->widthType === ezcConsoleTable::WIDTH_FIXED ) { $colWidth[count( $colWidth ) - 1] -= $colSum - $globalWidth; } return $colWidth; } /** * Returns the given $text formatted with $format. * * In case $useFormats is set to false in the output handler, the text is * returned as given, without any formatting. * * @param string $text * @param string $format * @return string */ private function formatText( $text, $format ) { if ( $this->outputHandler->options->useFormats ) { return $this->outputHandler->formatText( $text, $format ); } else { return $text; } } } ?> ConsoleTools-1.7.5/src/table/000077500000000000000000000000001470000742400160235ustar00rootroot00000000000000ConsoleTools-1.7.5/src/table/cell.php000066400000000000000000000124011470000742400174510ustar00rootroot00000000000000mixed) */ protected $properties; /** * Create a new ezcConsoleProgressbarCell. * Creates a new ezcConsoleProgressbarCell. You can either submit the cell * data through the constructor or set them as properties. * * @param string $content Content of the cell. * @param string $format Format to display the cell with. * @param mixed $align Alignment of the content in the cell. * @return void */ public function __construct( $content = '', $format = 'default', $align = ezcConsoleTable::ALIGN_DEFAULT ) { $this->__set( 'content', $content ); $this->__set( 'format', $format ); $this->__set( 'align', $align ); } /** * Property read access. * * @param string $key Name of the property. * @return mixed Value of the property or null. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * @ignore */ public function __get( $key ) { if ( isset( $this->properties[$key] ) ) { return $this->properties[$key]; } throw new ezcBasePropertyNotFoundException( $key ); } /** * Property write access. * * @param string $key Name of the property. * @param mixed $val The value for the property. * * @throws ezcBaseValueException * If a the value submitted for the align is not in the range of * {@link ezcConsoleTable::ALIGN_LEFT}, * {@link ezcConsoleTable::ALIGN_CENTER}, * {@link ezcConsoleTable::ALIGN_RIGHT}, * {@link ezcConsoleTable::ALIGN_DEFAULT} * * @ignore */ public function __set( $key, $val ) { switch ( $key ) { case 'content': if ( is_string( $val ) === false ) { throw new ezcBaseValueException( $key, $val, "string" ); } break; case 'format': if ( is_string( $val ) === false || strlen( $val ) < 1 ) { throw new ezcBaseValueException( $key, $val, "string, length > 0" ); } break; case 'align': if ( $val !== ezcConsoleTable::ALIGN_LEFT && $val !== ezcConsoleTable::ALIGN_CENTER && $val !== ezcConsoleTable::ALIGN_RIGHT && $val !== ezcConsoleTable::ALIGN_DEFAULT ) { throw new ezcBaseValueException( $key, $val, 'ezcConsoleTable::ALIGN_DEFAULT, ezcConsoleTable::ALIGN_LEFT, ezcConsoleTable::ALIGN_CENTER, ezcConsoleTable::ALIGN_RIGHT' ); } break; default: throw new ezcBasePropertyNotFoundException( $key ); } $this->properties[$key] = $val; return; } /** * Property isset access. * * @param string $key Name of the property. * @return bool True is the property is set, otherwise false. * @ignore */ public function __isset( $key ) { switch ( $key ) { case 'content': case 'format': case 'align': return true; default: break; } return false; } } ?> ConsoleTools-1.7.5/src/table/row.php000066400000000000000000000302171470000742400173460ustar00rootroot00000000000000 * // Create new table row * $row = new ezcConsoleTableRow(); * * // Set default format of the row's cells * $row->format = 'headline'; * * // On the fly create the cell no 0 * $row[0]->content = 'Name'; * // On the fly create the cell no 1 * $row[1]->content = 'Cellphone'; * * // Change a setting on cell 0 * $row[0]->align = ezcConsoleTable::ALIGN_CENTER; * * // Iterate through the row's cells. * foreach ( $row as $cell ) * { * var_dump( $cell ); * } * * // Set the default align property for all cells in the row * $row->align = ezcConsoleTable::ALIGN_CENTER; * * * * This class stores the rows for the {@link ezcConsoleTable} class. * * @property string $borderFormat * Set the format applied to the borders of this row. See * {@link ezcConsoleOutput} * @property string $format * Format applied to cell contents of cells marked with * format "default" in this row. * @property mixed $align * Alignment applied to cells marked with * ezcConsoleTable::ALIGN_DEFAULT. * * @package ConsoleTools * @version //autogen// */ class ezcConsoleTableRow implements Countable, Iterator, ArrayAccess { /** * Container to hold the properties * * @var array(string=>mixed) */ protected $properties; /** * The cells of the row. * * @var array(ezcConsoleTableCell) */ protected $cells = array(); /** * Create a new ezcConsoleProgressbarRow. * Creates a new ezcConsoleProgressbarRow. * * This method takes any number of {@link ezcConsoleTableCell} objects as * parameter, which will be added as table cells to the row in their * specified order. * * @throws ezcBaseValueException * If a parameter is not of type {@link ezcConsoleTableCell}. */ public function __construct() { $this->properties['borderFormat'] = 'default'; $this->properties['format'] = 'default'; $this->properties['align'] = ezcConsoleTable::ALIGN_DEFAULT; if ( func_num_args() > 0 ) { foreach ( func_get_args() as $id => $arg ) { if ( !( $arg instanceof ezcConsoleTableCell ) ) { throw new ezcBaseValueException( 'Parameter'.$id, $arg, 'ezcConsoleTableCell' ); } $this->cells[] = $arg; } } } /** * Returns if the given offset exists. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. * * @param int $offset The offset to check. * @return bool True when the offset exists, otherwise false. * * @throws ezcBaseValueException * If a non numeric cell ID is requested. */ #[ReturnTypeWillChange] public function offsetExists( $offset ) { if ( !is_int( $offset ) || $offset < 0 ) { throw new ezcBaseValueException( 'offset', $offset, 'int >= 0' ); } return isset( $this->cells[$offset] ); } /** * Returns the element with the given offset. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. In case of the * ezcConsoleTableRow class this method always returns a valid cell object * since it creates them on the fly, if a given item does not exist. * * @param int $offset The offset to check. * @return ezcConsoleTableCell * * @throws ezcBaseValueException * If a non numeric cell ID is requested. */ #[ReturnTypeWillChange] public function offsetGet( $offset ) { if ( !isset( $offset ) ) { $offset = count( $this ); $this->cells[$offset] = new ezcConsoleTableCell(); } if ( !is_int( $offset ) || $offset < 0 ) { throw new ezcBaseValueException( 'offset', $offset, 'int >= 0' ); } if ( !isset( $this->cells[$offset] ) ) { $this->cells[$offset] = new ezcConsoleTableCell(); } return $this->cells[$offset]; } /** * Set the element with the given offset. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. * * @param int $offset The offset to assign an item to. * @param ezcConsoleTableCell $value The cell to assign. * @return void * * @throws ezcBaseValueException * If a non numeric cell ID is requested. * @throws ezcBaseValueException * If the provided value is not of type {@ling ezcConsoleTableCell}. */ #[ReturnTypeWillChange] public function offsetSet( $offset, $value ) { if ( !( $value instanceof ezcConsoleTableCell ) ) { throw new ezcBaseValueException( 'value', $value, 'ezcConsoleTableCell' ); } if ( !isset( $offset ) ) { $offset = count( $this ); } if ( !is_int( $offset ) || $offset < 0 ) { throw new ezcBaseValueException( 'offset', $offset, 'int >= 0' ); } $this->cells[$offset] = $value; } /** * Unset the element with the given offset. * This method is part of the ArrayAccess interface to allow access to the * data of this object as if it was an array. * * @param int $offset The offset to unset the value for. * @return void * * @throws ezcBaseValueException * If a non numeric cell ID is requested. */ #[ReturnTypeWillChange] public function offsetUnset( $offset ) { if ( !is_int( $offset ) || $offset < 0 ) { throw new ezcBaseValueException( 'offset', $offset, 'int >= 0' ); } if ( isset( $this->cells[$offset] ) ) { unset( $this->cells[$offset] ); } } /** * Returns the number of cells in the row. * This method is part of the Countable interface to allow the usage of * PHP's count() function to check how many cells this row has. * * @return int Number of cells in this row. */ #[ReturnTypeWillChange] public function count() { $keys = array_keys( $this->cells ); return count( $keys ) > 0 ? ( end( $keys ) + 1 ) : 0; } /** * Returns the currently selected cell. * This method is part of the Iterator interface to allow acces to the * cells of this row by iterating over it like an array (e.g. using * foreach). * * @return ezcConsoleTableCell The currently selected cell. */ #[ReturnTypeWillChange] public function current() { return current( $this->cells ); } /** * Returns the key of the currently selected cell. * This method is part of the Iterator interface to allow acces to the * cells of this row by iterating over it like an array (e.g. using * foreach). * * @return int The key of the currently selected cell. */ #[ReturnTypeWillChange] public function key() { return key( $this->cells ); } /** * Returns the next cell and selects it or false on the last cell. * This method is part of the Iterator interface to allow acces to the * cells of this row by iterating over it like an array (e.g. using * foreach). * * @return mixed ezcConsoleTableCell if the next cell exists, or false. */ #[ReturnTypeWillChange] public function next() { return next( $this->cells ); } /** * Selects the very first cell and returns it. * This method is part of the Iterator interface to allow acces to the * cells of this row by iterating over it like an array (e.g. using * foreach). * * @return ezcConsoleTableCell The very first cell of this row. */ #[ReturnTypeWillChange] public function rewind() { return reset( $this->cells ); } /** * Returns if the current cell is valid. * This method is part of the Iterator interface to allow acces to the * cells of this row by iterating over it like an array (e.g. using * foreach). * * @return ezcConsoleTableCell The very first cell of this row. */ #[ReturnTypeWillChange] public function valid() { return current( $this->cells ) !== false; } /** * Property read access. * * @param string $key Name of the property. * @return mixed Value of the property or null. * * @throws ezcBasePropertyNotFoundException * If the the desired property is not found. * @ignore */ public function __get( $key ) { if ( isset( $this->properties[$key] ) ) { return $this->properties[$key]; } throw new ezcBasePropertyNotFoundException( $key ); } /** * Property write access. * * @param string $key Name of the property. * @param mixed $val The value for the property. * * @throws ezcBaseValueException * If a the value submitted for the align is not in the range of * {@link ezcConsoleTable::ALIGN_LEFT}, * {@link ezcConsoleTable::ALIGN_CENTER}, * {@link ezcConsoleTable::ALIGN_RIGHT}, * {@link ezcConsoleTable::ALIGN_DEFAULT} * * @ignore */ public function __set( $key, $val ) { switch ( $key ) { case 'format': case 'borderFormat': if ( is_string( $val ) === false || strlen( $val ) < 1 ) { throw new ezcBaseValueException( $key, $val, "string, length > 0" ); } break; case 'align': if ( $val !== ezcConsoleTable::ALIGN_LEFT && $val !== ezcConsoleTable::ALIGN_CENTER && $val !== ezcConsoleTable::ALIGN_RIGHT && $val !== ezcConsoleTable::ALIGN_DEFAULT ) { throw new ezcBaseValueException( $key, $val, 'ezcConsoleTable::ALIGN_DEFAULT, ezcConsoleTable::ALIGN_LEFT, ezcConsoleTable::ALIGN_CENTER, ezcConsoleTable::ALIGN_RIGHT' ); } break; default: throw new ezcBasePropertyNotFoundException( $key ); } $this->properties[$key] = $val; } /** * Property isset access. * * @param string $key Name of the property. * @return bool True is the property is set, otherwise false. * @ignore */ public function __isset( $key ) { switch ( $key ) { case 'format': case 'borderFormat': case 'align': return true; default: return false; } } } ?> ConsoleTools-1.7.5/src/tools/000077500000000000000000000000001470000742400160745ustar00rootroot00000000000000ConsoleTools-1.7.5/src/tools/string.php000066400000000000000000000143541470000742400201220ustar00rootroot00000000000000= $width ) { $newtext .= iconv_substr( $str, $laststart, $current - $laststart, 'UTF-8' ) . $break; $laststart = $current + 1; } $lastspace = $current; } // Special cut case, if no space has been seen else if ( $current - $laststart >= $width && $cut && $laststart >= $lastspace ) { $newtext .= iconv_substr( $str, $laststart, $current - $laststart, 'UTF-8' ) . $break; $laststart = $lastspace = $current; } // Usual case that line got longer than expected else if ( $current - $laststart >= $width && $laststart < $lastspace ) { $newtext .= iconv_substr( $str, $laststart, $lastspace - $laststart, 'UTF-8' ) . $break; // $laststart = $lastspace = $lastspace + 1; $laststart = ++$lastspace; } } // Rest of the string if ( $laststart !== $current ) { $newtext .= iconv_substr( $str, $laststart, $current - $laststart, 'UTF-8' ); } return $newtext; } /** * Binary safe str_pad() replacement. * * This method is a multi-byte encoding safe replacement for the PHP * function str_pad(). It mimics exactly the behavior of str_pad(), but * uses iconv_* functions with UTF-8 encoding. The parameters received by * this method equal the parameters of {@link http://php.net/str_pad * str_pad()}. Note: Make sure to hand only UTF-8 encoded content to this * method. * * @param string $input * @param int $padLength * @param string $padString * @param int $padType * @return string */ public function strPad( $input, $padLength, $padString = ' ', $padType = STR_PAD_RIGHT ) { $input = (string) $input; $strLen = iconv_strlen( $input, 'UTF-8' ); $padStrLen = iconv_strlen( $padString, 'UTF-8' ); if ( $strLen >= $padLength ) { return $input; } if ( $padType === STR_PAD_BOTH ) { return $this->strPad( $this->strPad( $input, $strLen + ceil( ( $padLength - $strLen ) / 2 ), $padString ), $padLength, $padString, STR_PAD_LEFT ); } $fullStrRepeats = (int) ( ( $padLength - $strLen ) / $padStrLen ); $partlyPad = iconv_substr( $padString, 0, ( ( $padLength - $strLen ) % $padStrLen ) ); $padding = str_repeat( $padString, $fullStrRepeats ) . $partlyPad; switch ( $padType ) { case STR_PAD_LEFT: return $padding . $input; case STR_PAD_RIGHT: default: return $input . $padding; } } } ?> ConsoleTools-1.7.5/tests/000077500000000000000000000000001470000742400153075ustar00rootroot00000000000000ConsoleTools-1.7.5/tests/argument_test.php000066400000000000000000000150751470000742400207110ustar00rootroot00000000000000assertEquals( "testname", $arg->name ); $this->assertEquals( ezcConsoleInput::TYPE_INT, $arg->type ); $this->assertEquals( "Little short help", $arg->shorthelp ); $this->assertEquals( "Little long help", $arg->longhelp ); $this->assertEquals( false, $arg->mandatory ); $this->assertEquals( array( "test" ), $arg->default ); $this->assertEquals( true, $arg->multiple ); } public function testConstructorFailure() { $exceptionThrown = false; try { $arg = new ezcConsoleArgument( 23 ); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid name." ); $exceptionThrown = false; try { $arg = new ezcConsoleArgument( "" ); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid name." ); } public function testGetAccessSuccess() { $arg = new ezcConsoleArgument( "testname" ); $this->assertEquals( "testname", $arg->name ); $this->assertEquals( ezcConsoleInput::TYPE_STRING, $arg->type ); $this->assertEquals( "No help available.", $arg->shorthelp ); $this->assertEquals( "There is no help for this argument available.", $arg->longhelp ); $this->assertEquals( true, $arg->mandatory ); $this->assertEquals( false, $arg->multiple ); $this->assertEquals( null, $arg->default ); $this->assertEquals( null, $arg->value ); } public function testGetAccessFailure() { $arg = new ezcConsoleArgument( "testname" ); try { echo $arg->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "ezcBasePropertyNotFoundException not found on access to invalid property foo." ); } public function testSetAccessSuccess() { $arg = new ezcConsoleArgument( "testname" ); $this->assertSetProperty( $arg, "type", array( ezcConsoleInput::TYPE_STRING, ezcConsoleInput::TYPE_INT ), "ezcBaseValueException" ); $this->assertSetProperty( $arg, "shorthelp", array( "", "foo" ), "ezcBaseValueException" ); $this->assertSetProperty( $arg, "longhelp", array( "", "foo" ), "ezcBaseValueException" ); $this->assertSetProperty( $arg, "mandatory", array( true, false ), "ezcBaseValueException" ); $this->assertSetProperty( $arg, "multiple", array( true, false ), "ezcBaseValueException" ); $this->assertSetProperty( $arg, "default", array( "", "foo", array( "foo", "bar" ), null ), "ezcBaseValueException" ); $this->assertSetProperty( $arg, "value", array( "", "foo", array( "foo", "bar" ), null ), "ezcBaseValueException" ); } public function testSetAccessFailure() { $arg = new ezcConsoleArgument( "testname" ); $this->assertSetPropertyFails( $arg, "name", array( "", "foo", 1, true, array(), new stdClass() ), "ezcBasePropertyPermissionException" ); $this->assertSetPropertyFails( $arg, "type", array( "", "foo", 23, true, array(), new stdClass() ), "ezcBaseValueException" ); $this->assertSetPropertyFails( $arg, "shorthelp", array( 23, true, array(), new stdClass() ), "ezcBaseValueException" ); $this->assertSetPropertyFails( $arg, "longhelp", array( 23, true, array(), new stdClass() ), "ezcBaseValueException" ); $this->assertSetPropertyFails( $arg, "mandatory", array( "", "foo", 23, array(), new stdClass() ), "ezcBaseValueException" ); $this->assertSetPropertyFails( $arg, "multiple", array( "", "foo", 23, array(), new stdClass() ), "ezcBaseValueException" ); $this->assertSetPropertyFails( $arg, "default", array( new stdClass() ), "ezcBaseValueException" ); $this->assertSetPropertyFails( $arg, "value", array( new stdClass() ), "ezcBaseValueException" ); $this->assertSetPropertyFails( $arg, "foo", array( "foo" ), "ezcBasePropertyNotFoundException" ); } } ?> ConsoleTools-1.7.5/tests/arguments_test.php000066400000000000000000000257211470000742400210730ustar00rootroot00000000000000 "arg 0", 2 => "arg 2", 4 => "arg 4", 5 => "arg 5", 6 => "arg 6" ); reset( $refNames ); foreach ( $args as $name => $arg ) { $this->assertEquals( key( $refNames ), $name, "Name not correctly returned as key." ); $this->assertEquals( current( $refNames ), $arg->name, "Arg not correctly returned." ); next( $refNames ); } } public function testOffsetSetFailure() { $args = new ezcConsoleArguments(); $exceptionThrown = false; try { $args[0] = new stdClass(); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid value object." ); $exceptionThrown = false; try { $args[0] = array(); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid value array." ); $exceptionThrown = false; try { $args[0] = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid value scalar." ); $exceptionThrown = false; try { $args[""] = new ezcConsoleArgument( "testname" ); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid offset string." ); $exceptionThrown = false; try { $args[0] = new ezcConsoleArgument( "testname" ); $args[0] = new ezcConsoleArgument( "testname 2" ); } catch ( ezcConsoleArgumentAlreadyRegisteredException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcConsoleArgumentAlreadyRegisteredException not thrown double registered offset." ); $exceptionThrown = false; try { $args[2] = new ezcConsoleArgument( "testname" ); $args[3] = new ezcConsoleArgument( "testname" ); } catch ( ezcConsoleArgumentAlreadyRegisteredException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcConsoleArgumentAlreadyRegisteredException not thrown double registered name." ); } public function testOffsetGetSuccess() { $args = new ezcConsoleArguments(); $args[0] = new ezcConsoleArgument( "arg 0" ); $args[1] = new ezcConsoleArgument( "arg 1" ); $args[5] = new ezcConsoleArgument( "arg 5" ); $args[] = new ezcConsoleArgument( "arg 6" ); $args[4] = new ezcConsoleArgument( "arg 4" ); $args[2] = new ezcConsoleArgument( "arg 2" ); // This is basically unset! $args[1] = null; $this->assertEquals( $args[0], new ezcConsoleArgument( "arg 0" ) ); $this->assertEquals( $args[2], new ezcConsoleArgument( "arg 2" ) ); $this->assertEquals( $args[4], new ezcConsoleArgument( "arg 4" ) ); $this->assertEquals( $args[5], new ezcConsoleArgument( "arg 5" ) ); $this->assertEquals( $args[6], new ezcConsoleArgument( "arg 6" ) ); $this->assertEquals( $args["arg 0"], new ezcConsoleArgument( "arg 0" ) ); $this->assertEquals( $args["arg 2"], new ezcConsoleArgument( "arg 2" ) ); $this->assertEquals( $args["arg 4"], new ezcConsoleArgument( "arg 4" ) ); $this->assertEquals( $args["arg 5"], new ezcConsoleArgument( "arg 5" ) ); $this->assertEquals( $args["arg 6"], new ezcConsoleArgument( "arg 6" ) ); } public function testOffsetGetFailure() { $args = new ezcConsoleArguments(); $args[0] = new ezcConsoleArgument( "arg 0" ); $args[1] = new ezcConsoleArgument( "arg 1" ); $exceptionThrown = false; try { var_dump( $args[true] ); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on get access with invalid offset true." ); $exceptionThrown = false; try { var_dump( $args[2] ); } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseProperytNotFoundException not thrown on get access with non-existent integer offset 2." ); $exceptionThrown = false; try { var_dump( $args["arg 2"] ); } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseProperytNotFoundException not thrown on get access with non-existent string offset 'arg 2'." ); } public function testOffsetUnsetSuccess() { $args = new ezcConsoleArguments(); $args[0] = new ezcConsoleArgument( "arg 0" ); $args[1] = new ezcConsoleArgument( "arg 1" ); $args[5] = new ezcConsoleArgument( "arg 5" ); $args[] = new ezcConsoleArgument( "arg 6" ); $args[4] = new ezcConsoleArgument( "arg 4" ); $args[2] = new ezcConsoleArgument( "arg 2" ); // This is basically unset! $args[1] = null; unset( $args[0], $args[2], $args[4], $args[5], $args[6] ); $this->assertAttributeEquals( array(), "ordered", $args, "Unset on ordered array failed." ); $this->assertAttributeEquals( array(), "named", $args, "Unset on named array failed." ); } public function testOffsetUnsetFailure() { $args = new ezcConsoleArguments(); $args[0] = new ezcConsoleArgument( "arg 0" ); $args[1] = new ezcConsoleArgument( "arg 1" ); $exceptionThrown = false; try { unset( $args["foo"] ); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on unset access to invald string offset." ); $exceptionThrown = false; try { unset( $args[true] ); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on unset access to invald boolean offset." ); } public function testOffsetExistsSuccess() { $args = new ezcConsoleArguments(); $args[0] = new ezcConsoleArgument( "arg 0" ); $args[1] = new ezcConsoleArgument( "arg 1" ); $args[5] = new ezcConsoleArgument( "arg 5" ); $args[] = new ezcConsoleArgument( "arg 6" ); $this->assertTrue( isset( $args[0] ) ); $this->assertTrue( isset( $args["arg 0"] ) ); $this->assertTrue( isset( $args[1] ) ); $this->assertTrue( isset( $args["arg 1"] ) ); $this->assertTrue( isset( $args[5] ) ); $this->assertTrue( isset( $args["arg 5"] ) ); $this->assertTrue( isset( $args[6] ) ); $this->assertTrue( isset( $args["arg 6"] ) ); $this->assertFalse( isset( $args[2] ) ); $this->assertFalse( isset( $args["arg 2"] ) ); } public function testOffsetExistsFailure() { $args = new ezcConsoleArguments(); try { isset( $args[true] ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "ezcBaseValueException not thrown on isset access to invalid offset true." ); } public function testIterator() { $refNames = array( 0 => "arg 0", 1 => "arg 1", 2 => "arg 2", 5 => "arg 5", 10 => "arg 10" ); $args = new ezcConsoleArguments(); foreach ( $refNames as $key => $name ) { $args[$key] = new ezcConsoleArgument( $name ); } reset( $refNames ); foreach ( $args as $key => $arg ) { $this->assertEquals( key( $refNames ), $key ); $this->assertEquals( current( $refNames ), $arg->name ); next( $refNames ); } reset( $refNames ); foreach ( $args as $key => $arg ) { $this->assertEquals( key( $refNames ), $key ); $this->assertEquals( current( $refNames ), $arg->name ); next( $refNames ); } } public function testCount() { $refNames = array( 0 => "arg 0", 1 => "arg 1", 2 => "arg 2", 5 => "arg 5", 10 => "arg 10" ); $args = new ezcConsoleArguments(); foreach ( $refNames as $key => $name ) { $args[$key] = new ezcConsoleArgument( $name ); } $this->assertEquals( 5, count( $args ) ); } } ?> ConsoleTools-1.7.5/tests/bootstrap.php000066400000000000000000000004341470000742400200360ustar00rootroot00000000000000text = "Please choose a possibility:\n"; $opts->validator = new ezcConsoleMenuDialogDefaultValidator( array( "A" => "Selection A", "B" => "Selection B", "C" => "Selection C", "D" => "Selection D", "Z" => "Selection Z", ), "Z" ); $dialog = new ezcConsoleMenuDialog( $out, $opts ); $res = ezcConsoleDialogViewer::displayDialog( $dialog ); echo "User seletced $res\n"; ?> ConsoleTools-1.7.5/tests/data/posix/ezcConsoleMenuDialogTest__testDialog1_res.php000066400000000000000000000025231470000742400302560ustar00rootroot00000000000000 'Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 1 => 'Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 2 => 'User seletced Z ', ); ?>ConsoleTools-1.7.5/tests/data/posix/ezcConsoleMenuDialogTest__testDialog2.php000066400000000000000000000031621470000742400274060ustar00rootroot00000000000000text = "Please choose a possibility:\n"; $opts->validator = new ezcConsoleMenuDialogDefaultValidator( array( "A" => "Selection A", "B" => "Selection B", "C" => "Selection C", "D" => "Selection D", "Z" => "Selection Z", ), "Z", ezcConsoleMenuDialogDefaultValidator::CONVERT_UPPER ); $dialog = new ezcConsoleMenuDialog( $out, $opts ); $res = ezcConsoleDialogViewer::displayDialog( $dialog ); echo "User seletced $res\n"; ?> ConsoleTools-1.7.5/tests/data/posix/ezcConsoleMenuDialogTest__testDialog2_res.php000066400000000000000000000025231470000742400302570ustar00rootroot00000000000000 'Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 1 => 'Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 2 => 'User seletced Z ', ); ?>ConsoleTools-1.7.5/tests/data/posix/ezcConsoleMenuDialogTest__testDialog3.php000066400000000000000000000032471470000742400274130ustar00rootroot00000000000000text = "Please choose a possibility:\n"; $opts->validator = new ezcConsoleMenuDialogDefaultValidator( array( "A" => "Selection A", "B" => "Selection B", "C" => "Selection C", "D" => "Selection D", "Z" => "Selection Z", ), "Z", ezcConsoleMenuDialogDefaultValidator::CONVERT_UPPER ); $dialog = new ezcConsoleMenuDialog( $out, $opts ); while ( ( $res = ezcConsoleDialogViewer::displayDialog( $dialog ) ) !== 'Z' ) { echo "User seletced $res\n"; } echo "User quitted\n"; ?> ConsoleTools-1.7.5/tests/data/posix/ezcConsoleMenuDialogTest__testDialog3_res.php000066400000000000000000000034511470000742400302610ustar00rootroot00000000000000 'Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 1 => 'User seletced A Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 2 => 'Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 3 => 'User seletced B Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 4 => 'Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 5 => 'User quitted ', ); ?>ConsoleTools-1.7.5/tests/data/posix/ezcConsoleMenuDialogTest__testDialog4.php000066400000000000000000000034231470000742400274100ustar00rootroot00000000000000text = "Please choose a possibility:\n"; $opts->validator = new ezcConsoleMenuDialogDefaultValidator( array( "A" => "Selection A", "B" => "Selection B", "C" => "Selection C", "D" => "Selection D", "Z" => "Selection Z", ), "Z", ezcConsoleMenuDialogDefaultValidator::CONVERT_UPPER ); $dialog = new ezcConsoleMenuDialog( $out, $opts ); try { while ( ( $res = ezcConsoleDialogViewer::displayDialog( $dialog ) ) !== 'Z' ) { echo "User seletced $res\n"; } } catch ( ezcConsoleDialogAbortException $e ) { echo "User manually aborted\n"; } echo "User quitted\n"; ?> ConsoleTools-1.7.5/tests/data/posix/ezcConsoleMenuDialogTest__testDialog4_res.php000066400000000000000000000030111470000742400302520ustar00rootroot00000000000000 'Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 1 => 'User seletced A Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 2 => 'Please choose a possibility: A) Selection A B) Selection B C) Selection C D) Selection D Z) Selection Z Select: [Z] ', 3 => 'User manually aborted User quitted ', ); ?>ConsoleTools-1.7.5/tests/data/posix/ezcConsoleQuestionDialogTest__testDialog1.php000066400000000000000000000030561470000742400303120ustar00rootroot00000000000000text = "Do you want to proceed?"; $opts->showResults = true; $opts->validator = new ezcConsoleQuestionDialogCollectionValidator( array( "y", "n" ), "n", ezcConsoleQuestionDialogCollectionValidator::CONVERT_LOWER ); $dialog = new ezcConsoleQuestionDialog( $out, $opts ); echo "The user decided to " . ( ezcConsoleDialogViewer::displayDialog( $dialog ) === "n" ? "not " : "" ) . "proceed.\n"; ?> ConsoleTools-1.7.5/tests/data/posix/ezcConsoleQuestionDialogTest__testDialog1_res.php000066400000000000000000000022441470000742400311610ustar00rootroot00000000000000 'Do you want to proceed? (y/n) [n] ', 1 => 'Do you want to proceed? (y/n) [n] ', 2 => 'The user decided to proceed. ', ); ?> ConsoleTools-1.7.5/tests/data/posix/ezcConsoleQuestionDialogTest__testDialog2.php000066400000000000000000000030771470000742400303160ustar00rootroot00000000000000text = "How old are you?"; $opts->showResults = true; $opts->validator = new ezcConsoleQuestionDialogTypeValidator( ezcConsoleQuestionDialogTypeValidator::TYPE_INT ); $dialog = new ezcConsoleQuestionDialog( $out, $opts ); if ( ( $res = ezcConsoleDialogViewer::displayDialog( $dialog ) ) < 8 ) { echo "Sorry, I can not believe that you are $res years old!\n"; } else { echo "Hey, you're still young! :)\n"; } ?> ConsoleTools-1.7.5/tests/data/posix/ezcConsoleQuestionDialogTest__testDialog2_res.php000066400000000000000000000023521470000742400311620ustar00rootroot00000000000000 'How old are you? () ', 1 => 'How old are you? () ', 2 => 'How old are you? () ', 3 => 'How old are you? () ', 4 => 'Hey, you\'re still young! :) ', ); ?> ConsoleTools-1.7.5/tests/data/posix/ezcConsoleQuestionDialogTest__testDialog3.php000066400000000000000000000026261470000742400303160ustar00rootroot00000000000000 ConsoleTools-1.7.5/tests/data/posix/ezcConsoleQuestionDialogTest__testDialog3_res.php000066400000000000000000000023061470000742400311620ustar00rootroot00000000000000 'Is the answer to everything 42? (y/n) [y] ', 1 => 'Is the answer to everything 42? (y/n) [y] ', 2 => 'You are so right! Don\'t forget your towel! :) ', ); ?> ConsoleTools-1.7.5/tests/data/posix/ezcConsoleQuestionDialogTest__testDialog4.php000066400000000000000000000027111470000742400303120ustar00rootroot00000000000000text = "Please enter your email address: "; $opts->validator = new ezcConsoleQuestionDialogRegexValidator( "/[a-z0-9_\.]+@[a-z0-9_\.]+\.[a-z0-9_\.]+/" ); $dialog = new ezcConsoleQuestionDialog( $out, $opts ); echo "The email address is " . ezcConsoleDialogViewer::displayDialog( $dialog ) . ".\n"; ?> ConsoleTools-1.7.5/tests/data/posix/ezcConsoleQuestionDialogTest__testDialog4_res.php000066400000000000000000000024331470000742400311640ustar00rootroot00000000000000 'Please enter your email address: ', 1 => 'Please enter your email address: ', 2 => 'Please enter your email address: ', 3 => 'Please enter your email address: ', 4 => 'The email address is foo.bar@example.com. ', ); ?>ConsoleTools-1.7.5/tests/data/posix/ezcConsoleQuestionDialogTest__testDialog5.php000066400000000000000000000030471470000742400303160ustar00rootroot00000000000000text = "Please enter your email address: "; $opts->validator = new ezcConsoleQuestionDialogRegexValidator( "/[a-z0-9_\.]+@[a-z0-9_\.]+\.[a-z0-9_\.]+/" ); $dialog = new ezcConsoleQuestionDialog( $out, $opts ); try { echo "The email address is " . ezcConsoleDialogViewer::displayDialog( $dialog ) . ".\n"; } catch ( ezcConsoleDialogAbortException $e ) { echo "User manually aborted\n"; } ?> ConsoleTools-1.7.5/tests/data/posix/ezcConsoleQuestionDialogTest__testDialog5_res.php000066400000000000000000000022351470000742400311650ustar00rootroot00000000000000 'Please enter your email address: ', 1 => 'Please enter your email address: ', 2 => "User manually aborted\n", ); ?> ConsoleTools-1.7.5/tests/data/posix/testEmptyTable.dat000066400000000000000000000002711470000742400230220ustar00rootroot00000000000000+----------------------------------------------------------------------------------+ +----------------------------------------------------------------------------------+ConsoleTools-1.7.5/tests/data/posix/testFinish.dat000066400000000000000000000007471470000742400222040ustar00rootroot0000000000000078 1 / 10 [+++++>------------------------------------------------------] 10.00% 8 2 / 10 [+++++++++++>------------------------------------------------] 20.00% 8 3 / 10 [+++++++++++++++++>------------------------------------------] 30.00% 8 4 / 10 [+++++++++++++++++++++++>------------------------------------] 40.00% 8 5 / 10 [+++++++++++++++++++++++++++++>------------------------------] 50.00% 810 / 10 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/posix/testOneRowOneColumnTable.dat000066400000000000000000000000771470000742400247610ustar00rootroot00000000000000+-----+ | foo | +-----+ConsoleTools-1.7.5/tests/data/posix/testProgress1.dat000066400000000000000000000005051470000742400226410ustar00rootroot000000000000007813 / 42 [+++++++++++++++++++>----------------------------------------] 30.95% 826 / 42 [+++++++++++++++++++++++++++++++++++++++>--------------------] 61.90% 839 / 42 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] 92.86% 842 / 42 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress10.dat000066400000000000000000000014531470000742400227240ustar00rootroot0000000000000078 10 / 100 [+++++>----------------------------------------------------] 10.00% 8 20 / 100 [+++++++++++>----------------------------------------------] 20.00% 8 30 / 100 [+++++++++++++++++>----------------------------------------] 30.00% 8 40 / 100 [+++++++++++++++++++++++>----------------------------------] 40.00% 8 50 / 100 [++++++++++++++++++++++++++++>-----------------------------] 50.00% 8 60 / 100 [++++++++++++++++++++++++++++++++++>-----------------------] 60.00% 8 70 / 100 [++++++++++++++++++++++++++++++++++++++++>-----------------] 70.00% 8 80 / 100 [++++++++++++++++++++++++++++++++++++++++++++++>-----------] 80.00% 8 90 / 100 [++++++++++++++++++++++++++++++++++++++++++++++++++++>-----] 90.00% 8100 / 100 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress11.dat000066400000000000000000000062511470000742400227260ustar00rootroot0000000000000078 2.50 / 100.00 [+>--------------------------------------------------] 2.50% 8 5.00 / 100.00 [++>-------------------------------------------------] 5.00% 8 7.50 / 100.00 [+++>------------------------------------------------] 7.50% 8 10.00 / 100.00 [+++++>----------------------------------------------] 10.00% 8 12.50 / 100.00 [++++++>---------------------------------------------] 12.50% 8 15.00 / 100.00 [+++++++>--------------------------------------------] 15.00% 8 17.50 / 100.00 [+++++++++>------------------------------------------] 17.50% 8 20.00 / 100.00 [++++++++++>-----------------------------------------] 20.00% 8 22.50 / 100.00 [+++++++++++>----------------------------------------] 22.50% 8 25.00 / 100.00 [++++++++++++>---------------------------------------] 25.00% 8 27.50 / 100.00 [++++++++++++++>-------------------------------------] 27.50% 8 30.00 / 100.00 [+++++++++++++++>------------------------------------] 30.00% 8 32.50 / 100.00 [++++++++++++++++>-----------------------------------] 32.50% 8 35.00 / 100.00 [++++++++++++++++++>---------------------------------] 35.00% 8 37.50 / 100.00 [+++++++++++++++++++>--------------------------------] 37.50% 8 40.00 / 100.00 [++++++++++++++++++++>-------------------------------] 40.00% 8 42.50 / 100.00 [++++++++++++++++++++++>-----------------------------] 42.50% 8 45.00 / 100.00 [+++++++++++++++++++++++>----------------------------] 45.00% 8 47.50 / 100.00 [++++++++++++++++++++++++>---------------------------] 47.50% 8 50.00 / 100.00 [+++++++++++++++++++++++++>--------------------------] 50.00% 8 52.50 / 100.00 [+++++++++++++++++++++++++++>------------------------] 52.50% 8 55.00 / 100.00 [++++++++++++++++++++++++++++>-----------------------] 55.00% 8 57.50 / 100.00 [+++++++++++++++++++++++++++++>----------------------] 57.50% 8 60.00 / 100.00 [+++++++++++++++++++++++++++++++>--------------------] 60.00% 8 62.50 / 100.00 [++++++++++++++++++++++++++++++++>-------------------] 62.50% 8 65.00 / 100.00 [+++++++++++++++++++++++++++++++++>------------------] 65.00% 8 67.50 / 100.00 [+++++++++++++++++++++++++++++++++++>----------------] 67.50% 8 70.00 / 100.00 [++++++++++++++++++++++++++++++++++++>---------------] 70.00% 8 72.50 / 100.00 [+++++++++++++++++++++++++++++++++++++>--------------] 72.50% 8 75.00 / 100.00 [++++++++++++++++++++++++++++++++++++++>-------------] 75.00% 8 77.50 / 100.00 [++++++++++++++++++++++++++++++++++++++++>-----------] 77.50% 8 80.00 / 100.00 [+++++++++++++++++++++++++++++++++++++++++>----------] 80.00% 8 82.50 / 100.00 [++++++++++++++++++++++++++++++++++++++++++>---------] 82.50% 8 85.00 / 100.00 [++++++++++++++++++++++++++++++++++++++++++++>-------] 85.00% 8 87.50 / 100.00 [+++++++++++++++++++++++++++++++++++++++++++++>------] 87.50% 8 90.00 / 100.00 [++++++++++++++++++++++++++++++++++++++++++++++>-----] 90.00% 8 92.50 / 100.00 [++++++++++++++++++++++++++++++++++++++++++++++++>---] 92.50% 8 95.00 / 100.00 [+++++++++++++++++++++++++++++++++++++++++++++++++>--] 95.00% 8 97.50 / 100.00 [++++++++++++++++++++++++++++++++++++++++++++++++++>-] 97.50% 8100.00 / 100.00 [+++++++++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress12.dat000066400000000000000000000062511470000742400227270ustar00rootroot0000000000000078 2.50 / 100.00000000 [+>--------------------------------------------] 2.50% 8 5.00 / 100.00000000 [++>-------------------------------------------] 5.00% 8 7.50 / 100.00000000 [+++>------------------------------------------] 7.50% 8 10.00 / 100.00000000 [++++>-----------------------------------------] 10.00% 8 12.50 / 100.00000000 [+++++>----------------------------------------] 12.50% 8 15.00 / 100.00000000 [++++++>---------------------------------------] 15.00% 8 17.50 / 100.00000000 [++++++++>-------------------------------------] 17.50% 8 20.00 / 100.00000000 [+++++++++>------------------------------------] 20.00% 8 22.50 / 100.00000000 [++++++++++>-----------------------------------] 22.50% 8 25.00 / 100.00000000 [+++++++++++>----------------------------------] 25.00% 8 27.50 / 100.00000000 [++++++++++++>---------------------------------] 27.50% 8 30.00 / 100.00000000 [+++++++++++++>--------------------------------] 30.00% 8 32.50 / 100.00000000 [++++++++++++++>-------------------------------] 32.50% 8 35.00 / 100.00000000 [++++++++++++++++>-----------------------------] 35.00% 8 37.50 / 100.00000000 [+++++++++++++++++>----------------------------] 37.50% 8 40.00 / 100.00000000 [++++++++++++++++++>---------------------------] 40.00% 8 42.50 / 100.00000000 [+++++++++++++++++++>--------------------------] 42.50% 8 45.00 / 100.00000000 [++++++++++++++++++++>-------------------------] 45.00% 8 47.50 / 100.00000000 [+++++++++++++++++++++>------------------------] 47.50% 8 50.00 / 100.00000000 [++++++++++++++++++++++>-----------------------] 50.00% 8 52.50 / 100.00000000 [++++++++++++++++++++++++>---------------------] 52.50% 8 55.00 / 100.00000000 [+++++++++++++++++++++++++>--------------------] 55.00% 8 57.50 / 100.00000000 [++++++++++++++++++++++++++>-------------------] 57.50% 8 60.00 / 100.00000000 [+++++++++++++++++++++++++++>------------------] 60.00% 8 62.50 / 100.00000000 [++++++++++++++++++++++++++++>-----------------] 62.50% 8 65.00 / 100.00000000 [+++++++++++++++++++++++++++++>----------------] 65.00% 8 67.50 / 100.00000000 [+++++++++++++++++++++++++++++++>--------------] 67.50% 8 70.00 / 100.00000000 [++++++++++++++++++++++++++++++++>-------------] 70.00% 8 72.50 / 100.00000000 [+++++++++++++++++++++++++++++++++>------------] 72.50% 8 75.00 / 100.00000000 [++++++++++++++++++++++++++++++++++>-----------] 75.00% 8 77.50 / 100.00000000 [+++++++++++++++++++++++++++++++++++>----------] 77.50% 8 80.00 / 100.00000000 [++++++++++++++++++++++++++++++++++++>---------] 80.00% 8 82.50 / 100.00000000 [+++++++++++++++++++++++++++++++++++++>--------] 82.50% 8 85.00 / 100.00000000 [+++++++++++++++++++++++++++++++++++++++>------] 85.00% 8 87.50 / 100.00000000 [++++++++++++++++++++++++++++++++++++++++>-----] 87.50% 8 90.00 / 100.00000000 [+++++++++++++++++++++++++++++++++++++++++>----] 90.00% 8 92.50 / 100.00000000 [++++++++++++++++++++++++++++++++++++++++++>---] 92.50% 8 95.00 / 100.00000000 [+++++++++++++++++++++++++++++++++++++++++++>--] 95.00% 8 97.50 / 100.00000000 [++++++++++++++++++++++++++++++++++++++++++++>-] 97.50% 8100.00 / 100.00000000 [+++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress13.dat000066400000000000000000000062511470000742400227300ustar00rootroot0000000000000078 2.50000000 / 100.00 [+>--------------------------------------------] 2.50% 8 5.00000000 / 100.00 [++>-------------------------------------------] 5.00% 8 7.50000000 / 100.00 [+++>------------------------------------------] 7.50% 8 10.00000000 / 100.00 [++++>-----------------------------------------] 10.00% 8 12.50000000 / 100.00 [+++++>----------------------------------------] 12.50% 8 15.00000000 / 100.00 [++++++>---------------------------------------] 15.00% 8 17.50000000 / 100.00 [++++++++>-------------------------------------] 17.50% 8 20.00000000 / 100.00 [+++++++++>------------------------------------] 20.00% 8 22.50000000 / 100.00 [++++++++++>-----------------------------------] 22.50% 8 25.00000000 / 100.00 [+++++++++++>----------------------------------] 25.00% 8 27.50000000 / 100.00 [++++++++++++>---------------------------------] 27.50% 8 30.00000000 / 100.00 [+++++++++++++>--------------------------------] 30.00% 8 32.50000000 / 100.00 [++++++++++++++>-------------------------------] 32.50% 8 35.00000000 / 100.00 [++++++++++++++++>-----------------------------] 35.00% 8 37.50000000 / 100.00 [+++++++++++++++++>----------------------------] 37.50% 8 40.00000000 / 100.00 [++++++++++++++++++>---------------------------] 40.00% 8 42.50000000 / 100.00 [+++++++++++++++++++>--------------------------] 42.50% 8 45.00000000 / 100.00 [++++++++++++++++++++>-------------------------] 45.00% 8 47.50000000 / 100.00 [+++++++++++++++++++++>------------------------] 47.50% 8 50.00000000 / 100.00 [++++++++++++++++++++++>-----------------------] 50.00% 8 52.50000000 / 100.00 [++++++++++++++++++++++++>---------------------] 52.50% 8 55.00000000 / 100.00 [+++++++++++++++++++++++++>--------------------] 55.00% 8 57.50000000 / 100.00 [++++++++++++++++++++++++++>-------------------] 57.50% 8 60.00000000 / 100.00 [+++++++++++++++++++++++++++>------------------] 60.00% 8 62.50000000 / 100.00 [++++++++++++++++++++++++++++>-----------------] 62.50% 8 65.00000000 / 100.00 [+++++++++++++++++++++++++++++>----------------] 65.00% 8 67.50000000 / 100.00 [+++++++++++++++++++++++++++++++>--------------] 67.50% 8 70.00000000 / 100.00 [++++++++++++++++++++++++++++++++>-------------] 70.00% 8 72.50000000 / 100.00 [+++++++++++++++++++++++++++++++++>------------] 72.50% 8 75.00000000 / 100.00 [++++++++++++++++++++++++++++++++++>-----------] 75.00% 8 77.50000000 / 100.00 [+++++++++++++++++++++++++++++++++++>----------] 77.50% 8 80.00000000 / 100.00 [++++++++++++++++++++++++++++++++++++>---------] 80.00% 8 82.50000000 / 100.00 [+++++++++++++++++++++++++++++++++++++>--------] 82.50% 8 85.00000000 / 100.00 [+++++++++++++++++++++++++++++++++++++++>------] 85.00% 8 87.50000000 / 100.00 [++++++++++++++++++++++++++++++++++++++++>-----] 87.50% 8 90.00000000 / 100.00 [+++++++++++++++++++++++++++++++++++++++++>----] 90.00% 8 92.50000000 / 100.00 [++++++++++++++++++++++++++++++++++++++++++>---] 92.50% 8 95.00000000 / 100.00 [+++++++++++++++++++++++++++++++++++++++++++>--] 95.00% 8 97.50000000 / 100.00 [++++++++++++++++++++++++++++++++++++++++++++>-] 97.50% 8100.00000000 / 100.00 [+++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress2.dat000066400000000000000000000001221470000742400226350ustar00rootroot000000000000007820 / 20 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress3.dat000066400000000000000000000005051470000742400226430ustar00rootroot000000000000007813 / 42 [###################>****************************************] 30.95% 826 / 42 [#######################################>********************] 61.90% 839 / 42 [###########################################################>] 92.86% 842 / 42 [###########################################################>] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress4.dat000066400000000000000000000003641470000742400226470ustar00rootroot000000000000007819 / 55 [+++++++++++++++++++&----------------------------------------] 34.55% 838 / 55 [+++++++++++++++++++++++++++++++++++++++&--------------------] 69.09% 855 / 55 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++&] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress5.dat000066400000000000000000000003511470000742400226440ustar00rootroot000000000000007813 / 42 [++++++++++++&------------------------] 30.95% 826 / 42 [++++++++++++++++++++++++&------------] 61.90% 839 / 42 [++++++++++++++++++++++++++++++++++++&] 92.86% 842 / 42 [++++++++++++++++++++++++++++++++++++&] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress6.dat000066400000000000000000000012411470000742400226440ustar00rootroot0000000000000078 3 / 22 [########&******************************************************] 13.64% 8 6 / 22 [#################&*********************************************] 27.27% 8 9 / 22 [##########################&************************************] 40.91% 812 / 22 [###################################&***************************] 54.55% 815 / 22 [############################################&******************] 68.18% 818 / 22 [#####################################################&*********] 81.82% 821 / 22 [##############################################################&] 95.45% 822 / 22 [##############################################################&] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress7.dat000066400000000000000000000007471470000742400226570ustar00rootroot0000000000000078 7 / 42 [123412341>98769876987698769876987698769876987698769876987698] 16.67% 814 / 42 [1234123412341234123>9876987698769876987698769876987698769876] 33.33% 821 / 42 [12341234123412341234123412341>987698769876987698769876987698] 50.00% 828 / 42 [123412341234123412341234123412341234123>98769876987698769876] 66.67% 835 / 42 [1234123412341234123412341234123412341234123412341>9876987698] 83.33% 842 / 42 [12341234123412341234123412341234123412341234123412341234123>] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress8.dat000066400000000000000000000007471470000742400226600ustar00rootroot0000000000000078 7 / 42 [1231231---98798798798798798798798798798798798798798798798798] 16.67% 814 / 42 [12312312312312312---9879879879879879879879879879879879879879] 33.33% 821 / 42 [123123123123123123123123123---987987987987987987987987987987] 50.00% 828 / 42 [1231231231231231231231231231231231231---98798798798798798798] 66.67% 835 / 42 [12312312312312312312312312312312312312312312312---9879879879] 83.33% 842 / 42 [123123123123123123123123123123123123123123123123123123123---] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgress9.dat000066400000000000000000000022561470000742400226560ustar00rootroot0000000000000078Actual progress: <123123---98798798798798798798798798798798798798798798798798798798798798798>  13 8Actual progress: <12312312312312---987987987987987987987987987987987987987987987987987987987>  26 8Actual progress: <1231231231231231231231---9879879879879879879879879879879879879879879879879>  42 8Actual progress: <123123123123123123123123123123---98798798798798798798798798798798798798798>  55 8Actual progress: <123123123123123123123123123123123123123---98798798798798798798798798798798>  71 8Actual progress: <12312312312312312312312312312312312312312312312---987987987987987987987987> 104 8Actual progress: <1231231231231231231231231231231231231231231231231231231---9879879879879879> 120 8Actual progress: <123123123123123123123123123123123123123123123123123123123123123---98798798> 133 8Actual progress: <12312312312312312312312312312312312312312312312312312312312312312312312---> 144ConsoleTools-1.7.5/tests/data/posix/testProgressMonitor1.dat000066400000000000000000000007331470000742400242140ustar00rootroot00000000000000 10.0% UPLOAD /var/upload/test.php  20.0% UPLOAD /var/upload/testing.php  30.0% UPLOAD /var/upload/foo.php  40.0% UPLOAD /var/upload/bar.php  50.0% UPLOAD /var/upload/baz.png  60.0% UPLOAD /var/upload/image.jpg  70.0% UPLOAD /var/upload/bar.gif  80.0% UPLOAD /var/upload/ez-logo.jpg  90.0% UPLOAD /var/upload/ez-logo.png  100.0% UPLOAD /var/upload/ez-components.png ConsoleTools-1.7.5/tests/data/posix/testProgressMonitor2.dat000066400000000000000000000005021470000742400242070ustar00rootroot00000000000000 14.3% UPLOAD /var/upload/test.php  28.6% UPLOAD /var/upload/testing.php  42.9% UPLOAD /var/upload/foo.php  57.1% UPLOAD /var/upload/bar.php  71.4% UPLOAD /var/upload/baz.png  85.7% UPLOAD /var/upload/image.jpg  100.0% UPLOAD /var/upload/bar.gif ConsoleTools-1.7.5/tests/data/posix/testProgressMonitor3.dat000066400000000000000000000005201470000742400242100ustar00rootroot00000000000000 UPLOAD 14.29% /var/upload/test.php  UPLOAD 28.57% /var/upload/testing.php  UPLOAD 42.86% /var/upload/foo.php  UPLOAD 57.14% /var/upload/bar.php  UPLOAD 71.43% /var/upload/baz.png  UPLOAD 85.71% /var/upload/image.jpg  UPLOAD 100.00% /var/upload/bar.gif ConsoleTools-1.7.5/tests/data/posix/testProgressMonitor4.dat000066400000000000000000000010331470000742400242110ustar00rootroot00000000000000 UPLOAD  14.29% /var/upload/test.php  UPLOAD  28.57% /var/upload/testing.php  UPLOAD  42.86% /var/upload/foo.php  UPLOAD  57.14% /var/upload/bar.php  UPLOAD  71.43% /var/upload/baz.png  UPLOAD  85.71% /var/upload/image.jpg  UPLOAD 100.00% /var/upload/bar.gif ConsoleTools-1.7.5/tests/data/posix/testProgressUtfInBar.dat000066400000000000000000000011051470000742400241500ustar00rootroot0000000000000078 2 / 10 [ööööööööööö>------------------------------------------------] 20.00% 8 4 / 10 [ööööööööööööööööööööööö>------------------------------------] 40.00% 8 6 / 10 [ööööööööööööööööööööööööööööööööööö>------------------------] 60.00% 8 8 / 10 [ööööööööööööööööööööööööööööööööööööööööööööööö>------------] 80.00% 810 / 10 [ööööööööööööööööööööööööööööööööööööööööööööööööööööööööööö>] 100.00%ConsoleTools-1.7.5/tests/data/posix/testProgressUtfInBoth.dat000066400000000000000000000011311470000742400243370ustar00rootroot0000000000000078 2ö/ä10ü[ööööööööööö>------------------------------------------------]ß 20.00% 8 4ö/ä10ü[ööööööööööööööööööööööö>------------------------------------]ß 40.00% 8 6ö/ä10ü[ööööööööööööööööööööööööööööööööööö>------------------------]ß 60.00% 8 8ö/ä10ü[ööööööööööööööööööööööööööööööööööööööööööööööö>------------]ß 80.00% 810ö/ä10ü[ööööööööööööööööööööööööööööööööööööööööööööööööööööööööööö>]ß100.00%ConsoleTools-1.7.5/tests/data/posix/testProgressUtfInText.dat000066400000000000000000000006521470000742400243760ustar00rootroot0000000000000078 2ö/ä10ü[+++++++++++>------------------------------------------------]ß 20.00% 8 4ö/ä10ü[+++++++++++++++++++++++>------------------------------------]ß 40.00% 8 6ö/ä10ü[+++++++++++++++++++++++++++++++++++>------------------------]ß 60.00% 8 8ö/ä10ü[+++++++++++++++++++++++++++++++++++++++++++++++>------------]ß 80.00% 810ö/ä10ü[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]ß100.00%ConsoleTools-1.7.5/tests/data/posix/testStatusbar1.dat000066400000000000000000000004541470000742400230100ustar00rootroot00000000000000+-++-+++--++-++-+-++---+-ConsoleTools-1.7.5/tests/data/posix/testStatusbar2.dat000066400000000000000000000000311470000742400230000ustar00rootroot00000000000000+-++-+++--++-++-+-++---+-ConsoleTools-1.7.5/tests/data/posix/testTable1a.dat000066400000000000000000000023741470000742400222330ustar00rootroot00000000000000+----------------------------------------+----------------------+-------------+ | Heading no. 1  | Longer heading no. 2 | Head 3  | +----------------------------------------+----------------------+-------------+ | Data cell 1  | Data cell 2  | Data cell 3 | +----------------------------------------+----------------------+-------------+ | Long long data cell with even more  | Data cell 4  | Data cell 5 | | text in it...  |   |   | +----------------------------------------+----------------------+-------------+ | a b c d e f g h i j k l m n o p q r s  | Data cell  | Data cell  | | t u v w x  |   |   | +----------------------------------------+----------------------+-------------+ConsoleTools-1.7.5/tests/data/posix/testTable1b.dat000066400000000000000000000033261470000742400222320ustar00rootroot00000000000000+------------+------------+------------+ | Heading  | Longer  | Head 3  | | no. 1  | heading  |   | |   | no. 2  |   | +------------+------------+------------+ | Data cell  | Data cell  | Data cell  | | 1  | 2  | 3  | +------------+------------+------------+ | Long long  | Data cell  | Data cell  | | data cell  | 4  | 5  | | with even  |   |   | | more text  |   |   | | in it...  |   |   | +------------+------------+------------+ | a b c d e  | Data cell  | Data cell  | | f g h i j  |   |   | | k l m n o  |   |   | | p q r s t  |   |   | | u v w x  |   |   | +------------+------------+------------+ConsoleTools-1.7.5/tests/data/posix/testTable2a.dat000066400000000000000000000010331470000742400222230ustar00rootroot00000000000000+---------+---------+---------+---------+---------+--------+ |  a |  b |  c |  d |  e |  f | +---------+---------+---------+---------+---------+--------+ |  g |  h |  i |  j |  k |  l | +---------+---------+---------+---------+---------+--------+ConsoleTools-1.7.5/tests/data/posix/testTable2b.dat000066400000000000000000000005541470000742400222330ustar00rootroot00000000000000+---+---+---+---+---+---+ | a | b | c | d | e | f | +---+---+---+---+---+---+ | g | h | i | j | k | l | +---+---+---+---+---+---+ConsoleTools-1.7.5/tests/data/posix/testTable3a.dat000066400000000000000000000032541470000742400222330ustar00rootroot00000000000000######################################################################################################################### #  Parameter  # Shortcut  # Descrition # ######################################################################################################################### #  Append text to a file. This parameter takes a string value and may be used multiple times.  # --append  #  -a  # ######################################################################################################################### # Prepend text to a file. This parameter takes a string value and may be used multiple times.  # --prepend #  -p  # ######################################################################################################################### #  Forces the action desired without paying attention to any errors.  #  --force  #  -f  # ######################################################################################################################### #  Silence all kinds of warnings issued by this program.  # --silent  #  -s  # #########################################################################################################################ConsoleTools-1.7.5/tests/data/posix/testTable3b.dat000066400000000000000000000034231470000742400222320ustar00rootroot00000000000000cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvc h Parameter  h Shortcut  h Descrition h cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvc h Append text to a file. This parameter takes a  h --append  h -a  h h string value and may be used multiple times.  h   h   h cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvc h Prepend text to a file. This parameter takes a  h --prepend h -p  h h string value and may be used multiple times.  h   h   h cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvc h Forces the action desired without paying attention h --force  h -f  h h to any errors.  h   h   h cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvc h Silence all kinds of warnings issued by this  h --silent  h -s  h h program.  h   h   h cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvcConsoleTools-1.7.5/tests/data/posix/testTable3c.dat000066400000000000000000000017441470000742400222370ustar00rootroot00000000000000+---------+---------+---------+ | Parameter | Shortcut | Descrition | +---------+---------+---------+ | Append text to a file. This parameter takes a string value and may be used multiple times. | --append | -a  | +---------+---------+---------+ | Prepend text to a file. This parameter takes a string value and may be used multiple times. | --prepend | -p  | +---------+---------+---------+ | Forces the action desired without paying attention to any errors. | --force | -f  | +---------+---------+---------+ | Silence all kinds of warnings issued by this program. | --silent | -s  | +---------+---------+---------+ConsoleTools-1.7.5/tests/data/posix/testTable3d.dat000066400000000000000000000011071470000742400222310ustar00rootroot00000000000000+-----+-----+-----+ | Par | Sho | Des | +-----+-----+-----+ | App | --a | -a  | +-----+-----+-----+ | Pre | --p | -p  | +-----+-----+-----+ | For | --f | -f  | +-----+-----+-----+ | Sil | --s | -s  | +-----+-----+-----+ConsoleTools-1.7.5/tests/data/posix/testTable4a.dat000066400000000000000000000030511470000742400222270ustar00rootroot00000000000000+-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | Some very very long data he |  Short  | Some very very long data he |  Short  | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ |  Short  | Some very very long data he |  Short  | Some very very long data he | |   |   |   |   | |   | and it becomes even much mu |   |   | |   |   |   |   | |   |  and even longer....  |   |   | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ConsoleTools-1.7.5/tests/data/posix/testTable4b.dat000066400000000000000000000050511470000742400222320ustar00rootroot00000000000000+-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | Some very very long data  | Short  | Some very very long data  | Short  | | here.... and it becomes  |   | here.... and it becomes  |   | | even much much longer...  |   | even much much longer...  |   | | and even longer....  |   | and even longer....  |   | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | Short  | Some very very long data  | Short  | Some very very long data  | |   | here....  |   | here.... and it becomes  | |   |   |   | even much much longer...  | |   | and it becomes even much  |   | and even longer....  | |   | much longer...  |   |   | |   |   |   |   | |   | and even longer....  |   |   | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ConsoleTools-1.7.5/tests/data/posix/testTable4c.dat000066400000000000000000000030511470000742400222310ustar00rootroot00000000000000+-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | Some very very long data he |  Short  | Some very very long data he |  Short  | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ |  Short  | Some very very long data he |  Short  | Some very very long data he | |   |   |   |   | |   | and it becomes even much mu |   |   | |   |   |   |   | |   |  and even longer....  |   |   | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ConsoleTools-1.7.5/tests/data/posix/testTable5autowidth.dat000066400000000000000000000014161470000742400240230ustar00rootroot00000000000000+------------+-----------------+ | Short text | More short text | +------------+-----------------+ | Short text | More short text | | Short text | More short text | | Short text | More short text | | Short text |  Short text | | Short text |   | | Short text |   | | Short text |   | |   |   | +------------+-----------------+ConsoleTools-1.7.5/tests/data/posix/testTableColPadding1.dat000066400000000000000000000013431470000742400240120ustar00rootroot00000000000000+----------------+----------------+----------------+----------------+----------------+-------------+ |~~~ a ~~~|~~~ b ~~~|~~~ c ~~~|~~~ d ~~~|~~~ e ~~~|~~~ f ~~~| +----------------+----------------+----------------+----------------+----------------+-------------+ |~~~ g ~~~|~~~ h ~~~|~~~ i ~~~|~~~ j ~~~|~~~ k ~~~|~~~ l ~~~| +----------------+----------------+----------------+----------------+----------------+-------------+ConsoleTools-1.7.5/tests/data/posix/testTableColPadding2.dat000066400000000000000000000007441470000742400240170ustar00rootroot00000000000000+-------+-------+-------+-------+-------+-------+ |~~~a~~~|~~~b~~~|~~~c~~~|~~~d~~~|~~~e~~~|~~~f~~~| +-------+-------+-------+-------+-------+-------+ |~~~g~~~|~~~h~~~|~~~i~~~|~~~j~~~|~~~k~~~|~~~l~~~| +-------+-------+-------+-------+-------+-------+ConsoleTools-1.7.5/tests/data/posix/testTableWithSpaceBorders.dat000066400000000000000000000047471470000742400251500ustar00rootroot00000000000000    Some very very long data    Short    Some very very long data    Short      here.... and it becomes        here.... and it becomes          even much much longer...        even much much longer...          and even longer....        and even longer....            Short    Some very very long data    Short    Some very very long data          here....        here.... and it becomes                  even much much longer...          and it becomes even much        and even longer....          much longer...                                    and even longer....             ConsoleTools-1.7.5/tests/data/posix/testTableWithoutBorders.dat000066400000000000000000000041261470000742400247130ustar00rootroot00000000000000 Some very very long data   Short   Some very very long data   Short    here.... and it becomes even     here.... and it becomes even      much much longer... and even     much much longer... and even      longer....      longer....       Short   Some very very long data   Short   Some very very long data       here....      here.... and it becomes even            much much longer... and even      and it becomes even much      longer....       much longer...                          and even longer....        ConsoleTools-1.7.5/tests/data/posix/testTableWithoutFormatting.dat000066400000000000000000000002011470000742400254130ustar00rootroot00000000000000+---+---+---+---+---+---+ | a | b | c | d | e | f | +---+---+---+---+---+---+ | g | h | i | j | k | l | +---+---+---+---+---+---+ConsoleTools-1.7.5/tests/data/posix/testTableWithoutHorizontalBorders.dat000066400000000000000000000047311470000742400267670ustar00rootroot00000000000000------------------------------------------------------------------------------------------------------------------------  Some very very long data   Short   Some very very long data   Short    here.... and it becomes even     here.... and it becomes even      much much longer... and even     much much longer... and even      longer....      longer....      ------------------------------------------------------------------------------------------------------------------------  Short   Some very very long data   Short   Some very very long data       here....      here.... and it becomes even            much much longer... and even      and it becomes even much      longer....       much longer...                          and even longer....         ------------------------------------------------------------------------------------------------------------------------ConsoleTools-1.7.5/tests/data/posix/testTableWithoutVerticalBorders.dat000066400000000000000000000041411470000742400264020ustar00rootroot00000000000000| Some very very long data  | Short  | Some very very long data  | Short  | | here.... and it becomes  |   | here.... and it becomes  |   | | even much much longer...  |   | even much much longer...  |   | | and even longer....  |   | and even longer....  |   | | Short  | Some very very long data  | Short  | Some very very long data  | |   | here....  |   | here.... and it becomes  | |   |   |   | even much much longer...  | |   | and it becomes even much  |   | and even longer....  | |   | much longer...  |   |   | |   |   |   |   | |   | and even longer....  |   |   |ConsoleTools-1.7.5/tests/data/posix/testUtf8TableHighlightNonUtf8.dat000066400000000000000000000015331470000742400256260ustar00rootroot00000000000000+--------------------+--------------------+-----------------------------------+ | Non UTF-8 column 1 | Non UTF-8 column 2 | Long long long long long long  | |   |   | long non UTF-8 column  | +--------------------+--------------------+-----------------------------------+ | Nön UTF-8 cölümn 1 | Nön UTF-8 cölümn 2 | Löng löng löng löng löng löng  | |   |   | löng nön UTF-8 cölümn  | +--------------------+--------------------+-----------------------------------+ConsoleTools-1.7.5/tests/data/posix/testUtf8TableHighlightUtf8.dat000066400000000000000000000015331470000742400251530ustar00rootroot00000000000000+--------------------+--------------------+-----------------------------------+ | Nön UTF-8 cölümn 1 | Nön UTF-8 cölümn 2 | Löng löng löng löng löng löng  | |   |   | löng nön UTF-8 cölümn  | +--------------------+--------------------+-----------------------------------+ | Non UTF-8 column 1 | Non UTF-8 column 2 | Long long long long long long  | |   |   | long non UTF-8 column  | +--------------------+--------------------+-----------------------------------+ConsoleTools-1.7.5/tests/data/string_tools_strpad_data.php000066400000000000000000000222261470000742400240310ustar00rootroot00000000000000 array( // $input array( 'input' => 'str_pad()', 'padLength' => 20, ), // $expected 'str_pad() ', ), 1 => array( // $input array( 'input' => 'str_pad()', 'padLength' => 20, 'padString' => '-+' ), // $expected 'str_pad()-+-+-+-+-+-', ), 2 => array( // $input array( 'input' => 'str_pad()', 'padLength' => 20, 'padString' => '-+', 'padType' => STR_PAD_LEFT ), // $expected '-+-+-+-+-+-str_pad()', ), 3 => array( // $input array( 'input' => 'str_pad()', 'padLength' => 20, 'padString' => '-+', 'padType' => STR_PAD_RIGHT ), // $expected 'str_pad()-+-+-+-+-+-', ), 4 => array( // $input array( 'input' => 'str_pad()', 'padLength' => 20, 'padString' => '-+', 'padType' => STR_PAD_BOTH ), // $expected '-+-+-str_pad()-+-+-+', ), ); $inputStrings = array( "variation", // normal string "", // empty string NULL, // NULL true, // boolean 15, // numeric 15.55, // numeric "2990" // numeric string ); $padLengths = array( -PHP_INT_MAX, // huge negative value -1, // negative value 0, // < sizeof(input_string) 9, // <= sizeof(input_string) 10, // > sizeof(input_string) 16, // > sizeof(input_string) ); $padString = '='; $results = array( 0 => 'variation', 1 => 'variation', 2 => 'variation', 3 => 'variation', 4 => 'variation', 5 => 'variation', 6 => 'variation', 7 => 'variation', 8 => 'variation', 9 => 'variation', 10 => 'variation', 11 => 'variation', 12 => 'variation', 13 => 'variation', 14 => 'variation', 15 => 'variation', 16 => 'variation', 17 => 'variation', 18 => 'variation', 19 => 'variation', 20 => 'variation ', 21 => 'variation=', 22 => '=variation', 23 => 'variation=', 24 => 'variation=', 25 => 'variation ', 26 => 'variation=======', 27 => '=======variation', 28 => 'variation=======', 29 => '===variation====', 30 => '', 31 => '', 32 => '', 33 => '', 34 => '', 35 => '', 36 => '', 37 => '', 38 => '', 39 => '', 40 => '', 41 => '', 42 => '', 43 => '', 44 => '', 45 => ' ', 46 => '=========', 47 => '=========', 48 => '=========', 49 => '=========', 50 => ' ', 51 => '==========', 52 => '==========', 53 => '==========', 54 => '==========', 55 => ' ', 56 => '================', 57 => '================', 58 => '================', 59 => '================', 60 => '', 61 => '', 62 => '', 63 => '', 64 => '', 65 => '', 66 => '', 67 => '', 68 => '', 69 => '', 70 => '', 71 => '', 72 => '', 73 => '', 74 => '', 75 => ' ', 76 => '=========', 77 => '=========', 78 => '=========', 79 => '=========', 80 => ' ', 81 => '==========', 82 => '==========', 83 => '==========', 84 => '==========', 85 => ' ', 86 => '================', 87 => '================', 88 => '================', 89 => '================', 90 => '1', 91 => '1', 92 => '1', 93 => '1', 94 => '1', 95 => '1', 96 => '1', 97 => '1', 98 => '1', 99 => '1', 100 => '1', 101 => '1', 102 => '1', 103 => '1', 104 => '1', 105 => '1 ', 106 => '1========', 107 => '========1', 108 => '1========', 109 => '====1====', 110 => '1 ', 111 => '1=========', 112 => '=========1', 113 => '1=========', 114 => '====1=====', 115 => '1 ', 116 => '1===============', 117 => '===============1', 118 => '1===============', 119 => '=======1========', 120 => '15', 121 => '15', 122 => '15', 123 => '15', 124 => '15', 125 => '15', 126 => '15', 127 => '15', 128 => '15', 129 => '15', 130 => '15', 131 => '15', 132 => '15', 133 => '15', 134 => '15', 135 => '15 ', 136 => '15=======', 137 => '=======15', 138 => '15=======', 139 => '===15====', 140 => '15 ', 141 => '15========', 142 => '========15', 143 => '15========', 144 => '====15====', 145 => '15 ', 146 => '15==============', 147 => '==============15', 148 => '15==============', 149 => '=======15=======', 150 => '15.55', 151 => '15.55', 152 => '15.55', 153 => '15.55', 154 => '15.55', 155 => '15.55', 156 => '15.55', 157 => '15.55', 158 => '15.55', 159 => '15.55', 160 => '15.55', 161 => '15.55', 162 => '15.55', 163 => '15.55', 164 => '15.55', 165 => '15.55 ', 166 => '15.55====', 167 => '====15.55', 168 => '15.55====', 169 => '==15.55==', 170 => '15.55 ', 171 => '15.55=====', 172 => '=====15.55', 173 => '15.55=====', 174 => '==15.55===', 175 => '15.55 ', 176 => '15.55===========', 177 => '===========15.55', 178 => '15.55===========', 179 => '=====15.55======', 180 => '2990', 181 => '2990', 182 => '2990', 183 => '2990', 184 => '2990', 185 => '2990', 186 => '2990', 187 => '2990', 188 => '2990', 189 => '2990', 190 => '2990', 191 => '2990', 192 => '2990', 193 => '2990', 194 => '2990', 195 => '2990 ', 196 => '2990=====', 197 => '=====2990', 198 => '2990=====', 199 => '==2990===', 200 => '2990 ', 201 => '2990======', 202 => '======2990', 203 => '2990======', 204 => '===2990===', 205 => '2990 ', 206 => '2990============', 207 => '============2990', 208 => '2990============', 209 => '======2990======', ); $i = 0; foreach ( $inputStrings as $inputString ) { foreach ( $padLengths as $padLength ) { $testSets[] = array( array( 'input' => $inputString, 'padLength' => $padLength, ), $results[$i++], ); $testSets[] = array( array( 'input' => $inputString, 'padLength' => $padLength, 'padString' => $padString, ), $results[$i++], ); $testSets[] = array( array( 'input' => $inputString, 'padLength' => $padLength, 'padString' => $padString, 'padType' => STR_PAD_LEFT ), $results[$i++], ); $testSets[] = array( array( 'input' => $inputString, 'padLength' => $padLength, 'padString' => $padString, 'padType' => STR_PAD_RIGHT ), $results[$i++], ); $testSets[] = array( array( 'input' => $inputString, 'padLength' => $padLength, 'padString' => $padString, 'padType' => STR_PAD_BOTH ), $results[$i++], ); } } return $testSets; ?> ConsoleTools-1.7.5/tests/data/string_tools_wordwrap_data.php000066400000000000000000000360401470000742400244000ustar00rootroot00000000000000 array( // $input array( 'str' => 'Some short string', ), // $expected 'Some short string', ), // Default width, UTF-8, single line, no wrapping 1 => array( // $input array( 'str' => 'Söme shört string', ), // $expected 'Söme shört string', ), // Default width, no UTF-8, single line, wrapping 2 => array( // $input array( 'str' => 'This is a string which is longer than 75 characters but does not contain UTF-8-encoded characters. So what?', ), // $expected "This is a string which is longer than 75 characters but does not contain\nUTF-8-encoded characters. So what?" ), // Default width, no UTF-8, multiple lines, no wrapping 3 => array( // $input array( 'str' => "This is a string which is longer than 75 \ncharacters but does not contain UTF-8-encoded\n characters. So what?", ), // $expected "This is a string which is longer than 75 \ncharacters but does not contain UTF-8-encoded\n characters. So what?" ), // Default width, UTF-8, multiple lines, no wrapping 4 => array( // $inpüt array( 'str' => "This is ä string which is lönger thän 75 \nchäräcters büt döes nöt cöntäin UTF-8-encöded\n chäräcters. Sö whät?", ), // $expected "This is ä string which is lönger thän 75 \nchäräcters büt döes nöt cöntäin UTF-8-encöded\n chäräcters. Sö whät?" ), // Default width, no UTF-8, multiple lines, wrapping 4 => array( // $input array( 'str' => "This is\n a string which is longer than 75 characters but does not contain UTF-8-encoded characters. So what?", ), // $expected "This is\n a string which is longer than 75 characters but does not contain\nUTF-8-encoded characters. So what?" ), // Default width, no UTF-8, multiple lines, wrapping 6 => array( // $inpüt array( 'str' => "This is\n ä string which is lönger thän 75 chäräcters büt döes nöt cöntäin UTF-8-encöded chäräcters. Sö whät?", ), // $expected "This is\n ä string which is lönger thän 75 chäräcters büt döes nöt cöntäin\nUTF-8-encöded chäräcters. Sö whät?" ), // ----------------- // Custom width, no UTF-8, single line, no wrapping 7 => array( // $input array( 'str' => 'Some short string', 'width' => 18, ), // $expected 'Some short string', ), // Custom width, UTF-8, single line, no wrapping 8 => array( // $input array( 'str' => 'Söme shört string', 'width' => 18, ), // $expected 'Söme shört string', ), // Custom width, no UTF-8, single line, wrapping 9 => array( // $input array( 'str' => 'This is a string which is longer than 75 characters but does not contain UTF-8-encoded characters. So what?', 'width' => 17, ), // $expected "This is a string\nwhich is longer\nthan 75\ncharacters but\ndoes not contain\nUTF-8-encoded\ncharacters. So\nwhat?" ), // Custom width, UTF-8, single line, wrapping 10 => array( // $input array( 'str' => 'This is ä string which is lönger thän 75 chäräcters büt döes nöt cöntäin UTF-8-encöded chäräcters. Sö whät?', 'width' => 17, ), // $expected "This is ä string\nwhich is lönger\nthän 75\nchäräcters büt\ndöes nöt cöntäin\nUTF-8-encöded\nchäräcters. Sö\nwhät?" ), // Default width, UTF-8, wrapping 11 => array( // $input array( 'str' => 'This is ä string which is lönger thän 75 chäräcters büt döes nöt cöntäin UTF-8-encöded chäräcters. Sö whät?', ), // $expected "This is ä string which is lönger thän 75 chäräcters büt döes nöt cöntäin\nUTF-8-encöded chäräcters. Sö whät?" ), // ----------------- // Cut, single line, non UTF-8 12 => array( // $input array( 'str' => 'This is a string which is longer than 75 characters but does not contain UTF-8-encoded characters. So what?', 'width' => 75, 'break' => "\n", 'cut' => true, ), // $expected "This is a string which is longer than 75 characters but does not contain\nUTF-8-encoded characters. So what?" ), // Cut, single line, UTF-8 13 => array( // $input array( 'str' => 'This is ä string which is lönger thän 75 chäräcters büt döes nöt cöntäin UTF-8-encöded chäräcters. Sö whät?', 'width' => 75, 'break' => "\n", 'cut' => true, ), // $expected "This is ä string which is lönger thän 75 chäräcters büt döes nöt cöntäin\nUTF-8-encöded chäräcters. Sö whät?" ), // No cut, multiple lines, non UTF-8 14 => array( // $input array( 'str' => "This\nis a string which is longer than 75 characters but does not contain UTF-8-encoded characters. So what?", 'width' => 75, 'break' => "\n", 'cut' => true, ), // $expected "This\nis a string which is longer than 75 characters but does not contain\nUTF-8-encoded characters. So what?" ), // No cut, multiple lines, UTF-8 15 => array( // $input array( 'str' => "This\nis ä string which is lönger thän 75 chäräcters büt döes nöt cöntäin UTF-8-encöded chäräcters. Sö whät?", 'width' => 75, 'break' => "\n", 'cut' => true, ), // $expected "This\nis ä string which is lönger thän 75 chäräcters büt döes nöt cöntäin\nUTF-8-encöded chäräcters. Sö whät?" ), // ----------------- // Default width, no UTF-8, single line, wrapping, custom wrap char 16 => array( // $input array( 'str' => 'This is a string which is longer than 75 characters but does not contain UTF-8-encoded characters. So what?', 'width' => 75, 'break' => '---' ), // $expected "This is a string which is longer than 75 characters but does not contain---UTF-8-encoded characters. So what?" ), // Default width, no UTF-8, multiple lines, wrapping, custom wrap char 17 => array( // $input array( 'str' => "This is a string which is longer than 75 \ncharacters but does not contain UTF-8-encoded\n characters. So what?", 'width' => 75, 'break' => '---' ), // $expected "This is a string which is longer than 75 \ncharacters but does not contain---UTF-8-encoded\n characters. So what?" ), // Default width, UTF-8, multiple lines, wrapping, custom wrap char 18 => array( // $inpüt array( 'str' => "This is ä string which is lönger thän 75 \nchäräcters büt döes nöt cöntäin UTF-8-encöded\n chäräcters. Sö whät?", 'width' => 75, 'break' => '---' ), // $expected "This is ä string which is lönger thän 75 \nchäräcters büt döes nöt cöntäin---UTF-8-encöded\n chäräcters. Sö whät?" ), // ---------- PHP tests to ensure compatibility ------------ // ext/standard/tests/strings/wordwrap.phpt 19 => array( array( 'str' => "12345 12345 12345 12345", ), "12345 12345 12345 12345", ), 20 => array( array( 'str' => "12345 12345 1234567890 1234567890", 12 ), "12345 12345\n1234567890\n1234567890", ), 21 => array( array( 'str' => "12345 12345 12345 12345", 0 ), "12345\n12345\n12345\n12345", ), 22 => array( array( 'str' => "12345 12345 12345 12345", 0, "ab" ), "12345ab12345ab12345ab12345", ), 23 => array( array( 'str' => "12345 12345 1234567890 1234567890", 12, "ab" ), "12345 12345ab1234567890ab1234567890" ), 24 => array( array( 'str' => "123ab123ab123", 3, "ab" ), "123ab123ab123" ), 25 => array( array( 'str' => "123ab123ab123", 5, "ab" ), "123ab123ab123", ), 26 => array( array( 'str' => "123 123ab123", 3, "ab" ), "123ab 123ab123", ), 27 => array( array( 'str' => "123 123ab123", 5, "ab" ), "123ab123ab123", ), 28 => array( array( 'str' => "123 123 123", 10, "ab" ), "123 123ab123", ), 29 => array( array( 'str' => "123ab123ab123", 3, "ab", 1 ), "123ab123ab123", ), 30 => array( array( 'str' => "123ab123ab123", 5, "ab", 1 ), "123ab123ab123", ), 31 => array( array( 'str' => "123 123ab123", 3, "ab", 1 ), "123ab 12ab3ab123", ), 32 => array( array( 'str' => "123 123ab123", 5, "ab", 1 ), "123 ab123ab123" ), 33 => array( array( 'str' => "123 123 123", 8, "ab", 1 ), "123 123ab 123" ), 34 => array( array( 'str' => "123 12345 123", 8, "ab", 1 ), "123 ab12345 ab123" ), 35 => array( array( 'str' => "1234", 1, "ab", 1 ), "1ab2ab3ab4" ), 36 => array( array( 'str' => "12345 1234567890", 5, "|", 1 ), "12345|12345|67890" ), 37 => array( array( 'str' => "123 1234567890 123", 10, "|==", 1 ), "123|==1234567890|==123" ), 38 => array( array( chr(0), 0, "" ), false ), // ext/standard/tests/strings/wordwrap_basic.phpt 39 => array( array( 'str' => 'The quick brown foooooooooox jummmmmmmmmmmmped over the lazzzzzzzzzzzy doooooooooooooooooooooog.', ), "The quick brown foooooooooox jummmmmmmmmmmmped over the lazzzzzzzzzzzy\ndoooooooooooooooooooooog.", ), 40 => array( array( 'str' => 'The quick brown foooooooooox jummmmmmmmmmmmped over the lazzzzzzzzzzzy doooooooooooooooooooooog.', 'width' => 80, ), "The quick brown foooooooooox jummmmmmmmmmmmped over the lazzzzzzzzzzzy\ndoooooooooooooooooooooog.", ), 40 => array( array( 'str' => 'The quick brown foooooooooox jummmmmmmmmmmmped over the lazzzzzzzzzzzy doooooooooooooooooooooog.', 'width' => 80, 'break' => '
\n', ), 'The quick brown foooooooooox jummmmmmmmmmmmped over the lazzzzzzzzzzzy
\ndoooooooooooooooooooooog.', ), 41 => array( array( 'str' => 'The quick brown foooooooooox jummmmmmmmmmmmped over the lazzzzzzzzzzzy doooooooooooooooooooooog.', 'width' => 10, 'break' => '
\n', 'cut' => true, ), 'The quick
\nbrown
\nfooooooooo
\nox
\njummmmmmmm
\nmmmmped
\nover the
\nlazzzzzzzz
\nzzzy
\ndooooooooo
\noooooooooo
\nooog.' ), 42 => array( array( 'str' => 'The quick brown foooooooooox jummmmmmmmmmmmped over the lazzzzzzzzzzzy doooooooooooooooooooooog.', 'width' => 10, 'break' => '
\n', 'cut' => false, ), 'The quick
\nbrown
\nfoooooooooox
\njummmmmmmmmmmmped
\nover the
\nlazzzzzzzzzzzy
\ndoooooooooooooooooooooog.' ), // ext/standard/tests/strings/wordwrap_variation5.phpt 43 => array( array( 'str' => 'Testing wordrap function', 'width' => 1, ), "Testing\nwordrap\nfunction", ), 44 => array( array( 'str' => 'Testing wordrap function', 'width' => 1, 'break' => ' ', ), 'Testing wordrap function' ), 44 => array( array( 'str' => 'Testing wordrap function', 'width' => 1, 'break' => ' ', ), 'Testing wordrap function' ), 45 => array( array( 'str' => 'Testing wordrap function', 'width' => 1, 'break' => ' ', 'cut' => false, ), 'Testing wordrap function' ), 46 => array( array( 'str' => 'Testing wordrap function', 'width' => 1, 'break' => ' ', 'cut' => false, ), 'Testing wordrap function' ), 47 => array( array( 'str' => 'Testing wordrap function', 'width' => 1, 'break' => ' ', 'cut' => true, ), 'T e s t i n g w o r d r a p f u n c t i o n' ), 48 => array( array( 'str' => 'Testing wordrap function', 'width' => 1, 'break' => ' ', 'cut' => true, ), 'T e s t i n g w o r d r a p f u n c t i o n' ), ); ?> ConsoleTools-1.7.5/tests/data/windows/000077500000000000000000000000001470000742400177125ustar00rootroot00000000000000ConsoleTools-1.7.5/tests/data/windows/ezcConsoleMenuDialogTest__testDialog1.php000066400000000000000000000030711470000742400277340ustar00rootroot00000000000000text = "Please choose a possibility:\n"; $opts->validator = new ezcConsoleMenuDialogDefaultValidator( array( "A" => "Selection A", "B" => "Selection B", "C" => "Selection C", "D" => "Selection D", "Z" => "Selection Z", ), "Z" ); $dialog = new ezcConsoleMenuDialog( $out, $opts ); $res = ezcConsoleDialogViewer::displayDialog( $dialog ); echo "User seletced $res\n"; ?> ConsoleTools-1.7.5/tests/data/windows/ezcConsoleMenuDialogTest__testDialog1_res.php000066400000000000000000000020631470000742400306050ustar00rootroot00000000000000 '', 1 => '', 2 => '', ); ?>ConsoleTools-1.7.5/tests/data/windows/ezcConsoleMenuDialogTest__testDialog2.php000066400000000000000000000031621470000742400277360ustar00rootroot00000000000000text = "Please choose a possibility:\n"; $opts->validator = new ezcConsoleMenuDialogDefaultValidator( array( "A" => "Selection A", "B" => "Selection B", "C" => "Selection C", "D" => "Selection D", "Z" => "Selection Z", ), "Z", ezcConsoleMenuDialogDefaultValidator::CONVERT_UPPER ); $dialog = new ezcConsoleMenuDialog( $out, $opts ); $res = ezcConsoleDialogViewer::displayDialog( $dialog ); echo "User seletced $res\n"; ?> ConsoleTools-1.7.5/tests/data/windows/ezcConsoleMenuDialogTest__testDialog2_res.php000066400000000000000000000020631470000742400306060ustar00rootroot00000000000000 '', 1 => '', 2 => '', ); ?>ConsoleTools-1.7.5/tests/data/windows/ezcConsoleMenuDialogTest__testDialog3.php000066400000000000000000000032471470000742400277430ustar00rootroot00000000000000text = "Please choose a possibility:\n"; $opts->validator = new ezcConsoleMenuDialogDefaultValidator( array( "A" => "Selection A", "B" => "Selection B", "C" => "Selection C", "D" => "Selection D", "Z" => "Selection Z", ), "Z", ezcConsoleMenuDialogDefaultValidator::CONVERT_UPPER ); $dialog = new ezcConsoleMenuDialog( $out, $opts ); while ( ( $res = ezcConsoleDialogViewer::displayDialog( $dialog ) ) !== 'Z' ) { echo "User seletced $res\n"; } echo "User quitted\n"; ?> ConsoleTools-1.7.5/tests/data/windows/ezcConsoleMenuDialogTest__testDialog3_res.php000066400000000000000000000021241470000742400306050ustar00rootroot00000000000000 '', 1 => '', 2 => '', 3 => '', 4 => '', 5 => '', ); ?>ConsoleTools-1.7.5/tests/data/windows/ezcConsoleMenuDialogTest__testDialog4.php000066400000000000000000000034231470000742400277400ustar00rootroot00000000000000text = "Please choose a possibility:\n"; $opts->validator = new ezcConsoleMenuDialogDefaultValidator( array( "A" => "Selection A", "B" => "Selection B", "C" => "Selection C", "D" => "Selection D", "Z" => "Selection Z", ), "Z", ezcConsoleMenuDialogDefaultValidator::CONVERT_UPPER ); $dialog = new ezcConsoleMenuDialog( $out, $opts ); try { while ( ( $res = ezcConsoleDialogViewer::displayDialog( $dialog ) ) !== 'Z' ) { echo "User seletced $res\n"; } } catch ( ezcConsoleDialogAbortException $e ) { echo "User manually aborted\n"; } echo "User quitted\n"; ?> ConsoleTools-1.7.5/tests/data/windows/ezcConsoleMenuDialogTest__testDialog4_res.php000066400000000000000000000020761470000742400306140ustar00rootroot00000000000000 '', 1 => '', 2 => '', 3 => '', ); ?>ConsoleTools-1.7.5/tests/data/windows/ezcConsoleQuestionDialogTest__testDialog1.php000066400000000000000000000030561470000742400306420ustar00rootroot00000000000000text = "Do you want to proceed?"; $opts->showResults = true; $opts->validator = new ezcConsoleQuestionDialogCollectionValidator( array( "y", "n" ), "n", ezcConsoleQuestionDialogCollectionValidator::CONVERT_LOWER ); $dialog = new ezcConsoleQuestionDialog( $out, $opts ); echo "The user decided to " . ( ezcConsoleDialogViewer::displayDialog( $dialog ) === "n" ? "not " : "" ) . "proceed.\n"; ?> ConsoleTools-1.7.5/tests/data/windows/ezcConsoleQuestionDialogTest__testDialog1_res.php000066400000000000000000000020631470000742400315100ustar00rootroot00000000000000 '', 1 => '', 2 => '', ); ?>ConsoleTools-1.7.5/tests/data/windows/ezcConsoleQuestionDialogTest__testDialog2.php000066400000000000000000000030771470000742400306460ustar00rootroot00000000000000text = "How old are you?"; $opts->showResults = true; $opts->validator = new ezcConsoleQuestionDialogTypeValidator( ezcConsoleQuestionDialogTypeValidator::TYPE_INT ); $dialog = new ezcConsoleQuestionDialog( $out, $opts ); if ( ( $res = ezcConsoleDialogViewer::displayDialog( $dialog ) ) < 8 ) { echo "Sorry, I can not believe that you are $res years old!\n"; } else { echo "Hey, you're still young! :)\n"; } ?> ConsoleTools-1.7.5/tests/data/windows/ezcConsoleQuestionDialogTest__testDialog2_res.php000066400000000000000000000021111470000742400315030ustar00rootroot00000000000000 '', 1 => '', 2 => '', 3 => '', 4 => '', ); ?>ConsoleTools-1.7.5/tests/data/windows/ezcConsoleQuestionDialogTest__testDialog3.php000066400000000000000000000026261470000742400306460ustar00rootroot00000000000000 ConsoleTools-1.7.5/tests/data/windows/ezcConsoleQuestionDialogTest__testDialog3_res.php000066400000000000000000000020631470000742400315120ustar00rootroot00000000000000 '', 1 => '', 2 => '', ); ?>ConsoleTools-1.7.5/tests/data/windows/ezcConsoleQuestionDialogTest__testDialog4.php000066400000000000000000000027111470000742400306420ustar00rootroot00000000000000text = "Please enter your email address: "; $opts->validator = new ezcConsoleQuestionDialogRegexValidator( "/[a-z0-9_\.]+@[a-z0-9_\.]+\.[a-z0-9_\.]+/" ); $dialog = new ezcConsoleQuestionDialog( $out, $opts ); echo "The email address is " . ezcConsoleDialogViewer::displayDialog( $dialog ) . ".\n"; ?> ConsoleTools-1.7.5/tests/data/windows/ezcConsoleQuestionDialogTest__testDialog4_res.php000066400000000000000000000021111470000742400315050ustar00rootroot00000000000000 '', 1 => '', 2 => '', 3 => '', 4 => '', ); ?>ConsoleTools-1.7.5/tests/data/windows/ezcConsoleQuestionDialogTest__testDialog5.php000066400000000000000000000030521470000742400306420ustar00rootroot00000000000000text = "Please enter your email address: "; $opts->validator = new ezcConsoleQuestionDialogRegexValidator( "/[a-z0-9_\.]+@[a-z0-9_\.]+\.[a-z0-9_\.]+/" ); $dialog = new ezcConsoleQuestionDialog( $out, $opts ); try { echo "The email address is " . ezcConsoleDialogViewer::displayDialog( $dialog ) . ".\n"; } catch ( ezcConsoleDialogAbortException $e ) { echo "User manually aborted\ņ"; } ?> ConsoleTools-1.7.5/tests/data/windows/ezcConsoleQuestionDialogTest__testDialog5_res.php000066400000000000000000000020631470000742400315140ustar00rootroot00000000000000 '', 1 => '', 2 => '', ); ?>ConsoleTools-1.7.5/tests/data/windows/testEmptyTable.dat000066400000000000000000000002521470000742400233510ustar00rootroot00000000000000+----------------------------------------------------------------------------------+ +----------------------------------------------------------------------------------+ConsoleTools-1.7.5/tests/data/windows/testFinish.dat000066400000000000000000000016621470000742400225310ustar00rootroot00000000000000 1 / 10 [+++++>------------------------------------------------------] 10.00%  2 / 10 [+++++++++++>------------------------------------------------] 20.00%  3 / 10 [+++++++++++++++++>------------------------------------------] 30.00%  4 / 10 [+++++++++++++++++++++++>------------------------------------] 40.00%  5 / 10 [+++++++++++++++++++++++++++++>------------------------------] 50.00% 10 / 10 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/windows/testOneRowOneColumnTable.dat000066400000000000000000000000311470000742400252770ustar00rootroot00000000000000+-----+ | foo | +-----+ConsoleTools-1.7.5/tests/data/windows/testProgress1.dat000066400000000000000000000011661470000742400231750ustar00rootroot0000000000000013 / 42 [+++++++++++++++++++>----------------------------------------] 30.95% 26 / 42 [+++++++++++++++++++++++++++++++++++++++>--------------------] 61.90% 39 / 42 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] 92.86% 42 / 42 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress10.dat000066400000000000000000000030521470000742400232510ustar00rootroot00000000000000 10 / 100 [+++++>----------------------------------------------------] 10.00%  20 / 100 [+++++++++++>----------------------------------------------] 20.00%  30 / 100 [+++++++++++++++++>----------------------------------------] 30.00%  40 / 100 [+++++++++++++++++++++++>----------------------------------] 40.00%  50 / 100 [++++++++++++++++++++++++++++>-----------------------------] 50.00%  60 / 100 [++++++++++++++++++++++++++++++++++>-----------------------] 60.00%  70 / 100 [++++++++++++++++++++++++++++++++++++++++>-----------------] 70.00%  80 / 100 [++++++++++++++++++++++++++++++++++++++++++++++>-----------] 80.00%  90 / 100 [++++++++++++++++++++++++++++++++++++++++++++++++++++>-----] 90.00% 100 / 100 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress11.dat000066400000000000000000000142561470000742400232620ustar00rootroot00000000000000 2.50 / 100.00 [+>--------------------------------------------------] 2.50%  5.00 / 100.00 [++>-------------------------------------------------] 5.00%  7.50 / 100.00 [+++>------------------------------------------------] 7.50%  10.00 / 100.00 [+++++>----------------------------------------------] 10.00%  12.50 / 100.00 [++++++>---------------------------------------------] 12.50%  15.00 / 100.00 [+++++++>--------------------------------------------] 15.00%  17.50 / 100.00 [+++++++++>------------------------------------------] 17.50%  20.00 / 100.00 [++++++++++>-----------------------------------------] 20.00%  22.50 / 100.00 [+++++++++++>----------------------------------------] 22.50%  25.00 / 100.00 [++++++++++++>---------------------------------------] 25.00%  27.50 / 100.00 [++++++++++++++>-------------------------------------] 27.50%  30.00 / 100.00 [+++++++++++++++>------------------------------------] 30.00%  32.50 / 100.00 [++++++++++++++++>-----------------------------------] 32.50%  35.00 / 100.00 [++++++++++++++++++>---------------------------------] 35.00%  37.50 / 100.00 [+++++++++++++++++++>--------------------------------] 37.50%  40.00 / 100.00 [++++++++++++++++++++>-------------------------------] 40.00%  42.50 / 100.00 [++++++++++++++++++++++>-----------------------------] 42.50%  45.00 / 100.00 [+++++++++++++++++++++++>----------------------------] 45.00%  47.50 / 100.00 [++++++++++++++++++++++++>---------------------------] 47.50%  50.00 / 100.00 [+++++++++++++++++++++++++>--------------------------] 50.00%  52.50 / 100.00 [+++++++++++++++++++++++++++>------------------------] 52.50%  55.00 / 100.00 [++++++++++++++++++++++++++++>-----------------------] 55.00%  57.50 / 100.00 [+++++++++++++++++++++++++++++>----------------------] 57.50%  60.00 / 100.00 [+++++++++++++++++++++++++++++++>--------------------] 60.00%  62.50 / 100.00 [++++++++++++++++++++++++++++++++>-------------------] 62.50%  65.00 / 100.00 [+++++++++++++++++++++++++++++++++>------------------] 65.00%  67.50 / 100.00 [+++++++++++++++++++++++++++++++++++>----------------] 67.50%  70.00 / 100.00 [++++++++++++++++++++++++++++++++++++>---------------] 70.00%  72.50 / 100.00 [+++++++++++++++++++++++++++++++++++++>--------------] 72.50%  75.00 / 100.00 [++++++++++++++++++++++++++++++++++++++>-------------] 75.00%  77.50 / 100.00 [++++++++++++++++++++++++++++++++++++++++>-----------] 77.50%  80.00 / 100.00 [+++++++++++++++++++++++++++++++++++++++++>----------] 80.00%  82.50 / 100.00 [++++++++++++++++++++++++++++++++++++++++++>---------] 82.50%  85.00 / 100.00 [++++++++++++++++++++++++++++++++++++++++++++>-------] 85.00%  87.50 / 100.00 [+++++++++++++++++++++++++++++++++++++++++++++>------] 87.50%  90.00 / 100.00 [++++++++++++++++++++++++++++++++++++++++++++++>-----] 90.00%  92.50 / 100.00 [++++++++++++++++++++++++++++++++++++++++++++++++>---] 92.50%  95.00 / 100.00 [+++++++++++++++++++++++++++++++++++++++++++++++++>--] 95.00%  97.50 / 100.00 [++++++++++++++++++++++++++++++++++++++++++++++++++>-] 97.50% 100.00 / 100.00 [+++++++++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress12.dat000066400000000000000000000142561470000742400232630ustar00rootroot00000000000000 2.50 / 100.00000000 [+>--------------------------------------------] 2.50%  5.00 / 100.00000000 [++>-------------------------------------------] 5.00%  7.50 / 100.00000000 [+++>------------------------------------------] 7.50%  10.00 / 100.00000000 [++++>-----------------------------------------] 10.00%  12.50 / 100.00000000 [+++++>----------------------------------------] 12.50%  15.00 / 100.00000000 [++++++>---------------------------------------] 15.00%  17.50 / 100.00000000 [++++++++>-------------------------------------] 17.50%  20.00 / 100.00000000 [+++++++++>------------------------------------] 20.00%  22.50 / 100.00000000 [++++++++++>-----------------------------------] 22.50%  25.00 / 100.00000000 [+++++++++++>----------------------------------] 25.00%  27.50 / 100.00000000 [++++++++++++>---------------------------------] 27.50%  30.00 / 100.00000000 [+++++++++++++>--------------------------------] 30.00%  32.50 / 100.00000000 [++++++++++++++>-------------------------------] 32.50%  35.00 / 100.00000000 [++++++++++++++++>-----------------------------] 35.00%  37.50 / 100.00000000 [+++++++++++++++++>----------------------------] 37.50%  40.00 / 100.00000000 [++++++++++++++++++>---------------------------] 40.00%  42.50 / 100.00000000 [+++++++++++++++++++>--------------------------] 42.50%  45.00 / 100.00000000 [++++++++++++++++++++>-------------------------] 45.00%  47.50 / 100.00000000 [+++++++++++++++++++++>------------------------] 47.50%  50.00 / 100.00000000 [++++++++++++++++++++++>-----------------------] 50.00%  52.50 / 100.00000000 [++++++++++++++++++++++++>---------------------] 52.50%  55.00 / 100.00000000 [+++++++++++++++++++++++++>--------------------] 55.00%  57.50 / 100.00000000 [++++++++++++++++++++++++++>-------------------] 57.50%  60.00 / 100.00000000 [+++++++++++++++++++++++++++>------------------] 60.00%  62.50 / 100.00000000 [++++++++++++++++++++++++++++>-----------------] 62.50%  65.00 / 100.00000000 [+++++++++++++++++++++++++++++>----------------] 65.00%  67.50 / 100.00000000 [+++++++++++++++++++++++++++++++>--------------] 67.50%  70.00 / 100.00000000 [++++++++++++++++++++++++++++++++>-------------] 70.00%  72.50 / 100.00000000 [+++++++++++++++++++++++++++++++++>------------] 72.50%  75.00 / 100.00000000 [++++++++++++++++++++++++++++++++++>-----------] 75.00%  77.50 / 100.00000000 [+++++++++++++++++++++++++++++++++++>----------] 77.50%  80.00 / 100.00000000 [++++++++++++++++++++++++++++++++++++>---------] 80.00%  82.50 / 100.00000000 [+++++++++++++++++++++++++++++++++++++>--------] 82.50%  85.00 / 100.00000000 [+++++++++++++++++++++++++++++++++++++++>------] 85.00%  87.50 / 100.00000000 [++++++++++++++++++++++++++++++++++++++++>-----] 87.50%  90.00 / 100.00000000 [+++++++++++++++++++++++++++++++++++++++++>----] 90.00%  92.50 / 100.00000000 [++++++++++++++++++++++++++++++++++++++++++>---] 92.50%  95.00 / 100.00000000 [+++++++++++++++++++++++++++++++++++++++++++>--] 95.00%  97.50 / 100.00000000 [++++++++++++++++++++++++++++++++++++++++++++>-] 97.50% 100.00 / 100.00000000 [+++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress13.dat000066400000000000000000000142561470000742400232640ustar00rootroot00000000000000 2.50000000 / 100.00 [+>--------------------------------------------] 2.50%  5.00000000 / 100.00 [++>-------------------------------------------] 5.00%  7.50000000 / 100.00 [+++>------------------------------------------] 7.50%  10.00000000 / 100.00 [++++>-----------------------------------------] 10.00%  12.50000000 / 100.00 [+++++>----------------------------------------] 12.50%  15.00000000 / 100.00 [++++++>---------------------------------------] 15.00%  17.50000000 / 100.00 [++++++++>-------------------------------------] 17.50%  20.00000000 / 100.00 [+++++++++>------------------------------------] 20.00%  22.50000000 / 100.00 [++++++++++>-----------------------------------] 22.50%  25.00000000 / 100.00 [+++++++++++>----------------------------------] 25.00%  27.50000000 / 100.00 [++++++++++++>---------------------------------] 27.50%  30.00000000 / 100.00 [+++++++++++++>--------------------------------] 30.00%  32.50000000 / 100.00 [++++++++++++++>-------------------------------] 32.50%  35.00000000 / 100.00 [++++++++++++++++>-----------------------------] 35.00%  37.50000000 / 100.00 [+++++++++++++++++>----------------------------] 37.50%  40.00000000 / 100.00 [++++++++++++++++++>---------------------------] 40.00%  42.50000000 / 100.00 [+++++++++++++++++++>--------------------------] 42.50%  45.00000000 / 100.00 [++++++++++++++++++++>-------------------------] 45.00%  47.50000000 / 100.00 [+++++++++++++++++++++>------------------------] 47.50%  50.00000000 / 100.00 [++++++++++++++++++++++>-----------------------] 50.00%  52.50000000 / 100.00 [++++++++++++++++++++++++>---------------------] 52.50%  55.00000000 / 100.00 [+++++++++++++++++++++++++>--------------------] 55.00%  57.50000000 / 100.00 [++++++++++++++++++++++++++>-------------------] 57.50%  60.00000000 / 100.00 [+++++++++++++++++++++++++++>------------------] 60.00%  62.50000000 / 100.00 [++++++++++++++++++++++++++++>-----------------] 62.50%  65.00000000 / 100.00 [+++++++++++++++++++++++++++++>----------------] 65.00%  67.50000000 / 100.00 [+++++++++++++++++++++++++++++++>--------------] 67.50%  70.00000000 / 100.00 [++++++++++++++++++++++++++++++++>-------------] 70.00%  72.50000000 / 100.00 [+++++++++++++++++++++++++++++++++>------------] 72.50%  75.00000000 / 100.00 [++++++++++++++++++++++++++++++++++>-----------] 75.00%  77.50000000 / 100.00 [+++++++++++++++++++++++++++++++++++>----------] 77.50%  80.00000000 / 100.00 [++++++++++++++++++++++++++++++++++++>---------] 80.00%  82.50000000 / 100.00 [+++++++++++++++++++++++++++++++++++++>--------] 82.50%  85.00000000 / 100.00 [+++++++++++++++++++++++++++++++++++++++>------] 85.00%  87.50000000 / 100.00 [++++++++++++++++++++++++++++++++++++++++>-----] 87.50%  90.00000000 / 100.00 [+++++++++++++++++++++++++++++++++++++++++>----] 90.00%  92.50000000 / 100.00 [++++++++++++++++++++++++++++++++++++++++++>---] 92.50%  95.00000000 / 100.00 [+++++++++++++++++++++++++++++++++++++++++++>--] 95.00%  97.50000000 / 100.00 [++++++++++++++++++++++++++++++++++++++++++++>-] 97.50% 100.00000000 / 100.00 [+++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress2.dat000066400000000000000000000002341470000742400231710ustar00rootroot0000000000000020 / 20 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress3.dat000066400000000000000000000011661470000742400231770ustar00rootroot0000000000000013 / 42 [###################>****************************************] 30.95% 26 / 42 [#######################################>********************] 61.90% 39 / 42 [###########################################################>] 92.86% 42 / 42 [###########################################################>] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress4.dat000066400000000000000000000007301470000742400231740ustar00rootroot0000000000000019 / 55 [+++++++++++++++++++&----------------------------------------] 34.55% 38 / 55 [+++++++++++++++++++++++++++++++++++++++&--------------------] 69.09% 55 / 55 [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++&] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress5.dat000066400000000000000000000006761470000742400232060ustar00rootroot0000000000000013 / 42 [++++++++++++&------------------------] 30.95% 26 / 42 [++++++++++++++++++++++++&------------] 61.90% 39 / 42 [++++++++++++++++++++++++++++++++++++&] 92.86% 42 / 42 [++++++++++++++++++++++++++++++++++++&] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress6.dat000066400000000000000000000024361470000742400232030ustar00rootroot00000000000000 3 / 22 [########&******************************************************] 13.64%  6 / 22 [#################&*********************************************] 27.27%  9 / 22 [##########################&************************************] 40.91% 12 / 22 [###################################&***************************] 54.55% 15 / 22 [############################################&******************] 68.18% 18 / 22 [#####################################################&*********] 81.82% 21 / 22 [##############################################################&] 95.45% 22 / 22 [##############################################################&] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress7.dat000066400000000000000000000016621470000742400232040ustar00rootroot00000000000000 7 / 42 [123412341>98769876987698769876987698769876987698769876987698] 16.67% 14 / 42 [1234123412341234123>9876987698769876987698769876987698769876] 33.33% 21 / 42 [12341234123412341234123412341>987698769876987698769876987698] 50.00% 28 / 42 [123412341234123412341234123412341234123>98769876987698769876] 66.67% 35 / 42 [1234123412341234123412341234123412341234123412341>9876987698] 83.33% 42 / 42 [12341234123412341234123412341234123412341234123412341234123>] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress8.dat000066400000000000000000000016621470000742400232050ustar00rootroot00000000000000 7 / 42 [1231231---98798798798798798798798798798798798798798798798798] 16.67% 14 / 42 [12312312312312312---9879879879879879879879879879879879879879] 33.33% 21 / 42 [123123123123123123123123123---987987987987987987987987987987] 50.00% 28 / 42 [1231231231231231231231231231231231231---98798798798798798798] 66.67% 35 / 42 [12312312312312312312312312312312312312312312312---9879879879] 83.33% 42 / 42 [123123123123123123123123123123123123123123123123123123123---] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgress9.dat000066400000000000000000000033421470000742400232030ustar00rootroot00000000000000Actual progress: <123123---98798798798798798798798798798798798798798798798798798798798798798> 13 Actual progress: <12312312312312---987987987987987987987987987987987987987987987987987987987> 26 Actual progress: <1231231231231231231231---9879879879879879879879879879879879879879879879879> 42 Actual progress: <123123123123123123123123123123---98798798798798798798798798798798798798798> 55 Actual progress: <123123123123123123123123123123123123123---98798798798798798798798798798798> 71 Actual progress: <12312312312312312312312312312312312312312312312---987987987987987987987987> 104 Actual progress: <1231231231231231231231231231231231231231231231231231231---9879879879879879> 120 Actual progress: <123123123123123123123123123123123123123123123123123123123123123---98798798> 133 Actual progress: <12312312312312312312312312312312312312312312312312312312312312312312312---> 144ConsoleTools-1.7.5/tests/data/windows/testProgressMonitor1.dat000066400000000000000000000006251470000742400245440ustar00rootroot00000000000000 10.0% UPLOAD /var/upload/test.php 20.0% UPLOAD /var/upload/testing.php 30.0% UPLOAD /var/upload/foo.php 40.0% UPLOAD /var/upload/bar.php 50.0% UPLOAD /var/upload/baz.png 60.0% UPLOAD /var/upload/image.jpg 70.0% UPLOAD /var/upload/bar.gif 80.0% UPLOAD /var/upload/ez-logo.jpg 90.0% UPLOAD /var/upload/ez-logo.png 100.0% UPLOAD /var/upload/ez-components.png ConsoleTools-1.7.5/tests/data/windows/testProgressMonitor2.dat000066400000000000000000000004211470000742400245370ustar00rootroot00000000000000 14.3% UPLOAD /var/upload/test.php 28.6% UPLOAD /var/upload/testing.php 42.9% UPLOAD /var/upload/foo.php 57.1% UPLOAD /var/upload/bar.php 71.4% UPLOAD /var/upload/baz.png 85.7% UPLOAD /var/upload/image.jpg 100.0% UPLOAD /var/upload/bar.gif ConsoleTools-1.7.5/tests/data/windows/testProgressMonitor3.dat000066400000000000000000000004371470000742400245470ustar00rootroot00000000000000 UPLOAD 14.29% /var/upload/test.php UPLOAD 28.57% /var/upload/testing.php UPLOAD 42.86% /var/upload/foo.php UPLOAD 57.14% /var/upload/bar.php UPLOAD 71.43% /var/upload/baz.png UPLOAD 85.71% /var/upload/image.jpg UPLOAD 100.00% /var/upload/bar.gif ConsoleTools-1.7.5/tests/data/windows/testProgressMonitor4.dat000066400000000000000000000004371470000742400245500ustar00rootroot00000000000000 UPLOAD 14.29% /var/upload/test.php UPLOAD 28.57% /var/upload/testing.php UPLOAD 42.86% /var/upload/foo.php UPLOAD 57.14% /var/upload/bar.php UPLOAD 71.43% /var/upload/baz.png UPLOAD 85.71% /var/upload/image.jpg UPLOAD 100.00% /var/upload/bar.gif ConsoleTools-1.7.5/tests/data/windows/testProgressUtfInBar.dat000066400000000000000000000017031470000742400245040ustar00rootroot00000000000000 2 / 10 [ööööööööööö>------------------------------------------------] 20.00%  4 / 10 [ööööööööööööööööööööööö>------------------------------------] 40.00%  6 / 10 [ööööööööööööööööööööööööööööööööööö>------------------------] 60.00%  8 / 10 [ööööööööööööööööööööööööööööööööööööööööööööööö>------------] 80.00% 10 / 10 [ööööööööööööööööööööööööööööööööööööööööööööööööööööööööööö>] 100.00%ConsoleTools-1.7.5/tests/data/windows/testProgressUtfInBoth.dat000066400000000000000000000017271470000742400247020ustar00rootroot00000000000000 2ö/ä10ü[ööööööööööö>------------------------------------------------]ß 20.00%  4ö/ä10ü[ööööööööööööööööööööööö>------------------------------------]ß 40.00%  6ö/ä10ü[ööööööööööööööööööööööööööööööööööö>------------------------]ß 60.00%  8ö/ä10ü[ööööööööööööööööööööööööööööööööööööööööööööööö>------------]ß 80.00% 10ö/ä10ü[ööööööööööööööööööööööööööööööööööööööööööööööööööööööööööö>]ß100.00%ConsoleTools-1.7.5/tests/data/windows/testProgressUtfInText.dat000066400000000000000000000014501470000742400247230ustar00rootroot00000000000000 2ö/ä10ü[+++++++++++>------------------------------------------------]ß 20.00%  4ö/ä10ü[+++++++++++++++++++++++>------------------------------------]ß 40.00%  6ö/ä10ü[+++++++++++++++++++++++++++++++++++>------------------------]ß 60.00%  8ö/ä10ü[+++++++++++++++++++++++++++++++++++++++++++++++>------------]ß 80.00% 10ö/ä10ü[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]ß100.00%ConsoleTools-1.7.5/tests/data/windows/testStatusbar1.dat000066400000000000000000000000311470000742400233270ustar00rootroot00000000000000+-++-+++--++-++-+-++---+-ConsoleTools-1.7.5/tests/data/windows/testStatusbar2.dat000066400000000000000000000000311470000742400233300ustar00rootroot00000000000000+-++-+++--++-++-+-++---+-ConsoleTools-1.7.5/tests/data/windows/testTable1a.dat000066400000000000000000000015711470000742400225610ustar00rootroot00000000000000+----------------------------------------+----------------------+-------------+ | Heading no. 1 | Longer heading no. 2 | Head 3 | +----------------------------------------+----------------------+-------------+ | Data cell 1 | Data cell 2 | Data cell 3 | +----------------------------------------+----------------------+-------------+ | Long long data cell with even more | Data cell 4 | Data cell 5 | | text in it... | | | +----------------------------------------+----------------------+-------------+ | a b c d e f g h i j k l m n o p q r s | Data cell | Data cell | | t u v w x | | | +----------------------------------------+----------------------+-------------+ConsoleTools-1.7.5/tests/data/windows/testTable1b.dat000066400000000000000000000015061470000742400225600ustar00rootroot00000000000000+------------+------------+------------+ | Heading | Longer | Head 3 | | no. 1 | heading | | | | no. 2 | | +------------+------------+------------+ | Data cell | Data cell | Data cell | | 1 | 2 | 3 | +------------+------------+------------+ | Long long | Data cell | Data cell | | data cell | 4 | 5 | | with even | | | | more text | | | | in it... | | | +------------+------------+------------+ | a b c d e | Data cell | Data cell | | f g h i j | | | | k l m n o | | | | p q r s t | | | | u v w x | | | +------------+------------+------------+ConsoleTools-1.7.5/tests/data/windows/testTable2a.dat000066400000000000000000000004641470000742400225620ustar00rootroot00000000000000+---------+---------+---------+---------+---------+--------+ | a | b | c | d | e | f | +---------+---------+---------+---------+---------+--------+ | g | h | i | j | k | l | +---------+---------+---------+---------+---------+--------+ConsoleTools-1.7.5/tests/data/windows/testTable2b.dat000066400000000000000000000002051470000742400225540ustar00rootroot00000000000000+---+---+---+---+---+---+ | a | b | c | d | e | f | +---+---+---+---+---+---+ | g | h | i | j | k | l | +---+---+---+---+---+---+ConsoleTools-1.7.5/tests/data/windows/testTable3a.dat000066400000000000000000000025071470000742400225630ustar00rootroot00000000000000######################################################################################################################### # Parameter # Shortcut # Descrition # ######################################################################################################################### # Append text to a file. This parameter takes a string value and may be used multiple times. # --append # -a # ######################################################################################################################### # Prepend text to a file. This parameter takes a string value and may be used multiple times. # --prepend # -p # ######################################################################################################################### # Forces the action desired without paying attention to any errors. # --force # -f # ######################################################################################################################### # Silence all kinds of warnings issued by this program. # --silent # -s # #########################################################################################################################ConsoleTools-1.7.5/tests/data/windows/testTable3b.dat000066400000000000000000000022751470000742400225660ustar00rootroot00000000000000cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvc h Parameter h Shortcut h Descrition h cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvc h Append text to a file. This parameter takes a h --append h -a h h string value and may be used multiple times. h h h cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvc h Prepend text to a file. This parameter takes a h --prepend h -p h h string value and may be used multiple times. h h h cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvc h Forces the action desired without paying attention h --force h -f h h to any errors. h h h cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvc h Silence all kinds of warnings issued by this h --silent h -s h h program. h h h cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvcvvvvvvvvvvvvcConsoleTools-1.7.5/tests/data/windows/testTable3c.dat000066400000000000000000000012021470000742400225540ustar00rootroot00000000000000+---------+---------+---------+ | Parameter | Shortcut | Descrition | +---------+---------+---------+ | Append text to a file. This parameter takes a string value and may be used multiple times. | --append | -a | +---------+---------+---------+ | Prepend text to a file. This parameter takes a string value and may be used multiple times. | --prepend | -p | +---------+---------+---------+ | Forces the action desired without paying attention to any errors. | --force | -f | +---------+---------+---------+ | Silence all kinds of warnings issued by this program. | --silent | -s | +---------+---------+---------+ConsoleTools-1.7.5/tests/data/windows/testTable3d.dat000066400000000000000000000003451470000742400225640ustar00rootroot00000000000000+-----+-----+-----+ | Par | Sho | Des | +-----+-----+-----+ | App | --a | -a | +-----+-----+-----+ | Pre | --p | -p | +-----+-----+-----+ | For | --f | -f | +-----+-----+-----+ | Sil | --s | -s | +-----+-----+-----+ConsoleTools-1.7.5/tests/data/windows/testTable4a.dat000066400000000000000000000021211470000742400225540ustar00rootroot00000000000000+-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | Some very very long data he | Short | Some very very long data he | Short | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | Short | Some very very long data he | Short | Some very very long data he | | | | | | | | and it becomes even much mu | | | | | | | | | | and even longer.... | | | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ConsoleTools-1.7.5/tests/data/windows/testTable4b.dat000066400000000000000000000032701470000742400225630ustar00rootroot00000000000000+-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | Some very very long data | Short | Some very very long data | Short | | here.... and it becomes | | here.... and it becomes | | | even much much longer... | | even much much longer... | | | and even longer.... | | and even longer.... | | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | Short | Some very very long data | Short | Some very very long data | | | here.... | | here.... and it becomes | | | | | even much much longer... | | | and it becomes even much | | and even longer.... | | | much longer... | | | | | | | | | | and even longer.... | | | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ConsoleTools-1.7.5/tests/data/windows/testTable4c.dat000066400000000000000000000021211470000742400225560ustar00rootroot00000000000000+-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | Some very very long data he | Short | Some very very long data he | Short | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | Short | Some very very long data he | Short | Some very very long data he | | | | | | | | and it becomes even much mu | | | | | | | | | | and even longer.... | | | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ConsoleTools-1.7.5/tests/data/windows/testTable5autowidth.dat000066400000000000000000000027021470000742400243520ustar00rootroot00000000000000+-----------------------------------------------------------+-----------------------------------------------------------+ | Short text | More short text | +-----------------------------------------------------------+-----------------------------------------------------------+ | Short text | More short text | | Short text | More short text | | Short text | More short text | | Short text | Short text | | Short text | | | Short text | | | Short text | | | | | +-----------------------------------------------------------+-----------------------------------------------------------+ConsoleTools-1.7.5/tests/data/windows/testTableColPadding1.dat000066400000000000000000000007741470000742400243510ustar00rootroot00000000000000+----------------+----------------+----------------+----------------+----------------+-------------+ |~~~ a ~~~|~~~ b ~~~|~~~ c ~~~|~~~ d ~~~|~~~ e ~~~|~~~ f ~~~| +----------------+----------------+----------------+----------------+----------------+-------------+ |~~~ g ~~~|~~~ h ~~~|~~~ i ~~~|~~~ j ~~~|~~~ k ~~~|~~~ l ~~~| +----------------+----------------+----------------+----------------+----------------+-------------+ConsoleTools-1.7.5/tests/data/windows/testTableColPadding2.dat000066400000000000000000000003751470000742400243470ustar00rootroot00000000000000+-------+-------+-------+-------+-------+-------+ |~~~a~~~|~~~b~~~|~~~c~~~|~~~d~~~|~~~e~~~|~~~f~~~| +-------+-------+-------+-------+-------+-------+ |~~~g~~~|~~~h~~~|~~~i~~~|~~~j~~~|~~~k~~~|~~~l~~~| +-------+-------+-------+-------+-------+-------+ConsoleTools-1.7.5/tests/data/windows/testTableWithSpaceBorders.dat000066400000000000000000000032701470000742400254660ustar00rootroot00000000000000 Some very very long data Short Some very very long data Short here.... and it becomes here.... and it becomes even much much longer... even much much longer... and even longer.... and even longer.... Short Some very very long data Short Some very very long data here.... here.... and it becomes even much much longer... and it becomes even much and even longer.... much longer... and even longer.... ConsoleTools-1.7.5/tests/data/windows/testTableWithoutBorders.dat000066400000000000000000000024741470000742400252470ustar00rootroot00000000000000 Some very very long data Short Some very very long data Short here.... and it becomes even here.... and it becomes even much much longer... and even much much longer... and even longer.... longer.... Short Some very very long data Short Some very very long data here.... here.... and it becomes even much much longer... and even and it becomes even much longer.... much longer... and even longer.... ConsoleTools-1.7.5/tests/data/windows/testTableWithoutFormatting.dat000066400000000000000000000002051470000742400257470ustar00rootroot00000000000000+---+---+---+---+---+---+ | a | b | c | d | e | f | +---+---+---+---+---+---+ | g | h | i | j | k | l | +---+---+---+---+---+---+ConsoleTools-1.7.5/tests/data/windows/testTableWithoutHorizontalBorders.dat000066400000000000000000000032521470000742400273140ustar00rootroot00000000000000------------------------------------------------------------------------------------------------------------------------ Some very very long data Short Some very very long data Short here.... and it becomes even here.... and it becomes even much much longer... and even much much longer... and even longer.... longer.... ------------------------------------------------------------------------------------------------------------------------ Short Some very very long data Short Some very very long data here.... here.... and it becomes even much much longer... and even and it becomes even much longer.... much longer... and even longer.... ------------------------------------------------------------------------------------------------------------------------ConsoleTools-1.7.5/tests/data/windows/testTableWithoutVerticalBorders.dat000066400000000000000000000025071470000742400267360ustar00rootroot00000000000000| Some very very long data | Short | Some very very long data | Short | | here.... and it becomes | | here.... and it becomes | | | even much much longer... | | even much much longer... | | | and even longer.... | | and even longer.... | | | Short | Some very very long data | Short | Some very very long data | | | here.... | | here.... and it becomes | | | | | even much much longer... | | | and it becomes even much | | and even longer.... | | | much longer... | | | | | | | | | | and even longer.... | | |ConsoleTools-1.7.5/tests/data/windows/testUtf8TableHighlightNonUtf8.dat000066400000000000000000000011051470000742400261510ustar00rootroot00000000000000+--------------------+--------------------+-----------------------------------+ | Non UTF-8 column 1 | Non UTF-8 column 2 | Long long long long long long | | | | long non UTF-8 column | +--------------------+--------------------+-----------------------------------+ | Nön UTF-8 cölümn 1 | Nön UTF-8 cölümn 2 | Löng löng löng löng löng löng | | | | löng nön UTF-8 cölümn | +--------------------+--------------------+-----------------------------------+ConsoleTools-1.7.5/tests/data/windows/testUtf8TableHighlightUtf8.dat000066400000000000000000000011051470000742400254760ustar00rootroot00000000000000+--------------------+--------------------+-----------------------------------+ | Nön UTF-8 cölümn 1 | Nön UTF-8 cölümn 2 | Löng löng löng löng löng löng | | | | löng nön UTF-8 cölümn | +--------------------+--------------------+-----------------------------------+ | Non UTF-8 column 1 | Non UTF-8 column 2 | Long long long long long long | | | | long non UTF-8 column | +--------------------+--------------------+-----------------------------------+ConsoleTools-1.7.5/tests/dialog_options_test.php000066400000000000000000000100351470000742400220700ustar00rootroot00000000000000assertNull( $opts->validator ); $this->assertEquals( "default", $opts->format ); } public function testGetAccessCustomSuccess() { $opts = new ezcConsoleDialogOptions( array( "validator" => new ezcConsoleQuestionDialogCollectionValidator( array( "a", "b" ) ), "format" => "test", ) ); $this->assertInstanceOf( "ezcConsoleQuestionDialogCollectionValidator", $opts->validator ); $this->assertEquals( "test", $opts->format ); } public function testGetAccessFailure() { $opts = new ezcConsoleDialogOptions(); try { echo $opts->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on invalid property foo." ); } public function testSetAccessSuccess() { $opts = new ezcConsoleDialogOptions(); $opts->validator = new ezcConsoleQuestionDialogCollectionValidator( array( "a", "b" ) ); $opts->format = "test"; $this->assertInstanceOf( "ezcConsoleQuestionDialogCollectionValidator", $opts->validator ); $this->assertEquals( "test", $opts->format ); } public function testSetAccessFailure() { $opts = new ezcConsoleDialogOptions(); $exceptionCaught = false; try { $opts->validator = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property validator." ); $exceptionCaught = false; try { $opts->format = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property format." ); $exceptionCaught = false; try { $opts->foo = "Foo"; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on access of nonexistent property foo." ); } public function testIssetAccess() { $opts = new ezcConsoleDialogOptions(); $this->assertTrue( isset( $opts->validator ), "Property validator not set." ); $this->assertTrue( isset( $opts->format ), "Property format not set." ); $this->assertFalse( isset( $opts->foo ), "Property foo set." ); } } ?> ConsoleTools-1.7.5/tests/dialog_test.php000066400000000000000000000071621470000742400203240ustar00rootroot00000000000000dataDir = dirname( __FILE__ ) . DIRECTORY_SEPARATOR . "data" . DIRECTORY_SEPARATOR . ( ezcBaseFeatures::os() === 'Windows' ? "windows" : "posix" ); $this->determinePhpPath(); $this->output = new ezcConsoleOutput(); $this->output->formats->test->color = "blue"; } protected function determinePhpPath() { if ( isset( $_SERVER["_"] ) ) { $this->phpPath = $_SERVER["_"]; } else if ( ezcBaseFeatures::os() === 'Windows' ) { $this->phpPath = 'php.exe'; } else { $this->phpPath = '/usr/bin/env php'; } } protected function tearDown() : void { unset( $this->output ); } protected function runDialog( $methodName ) { $methodName = strtr( $methodName, array( ":" => "_", ) ); $scriptFile = $this->dataDir . DIRECTORY_SEPARATOR . $methodName . '.php'; $resFile = $this->dataDir . DIRECTORY_SEPARATOR . $methodName . '_res.php'; if ( !file_exists( $scriptFile ) ) { throw new RuntimeException( "Missing script file '$scriptFile'!" ); } $desc = array( 0 => array( "pipe", "r" ), // stdin 1 => array( "pipe", "w" ), // stdout 2 => array( "pipe", "w" ) // stderr ); $this->proc = proc_open("{$this->phpPath} '{$scriptFile}'", $desc, $this->pipes ); $this->res = ( file_exists( $resFile ) ? require( $resFile ) : false ); } protected function closeDialog() { proc_close( $this->proc ); unset( $this->pipes, $this->res ); } protected function saveDialogResult( $methodName, $res ) { $methodName = strtr( $methodName, array( ":" => "_", ) ); $resFile = "{$this->dataDir}/{$methodName}_res.php"; file_put_contents( $resFile, "" ); } protected function readPipe( $pipe ) { usleep( self::PIPE_READ_SLEEP ); return fread( $pipe, 1024 ); } } ?> ConsoleTools-1.7.5/tests/input_test.php000066400000000000000000002754441470000742400202360ustar00rootroot00000000000000 't', 'long' => 'testing', 'options' => array(), ), array( 'short' => 's', 'long' => 'subway', 'options' => array(), ), array( 'short' => '', 'long' => 'carry', 'options' => array(), ), array( 'short' => 'v', 'long' => 'visual', 'options' => array( 'multiple' => true, 'arguments' => false, ), ), array( 'short' => 'o', 'long' => 'original', 'options' => array( 'type' => ezcConsoleInput::TYPE_STRING, ), ), array( 'short' => 'b', 'long' => 'build', 'options' => array( 'type' => ezcConsoleInput::TYPE_INT, 'default' => 42, ), ), array( 'short' => 'd', 'long' => 'destroy', 'options' => array( 'type' => ezcConsoleInput::TYPE_STRING, 'default' => 'world', ), ), array( 'short' => 'y', 'long' => 'yank', 'options' => array( 'type' => ezcConsoleInput::TYPE_STRING, 'multiple' => true, 'shorthelp' => 'Some stupid short text.', 'longhelp' => 'Some even more stupid, but somewhat longer long describtion.', ), ), array( 'short' => 'c', 'long' => 'console', 'options' => array( 'shorthelp' => 'Some stupid short text.', 'longhelp' => 'Some even more stupid, but somewhat longer long describtion.', 'depends' => array( 't', 'o', 'b', 'y' ), ), ), array( 'short' => 'e', 'long' => 'edit', 'options' => array( 'excludes' => array( 't', 'y' ), 'arguments' => false, ), ), array( 'short' => 'n', 'long' => 'new', 'options' => array( 'depends' => array( 't', 'o' ), 'excludes' => array( 'b', 'y' ), 'arguments' => false, ), ), ); private $testAliasesSuccess = array( array( 'short' => 'k', 'long' => 'kelvin', 'ref' => 't', ), array( 'short' => 'f', 'long' => 'foobar', 'ref' => 'o', ), ); private $testAliasesFailure = array( array( 'short' => 'l', 'long' => 'lurking', 'ref' => 'x', ), array( 'short' => 'e', 'long' => 'elvis', 'ref' => 'z', ), array( 'short' => 'd', 'long' => 'destroy', 'ref' => 'd', ), ); private $testArgsSuccess = array( array( 'foo.php', '-o', '"Test string2"', '--build', '42', ), array( 'foo.php', '-b', '42', '--yank', '"a"', '--yank', '"b"', '--yank', '"c"', ), array( 'foo.php', '--yank=a', '--yank=b', '--yank="c"', '-y', '1', '-y', '2' ), array( 'foo.php', '--yank=a', '--yank=b', '-y', '1', 'arg1', 'arg2', ), ); private $input = null; public static function suite() { return new PHPUnit\Framework\TestSuite( "ezcConsoleInputTest" ); } protected function setUp() : void { $this->input = new ezcConsoleInput(); foreach ( $this->testOptions as $paramData ) { $this->input->registerOption( $this->createFakeOption( $paramData ) ); } } protected function createFakeOption( $paramData ) { $param = new ezcConsoleOption( $paramData['short'], $paramData['long'] ); foreach ( $paramData['options'] as $name => $val ) { if ( $name === 'depends' ) { foreach ( $val as $dep ) { $param->addDependency( new ezcConsoleOptionRule( $this->input->getOption( $dep ) ) ); } continue; } if ( $name === 'excludes' ) { foreach ( $val as $dep ) { $param->addExclusion(new ezcConsoleOptionRule( $this->input->getOption( $dep ) ) ); } continue; } $param->$name = $val; } return $param; } protected function tearDown() : void { unset( $this->input ); } public function testRegisterOptionSuccess() { $input = new ezcConsoleInput(); foreach ( $this->testOptions as $optionData ) { $option = $this->createFakeOption( $optionData ); $input->registerOption( $option ); if ( $option->short !== '' ) { $this->assertEquals( $option, $input->getOption( $optionData['short'] ), 'Parameter not registered correctly with short name <' . $optionData['short'] . '>.' ); } $this->assertEquals( $option, $input->getOption( $optionData['long'] ), 'Parameter not registered correctly with long name <' . $optionData['long'] . '>.' ); } } public function testRegisterOptionFailure() { $input = new ezcConsoleInput(); foreach ( $this->testOptions as $optionData ) { $option = $this->createFakeOption( $optionData ); $input->registerOption( $option ); } foreach ( $this->testOptions as $optionData ) { $option = $this->createFakeOption( $optionData ); $exceptionThrown = false; try { $input->registerOption( $option ); } catch ( ezcConsoleOptionAlreadyRegisteredException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on double registered option " . $optionData["short"] === "" ? "determined by long name." : "determined by short name." ); } } public function testUnregisterOptionSuccess() { // register aliases for testing $validParams = array(); foreach ( $this->input->getOptions() as $param ) { $validParams[$param->short] = $param; } foreach ( $this->testAliasesSuccess as $alias ) { $this->input->registerAlias( $alias['short'], $alias['long'], $validParams[$alias['ref']] ); } // test itself foreach ( $this->input->getOptions() as $option ) { $this->input->unregisterOption( $option ); $exceptionThrown = false; try { $this->input->getOption( isset( $option->short ) ? $option->short : $option->long ); } catch ( ezcConsoleOptionNotExistsException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not unregistered correctly -{$option->short}/--{$option->long}." ); } $this->assertEquals( 0, count( $this->input->getOptions() ) ); } public function testUnregisterOptionFailure() { $option = new ezcConsoleOption( "x", "execute" ); try { $this->input->unregisterOption( $option ); } catch ( ezcConsoleOptionNotExistsException $e ) { return; } $this->fail( "Exception not thrown on unregistering a non existent option." ); } public function testFromStringSuccess() { $param = new ezcConsoleInput(); $param->registerOptionString( '[a:|all:][u?|user?][i|info][o+test|overall+][d*|destroy*]' ); $res['a'] = new ezcConsoleOption( 'a', 'all', ezcConsoleInput::TYPE_NONE, NULL, false, 'No help available.', 'Sorry, there is no help text available for this parameter.', array(), array (), true ); $res['u'] = new ezcConsoleOption( 'u', 'user', ezcConsoleInput::TYPE_STRING, '', false, 'No help available.', 'Sorry, there is no help text available for this parameter.', array (), array (), true ); $res['o'] = new ezcConsoleOption( 'o', 'overall', ezcConsoleInput::TYPE_STRING, 'test', true, 'No help available.', 'Sorry, there is no help text available for this parameter.', array (), array (), true ); $res['d'] = new ezcConsoleOption( 'd', 'destroy', ezcConsoleInput::TYPE_NONE, null, true, 'No help available.', 'Sorry, there is no help text available for this parameter.', array (), array (), true ); $this->assertEquals( $res['a'], $param->getOption( 'a' ), 'Option -a not registered correctly.' ); $this->assertEquals( $res['u'], $param->getOption( 'u' ), 'Option -u not registered correctly.' ); $this->assertEquals( $res['o'], $param->getOption( 'o' ), 'Option -o not registered correctly.' ); $this->assertEquals( $res['d'], $param->getOption( 'd' ), 'Option -d not registered correctly.' ); } public function testFromStringFailure() { $param = new ezcConsoleInput(); try { $param->registerOptionString( '[a:]' ); } catch ( ezcConsoleOptionStringNotWellformedException $e ) { return; } $this->fail( "Exception not thrown on not wellformed option string." ); } /** * testRegisterAliasSuccess * * @access public */ public function testRegisterAliasSuccess() { $validParams = array(); foreach ( $this->input->getOptions() as $param ) { $validParams[$param->short] = $param; } foreach ( $this->testAliasesSuccess as $alias ) { $this->input->registerAlias( $alias['short'], $alias['long'], $validParams[$alias['ref']] ); $this->assertTrue( $this->input->hasOption( $alias['short'] ), "Short name not available after alias registration." ); $this->assertTrue( $this->input->hasOption( $alias['long'] ), "Long name not available after alias registration." ); } } /** * testRegisterAliasFailure * * @access public */ public function testRegisterAliasFailure() { $refOption = new ezcConsoleOption( 'foo', 'bar' ); foreach ( $this->testAliasesFailure as $alias ) { $exceptionThrown = false; try { $this->input->registerAlias( $alias['short'], $alias['long'], $refOption ); } catch ( ezcConsoleOptionNotExistsException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on regstering invalid alias --{$alias['short']}/--{$alias['long']}." ); } foreach ( $this->testOptions as $option ) { $exceptionThrown = false; try { $this->input->registerAlias( $option['short'], $option['long'], $this->input->getOption( "t" ) ); } catch ( ezcConsoleOptionAlreadyRegisteredException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on regstering already existent option as alias --{$alias['short']}/--{$alias['long']}." ); } } public function testUnregisterAliasSuccess() { // test preperation $validParams = array(); foreach ( $this->input->getOptions() as $param ) { $validParams[$param->short] = $param; } foreach ( $this->testAliasesSuccess as $alias ) { $this->input->registerAlias( $alias['short'], $alias['long'], $validParams[$alias['ref']] ); } foreach ( $this->testAliasesSuccess as $alias ) { $this->assertTrue( $this->input->hasOption( $alias['short'] ), "Alias incorrectly registered, cannot unregister." ); $this->input->unregisterAlias( $alias['short'], $alias['long'] ); $this->assertFalse( $this->input->hasOption( $alias['short'] ), "Alias incorrectly unregistered." ); } } public function testUnregisterAliasFailure() { foreach ( $this->testOptions as $option ) { $exceptionThrown = false; try { $this->input->unregisterAlias( !empty( $option['short'] ) ? $option['short'] : "f", $option['long'] ); } catch ( ezcConsoleOptionNoAliasException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not trown on try to unregister an option as an alias." ); } } public function testGetAccessSuccess() { $this->assertNull( $this->input->argumentDefinition ); } public function testGetAccessFailure() { try { echo $this->input->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "ezcBasePropertyNotFoundException not thrown on get access to invalid property foo." ); } public function testSetAccessSuccess() { $this->assertSetProperty( $this->input, "argumentDefinition", array( new ezcConsoleArguments(), null ) ); } public function testSetAccessFailure() { $this->assertSetPropertyFails( $this->input, "argumentDefinition", array( "", "foo", 23, true, array(), new stdClass() ), "ezcBaseValueException" ); $this->assertSetPropertyFails( $this->input, "foo", array( "" ), "ezcBasePropertyNotFoundException" ); } public function testIssetAccess() { $this->assertTrue( isset( $this->input->argumentDefinition ) ); $this->assertFalse( isset( $this->input->foo ) ); } // Single parameter tests public function testProcessSuccessSingleShortNoValue() { $args = array( 'foo.php', '-t', ); $res = array( 't' => true, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessSingleShortValue() { $args = array( 'foo.php', '-o', 'bar' ); $res = array( 'o' => 'bar', ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessSingleLongNoValue() { $args = array( 'foo.php', '--testing', ); $res = array( 't' => true, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessSingleLongValue() { $args = array( 'foo.php', '--original', 'bar' ); $res = array( 'o' => 'bar', ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessFailureSingleShortDefault() { $args = array( 'foo.php', '-b' ); $res = array( 'b' => 42, ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionMissingValueException' ); } public function testProcessFailureSingleLongDefault() { $args = array( 'foo.php', '--build' ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionMissingValueException' ); } public function testProcessSuccessFromArgv() { $_SERVER["argv"] = array( 'foo.php', '--build', '42' ); $this->input->process(); $this->assertEquals( array( "b" => 42, "d" => "world" ), $this->input->getOptionValues(), "Processing from \$_SERVER['argv'] did not work." ); } public function testProcessSuccessGetOptionValuesLongnames() { $_SERVER["argv"] = array( 'foo.php', '--build', '42' ); $this->input->process(); $this->assertEquals( array( "build" => 42, "destroy" => "world" ), $this->input->getOptionValues( true ), "Processing from \$_SERVER['argv'] did not work." ); } public function testProcessSuccessSingleShortNoValueArguments() { $args = array( 'foo.php', '-s', '--', '-foo', '--bar', 'baz', ); $res = array( 's' => true, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessSingleLongNoValueArguments() { $args = array( 'foo.php', '--subway', '--', '-foo', '--bar', 'baz', ); $res = array( 's' => true, ); $this->commonProcessTestSuccess( $args, $res ); } // Multiple parameter tests public function testProcessSuccessMultipleShortNoValue() { $args = array( 'foo.php', '-t', '-s', ); $res = array( 't' => true, 's' => true, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessMultipleShortValue() { $args = array( 'foo.php', '-o', 'bar', '-b', '23' ); $res = array( 'o' => 'bar', 'b' => 23, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessMultipleLongNoValue() { $args = array( 'foo.php', '--testing', '--subway', ); $res = array( 't' => true, 's' => true, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessMultipleLongValue() { $args = array( 'foo.php', '--original', 'bar', '--build', '23', ); $res = array( 'o' => 'bar', 'b' => 23, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessMultipleLongValueWithEquals() { $args = array( 'foo.php', '--original', 'bar', '--build=23', ); $res = array( 'o' => 'bar', 'b' => 23, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessFailureMultipleShortDefault() { $args = array( 'foo.php', '-b', '-d', ); $res = array( 'b' => 42, 'd' => 'world', ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionMissingValueException' ); } public function testProcessFailureMultipleLongDefault() { $args = array( 'foo.php', '--build', '--destroy', ); $res = array( 'b' => 42, 'd' => 'world', ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionMissingValueException' ); } // Bug #8645: Default values not set correctly in ezcConsoleInput public function testProcessSuccessDefault() { $args = array( 'foo.php', ); $res = array( 'b' => 42, 'd' => 'world', ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessMultipleLongSameNoValue() { $args = array( 'foo.php', '--visual', '--visual', ); $res = array( 'v' => array( true, true ), ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessArguments_1() { $args = array( 'foo.php', '--original', 'bar', '--build', '23', 'argument', '1', '2', ); $res = array( 0 => 'argument', 1 => '1', 2 => '2', ); $this->argumentsProcessTestSuccess( $args, $res ); } public function testProcessSuccessDependencies() { $args = array( 'foo.php', '-t', '-o', 'bar', '--build', 23, '-y', 'text', '--yank', 'moretext', '-c' // This one depends on -t, -o, -b and -y ); $res = array( 't' => true, 'o' => 'bar', 'b' => 23, 'y' => array( 'text', 'moretext' ), 'c' => true, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessExclusions() { $args = array( 'foo.php', '-o', 'bar', '--build', 23, '--edit' // This one exclude -t and -y ); $res = array( 'o' => 'bar', 'b' => 23, 'e' => true, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessDependenciesExclusions() { $args = array( 'foo.php', '-t', '-o', 'bar', '-n' // This one depends on -t and -o, but excludes -b and -y ); $res = array( 't' => true, 'o' => 'bar', 'n' => true, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessDependencieValues() { $rule = new ezcConsoleOptionRule( $this->input->getOption( "y" ), array( "foo", "bar" ) ); $option = new ezcConsoleOption( "x", "execute" ); $option->addDependency( $rule ); $this->input->registerOption( $option ); $args = array( 'foo.php', '-x', '-y', 'bar', ); $res = array( 'x' => true, 'y' => array( 'bar' ), ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessExclusionValues() { $rule = new ezcConsoleOptionRule( $this->input->getOption( "y" ), array( "foo", "bar" ) ); $option = new ezcConsoleOption( "x", "execute" ); $option->addExclusion( $rule ); $this->input->registerOption( $option ); $args = array( 'foo.php', '-x', '-y', 'baz', ); $res = array( 'x' => true, 'y' => array( 'baz' ), ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessMandatory() { $args = array( 'foo.php', '-q', ); $this->input->registerOption( $this->createFakeOption( array( 'short' => 'q', 'long' => 'quite', 'options' => array( 'mandatory' => true, ), ) ) ); $res = array( 'q' => true, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessMandatoryDefault() { $args = array( 'foo.php', '-q', ); $this->input->registerOption( $this->createFakeOption( array( 'short' => 'q', 'long' => 'quite', 'options' => array( 'default' => 'test', 'mandatory' => true, ), ) ) ); $res = array( 'q' => true, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessHelp() { $args = array( 'foo.php', '-h', ); $this->input->registerOption( $this->createFakeOption( array( 'short' => 'q', 'long' => 'quite', 'options' => array( 'mandatory' => true, ), ) ) ); $this->input->registerOption( $this->createFakeOption( array( 'short' => 'h', 'long' => 'help', 'options' => array( 'isHelpOption' => true, ), ) ) ); $res = array( 'h' => true, ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessSuccessNewArgumentsSimple() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->process( array( "foo.php", "'some file'", "file" ) ); $this->assertEquals( "some file", $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( "file", $this->input->argumentDefinition["file2"]->value ); } public function testProcessFailureNewArgumentsSimple() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $args = array( "foo.php" ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentMandatoryViolationException' ); } public function testProcessFailureNewArgumentsTooMany() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $args = array( "foo.php", "'test'", "'foo'", "'bar'" ); $this->commonProcessTestFailure( $args, 'ezcConsoleTooManyArgumentsException' ); } public function testProcessSuccessNewArgumentsOptionalAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->mandatory = false; $this->input->process( array( "foo.php", "'some file'", "file" ) ); $this->assertEquals( "some file", $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( "file", $this->input->argumentDefinition["file2"]->value ); } public function testProcessFailureNewArgumentsOptionalAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->mandatory = false; $args = array( "foo.php" ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentMandatoryViolationException' ); } public function testProcessSuccessNewArgumentsAutoOptionalAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->mandatory = false; $this->input->argumentDefinition[2] = new ezcConsoleArgument( "file3" ); $this->input->process( array( "foo.php", "'some file'", "file", "\"another file\"" ) ); $this->assertEquals( "some file", $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( "file", $this->input->argumentDefinition["file2"]->value ); $this->assertEquals( "another file", $this->input->argumentDefinition["file3"]->value ); } public function testProcessFailureNewArgumentsAutoOptionalAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->mandatory = false; $this->input->argumentDefinition[2] = new ezcConsoleArgument( "file3" ); $args = array( "foo.php" ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentMandatoryViolationException' ); } public function testProcessSuccessNewArgumentsOptionalNotAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->mandatory = false; $this->input->process( array( "foo.php", "'some file'" ) ); $this->assertEquals( "some file", $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( null, $this->input->argumentDefinition["file2"]->value ); } // Issue #10873: ezcConsoleArgument default value not working public function testProcessSuccessNewArgumentsOptionalNotAvailableDefault() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->mandatory = false; $this->input->argumentDefinition[1]->default = "some other file"; $this->input->process( array( "foo.php", "'some file'" ) ); $this->assertEquals( "some file", $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( "some other file", $this->input->argumentDefinition["file2"]->value ); } public function testProcessFailureNewArgumentsOptionalNotAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->mandatory = false; $args = array( "foo.php" ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentMandatoryViolationException' ); } public function testProcessSuccessNewArgumentsAutoOptionalNotAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->mandatory = false; $this->input->argumentDefinition[2] = new ezcConsoleArgument( "file3" ); $this->input->process( array( "foo.php", "'some file'" ) ); $this->assertEquals( "some file", $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( null, $this->input->argumentDefinition["file2"]->value ); $this->assertEquals( null, $this->input->argumentDefinition["file3"]->value ); } public function testProcessFailureNewArgumentsAutoOptionalNotAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->mandatory = false; $this->input->argumentDefinition[2] = new ezcConsoleArgument( "file3" ); $args = array( "foo.php" ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentMandatoryViolationException' ); } public function testProcessSuccessNewArgumentsMultipleOne() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[0]->multiple = true; $this->input->process( array( "foo.php", "'some file'", "file", "\"another file\"" ) ); $this->assertEquals( array( "some file", "file", "another file" ), $this->input->argumentDefinition["file1"]->value ); } public function testProcessFailureNewArgumentsMultipleOne() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[0]->multiple = true; $args = array( "foo.php" ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentMandatoryViolationException' ); } public function testProcessSuccessNewArgumentsMultipleMultiple() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->multiple = true; $this->input->process( array( "foo.php", "'some file'", "file", "\"another file\"" ) ); $this->assertEquals( "some file", $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( array( "file", "another file" ), $this->input->argumentDefinition["file2"]->value ); } public function testProcessFailureNewArgumentsMultipleMultiple() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->multiple = true; $args = array( "foo.php", "'test'" ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentMandatoryViolationException' ); } public function testProcessSuccessNewArgumentsMultipleOptionalAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->multiple = true; $this->input->argumentDefinition[1]->mandatory = false; $this->input->process( array( "foo.php", "'some file'", "file", "\"another file\"" ) ); $this->assertEquals( "some file", $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( array( "file", "another file" ), $this->input->argumentDefinition["file2"]->value ); // Old handling $this->assertEquals( array( "some file", "file", "another file" ), $this->input->getArguments() ); } public function testProcessSuccessNewArgumentsMultipleOptionalNotAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->multiple = true; $this->input->argumentDefinition[1]->mandatory = false; $this->input->process( array( "foo.php", "'some file'" ) ); $this->assertEquals( "some file", $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( null, $this->input->argumentDefinition["file2"]->value ); // Old handling $this->assertEquals( array( "some file" ), $this->input->getArguments() ); } public function testProcessSuccessNewArgumentsMultipleAutoOptionalAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[0]->mandatory = false; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->multiple = true; $this->input->process( array( "foo.php", "'some file'", "file", "\"another file\"" ) ); $this->assertEquals( "some file", $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( array( "file", "another file" ), $this->input->argumentDefinition["file2"]->value ); // Old handling $this->assertEquals( array( "some file", "file", "another file" ), $this->input->getArguments() ); } public function testProcessSuccessNewArgumentsMultipleAutoOptionalNotAvailable() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[0]->mandatory = false; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[1]->multiple = true; $this->input->process( array( "foo.php", "'some file'" ) ); $this->assertEquals( "some file", $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( null, $this->input->argumentDefinition["file2"]->value ); // Old handling $this->assertEquals( array( "some file" ), $this->input->getArguments() ); } public function testProcessSuccessNewArgumentsMultipleIgnore() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "file1" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "file2" ); $this->input->argumentDefinition[0]->multiple = true; $this->input->process( array( "foo.php", "'some file'", "file", "\"another file\"" ) ); $this->assertEquals( array( "some file", "file", "another file" ), $this->input->argumentDefinition["file1"]->value ); $this->assertEquals( null, $this->input->argumentDefinition["file2"]->value ); // Old handling $this->assertEquals( array( "some file", "file", "another file" ), $this->input->getArguments() ); } public function testProcessSuccessNewArgumentsTypeInt() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->process( array( "foo.php", 23 ) ); $this->assertEquals( 23, $this->input->argumentDefinition["number"]->value ); // Old handling $this->assertEquals( array( 23 ), $this->input->getArguments() ); } public function testProcessFailureNewArgumentsTypeInt() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $args = array( "foo.php", "'test'" ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentTypeViolationException' ); } public function testProcessSuccessNewArgumentsMultipleTypeInt() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[0]->multiple = true; $this->input->process( array( "foo.php", 23, 42 ) ); $this->assertEquals( array( 23, 42 ), $this->input->argumentDefinition["number"]->value ); // Old handling $this->assertEquals( array( 23, 42 ), $this->input->getArguments() ); } public function testProcessFailureNewArgumentsMultipleTypeInt() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[0]->multiple = true; $args = array( "foo.php", 23, "test" ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentTypeViolationException' ); } public function testProcessSuccessNewArgumentsComplex() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "string" ); $this->input->argumentDefinition[2] = new ezcConsoleArgument( "array" ); $this->input->argumentDefinition[2]->multiple = true; $args = array( "foo.php", "-o", "'test file'", "-b", "23", "42", "'test string'", "val1", "val2" ); $res = array( 'o' => "test file", 'b' => 23, ); $this->commonProcessTestSuccess( $args, $res ); $this->assertEquals( 42, $this->input->argumentDefinition["number"]->value ); $this->assertEquals( "test string", $this->input->argumentDefinition["string"]->value ); $this->assertEquals( array( "val1", "val2" ), $this->input->argumentDefinition["array"]->value ); // Old handling $this->assertEquals( array( 42, "test string", "val1", "val2"), $this->input->getArguments() ); } public function testProcessSuccessNewArgumentsHelpOptionSet() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "string" ); $this->input->argumentDefinition[2] = new ezcConsoleArgument( "array" ); $this->input->argumentDefinition[2]->multiple = true; $this->input->getOption( 't' )->isHelpOption = true; $args = array( "foo.php", "-t" ); $res = array( 't' => true, ); $this->commonProcessTestSuccess( $args, $res ); $this->assertNull( $this->input->argumentDefinition["number"]->value ); $this->assertNull( $this->input->argumentDefinition["string"]->value ); $this->assertNull( $this->input->argumentDefinition["array"]->value ); $this->assertTrue( $this->input->helpOptionSet() ); // Old handling $this->assertEquals( array(), $this->input->getArguments() ); } public function testProcessSuccessNewArgumentsDisallowedSuccess() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "string" ); $this->input->argumentDefinition[2] = new ezcConsoleArgument( "array" ); $this->input->argumentDefinition[2]->multiple = true; $this->input->getOption( 't' )->arguments = false; $args = array( "foo.php", "-t" ); $res = array( 't' => true, ); $this->commonProcessTestSuccess( $args, $res ); $this->assertNull( $this->input->argumentDefinition["number"]->value ); $this->assertNull( $this->input->argumentDefinition["string"]->value ); $this->assertNull( $this->input->argumentDefinition["array"]->value ); // Old handling $this->assertEquals( array(), $this->input->getArguments() ); } public function testProcessSuccessNewArgumentsDisallowedFailure() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "string" ); $this->input->argumentDefinition[2] = new ezcConsoleArgument( "array" ); $this->input->argumentDefinition[2]->multiple = true; $this->input->getOption( 't' )->arguments = false; $args = array( "foo.php", "-t", "--", "23" ); $res = array( 't' => true, ); $this->commonProcessTestFailure( $args, "ezcConsoleOptionArgumentsViolationException" ); } public function testReset() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "string" ); $this->input->argumentDefinition[2] = new ezcConsoleArgument( "array" ); $this->input->argumentDefinition[2]->multiple = true; $args = array( "foo.php", "-o", "'test file'", "-b", "23", "42", "'test string'", "val1", "val2" ); $res = array( 'o' => "test file", 'b' => 23, ); $this->commonProcessTestSuccess( $args, $res ); $this->assertEquals( 42, $this->input->argumentDefinition["number"]->value ); $this->assertEquals( "test string", $this->input->argumentDefinition["string"]->value ); $this->assertEquals( array( "val1", "val2" ), $this->input->argumentDefinition["array"]->value ); // Old handling $this->assertEquals( array( 42, "test string", "val1", "val2"), $this->input->getArguments() ); $this->input->reset(); $this->assertEquals( array(), $this->input->getOptionValues() ); foreach ( $this->input->argumentDefinition as $argument ) { $this->assertNull( $argument->value ); } $this->assertEquals( array(), $this->input->getArguments() ); } public function testProcessTwice() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "string" ); $this->input->argumentDefinition[1]->mandatory = false; $args = array( "foo.php", "-o", "'test file'", "-b", "23", "42", "'test string'" ); $res = array( 'o' => "test file", 'b' => 23, ); $this->commonProcessTestSuccess( $args, $res ); $this->assertEquals( 42, $this->input->argumentDefinition["number"]->value ); $this->assertEquals( "test string", $this->input->argumentDefinition["string"]->value ); // Old handling $this->assertEquals( array( 42, "test string" ), $this->input->getArguments() ); // Second run $args = array( "foo.php", "-t", '23' ); $res = array( 't' => true ); $this->commonProcessTestSuccess( $args, $res ); $this->assertEquals( 23, $this->input->argumentDefinition["number"]->value ); $this->assertEquals( null, $this->input->argumentDefinition["string"]->value ); // Old handling $this->assertEquals( array( '23' ), $this->input->getArguments() ); } public function testProcessFailureNewArgumentsComplexType() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "string" ); $this->input->argumentDefinition[2] = new ezcConsoleArgument( "array" ); $this->input->argumentDefinition[2]->multiple = true; $args = array( "foo.php", "-o", "'test file'", "-b", "23", "foo", "'test string'", "val1", "val2" ); $res = array( 'o' => "test file", 'b' => 23, ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentTypeViolationException' ); } public function testProcessFailureNewArgumentsComplexMissing() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "string" ); $this->input->argumentDefinition[2] = new ezcConsoleArgument( "array" ); $this->input->argumentDefinition[2]->multiple = true; $args = array( "foo.php", "-o", "'test file'", "-b", "23", "42" ); $res = array( 'o' => "test file", 'b' => 23, ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentMandatoryViolationException' ); } public function testProcessFailureNewArgumentsComplexMissing_2() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "string" ); $this->input->argumentDefinition[2] = new ezcConsoleArgument( "array" ); $this->input->argumentDefinition[2]->multiple = true; $args = array( "foo.php", "-o", "'test file'", "-b", "23", "42", "'test string'" ); $res = array( 'o' => "test file", 'b' => 23, ); $this->commonProcessTestFailure( $args, 'ezcConsoleArgumentMandatoryViolationException' ); } public function testProcessFailureNewArgumentsSwitchedOff() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[0]->type = ezcConsoleInput::TYPE_INT; $args = array( "foo.php", "-v", "--", 23 ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionArgumentsViolationException' ); } public function testProcessFailureExistance_1() { $args = array( 'foo.php', '-q', ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionNotExistsException' ); } public function testProcessFailureExistance_2() { $args = array( 'foo.php', '-tools', ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionNotExistsException' ); } public function testProcessFailureExistance_3() { $args = array( 'foo.php', '-testingaeiou', ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionNotExistsException' ); } public function testProcessFailureTypeInt() { $args = array( 'foo.php', '-b', 'not_an_int' ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionTypeViolationException' ); } // Bug #9046: New bug: [ConsoleTools] Last argument not treated invalid option value public function testProcessNoFailureTypeNone() { $args = array( 'foo.php', '-s', 'a_parameter' ); $res = array( "s" => true ); $this->commonProcessTestSuccess( $args, $res ); } public function testProcessFailureTypeNone() { $args = array( 'foo.php', '-s', 'a_parameter', 'another_parameter' ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionTypeViolationException' ); } public function testProcessFailureNovalue() { $args = array( 'foo.php', '-o', ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionMissingValueException' ); } public function testProcessFailureMultiple() { $args = array( 'foo.php', '-d', 'mars', '--destroy', 'venus', ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionTooManyValuesException' ); } public function testProcessFailureDependencies() { $args = array( 'foo.php', '-t', // '-o', // 'bar', '--build', 23, '-y', 'text', '--yank', 'moretext', '-c' // This one depends on -t, -o, -b and -y ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionDependencyViolationException', "The option 'console' depends on the option 'original' but this one was not submitted." ); } public function testProcessFailureDependencieValues() { $rule = new ezcConsoleOptionRule( $this->input->getOption( "y" ), array( "foo", "bar" ) ); $option = new ezcConsoleOption( "x", "execute" ); $option->addDependency( $rule ); $this->input->registerOption( $option ); $args = array( 'foo.php', '-y', 'baz', '-x', ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionDependencyViolationException', "The option 'execute' depends on the option 'yank' to have a value in 'foo, bar' but this one was not submitted." ); } public function testProcessFailureExclusions() { $args = array( 'foo.php', '-t', '-o', 'bar', '--build', 23, '--edit' // This one excludes -t and -y ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionExclusionViolationException' ); } public function testProcessFailureExclusionValues() { $rule = new ezcConsoleOptionRule( $this->input->getOption( "y" ), array( "foo", "bar" ) ); $option = new ezcConsoleOption( "x", "execute" ); $option->addExclusion( $rule ); $this->input->registerOption( $option ); $args = array( 'foo.php', '-y', 'bar', '-x', ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionExclusionViolationException' ); } public function testProcessFailureArguments() { $args = array( 'foo.php', '-t', '--visual', // This one forbids arguments '-o', 'bar', 'someargument', ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionArgumentsViolationException' ); } public function testProcessFailureMandatory() { $args = array( 'foo.php', '-s', ); $this->input->registerOption( $this->createFakeOption( array( 'short' => 'q', 'long' => 'quite', 'options' => array( 'mandatory' => true, ), ) ) ); $this->commonProcessTestFailure( $args, 'ezcConsoleOptionMandatoryViolationException' ); } public function testGetHelp1() { $res = array( array( '-t / --testing', 'No help available.', ), array( '-s / --subway', 'No help available.', ), array( '--carry', 'No help available.', ), array( '-v / --visual', 'No help available.', ), array( '-o / --original', 'No help available.', ), array( '-b / --build', 'No help available.', ), array( '-d / --destroy', 'No help available.', ), array( '-y / --yank', 'Some stupid short text.', ), array( '-c / --console', 'Some stupid short text.', ), array( '-e / --edit', 'No help available.', ), array( '-n / --new', 'No help available.', ), ); $this->assertEquals( $res, $this->input->getHelp(), 'Help array was not generated correctly.' ); } public function testGetHelpWithGrouping() { $res = array( array( 'Section 1', '' ), array( '-t / --testing', 'No help available.', ), array( '--carry', 'No help available.', ), array( '-b / --build', 'No help available.', ), array( '', '' ), array( 'Another section', '' ), array( '-c / --console', 'Some stupid short text.', ), array( '-n / --new', 'No help available.', ), array( '-e / --edit', 'No help available.', ), array( '', '' ), array( 'Third section', '' ), array( '-s / --subway', 'No help available.', ), array( '-v / --visual', 'No help available.', ), array( '-o / --original', 'No help available.', ), array( '-d / --destroy', 'No help available.', ), array( '', '' ), array( 'Last section', '' ), array( '-y / --yank', 'Some stupid short text.', ), ); $this->assertEquals( $res, $this->input->getHelp( false, array(), array( 'Section 1' => array( 't', 'carry', 'build' ), 'Another section' => array( 'c', 'new', 'edit' ), 'Third section' => array( 'subway', 'v', 'o', 'd', ), 'Last section' => array( 'y', ), ) ), 'Help array was not generated correctly.' ); } public function testGetHelpNewArgs() { $res = array( array( '-t / --testing', 'No help available.', ), array( '-s / --subway', 'No help available.', ), array( '--carry', 'No help available.', ), array( '-v / --visual', 'No help available.', ), array( '-o / --original', 'No help available.', ), array( '-b / --build', 'No help available.', ), array( '-d / --destroy', 'No help available.', ), array( '-y / --yank', 'Some stupid short text.', ), array( '-c / --console', 'Some stupid short text.', ), array( '-e / --edit', 'No help available.', ), array( '-n / --new', 'No help available.', ), array( "Arguments:", "", ), array( '', 'A text.', ), array( '', 'A number.', ), ); $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "text" ); $this->input->argumentDefinition[0]->shorthelp = 'A text.'; $this->input->argumentDefinition[0]->longhelp = 'This argument is a simple text.'; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[1]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1]->shorthelp = 'A number.'; $this->input->argumentDefinition[1]->longhelp = 'This argument is a number.'; $this->assertEquals( $res, $this->input->getHelp(), 'Help array was not generated correctly.' ); } public function testGetHelp2() { $res = array( array( '-t / --testing', 'Sorry, there is no help text available for this parameter.', ), array( '-s / --subway', 'Sorry, there is no help text available for this parameter.', ), array( '--carry', 'Sorry, there is no help text available for this parameter.', ), array( '-v / --visual', 'Sorry, there is no help text available for this parameter.', ), array( '-o / --original', 'Sorry, there is no help text available for this parameter.', ), array( '-b / --build', 'Sorry, there is no help text available for this parameter.', ), array( '-d / --destroy', 'Sorry, there is no help text available for this parameter.', ), array( '-y / --yank', 'Some even more stupid, but somewhat longer long describtion.', ), array( '-c / --console', 'Some even more stupid, but somewhat longer long describtion.', ), array( '-e / --edit', 'Sorry, there is no help text available for this parameter.', ), array( '-n / --new', 'Sorry, there is no help text available for this parameter.', ), ); $this->assertEquals( $res, $this->input->getHelp( true ), 'Help array was not generated correctly.' ); } public function testGetHelp2NewArgs() { $res = array( array( '-t / --testing', 'Sorry, there is no help text available for this parameter.', ), array( '-s / --subway', 'Sorry, there is no help text available for this parameter.', ), array( '--carry', 'Sorry, there is no help text available for this parameter.', ), array( '-v / --visual', 'Sorry, there is no help text available for this parameter.', ), array( '-o / --original', 'Sorry, there is no help text available for this parameter.', ), array( '-b / --build', 'Sorry, there is no help text available for this parameter.', ), array( '-d / --destroy', 'Sorry, there is no help text available for this parameter.', ), array( '-y / --yank', 'Some even more stupid, but somewhat longer long describtion.', ), array( '-c / --console', 'Some even more stupid, but somewhat longer long describtion.', ), array( '-e / --edit', 'Sorry, there is no help text available for this parameter.', ), array( '-n / --new', 'Sorry, there is no help text available for this parameter.', ), array( "Arguments:", "", ), array( '', 'This argument is a simple text.', ), array( '', 'This argument is a number.', ), ); $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "text" ); $this->input->argumentDefinition[0]->shorthelp = 'A text.'; $this->input->argumentDefinition[0]->longhelp = 'This argument is a simple text.'; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[1]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1]->shorthelp = 'A number.'; $this->input->argumentDefinition[1]->longhelp = 'This argument is a number.'; $this->assertEquals( $res, $this->input->getHelp( true ), 'Help array was not generated correctly.' ); } public function testGetHelp2NewArgsOptional() { $res = array( array( '-t / --testing', 'Sorry, there is no help text available for this parameter.', ), array( '-s / --subway', 'Sorry, there is no help text available for this parameter.', ), array( '--carry', 'Sorry, there is no help text available for this parameter.', ), array( '-v / --visual', 'Sorry, there is no help text available for this parameter.', ), array( '-o / --original', 'Sorry, there is no help text available for this parameter.', ), array( '-b / --build', 'Sorry, there is no help text available for this parameter.', ), array( '-d / --destroy', 'Sorry, there is no help text available for this parameter.', ), array( '-y / --yank', 'Some even more stupid, but somewhat longer long describtion.', ), array( '-c / --console', 'Some even more stupid, but somewhat longer long describtion.', ), array( '-e / --edit', 'Sorry, there is no help text available for this parameter.', ), array( '-n / --new', 'Sorry, there is no help text available for this parameter.', ), array( "Arguments:", "", ), array( '', 'This argument is a simple text. (optional)', ), array( '', "This argument is a number. (optional, default = '23')", ), array( '', "Testing multiple values. (optional, default = 'foo' 'bar' 'baz')", ), ); $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "text" ); $this->input->argumentDefinition[0]->shorthelp = 'A text.'; $this->input->argumentDefinition[0]->longhelp = 'This argument is a simple text.'; $this->input->argumentDefinition[0]->mandatory = false; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[1]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1]->shorthelp = 'A number.'; $this->input->argumentDefinition[1]->longhelp = 'This argument is a number.'; $this->input->argumentDefinition[1]->mandatory = false; $this->input->argumentDefinition[1]->default = 23; $this->input->argumentDefinition[2] = new ezcConsoleArgument( "misc" ); $this->input->argumentDefinition[2]->shorthelp = 'A misc argument.'; $this->input->argumentDefinition[2]->longhelp = 'Testing multiple values.'; $this->input->argumentDefinition[2]->multiple = true; $this->input->argumentDefinition[2]->mandatory = false; $this->input->argumentDefinition[2]->default = array( "foo", "bar", "baz" ); $this->assertEquals( $res, $this->input->getHelp( true ), 'Help array was not generated correctly.' ); } public function testGetHelp3() { $res = array( array( '-t / --testing', 'No help available.', ), array( '-s / --subway', 'No help available.', ), array( '-v / --visual', 'No help available.', ), ); $this->assertEquals( $res, $this->input->getHelp(false, array( 't', 's', 'v' ) ), 'Help array was not generated correctly.' ); } public function testGetHelp4() { $res = array( array( '-t / --testing', 'Sorry, there is no help text available for this parameter.', ), array( '-s / --subway', 'Sorry, there is no help text available for this parameter.', ), array( '-y / --yank', 'Some even more stupid, but somewhat longer long describtion.', ), array( '-e / --edit', 'Sorry, there is no help text available for this parameter.', ), array( '-n / --new', 'Sorry, there is no help text available for this parameter.', ), ); $this->assertEquals( $res, $this->input->getHelp( true, array( 't', 'subway', 'yank', 'e', 'n' ) ), 'Help array was not generated correctly.' ); } public function testGetSynopsis() { $this->assertEquals( '$ '.$_SERVER['argv'][0].' [-t] [-s] [--carry] [-v] [-o ] [-b 42] [-d "world"] [-y ] [-c] [-e] [-n] [[--] ]', $this->input->getSynopsis(), 'Program synopsis not generated correctly.' ); } // Issue #012561 : getSynopsis() bugs when at least 2 options don't have short-names. public function testGetSynopsisLongOptionsWithoutShortNames() { $input = new ezcConsoleInput(); $input->registerOption( new ezcConsoleOption( "", "set-dericktory", ezcConsoleInput::TYPE_NONE ) ); $input->registerOption( new ezcConsoleOption( "", "set-directoby", ezcConsoleInput::TYPE_NONE ) ); $this->assertEquals( '$ '.$_SERVER['argv'][0].' [--set-dericktory] [--set-directoby] [[--] ]', $input->getSynopsis(), 'Program synopsis not generated correctly.' ); } public function testGetSynopsisNewArgumentsSimple() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "text" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[1]->type = ezcConsoleInput::TYPE_INT; $this->assertEquals( '$ '.$_SERVER['argv'][0].' [-t] [-s] [--carry] [-v] [-o ] [-b 42] [-d "world"] [-y ] [-c] [-e] [-n] [--] ', $this->input->getSynopsis(), 'Program synopsis not generated correctly.' ); } public function testGetSynopsisNewArgumentsMultiple() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "text" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[1]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1]->multiple = true; $this->assertEquals( '$ '.$_SERVER['argv'][0].' [-t] [-s] [--carry] [-v] [-o ] [-b 42] [-d "world"] [-y ] [-c] [-e] [-n] [--] [ ...]', $this->input->getSynopsis(), 'Program synopsis not generated correctly.' ); } public function testGetSynopsisNewArgumentsOptional() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "text" ); $this->input->argumentDefinition[1] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[1]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1]->mandatory = false; $this->assertEquals( '$ '.$_SERVER['argv'][0].' [-t] [-s] [--carry] [-v] [-o ] [-b 42] [-d "world"] [-y ] [-c] [-e] [-n] [--] []', $this->input->getSynopsis(), 'Program synopsis not generated correctly.' ); } public function testGetSynopsisNewArgumentsMultipleOptional() { $this->input->argumentDefinition = new ezcConsoleArguments(); $this->input->argumentDefinition[0] = new ezcConsoleArgument( "text" ); $this->input->argumentDefinition[0]->mandatory = false; $this->input->argumentDefinition[1] = new ezcConsoleArgument( "number" ); $this->input->argumentDefinition[1]->type = ezcConsoleInput::TYPE_INT; $this->input->argumentDefinition[1]->multiple = true; $this->assertEquals( '$ '.$_SERVER['argv'][0].' [-t] [-s] [--carry] [-v] [-o ] [-b 42] [-d "world"] [-y ] [-c] [-e] [-n] [--] [] [] [ ...]', $this->input->getSynopsis(), 'Program synopsis not generated correctly.' ); } public function testGetHelpTable() { $output = new ezcConsoleOutput(); $res = new ezcConsoleTable( $output, 80 ); $res[0][0]->content = '-t / --testing'; $res[0][1]->content = 'Sorry, there is no help text available for this parameter.'; $res[1][0]->content = '-s / --subway'; $res[1][1]->content = 'Sorry, there is no help text available for this parameter.'; $res[2][0]->content = '-y / --yank'; $res[2][1]->content = 'Some even more stupid, but somewhat longer long describtion.'; $res[3][0]->content = '-e / --edit'; $res[3][1]->content = 'Sorry, there is no help text available for this parameter.'; $table = new ezcConsoleTable( $output, 80 ); $table = $this->input->getHelpTable( $table, true, array( 't', 'subway', 'yank', 'e' ) ); $this->assertEquals( $res, $table, 'Help table not generated correctly.' ); } public function testGetHelpTableGrouping() { $output = new ezcConsoleOutput(); $res = new ezcConsoleTable( $output, 80 ); $res[0][0]->content = 'Section uno'; $res[0][1]->content = ''; $res[1][0]->content = '-t / --testing'; $res[1][1]->content = 'Sorry, there is no help text available for this parameter.'; $res[2][0]->content = ''; $res[2][1]->content = ''; $res[3][0]->content = 'Section 2'; $res[3][1]->content = ''; $res[4][0]->content = '-e / --edit'; $res[4][1]->content = 'Sorry, there is no help text available for this parameter.'; $res[5][0]->content = '-s / --subway'; $res[5][1]->content = 'Sorry, there is no help text available for this parameter.'; $res[6][0]->content = ''; $res[6][1]->content = ''; $res[7][0]->content = 'Final section'; $res[7][1]->content = ''; $res[8][0]->content = '-y / --yank'; $res[8][1]->content = 'Some even more stupid, but somewhat longer long describtion.'; $table = new ezcConsoleTable( $output, 80 ); $table = $this->input->getHelpTable( $table, true, array( 't', 'subway', 'yank', 'e' ), array( 'Section uno' => array( 't' ), 'Section 2' => array( 'e', 'subway' ), 'Final section' => array( 'y', ), ) ); $this->assertEquals( $res, $table, 'Help table not generated correctly.' ); } public function testGetHelpTableDefaultParameters() { $output = new ezcConsoleOutput(); $res = new ezcConsoleTable( $output, 80 ); $res[0][0]->content = '-t / --testing'; $res[0][1]->content = 'Sorry, there is no help text available for this parameter.'; $res[1][0]->content = '-s / --subway'; $res[1][1]->content = 'Sorry, there is no help text available for this parameter.'; $res[2][0]->content = '-y / --yank'; $res[2][1]->content = 'Some even more stupid, but somewhat longer long describtion.'; $res[3][0]->content = '-e / --edit'; $res[3][1]->content = 'Sorry, there is no help text available for this parameter.'; $table = new ezcConsoleTable( $output, 80 ); $table = $this->input->getHelpTable( $table ); $this->assertEquals( 11, sizeof( $table ), "Expected 11 elements in the generated HelpTable" ); } public function testGetHelpTextBinarySafe() { $input = new ezcConsoleInput(); $input->registerOption( new ezcConsoleOption( 'ö', 'öder', ezcConsoleInput::TYPE_NONE, null, false, 'ööö äää ööö äää', 'ööö äää ööö äää ööö äää ööö äää ööö äää ööö äää' ) ); $res = "Usage: $ {$_SERVER['argv'][0]} [-ö] [[--] ]" . PHP_EOL . 'Test with UTF-8' . PHP_EOL . 'characters...' . PHP_EOL . '' . PHP_EOL . '-ö / --öder ööö äää' . PHP_EOL . ' ööö äää' . PHP_EOL . ' ööö äää' . PHP_EOL . ' ööö äää' . PHP_EOL . ' ööö äää' . PHP_EOL . ' ööö äää' . PHP_EOL; $this->assertEquals( $res, $input->getHelpText( 'Test with UTF-8 characters...', 20, true ), 'Help text not generated correctly.' ); } public function testGetHelpText() { $res = "Usage: $ {$_SERVER['argv'][0]} [-y ] [-e] [[--] ]" . PHP_EOL . 'Lala' . PHP_EOL . '' . PHP_EOL . '-y / --yank Some' . PHP_EOL . ' even' . PHP_EOL . ' more' . PHP_EOL . ' stupid,' . PHP_EOL . ' but' . PHP_EOL . ' somewhat' . PHP_EOL . ' longer' . PHP_EOL . ' long' . PHP_EOL . ' describtion.' . PHP_EOL . '-e / --edit Sorry,' . PHP_EOL . ' there' . PHP_EOL . ' is no' . PHP_EOL . ' help' . PHP_EOL . ' text' . PHP_EOL . ' available' . PHP_EOL . ' for' . PHP_EOL . ' this' . PHP_EOL . ' parameter.' . PHP_EOL; $this->assertEquals( $res, $this->input->getHelpText( 'Lala', 20, true, array( 'e', 'y' ) ), 'Help text not generated correctly.' ); } public function testGetSynopsis1() { $this->assertEquals( '$ '.$_SERVER['argv'][0].' [-t] [-s] [-o ] [[--] ]', $this->input->getSynopsis( array( 't', 's', 'o' ) ), 'Program synopsis not generated correctly.' ); } /** * Tests bug #7923. * * @return void */ public function testGetSynopsis2() { $this->assertEquals( '$ '.$_SERVER['argv'][0].' [-t] [-s] [-v] [[--] ]', $this->input->getSynopsis( array( 't', 's', 'v' ) ), 'Program synopsis not generated correctly.' ); } public function testGetSynopsis3() { $this->assertEquals( '$ ' . $_SERVER['argv'][0] . ' [-s] [-b 42] [[--] ]', $this->input->getSynopsis( array( 'b', 's' ) ), 'Program synopsis not generated correctly.' ); } public function testGetSynopsis4() { $this->input->registerOption( new ezcConsoleOption( "x", "execute", ezcConsoleInput::TYPE_INT ) ); $this->assertEquals( '$ ' . $_SERVER['argv'][0] . ' [-s] [-x ] [[--] ]', $this->input->getSynopsis( array( 'x', 's' ) ), 'Program synopsis not generated correctly.' ); } public function testHelpOptionSet() { $args = array( 'foo.php', '-h', ); $this->input->registerOption( $this->createFakeOption( array( 'short' => 'q', 'long' => 'quite', 'options' => array( 'mandatory' => true, ), ) ) ); $this->input->registerOption( $this->createFakeOption( array( 'short' => 'h', 'long' => 'help', 'options' => array( 'isHelpOption' => true, ), ) ) ); $res = array( 'h' => true, ); $this->assertFalse( $this->input->helpOptionSet(), "Help option seems to be set, algthough nothing was processed." ); $this->commonProcessTestSuccess( $args, $res ); $this->assertTrue( $this->input->helpOptionSet(), "Help option seems not to be set, algthough it was." ); } public function testDependOptionNoShortName() { $inputOpt = $this->input->registerOption( new ezcConsoleOption( '', 'input' ) ); $outputOpt = $this->input->registerOption( new ezcConsoleOption( '', 'output' ) ); $inputOpt->addDependency( new ezcConsoleOptionRule( $outputOpt ) ); $outputOpt->addDependency( new ezcConsoleOptionRule( $inputOpt ) ); $args = array( 'somescript', '--input' ); try { $this->input->process( $args ); $this->fail( 'Processing did not throw an exception on violated dependency.' ); } catch ( ezcConsoleOptionDependencyViolationException $e ) {} $args = array( 'somescript', '--output' ); try { $this->input->process( $args ); $this->fail( 'Processing did not throw an exception on violated dependency.' ); } catch ( ezcConsoleOptionDependencyViolationException $e ) {} } // Issue #014803: Problem with ezcConsoleOption when short option name is empty public function testExcludeOptionNoShortName() { $inputOpt = $this->input->registerOption( new ezcConsoleOption( '', 'input' ) ); $outputOpt = $this->input->registerOption( new ezcConsoleOption( '', 'output' ) ); $inputOpt->addExclusion( new ezcConsoleOptionRule( $outputOpt ) ); $outputOpt->addExclusion( new ezcConsoleOptionRule( $inputOpt ) ); $args = array( 'somescript', '--input' ); // Should not throw an exception $this->input->process( $args ); $args = array( 'somescript', '--output' ); // Should not throw an exception $this->input->process( $args ); } public function testDependOptionValueNotSetNoShortName() { $inputOpt = $this->input->registerOption( new ezcConsoleOption( '', 'input' ) ); $outputOpt = $this->input->registerOption( new ezcConsoleOption( '', 'output' ) ); $inputOpt->addDependency( new ezcConsoleOptionRule( $outputOpt, array( 'foo', 'bar' ) ) ); $outputOpt->addDependency( new ezcConsoleOptionRule( $inputOpt, array( 'foo', 'bar' ) ) ); $args = array( 'somescript', '--input' ); try { $this->input->process( $args ); $this->fail( 'Processing did not throw an exception on violated dependency.' ); } catch ( ezcConsoleOptionDependencyViolationException $e ) {} $args = array( 'somescript', '--output' ); try { $this->input->process( $args ); $this->fail( 'Processing did not throw an exception on violated dependency.' ); } catch ( ezcConsoleOptionDependencyViolationException $e ) {} } public function testDependOptionValueWrongValueNoShortName() { $inputOpt = $this->input->registerOption( new ezcConsoleOption( '', 'input', ezcConsoleInput::TYPE_STRING ) ); $outputOpt = $this->input->registerOption( new ezcConsoleOption( '', 'output', ezcConsoleInput::TYPE_STRING ) ); $inputOpt->addDependency( new ezcConsoleOptionRule( $outputOpt, array( 'foo', 'bar' ) ) ); $outputOpt->addDependency( new ezcConsoleOptionRule( $inputOpt, array( 'foo', 'bar' ) ) ); $args = array( 'somescript', '--output=lala', '--input=lala' ); try { $this->input->process( $args ); $this->fail( 'Processing did not throw an exception on violated dependency.' ); } catch ( ezcConsoleOptionDependencyViolationException $e ) {} $args = array( 'somescript', '--input=lala', '--output=lala' ); try { $this->input->process( $args ); $this->fail( 'Processing did not throw an exception on violated dependency.' ); } catch ( ezcConsoleOptionDependencyViolationException $e ) {} } public function testExcludeOptionValueWrongValueNoShortName() { $inputOpt = $this->input->registerOption( new ezcConsoleOption( '', 'input', ezcConsoleInput::TYPE_STRING ) ); $outputOpt = $this->input->registerOption( new ezcConsoleOption( '', 'output', ezcConsoleInput::TYPE_STRING ) ); $inputOpt->addExclusion( new ezcConsoleOptionRule( $outputOpt, array( 'foo', 'bar' ) ) ); $outputOpt->addExclusion( new ezcConsoleOptionRule( $inputOpt, array( 'foo', 'bar' ) ) ); $args = array( 'somescript', '--output=foo', '--input=lala' ); try { $this->input->process( $args ); $this->fail( 'Processing did not throw an exception on violated exclusion.' ); } catch ( ezcConsoleOptionExclusionViolationException $e ) { $this->assertEquals( "The option 'input' excludes the option 'output' to have a value in 'foo, bar' but this one was submitted.", $e->getMessage() ); } $args = array( 'somescript', '--output=lala', '--input=bar' ); try { $this->input->process( $args ); $this->fail( 'Processing did not throw an exception on violated dependency.' ); } catch ( ezcConsoleOptionExclusionViolationException $e ) { $this->assertEquals( "The option 'output' excludes the option 'input' to have a value in 'foo, bar' but this one was submitted.", $e->getMessage() ); } } public function testDependencyOptionNotSet() { $aOpt = $this->input->registerOption( new ezcConsoleOption( 'a', 'abbrev', ezcConsoleInput::TYPE_NONE ) ); $aOpt->addDependency( new ezcConsoleOptionRule( $this->input->getOption( 't' ), array(), false ) ); $this->commonProcessTestFailure( array( 'foo.php' ), 'ezcConsoleOptionDependencyViolationException' ); } public function testExclusionOptionNotSet() { $aOpt = $this->input->registerOption( new ezcConsoleOption( 'a', 'abbrev', ezcConsoleInput::TYPE_NONE ) ); $aOpt->addExclusion( new ezcConsoleOptionRule( $this->input->getOption( 't' ), array(), false ) ); $this->commonProcessTestFailure( array( 'foo.php', '-t' ), 'ezcConsoleOptionExclusionViolationException' ); } private function arrayRecursiveDiff($aArray1, $aArray2) { $aReturn = array(); foreach ($aArray1 as $mKey => $mValue) { if (array_key_exists($mKey, $aArray2)) { if (is_array($mValue)) { $aRecursiveDiff = $this->arrayRecursiveDiff($mValue, $aArray2[$mKey]); if (count($aRecursiveDiff)) { $aReturn[$mKey] = $aRecursiveDiff; } } else { if ($mValue != $aArray2[$mKey]) { $aReturn[$mKey] = $mValue; } } } else { $aReturn[$mKey] = $mValue; } } return $aReturn; } private function commonProcessTestSuccess( $args, $res ) { $this->input->process( $args ); $values = $this->input->getOptionValues(); $this->assertTrue( count( $this->arrayRecursiveDiff( $res, $values ) ) == 0, 'Parameters processed incorrectly.' ); } private function commonProcessTestFailure( $args, $exceptionClass, $message = null ) { try { $this->input->process( $args ); } catch ( ezcConsoleException $e ) { $this->assertSame( $exceptionClass, get_class( $e ), 'Wrong exception thrown for invalid parameter submission. Expected class <'.$exceptionClass.'>, received <'.get_class( $e ).'>' ); if ( $message !== null ) { $this->assertEquals( $message, $e->getMessage(), 'Exception message incorrect.' ); } return; } $this->fail( 'Exception not thrown for invalid parameter submition.' ); } private function argumentsProcessTestSuccess( $args, $res ) { $this->input->process( $args ); $this->assertEquals( $res, $this->input->getArguments(), 'Arguments not parsed correctly.' ); } } ?> ConsoleTools-1.7.5/tests/menu_dialog_default_validator_test.php000066400000000000000000000163101470000742400251140ustar00rootroot00000000000000 "Foo", "B" => "Bar", "Z" => "Baz" ); $validator = new ezcConsoleMenuDialogDefaultValidator( $elements ); $this->assertEquals( $elements, $validator->elements ); $this->assertNull( $validator->default ); $this->assertEquals( ezcConsoleMenuDialogDefaultValidator::CONVERT_NONE, $validator->conversion ); } public function testGetAccessCustomSuccess() { $elements = array( "F" => "Foo", "B" => "Bar", "Z" => "Baz" ); $validator = new ezcConsoleMenuDialogDefaultValidator( $elements, "F", ezcConsoleMenuDialogDefaultValidator::CONVERT_UPPER ); $this->assertEquals( $elements, $validator->elements ); $this->assertEquals( "F", $validator->default ); $this->assertEquals( ezcConsoleMenuDialogDefaultValidator::CONVERT_UPPER, $validator->conversion ); } public function testGetAccessFailure() { $elements = array( "F" => "Foo", "B" => "Bar", "Z" => "Baz" ); $validator = new ezcConsoleMenuDialogDefaultValidator( $elements ); try { echo $validator->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on invalid property foo." ); } public function testSetAccessSuccess() { $elements = array( "F" => "Foo", "B" => "Bar", "Z" => "Baz" ); $validator = new ezcConsoleMenuDialogDefaultValidator( $elements ); $elementsNew = array( 23, 42 ); $validator->elements = $elementsNew; $validator->default = 23; $validator->conversion = ezcConsoleMenuDialogDefaultValidator::CONVERT_LOWER; $this->assertEquals( $elementsNew, $validator->elements ); $this->assertEquals( 23, $validator->default ); $this->assertEquals( ezcConsoleMenuDialogDefaultValidator::CONVERT_LOWER, $validator->conversion ); } public function testSetAccessFailure() { $elements = array( "F" => "Foo", "B" => "Bar", "Z" => "Baz" ); $validator = new ezcConsoleMenuDialogDefaultValidator( $elements ); $exceptionCaught = false; try { $validator->elements = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property elements." ); $exceptionCaught = false; try { $validator->default = array(); } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property default." ); $exceptionCaught = false; try { $validator->conversion = "Foo"; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property conversion." ); $exceptionCaught = false; try { $validator->foo = "Foo"; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on access of nonexistent property foo." ); } public function testIssetAccess() { $elements = array( "F" => "Foo", "B" => "Bar", "Z" => "Baz" ); $validator = new ezcConsoleMenuDialogDefaultValidator( $elements ); $this->assertTrue( isset( $validator->elements ), "Property elements not set." ); $this->assertTrue( isset( $validator->default ), "Property default not set." ); $this->assertTrue( isset( $validator->conversion ), "Property conversion not set." ); $this->assertFalse( isset( $validator->foo ), "Property foo set." ); } public function testValidate() { $elements = array( "F" => "Foo", "B" => "Bar", "Z" => "Baz" ); $validator = new ezcConsoleMenuDialogDefaultValidator( $elements ); $this->assertTrue( $validator->validate( "F" ) ); $this->assertFalse( $validator->validate( "test" ) ); } public function testFixup() { $elements = array( "F" => "Foo", "B" => "Bar", "Z" => "Baz" ); $validator = new ezcConsoleMenuDialogDefaultValidator( $elements, null ); $this->assertEquals( "F", $validator->fixup( "F" ) ); $this->assertEquals( "f", $validator->fixup( "f" ) ); $validator->conversion = ezcConsoleMenuDialogDefaultValidator::CONVERT_UPPER; $this->assertEquals( "F", $validator->fixup( "F" ) ); $this->assertEquals( "F", $validator->fixup( "f" ) ); $validator->conversion = ezcConsoleMenuDialogDefaultValidator::CONVERT_LOWER; $this->assertEquals( "f", $validator->fixup( "F" ) ); $this->assertEquals( "f", $validator->fixup( "f" ) ); $this->assertEquals( "", $validator->fixup( "" ) ); $validator->default = "F"; $this->assertEquals( "F", $validator->fixup( "" ) ); } public function testGetResultString() { $elements = array( "F" => "Foo", "B" => "Bar", "Z" => "Baz" ); $validator = new ezcConsoleMenuDialogDefaultValidator( $elements, null ); $this->assertEquals( "", $validator->getResultString() ); $validator->default = "F"; $this->assertEquals( "[F]", $validator->getResultString() ); } public function testGetElements() { $elements = array( "F" => "Foo", "B" => "Bar", "Z" => "Baz" ); $validator = new ezcConsoleMenuDialogDefaultValidator( $elements, null ); $this->assertEquals( $elements, $validator->getElements() ); } } ?> ConsoleTools-1.7.5/tests/menu_dialog_options_test.php000066400000000000000000000151051470000742400231170ustar00rootroot00000000000000assertEquals( "Please choose an item.", $opts->text ); $this->assertEquals( "%3s) %s\n", $opts->formatString ); $this->assertEquals( "Select: ", $opts->selectText ); $this->assertInstanceOf( "ezcConsoleMenuDialogDefaultValidator", $opts->validator ); $this->assertEquals( "default", $opts->format ); } public function testGetAccessCustomSuccess() { $menuElements = array( "F" => "Foo", "B" => "Bar", ); $opts = new ezcConsoleMenuDialogOptions( array( "text" => "Please select a fitting mode:\n", "validator" => new ezcConsoleMenuDialogDefaultValidator( $menuElements ), "selectText" => "Select a mode: ", "formatString" => "%10s] %s\n", "format" => "test", ) ); $this->assertEquals( "Please select a fitting mode:\n", $opts->text ); $this->assertEquals( "%10s] %s\n", $opts->formatString ); $this->assertInstanceOf( "ezcConsoleMenuDialogDefaultValidator", $opts->validator ); $this->assertEquals( $menuElements, $opts->validator->elements ); $this->assertEquals( "Select a mode: ", $opts->selectText ); $this->assertEquals( "test", $opts->format ); } public function testGetAccessFailure() { $opts = new ezcConsoleMenuDialogOptions(); try { echo $opts->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on invalid property foo." ); } public function testSetAccessSuccess() { $menuElements = array( "F" => "Foo", "B" => "Bar", ); $opts = new ezcConsoleMenuDialogOptions(); $opts->text = "Please select a fitting mode:\n"; $opts->validator = new ezcConsoleMenuDialogDefaultValidator( $menuElements ); $opts->selectText = "Select a mode: "; $opts->formatString = "%10s] %s\n"; $opts->format = "test"; $this->assertEquals( "Please select a fitting mode:\n", $opts->text ); $this->assertEquals( "%10s] %s\n", $opts->formatString ); $this->assertInstanceOf( "ezcConsoleMenuDialogDefaultValidator", $opts->validator ); $this->assertEquals( $menuElements, $opts->validator->elements ); $this->assertEquals( "Select a mode: ", $opts->selectText ); $this->assertEquals( "test", $opts->format ); } public function testSetAccessFailure() { $opts = new ezcConsoleMenuDialogOptions(); $exceptionCaught = false; try { $opts->text = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property text." ); $exceptionCaught = false; try { $opts->validator = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property validator." ); $exceptionCaught = false; try { $opts->selectText = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property selectText." ); $exceptionCaught = false; try { $opts->formatString = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property formatString." ); $exceptionCaught = false; try { $opts->format = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property format." ); $exceptionCaught = false; try { $opts->foo = "Foo"; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on access of nonexistent property foo." ); } public function testIssetAccess() { $opts = new ezcConsoleMenuDialogOptions(); $this->assertTrue( isset( $opts->text ), "Property text not set." ); $this->assertTrue( isset( $opts->validator ), "Property validator not set." ); $this->assertTrue( isset( $opts->selectText ), "Property selectText not set." ); $this->assertTrue( isset( $opts->formatString ), "Property formatString not set." ); $this->assertTrue( isset( $opts->format ), "Property format not set." ); $this->assertFalse( isset( $opts->foo ), "Property foo set." ); } } ?> ConsoleTools-1.7.5/tests/menu_dialog_test.php000066400000000000000000000165701470000742400213530ustar00rootroot00000000000000assertSame( $output, $dialog->output ); $this->assertEquals( new ezcConsoleMenuDialogOptions(), $dialog->options ); } public function testGetAccessFailure() { $output = new ezcConsoleOutput(); $dialog = new ezcConsoleMenuDialog( $output ); $exceptionCaught = false; try { echo $dialog->foo; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on access of nonexistent property foo." ); } public function testSetAccessSuccess() { $output = new ezcConsoleOutput(); $dialog = new ezcConsoleMenuDialog( $output ); $outputNew = new ezcConsoleOutput(); $optionsNew = new ezcConsoleMenuDialogOptions(); $dialog->output = $outputNew; $dialog->options = $optionsNew; $this->assertSame( $outputNew, $dialog->output ); $this->assertSame( $optionsNew, $dialog->options ); } public function testSetAccessFailure() { $output = new ezcConsoleOutput(); $dialog = new ezcConsoleMenuDialog( $output ); $exceptionCaught = false; try { $dialog->output = "Foo"; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for output." ); $exceptionCaught = false; try { $dialog->options = "Foo"; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for options." ); $exceptionCaught = false; try { $dialog->foo = "bar"; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on access of nonexistent property foo." ); $this->assertSame( $output, $dialog->output ); $this->assertEquals( new ezcConsoleMenuDialogOptions(), $dialog->options ); } public function testIssetAccess() { $output = new ezcConsoleOutput(); $dialog = new ezcConsoleMenuDialog( $output ); $this->assertTrue( isset( $dialog->options ), "Property options is not set." ); $this->assertTrue( isset( $dialog->output ), "Property options is not set." ); $this->assertFalse( isset( $dialog->foo ), "Property foo is set." ); } public function testBasicMethods() { $output = new ezcConsoleOutput(); $dialog = new ezcConsoleMenuDialog( $output ); $this->assertFalse( $dialog->hasValidResult(), "Fresh dialog has valid result." ); $exceptionCaught = false; try { $dialog->getResult(); } catch ( ezcConsoleNoValidDialogResultException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Excption not thrown on getResult() without result." ); $dialog->reset(); $exceptionCaught = false; try { $dialog->getResult(); } catch ( ezcConsoleNoValidDialogResultException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Excption not thrown on getResult() without result." ); } /** * @group interactive */ public function testDialog1() { $this->runDialog( __METHOD__ ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "K\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "Z\n" ); $res[] = $this->readPipe( $this->pipes[1] ); // $this->saveDialogResult( __METHOD__, $res ); $this->assertEquals( $this->res, $res ); } /** * @group interactive */ public function testDialog2() { $this->runDialog( __METHOD__ ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "k\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "z\n" ); $res[] = $this->readPipe( $this->pipes[1] ); // $this->saveDialogResult( __METHOD__, $res ); $this->assertEquals( $this->res, $res ); } /** * @group interactive */ public function testDialog3() { $this->runDialog( __METHOD__ ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "A\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "K\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "B\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "T\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "Z\n" ); $res[] = $this->readPipe( $this->pipes[1] ); //$this->saveDialogResult( __METHOD__, $res ); $this->assertEquals( $this->res, $res ); } /** * @group interactive */ public function testDialog4() { $this->runDialog( __METHOD__ ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "A\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "K\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fclose( $this->pipes[0] ); $res[] = $this->readPipe( $this->pipes[1] ); // $this->saveDialogResult( __METHOD__, $res ); $this->assertEquals( $this->res, $res ); } } ?> ConsoleTools-1.7.5/tests/option_rule_test.php000066400000000000000000000113741470000742400214240ustar00rootroot00000000000000assertSame( $option, $rule->option ); $this->assertEquals( array( "a", "b", "c" ), $rule->values ); $this->assertTrue( $rule->ifSet ); } public function testGetAccessFailure() { $option = new ezcConsoleOption( "a", "aaa" ); $rule = new ezcConsoleOptionRule( $option, array( "a", "b", "c" ) ); try { $foo = $rule->nonExistent; } catch ( ezcBasePropertyNotFoundException $e ) { return true; } $this->fail( "Exception not thrown on access of invalid property." ); } public function testSetAccessSuccess() { $option = new ezcConsoleOption( "a", "aaa" ); $rule = new ezcConsoleOptionRule( $option, array( "a" ) ); $rule->option = $option; $rule->values = array( "a", "b", "c" ); $rule->ifSet = false; $this->assertSame( $option, $rule->option ); $this->assertEquals( array( "a", "b", "c" ), $rule->values ); $this->assertFalse( $rule->ifSet ); } public function testSetAccessFailureOption() { $option = new ezcConsoleOption( "a", "aaa" ); $rule = new ezcConsoleOptionRule( $option, array( "a" ) ); try { $rule->option = array(); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOptionRule->option." ); } public function testSetAccessFailureValues() { $option = new ezcConsoleOption( "a", "aaa" ); $rule = new ezcConsoleOptionRule( $option, array( "a" ) ); try { $rule->values = 23; } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOptionRule->values." ); } public function testSetAccessFailureIfSet() { $option = new ezcConsoleOption( "a", "aaa" ); $rule = new ezcConsoleOptionRule( $option, array( "a" ) ); try { $rule->ifSet = 23; } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOptionRule->ifSet." ); } public function testSetAccessFailureNonExsitent() { $option = new ezcConsoleOption( "a", "aaa" ); $rule = new ezcConsoleOptionRule( $option, array( "a" ) ); try { $rule->nonExistent = 23; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOptionRule->nonExistent." ); } public function testIssetAccessSuccess() { $option = new ezcConsoleOption( "a", "aaa" ); $rule = new ezcConsoleOptionRule( $option, array( "a" ) ); $this->assertTrue( isset( $rule->option ) ); $this->assertTrue( isset( $rule->values ) ); $this->assertTrue( isset( $rule->ifSet ) ); } public function testIssetAccessFailure() { $option = new ezcConsoleOption( "a", "aaa" ); $rule = new ezcConsoleOptionRule( $option, array( "a" ) ); $this->assertFalse( isset( $rule->nonExsistent ) ); } } ?> ConsoleTools-1.7.5/tests/option_test.php000066400000000000000000000443511470000742400203760ustar00rootroot00000000000000assertEquals( $option->short, "a" ); $this->assertEquals( $option->long, "aaa" ); $this->assertEquals( ezcConsoleInput::TYPE_INT, $option->type ); $this->assertEquals( 23, $option->default ); $this->assertTrue( $option->multiple ); $this->assertEquals( "Shorthelp", $option->shorthelp ); $this->assertEquals( "Longhelp", $option->longhelp ); $this->assertEquals( array( $ruleDependency ), $option->getDependencies() ); $this->assertEquals( array( $ruleExclusion ), $option->getExclusions() ); $this->assertFalse( $option->arguments ); $this->assertTrue( $option->mandatory ); $this->assertTrue( $option->isHelpOption ); } public function testInvalidOptionName_short() { try { $option = new ezcConsoleOption( ' ', 'help' ); } catch ( ezcConsoleInvalidOptionNameException $e ) { return; } $this->fail( 'Exception not thrown on invalid option name.' ); } public function testInvalidOptionName_long() { try { $option = new ezcConsoleOption( 'h', '--help' ); } catch ( ezcConsoleInvalidOptionNameException $e ) { return; } $this->fail( 'Exception not thrown on invalid option name.' ); } public function testAddDependency() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $option_1->addDependency( $rule ); $option_1->addDependency( $rule ); $this->assertAttributeEquals( array( $rule ), "dependencies", $option_1 ); } public function testRemoveDependency() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $option_1->addDependency( $rule ); $option_1->removeDependency( $rule ); $this->assertAttributeEquals( array(), "dependencies", $option_1 ); } // Bug #9052: Exception because of invalid property in ezcConsoleOption public function testRemoveAllDependencies() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule_1 = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $rule_2 = new ezcConsoleOptionRule( $option_2, array( "d" ) ); $option_1->addDependency( $rule_1 ); $option_1->addDependency( $rule_2 ); $option_1->removeAllDependencies( $option_2 ); $this->assertAttributeEquals( array(), "dependencies", $option_1 ); } // Bug #9052: Exception because of invalid property in ezcConsoleOption public function testHasDependencySuccess() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $option_1->addDependency( $rule ); $this->assertTrue( $option_1->hasDependency( $option_2 ) ); } public function testHasDependencyFailure() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $this->assertFalse( $option_1->hasDependency( $option_2 ) ); } public function testResetDependencies() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $option_1->addDependency( $rule ); $option_1->resetDependencies(); $this->assertAttributeEquals( array(), "dependencies", $option_1 ); } public function testAddExclusion() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $option_1->addExclusion( $rule ); $option_1->addExclusion( $rule ); $this->assertAttributeEquals( array( $rule ), "exclusions", $option_1 ); } public function testRemoveExclusion() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $option_1->addExclusion( $rule ); $option_1->removeExclusion( $rule ); $this->assertAttributeEquals( array(), "exclusions", $option_1 ); } // Bug #9052: Exception because of invalid property in ezcConsoleOption public function testRemoveAllExclusions() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule_1 = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $rule_2 = new ezcConsoleOptionRule( $option_2, array( "d" ) ); $option_1->addExclusion( $rule_1 ); $option_1->addExclusion( $rule_2 ); $option_1->removeAllExclusions( $option_2 ); $this->assertAttributeEquals( array(), "exclusions", $option_1 ); } // Bug #9052: Exception because of invalid property in ezcConsoleOption public function testHasExclusionSuccess() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $option_1->addExclusion( $rule ); $this->assertTrue( $option_1->hasExclusion( $option_2 ) ); } public function testHasExclusionFailure() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $this->assertFalse( $option_1->hasExclusion( $option_2 ) ); } public function testResetExclusions() { $option_1 = new ezcConsoleOption( "a", "aaa" ); $option_2 = new ezcConsoleOption( "b", "bbb" ); $rule = new ezcConsoleOptionRule( $option_2, array( "c" ) ); $option_1->addExclusion( $rule ); $option_1->resetExclusions(); $this->assertAttributeEquals( array(), "exclusions", $option_1 ); } public function testPropertyGetAccessSuccess() { $option = new ezcConsoleOption( "a", "aaa" ); $this->assertEquals( "a", $option->short ); $this->assertEquals( "aaa", $option->long ); $this->assertEquals( ezcConsoleInput::TYPE_NONE, $option->type ); $this->assertNull( $option->default ); $this->assertFalse( $option->multiple ); $this->assertEquals( "No help available.", $option->shorthelp ); $this->assertEquals( "Sorry, there is no help text available for this parameter.", $option->longhelp ); $this->assertTrue( $option->arguments ); $this->assertFalse( $option->mandatory ); $this->assertFalse( $option->isHelpOption ); } public function testPropertyGetAccessFailureDependencies() { $option = new ezcConsoleOption( "a", "aaa" ); try { $foo = $option->dependencies; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on access of ezcConsoleOption->dependencies." ); } public function testPropertyGetAccessFailureExclusions() { $option = new ezcConsoleOption( "a", "aaa" ); try { $foo = $option->exclusions; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on access of ezcConsoleOption->exclusions." ); } public function testPropertyGetAccessFailureNotExisting() { $option = new ezcConsoleOption( "a", "aaa" ); try { $foo = $option->nonExisting; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on access of ezcConsoleOption->nonExisting." ); } public function testPropertySetAccessSuccess() { $option = new ezcConsoleOption( "a", "aaa" ); $option->type = ezcConsoleInput::TYPE_STRING; $option->type = ezcConsoleInput::TYPE_NONE; $option->type = ezcConsoleInput::TYPE_INT; $option->default = 10; $option->multiple = true; $option->shorthelp = "Shorthelp"; $option->longhelp = "Longhelp"; $option->arguments = false; $option->mandatory = true; $option->isHelpOption = true; $this->assertEquals( ezcConsoleInput::TYPE_INT, $option->type ); $this->assertEquals( 10, $option->default ); $this->assertTrue( $option->multiple ); $this->assertEquals( "Shorthelp", $option->shorthelp ); $this->assertEquals( "Longhelp", $option->longhelp ); $this->assertFalse( $option->arguments ); $this->assertTrue( $option->mandatory ); $this->assertTrue( $option->isHelpOption ); } public function testPropertySetAccessSuccessMultipleArray() { $option = new ezcConsoleOption( "a", "aaa" ); $option->multiple = true; $option->default = array( "foo", "bar" ); $this->assertEquals( array( "foo", "bar" ), $option->default ); } public function testPropertySetAccessSuccessMultipleScalar() { $option = new ezcConsoleOption( "a", "aaa" ); $option->multiple = true; $option->default = 10; $this->assertEquals( 10, $option->default ); } public function testPropertySetAccessFailureNoMultipleArray() { $option = new ezcConsoleOption( "a", "aaa" ); $option->multiple = false; try { $option->default = array( "foo", "bar" ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Assigning array when multiple is false worked." ); } public function testPropertySetAccessFailureShort() { $option = new ezcConsoleOption( "a", "aaa" ); try { $option->short = "b"; } catch ( ezcBasePropertyPermissionException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOption->short." ); } public function testPropertySetAccessFailureLong() { $option = new ezcConsoleOption( "a", "aaa" ); try { $option->long = "bbb"; } catch ( ezcBasePropertyPermissionException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOption->long." ); } public function testPropertySetAccessFailureType() { $option = new ezcConsoleOption( "a", "aaa" ); try { $option->type = "Invalid type"; } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOption->type." ); } public function testPropertySetAccessFailureDefault() { $option = new ezcConsoleOption( "a", "aaa" ); try { $option->default = array(); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOption->default." ); } public function testPropertySetAccessFailureMultiple() { $option = new ezcConsoleOption( "a", "aaa" ); try { $option->multiple = array(); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOption->multiple." ); } public function testPropertySetAccessFailureShorthelp() { $option = new ezcConsoleOption( "a", "aaa" ); try { $option->shorthelp = array(); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOption->shorthelp." ); } public function testPropertySetAccessFailureLonghelp() { $option = new ezcConsoleOption( "a", "aaa" ); try { $option->longhelp = array(); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOption->longhelp." ); } public function testPropertySetAccessFailureArguments() { $option = new ezcConsoleOption( "a", "aaa" ); try { $option->arguments = array(); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOption->arguments." ); } public function testPropertySetAccessFailureMandatory() { $option = new ezcConsoleOption( "a", "aaa" ); try { $option->mandatory = array(); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOption->mandatory." ); } public function testPropertySetAccessFailureIsHelpOption() { $option = new ezcConsoleOption( "a", "aaa" ); try { $option->isHelpOption = array(); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOption->isHelpOption." ); } public function testPropertySetAccessFailureNonExistent() { $option = new ezcConsoleOption( "a", "aaa" ); try { $option->nonExistent = array(); } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on invalid value for ezcConsoleOption->nonExsitent." ); } public function testPropertyIssetAccessSuccess() { $option = new ezcConsoleOption( "a", "aaa" ); $this->assertTrue( isset( $option->type ) ); $this->assertFalse( isset( $option->default ) ); $this->assertTrue( isset( $option->multiple ) ); $this->assertTrue( isset( $option->shorthelp ) ); $this->assertTrue( isset( $option->longhelp ) ); $this->assertTrue( isset( $option->arguments ) ); $this->assertTrue( isset( $option->mandatory ) ); $this->assertTrue( isset( $option->isHelpOption ) ); $this->assertFalse( isset( $option->nonExistent ) ); } } ?> ConsoleTools-1.7.5/tests/output_format_test.php000066400000000000000000000127351470000742400217770ustar00rootroot00000000000000assertEquals( $fake, new ezcConsoleOutputFormat(), 'Default values incorrect for ezcConsoleOutputFormat.' ); } public function testGetAccessSuccess() { $format = new ezcConsoleOutputFormat( 'blue', array( 'bold' ), 'red', ezcConsoleOutput::TARGET_STDERR ); $this->assertEquals( "blue", $format->color ); $this->assertEquals( array( "bold" ), $format->style ); $this->assertEquals( "red", $format->bgcolor ); $this->assertEquals( "php://stderr", $format->target ); } public function testGetAccessFailure() { $format = new ezcConsoleOutputFormat(); try { $foo = $format->nonExsitent; } catch ( ezcBasePropertyNotFoundException $e ) { return true; } $this->fail( "Exception not thrown on access of not existing property on ezcConsoleFormat." ); } public function testSetAccessSuccess() { $format = new ezcConsoleOutputFormat(); $format->color = "blue"; $format->style = array( "bold" ); $format->bgcolor = "red"; $format->target = ezcConsoleOutput::TARGET_STDERR; $this->assertEquals( "blue", $format->color ); $this->assertEquals( array( "bold" ), $format->style ); $this->assertEquals( "red", $format->bgcolor ); $this->assertEquals( "php://stderr", $format->target ); // Style can also be scalar $format->style = "bold"; $this->assertEquals( array( "bold" ), $format->style ); } public function testSetAccessFailureColor() { $format = new ezcConsoleOutputFormat(); try { $format->color = "nonExistent"; } catch ( ezcBaseValueException $e ) { return true; } $this->fail( "Exception not thrown on set of not existing color on ezcConsoleFormat->color." ); } public function testSetAccessFailureStyleArray() { $format = new ezcConsoleOutputFormat(); try { $format->style = array( "nonExistent" ); } catch ( ezcBaseValueException $e ) { return true; } $this->fail( "Exception not thrown on set of not existing format code as array on ezcConsoleFormat->style." ); } public function testSetAccessFailureStyleScalar() { $format = new ezcConsoleOutputFormat(); try { $format->style = "nonExistent"; } catch ( ezcBaseValueException $e ) { return true; } $this->fail( "Exception not thrown on set of not existing format code as scalar on ezcConsoleFormat->style." ); } public function testSetAccessFailureBgcolor() { $format = new ezcConsoleOutputFormat(); try { $format->bgcolor = "nonExistent"; } catch ( ezcBaseValueException $e ) { return true; } $this->fail( "Exception not thrown on set of not existing color on ezcConsoleFormat->bgcolor." ); } public function testSetAccessFailureNonexistent() { $format = new ezcConsoleOutputFormat(); try { $format->nonExsitent = "nonExistent"; } catch ( ezcBasePropertyNotFoundException $e ) { return true; } $this->fail( "Exception not thrown on set of not existing color on ezcConsoleFormat->bgcolor." ); } public function testIssetAccessSuccess() { $format = new ezcConsoleOutputFormat(); $this->assertTrue( isset( $format->color ) ); $this->assertTrue( isset( $format->style ) ); $this->assertTrue( isset( $format->bgcolor ) ); } public function testIssetAccessFailure() { $format = new ezcConsoleOutputFormat(); $this->assertFalse( isset( $format->nonExistent ) ); } } ?> ConsoleTools-1.7.5/tests/output_formats_test.php000066400000000000000000000112041470000742400221500ustar00rootroot00000000000000default = new ezcConsoleOutputFormat(); $this->assertEquals( $formats, new ezcConsoleOutputFormats(), 'Default values incorrect for ezcConsoleOutputFormats.' ); } public function testGetAccessNonExistent() { $formats = new ezcConsoleOutputFormats(); $format = new ezcConsoleOutputFormat(); $this->assertEquals( $format, $formats->foobar ); } public function testGetAccessExistent() { $formats = new ezcConsoleOutputFormats(); $format = new ezcConsoleOutputFormat(); $formats->foobar = $format; $this->assertEquals( $format, $formats->foobar ); } public function testGetAccessManipulate() { $formats = new ezcConsoleOutputFormats(); $formats->foobar->color = 'blue'; $format = new ezcConsoleOutputFormat(); $format->color = 'blue'; $this->assertEquals( $format, $formats->foobar ); } public function testSetAccessExistent() { $formats = new ezcConsoleOutputFormats(); $format = new ezcConsoleOutputFormat(); $formats->foobar = $format; $this->assertEquals( $format, $formats->foobar ); } public function testIssetAccessSuccess() { $formats = new ezcConsoleOutputFormats(); $formats->foobar = new ezcConsoleOutputFormat(); $this->assertTrue( isset( $formats->foobar ) ); } public function testIssetAccessFailure() { $formats = new ezcConsoleOutputFormats(); $this->assertFalse( isset( $formats->foobar ) ); } public function testIterator() { $formatsObj = new ezcConsoleOutputFormats(); $formatsArr = $this->readAttribute( $formatsObj, 'formats' ); reset( $formatsArr ); // First run foreach ( $formatsObj as $name => $format ) { $this->assertEquals( current( $formatsArr ), $format ); $this->assertEquals( key( $formatsArr ), $name ); next( $formatsArr ); } $this->assertFalse( current( $formatsArr ) ); $this->assertFalse( $formatsObj->valid() ); reset( $formatsArr ); // Second run foreach ( $formatsObj as $name => $format ) { $this->assertEquals( current( $formatsArr ), $format ); $this->assertEquals( key( $formatsArr ), $name ); next( $formatsArr ); } $this->assertFalse( current( $formatsArr ) ); $this->assertFalse( $formatsObj->valid() ); } public function testCountable() { $formatsObj = new ezcConsoleOutputFormats(); $this->assertEquals( 3, count( $formatsObj ) ); $formatsObj->foobar->color = 'blue'; $this->assertEquals( 4, count( $formatsObj ) ); } } ?> ConsoleTools-1.7.5/tests/output_options_test.php000066400000000000000000000125561470000742400222030ustar00rootroot00000000000000assertEquals( $fake, new ezcConsoleOutputOptions(), 'Default values incorrect for ezcConsoleOutputOptions.' ); } /** * testConstructorNew * * @access public */ public function testConstructorNew() { $fake = new ezcConsoleOutputOptions( array( "verbosityLevel" => 1, "autobreak" => 0, "useFormats" => true, ) ); $this->assertEquals( $fake, new ezcConsoleOutputOptions(), 'Default values incorrect for ezcConsoleOutputOptions.' ); } public function testCompatibility() { $old = new ezcConsoleOutputOptions( 5, 80 ); $new = new ezcConsoleOutputOptions( array( "verbosityLevel" => 5, "autobreak" => 80, ) ); $this->assertEquals( $old, $new, "Old construction method did not produce same result as old one." ); } public function testNewAccess() { $opt = new ezcConsoleOutputOptions(); $this->assertEquals( $opt->verbosityLevel, 1 ); $this->assertEquals( $opt->autobreak, 0 ); $this->assertEquals( $opt->useFormats, true ); $this->assertEquals( $opt["verbosityLevel"], 1 ); $this->assertEquals( $opt["autobreak"], 0 ); $this->assertEquals( $opt["useFormats"], true ); } public function testGetAccessSuccess() { $opt = new ezcConsoleOutputOptions(); $this->assertEquals( 1, $opt->verbosityLevel ); $this->assertEquals( 0, $opt->autobreak ); $this->assertEquals( true, $opt->useFormats ); } public function testGetAccessFailure() { $opt = new ezcConsoleOutputOptions(); $exceptionThrown = false; try { echo $opt->foo; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on get access of invalid property foo." ); } public function testSetAccessSuccess() { $opt = new ezcConsoleOutputOptions(); $this->assertSetProperty( $opt, 'verbosityLevel', array( 0, 1, 10, 42 ) ); $this->assertSetProperty( $opt, 'autobreak', array( 0, 1, 10, 42 ) ); $this->assertSetProperty( $opt, 'useFormats', array( false, true ) ); } public function testSetAccessFailure() { $opt = new ezcConsoleOutputOptions(); $this->assertSetPropertyFails( $opt, 'verbosityLevel', array( -1, 23.42, 'foo', '', true, false, array(), new stdClass() ) ); $this->assertSetPropertyFails( $opt, 'autobreak', array( -1, 23.42, 'foo', '', true, false, array(), new stdClass() ) ); $this->assertSetPropertyFails( $opt, 'useFormats', array( 'foo', '', 23, -42, 23.42, array(), new stdClass() ) ); $this->assertSetPropertyFails( $opt, 'foo', array( '', 'bar', 23, -42, 23.42, true, false, array(), new stdClass() ) ); $this->assertSetPropertyFails( $opt, 'characterEncoding', array( '', 'bar', 23, -42, 23.42, true, false, array(), new stdClass() ) ); } public function testIsset() { $opt = new ezcConsoleOutputOptions(); $this->assertTrue( isset( $opt->verbosityLevel ) ); $this->assertTrue( isset( $opt->autobreak ) ); $this->assertTrue( isset( $opt->useFormats ) ); $this->assertFalse( isset( $opt->characterEncoding ) ); $this->assertFalse( isset( $opt->foo ) ); } } ?> ConsoleTools-1.7.5/tests/output_test.php000066400000000000000000000273331470000742400204270ustar00rootroot00000000000000 array( 'in' => array( 'color' => 'blue', ), 'out' => "\033[34m%s\033[0m" ), 'color_only_2' => array( 'in' => array( 'color' => 'red', ), 'out' => "\033[31m%s\033[0m" ), 'bgcolor_only_1' => array( 'in' => array( 'bgcolor' => 'green', ), 'out' => "\033[42m%s\033[0m" ), 'bgcolor_only_2' => array( 'in' => array( 'bgcolor' => 'yellow', ), 'out' => "\033[43m%s\033[0m" ), 'style_only_1' => array( 'in' => array( 'style' => 'bold', ), 'out' => "\033[1m%s\033[0m" ), 'style_only_2' => array( 'in' => array( 'style' => 'negative', ), 'out' => "\033[7m%s\033[0m" ), ); /** * consoleOutput * * @var mixed */ private $consoleOutput; public static function suite() { return new PHPUnit\Framework\TestSuite( "ezcConsoleOutputTest" ); } protected function setUp() : void { $this->consoleOutput = new ezcConsoleOutput(); foreach ( $this->testFormats as $name => $inout ) { foreach ( $inout['in'] as $formatName => $val ) { $this->consoleOutput->formats->$name->$formatName = $val; } } } protected function tearDown() : void { unset( $this->consoleOutput ); } /** * testFormatText * * @access public */ public function testFormatText() { foreach ( $this->testFormats as $name => $inout ) { $realRes = $this->consoleOutput->formatText( $this->testString, $name ); $fakeRes = ezcBaseFeatures::os() !== "Windows" ? sprintf( $inout['out'], $this->testString ) : $this->testString; $this->assertEquals( $realRes, $fakeRes, "Test <{$name}> failed. String <{$realRes}> (real) is not equal to <{$fakeRes}> (fake)." ); } } /** * testOutputText * * @access public */ public function testOutputText() { foreach ( $this->testFormats as $name => $inout ) { ob_start(); $this->consoleOutput->outputText( $this->testString, $name ); $realRes = ob_get_contents(); ob_end_clean(); $fakeRes = ezcBaseFeatures::os() !== "Windows" ? sprintf( $inout['out'], $this->testString ) : $this->testString; $this->assertEquals( $fakeRes, $realRes, "Test <{$name}> failed. String <{$realRes}> (real) is not equal to <{$fakeRes}> (fake)." ); } } /** * testOutputTextAutobreak * * @access public */ public function testOutputTextAutobreak() { $this->consoleOutput->options->autobreak = 20; $testText = 'Some text which is obviously longer than 20 characters and should be broken.'; $testResText = 'Some text which is' . PHP_EOL . 'obviously longer' . PHP_EOL . 'than 20 characters' . PHP_EOL . 'and should be' . PHP_EOL . 'broken.'; foreach ( $this->testFormats as $name => $inout ) { ob_start(); $this->consoleOutput->outputText( $testText, $name ); $realRes = ob_get_contents(); ob_end_clean(); $fakeRes = ezcBaseFeatures::os() !== "Windows" ? sprintf( $inout['out'], $testResText ) : $testResText; $this->assertEquals( $fakeRes, $realRes, 'Test "' . $name . ' failed. String <' . $realRes . '> (real) is not equal to <' . $fakeRes . '> (fake).' ); } } public function testOutputColorAliases() { $this->consoleOutput->formats->aliasBG->bgcolor = "gray"; $this->consoleOutput->formats->aliasBG->color = "white"; $this->consoleOutput->formats->realBG->bgcolor = "black"; $this->consoleOutput->formats->realBG->color = "white"; $this->consoleOutput->formats->realFG->color = "gray"; $this->consoleOutput->formats->realFG->bgcolor = "white"; $this->consoleOutput->formats->aliasFG->color = "black"; $this->consoleOutput->formats->aliasFG->bgcolor = "white"; $this->assertEquals( $this->consoleOutput->formatText( "I am black!", "aliasBG" ), $this->consoleOutput->formatText( "I am black!", "realBG" ), "Backgroundcolor not correctly aliased to ." ); $this->assertEquals( $this->consoleOutput->formatText( "I am gray!", "aliasFG" ), $this->consoleOutput->formatText( "I am gray!", "realFG" ), "Foregroundcolor not correctly aliased to ." ); } public function testOutputToTarget() { $outFile = $this->createTempDir( __FUNCTION__ ) . "/outfile"; touch( $outFile ); $this->consoleOutput->formats->targetFile->target = $outFile; $this->consoleOutput->formats->targetFile->color = "blue"; $this->consoleOutput->outputText( "Hello, I'm a cool text, written to a file!", "targetFile" ); $fakeRes = $this->consoleOutput->formatText( "Hello, I'm a cool text, written to a file!", "targetFile" ); unset( $this->consoleOutput ); $this->assertEquals( $fakeRes, file_get_contents( $outFile ) ); $this->removeTempDir(); } public function testSetOptionsSuccess() { $optObj = new ezcConsoleOutputOptions(); $optObj->verbosityLevel = 10; $optObj->autobreak = 80; $optObj->useFormats = false; $output = new ezcConsoleOutput(); $output->setOptions( $optObj ); $this->assertEquals( $optObj, $output->options ); $output = new ezcConsoleOutput(); $output->setOptions( array( "verbosityLevel" => 10, "autobreak" => 80, "useFormats" => false, ) ); $this->assertEquals( $optObj, $output->options ); } public function testSetOptionsFailure() { $output = new ezcConsoleOutput(); try { $output->setOptions( true ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid parameter to ezcConsoleOutput->setOptions()." ); } public function testGetOptions() { $output = new ezcConsoleOutput(); $this->assertEquals( new ezcConsoleOutputOptions(), $output->getOptions() ); } public function testGetAccessSuccess() { $output = new ezcConsoleOutput(); $this->assertEquals( new ezcConsoleOutputOptions(), $output->options ); $this->assertEquals( new ezcConsoleOutputFormats(), $output->formats ); } public function testGetAccessFailure() { $output = new ezcConsoleOutput(); try { echo $output->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on get access to invalid property foo." ); } public function testSetAccessSuccess() { $optObj = new ezcConsoleOutputOptions(); $forObj = new ezcConsoleOutputFormats(); $output = new ezcConsoleOutput(); $output->options = $optObj; $output->formats = $forObj; $this->assertSame( $optObj, $output->options ); $this->assertSame( $forObj, $output->formats ); } public function testSetAccessFailure() { $output = new ezcConsoleOutput(); $exceptionThrown = false; try { $output->options = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property options." ); $exceptionThrown = false; try { $output->formats = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property formats." ); $exceptionThrown = false; try { $output->foo = 23; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on set access to invalid property foo." ); } public function testIssetAccess() { $output = new ezcConsoleOutput(); $this->assertTrue( isset( $output->options ) ); $this->assertTrue( isset( $output->formats ) ); $this->assertFalse( isset( $output->foo ) ); } public function testOutputTextFailure() { $output = new ezcConsoleOutput(); $output->formats->invalid->target = "foo://bar"; $exceptionThrown = false; try { $output->outputText( "Foo Bar", "invalid" ); } catch ( ezcConsoleInvalidOutputTargetException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on output to invalid output target." ); } public function testToPos() { if ( ezcBaseFeatures::os() === 'Windows' ) { $this->markTestSkipped( "Does not work on Windows" ); } $output = new ezcConsoleOutput(); ob_start(); $output->outputText( "Test 123" ); $output->toPos( 6 ); $output->outputText( "456" ); $res = ob_get_clean(); $exp = 'Test 123456'; $this->assertEquals( $exp, $res, "Position code not generated correctly." ); } public function testRestorePosFailure() { if ( ezcBaseFeatures::os() === 'Windows' ) { $this->markTestSkipped( "Does not work on Windows" ); } $output = new ezcConsoleOutput(); try { $output->restorePos(); } catch ( ezcConsoleNoPositionStoredException $e ) { return; } $this->fail( "Exception not thrown on restore position without stored position." ); } } ?> ConsoleTools-1.7.5/tests/progressbar_options_test.php000066400000000000000000000256761470000742400232030ustar00rootroot00000000000000 "+", "emptyChar" => "-", "formatString" => "%act% / %max% [%bar%] %fraction%%", "fractionFormat" => "%01.2f", "progressChar" => ">", "redrawFrequency" => 1, "step" => 1, "width" => 78, "actFormat" => "%.0f", "maxFormat" => "%.0f", "minVerbosity" => 1, "maxVerbosity" => false, ) ); $this->assertEquals( $fake, new ezcConsoleProgressbarOptions(), 'Default values incorrect for ezcConsoleProgressbarOptions.' ); } public function testNewAccess() { $opt = new ezcConsoleProgressbarOptions(); $this->assertEquals( "+", $opt->barChar ); $this->assertEquals( "-", $opt->emptyChar ); $this->assertEquals( "%act% / %max% [%bar%] %fraction%%", $opt->formatString ); $this->assertEquals( "%01.2f", $opt->fractionFormat ); $this->assertEquals( ">", $opt->progressChar ); $this->assertEquals( 1, $opt->redrawFrequency ); $this->assertEquals( 1, $opt->step ); $this->assertEquals( 78, $opt->width ); $this->assertEquals( "%.0f", $opt->actFormat ); $this->assertEquals( "%.0f", $opt->maxFormat ); $this->assertEquals( 1, $opt->minVerbosity ); $this->assertEquals( false, $opt->maxVerbosity ); $this->assertEquals( $opt["barChar"], "+" ); $this->assertEquals( $opt["emptyChar"], "-" ); $this->assertEquals( $opt["formatString"], "%act% / %max% [%bar%] %fraction%%" ); $this->assertEquals( $opt["fractionFormat"], "%01.2f" ); $this->assertEquals( $opt["progressChar"], ">" ); $this->assertEquals( $opt["redrawFrequency"], 1 ); $this->assertEquals( $opt["step"], 1 ); $this->assertEquals( $opt["width"], 78 ); $this->assertEquals( $opt["actFormat"], "%.0f" ); $this->assertEquals( $opt["maxFormat"], "%.0f" ); $this->assertEquals( $opt["minVerbosity"], 1 ); $this->assertEquals( $opt["maxVerbosity"], false ); } public function testGetAccessSuccess() { $opt = new ezcConsoleProgressbarOptions(); $this->assertEquals( "+", $opt->barChar ); $this->assertEquals( "-", $opt->emptyChar ); $this->assertEquals( "%act% / %max% [%bar%] %fraction%%", $opt->formatString ); $this->assertEquals( "%01.2f", $opt->fractionFormat ); $this->assertEquals( ">", $opt->progressChar ); $this->assertEquals( 1, $opt->redrawFrequency ); $this->assertEquals( 1, $opt->step ); $this->assertEquals( 78, $opt->width ); $this->assertEquals( '%.0f', $opt->actFormat ); $this->assertEquals( '%.0f', $opt->maxFormat ); $this->assertEquals( 1, $opt->minVerbosity ); $this->assertEquals( false, $opt->maxVerbosity ); } public function testGetAccessFailure() { $opt = new ezcConsoleProgressbarOptions(); $exceptionThrown = false; try { echo $opt->foo; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on get access of invalid property foo." ); } public function testSetAccessSuccess() { $opt = new ezcConsoleProgressbarOptions(); $opt->barChar = "*"; $opt->emptyChar = "_"; $opt->formatString = " %fraction%% %act% - %max% (%bar%)"; $opt->fractionFormat = "%01.1f"; $opt->progressChar = "]"; $opt->redrawFrequency = 5; $opt->step = 5; $opt->width = 42; $opt->actFormat = '%.10f'; $opt->maxFormat = '%.10f'; $opt->minVerbosity = 23; $opt->maxVerbosity = 42; $this->assertEquals( "*", $opt->barChar ); $this->assertEquals( "_", $opt->emptyChar ); $this->assertEquals( " %fraction%% %act% - %max% (%bar%)", $opt->formatString ); $this->assertEquals( "%01.1f", $opt->fractionFormat ); $this->assertEquals( "]", $opt->progressChar ); $this->assertEquals( 5, $opt->redrawFrequency ); $this->assertEquals( 5, $opt->step ); $this->assertEquals( 42, $opt->width ); $this->assertEquals( '%.10f', $opt->actFormat ); $this->assertEquals( '%.10f', $opt->maxFormat ); $this->assertEquals( 23, $opt->minVerbosity ); $this->assertEquals( 42, $opt->maxVerbosity ); } public function testSetAccessFailure() { $opt = new ezcConsoleProgressbarOptions(); $exceptionThrown = false; try { $opt->fractionFormat = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property fractionFormat." ); $exceptionThrown = false; try { $opt->progressChar = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property progressChar." ); $exceptionThrown = false; try { $opt->redrawFrequency = "foo"; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property redrawFrequency." ); $exceptionThrown = false; try { $opt->step = "foo"; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property step." ); $exceptionThrown = false; try { $opt->width = "foo"; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property width." ); $exceptionThrown = false; try { $opt->actFormat = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property actFormat." ); $exceptionThrown = false; try { $opt->maxFormat = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property maxFormat." ); $exceptionThrown = false; try { $opt->barChar = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property barChar." ); $exceptionThrown = false; try { $opt->emptyChar = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property emptyChar." ); $exceptionThrown = false; try { $opt->formatString = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property formatString." ); $exceptionThrown = false; try { $opt->minVerbosity = 'some val'; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property minVerbosity." ); $exceptionThrown = false; try { $opt->maxVerbosity = 'some val'; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property maxVerbosity." ); $exceptionThrown = false; try { $opt->foo = true; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on set access of invalid property foo." ); } public function testIsset() { $opt = new ezcConsoleProgressbarOptions(); $this->assertTrue( isset( $opt->barChar ) ); $this->assertTrue( isset( $opt->emptyChar ) ); $this->assertTrue( isset( $opt->formatString ) ); $this->assertTrue( isset( $opt->fractionFormat ) ); $this->assertTrue( isset( $opt->progressChar ) ); $this->assertTrue( isset( $opt->redrawFrequency ) ); $this->assertTrue( isset( $opt->step ) ); $this->assertTrue( isset( $opt->width ) ); $this->assertTrue( isset( $opt->actFormat ) ); $this->assertTrue( isset( $opt->maxFormat ) ); $this->assertTrue( isset( $opt->minVerbosity ) ); $this->assertTrue( isset( $opt->maxVerbosity ) ); $this->assertFalse( isset( $opt->foo ) ); } } ?> ConsoleTools-1.7.5/tests/progressbar_test.php000066400000000000000000000362151470000742400214170ustar00rootroot00000000000000commonProgressbarTest( __FUNCTION__, 42, 13, array () ); } public function testProgress2() { $this->commonProgressbarTest( __FUNCTION__, 20, 32, array () ); } public function testProgress3() { $this->commonProgressbarTest( __FUNCTION__, 42, 13, array ( 'barChar' => '#', 'emptyChar' => '*' ) ); } public function testProgress4() { $this->commonProgressbarTest( __FUNCTION__, 55, 19, array ( 'progressChar' => '&' ) ); } public function testProgress5() { $this->commonProgressbarTest( __FUNCTION__, 42, 13, array ( 'progressChar' => '&', 'width' => 55 ) ); } public function testProgress6() { $this->commonProgressbarTest( __FUNCTION__, 22, 3, array ( 'barChar' => '#', 'emptyChar' => '*', 'progressChar' => '&', 'width' => 81 ) ); } public function testProgress7() { $this->commonProgressbarTest( __FUNCTION__, 42, 7, array ( 'barChar' => '1234', 'emptyChar' => '9876' ) ); } public function testProgress8() { $this->commonProgressbarTest( __FUNCTION__, 42, 7, array ( 'barChar' => '123', 'emptyChar' => '987', 'progressChar' => '---' ) ); } public function testProgress9() { $out = new ezcConsoleOutput(); $formatString = '' . $out->formatText( 'Actual progress', 'success' ) . ': <' . $out->formatText( '%bar%', 'failure' ) . '> ' . $out->formatText( '%fraction%', 'success' ); $this->commonProgressbarTest( __FUNCTION__, 1073, 123, array( 'barChar' => '123', 'emptyChar' => '987', 'progressChar' => '---', 'width' => 97, 'formatString' => $formatString, 'fractionFormat' => '%o' ) ); } public function testProgress10() { $this->commonProgressbarTest( __FUNCTION__, 100, 1, array ( 'redrawFrequency' => 10 ) ); } public function testProgress11() { $this->commonProgressbarTest( __FUNCTION__, 100, 2.5, array ( 'actFormat' => '%01.2f', 'maxFormat' => '%01.2f' ) ); } public function testProgress12() { $this->commonProgressbarTest( __FUNCTION__, 100, 2.5, array ( 'actFormat' => '%01.2f', 'maxFormat' => '%01.8f' ) ); } public function testProgress13() { $this->commonProgressbarTest( __FUNCTION__, 100, 2.5, array ( 'actFormat' => '%01.8f', 'maxFormat' => '%01.2f' ) ); } public function testProgressUtfInBar() { $this->commonProgressbarTest( __FUNCTION__, 10, 2, array( 'barChar' => 'ö' ) ); } public function testProgressUtfInText() { $this->commonProgressbarTest( __FUNCTION__, 10, 2, array( 'formatString' => '%act%ö/ä%max%ü[%bar%]ß%fraction%%' ) ); } public function testProgressUtfInBoth() { $this->commonProgressbarTest( __FUNCTION__, 10, 2, array( 'formatString' => '%act%ö/ä%max%ü[%bar%]ß%fraction%%', 'barChar' => 'ö' ) ); } public function testProgressNoPrintMinVerbosity() { $out = new ezcConsoleOutput(); $out->options->verbosityLevel = 0; $progress = new ezcConsoleProgressbar( $out, 10 ); ob_start(); for ( $i = 0; $i < 10; ++$i ) { $progress->advance(); } $progress->finish(); $content = ob_get_clean(); $this->assertEquals( '', $content, 'Progress bar printed although verbosity level to low.' ); } public function testProgressNoPrintMinVerbosityNoDefault() { $out = new ezcConsoleOutput(); $out->options->verbosityLevel = 10; $progress = new ezcConsoleProgressbar( $out, 10 ); $progress->options->minVerbosity = 20; ob_start(); for ( $i = 0; $i < 10; ++$i ) { $progress->advance(); } $progress->finish(); $content = ob_get_clean(); $this->assertEquals( '', $content, 'Progress bar printed although verbosity level to low.' ); } public function testProgressNoPrintMaxVerbosity() { $out = new ezcConsoleOutput(); $progress = new ezcConsoleProgressbar( $out, 10 ); $progress->options->maxVerbosity = 0; ob_start(); for ( $i = 0; $i < 10; ++$i ) { $progress->advance(); } $progress->finish(); $content = ob_get_clean(); $this->assertEquals( '', $content, 'Progress bar printed although verbosity level to low.' ); } public function testProgressNoPrintMaxVerbosityNoDefault() { $out = new ezcConsoleOutput(); $out->options->verbosityLevel = 10; $progress = new ezcConsoleProgressbar( $out, 10 ); $progress->options->maxVerbosity = 9; ob_start(); for ( $i = 0; $i < 10; ++$i ) { $progress->advance(); } $progress->finish(); $content = ob_get_clean(); $this->assertEquals( '', $content, 'Progress bar printed although verbosity level to low.' ); } public function testProgressPrintMinMaxVerbosity() { $out = new ezcConsoleOutput(); $out->options->verbosityLevel = 10; $progress = new ezcConsoleProgressbar( $out, 10 ); $progress->options->maxVerbosity = 10; $progress->options->minVerbosity = 10; ob_start(); for ( $i = 0; $i < 10; ++$i ) { $progress->advance(); } $progress->finish(); $content = ob_get_clean(); $this->assertNotEquals( '', $content, 'Progress bar printed although verbosity level to low.' ); } public function testSetOptionsSuccess() { $out = new ezcConsoleOutput(); $opArr = array(); $optArr['barChar'] = "*"; $optArr['emptyChar'] = "#"; $optArr['formatString'] = "-- %act% / %max% [%bar%] %fraction%% --"; $optArr['fractionFormat'] = "%f"; $optArr['progressChar'] = "<"; $optArr['redrawFrequency'] = 42; $optArr['step'] = 23; $optArr['width'] = 42; $optArr['actFormat'] = '%f'; $optArr['maxFormat'] = '%f'; $optObj = new ezcConsoleProgressbarOptions(); $optObj->barChar = "*"; $optObj->emptyChar = "#"; $optObj->formatString = "-- %act% / %max% [%bar%] %fraction%% --"; $optObj->fractionFormat = "%f"; $optObj->progressChar = "<"; $optObj->redrawFrequency = 42; $optObj->step = 23; $optObj->width = 42; $optObj->actFormat = '%f'; $optObj->maxFormat = '%f'; $bar = new ezcConsoleProgressbar( $out, 10 ); $bar->setOptions( $optArr ); $this->assertEquals( $optObj, $bar->options, "Options not set correctly from array." ); $bar = new ezcConsoleProgressbar( $out, 10 ); $bar->setOptions( $optObj ); $this->assertEquals( $optObj, $bar->options, "Options not set correctly from object." ); } public function testSetOptionsFailure() { $out = new ezcConsoleOutput(); $bar = new ezcConsoleProgressbar( $out, 10 ); try { $bar->setOptions( 23 ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exeception not thrown on invalid parameter for setOptions()." ); } public function testGetOptions() { $out = new ezcConsoleOutput(); $bar = new ezcConsoleProgressbar( $out, 10 ); $opt = new ezcConsoleProgressbarOptions(); $bar->options = $opt; $this->assertSame( $opt, $bar->getOptions(), "Options not correctly returned from getOptions()." ); } public function testGetAccessSuccess() { $out = new ezcConsoleOutput(); $bar = new ezcConsoleProgressbar( $out, 10 ); $this->assertEquals( new ezcConsoleProgressbarOptions(), $bar->options ); $this->assertEquals( 10, $bar->max ); $this->assertEquals( 1, $bar->step ); } public function testGetAccessFailure() { $out = new ezcConsoleOutput(); $bar = new ezcConsoleProgressbar( $out, 10 ); try { echo $bar->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "ezcBasePropertyNotFoundException not thrown on get access of invalid property ezcConsoleProgressbar->foo." ); } public function testSetAccessSuccess() { $out = new ezcConsoleOutput(); $bar = new ezcConsoleProgressbar( $out, 10 ); $this->genericSetAccessTestSuccess( $bar, "options", new ezcConsoleProgressbarOptions() ); $this->genericSetAccessTestSuccess( $bar, "step", 10 ); $this->genericSetAccessTestSuccess( $bar, "max", 100 ); } public function testSetAccessFailure() { $out = new ezcConsoleOutput(); $bar = new ezcConsoleProgressbar( $out, 10 ); $this->genericSetAccessTestFailureValue( $bar, "options", 23 ); $this->genericSetAccessTestFailureValue( $bar, "max", true ); $this->genericSetAccessTestFailureValue( $bar, "max", -23 ); $this->genericSetAccessTestFailureValue( $bar, "step", true ); $this->genericSetAccessTestFailureValue( $bar, "step", -23 ); $this->genericSetAccessTestFailureNonexistent( $bar ); } public function testIssetAccess() { $out = new ezcConsoleOutput(); $bar = new ezcConsoleProgressbar( $out, 10 ); $this->assertTrue( isset( $bar->options ) ); $this->assertTrue( isset( $bar->max ) ); $this->assertTrue( isset( $bar->step ) ); $this->assertFalse( isset( $bar->foo ) ); } public function testFinish() { $out = new ezcConsoleOutput(); $bar = new ezcConsoleProgressbar( $out, 10 ); $res = array(); for ( $i = 0; $i < 5; ++$i ) { ob_start(); $bar->advance(); $resTmp = ob_get_clean(); if ( trim( $resTmp ) !== '') { $res[] = $resTmp; } } ob_start(); $bar->finish(); $res[] = ob_get_clean(); $refFile = dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . __FUNCTION__ . '.dat'; // Use the following line to regenerate test reference files // file_put_contents( $refFile, implode( PHP_EOL, $res ) ); $this->assertEquals( file_get_contents( $refFile ), implode( PHP_EOL, $res ), 'Progressbar not correctly generated for ' . $refFile . '.' ); } protected function genericSetAccessTestSuccess( $object, $propertyName, $value ) { $object->$propertyName = $value; if ( is_object( $value ) ) { $this->assertSame( $value, $object->$propertyName, "Property " . get_class( $object ) . "->$propertyName not set correctly on object of type " . get_class( $object ) . "." ); } else { $this->assertEquals( $value, $object->$propertyName, "Property " . get_class( $object ) . "->$propertyName not set correctly on object of type " . get_class( $object ) . "." ); } } protected function genericSetAccessTestFailureValue( $object, $propertyName, $value ) { try { $object->$propertyName = $value; } catch ( ezcBaseValueException $e ) { return; } $this->fail( "ezcBaseValueException not thrown on value '" . ( is_object( $value ) ) ? get_class( $value ) : $value . "' for property " . get_class( $object ) . "->$propertyName." ); } protected function genericSetAccessTestFailureNonexistent( $object ) { try { $object->foo = 23; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "ezcBasePropertyNotFoundException not thrown on access of not existent property " . get_class( $object ) . "->foo." ); } private function commonProgressbarTest( $refFile, $max, $step, $options ) { $out = new ezcConsoleOutput(); $bar = new ezcConsoleProgressbar( $out, $max, $options ); if ( $step != 1 ) { $bar->options->step = $step; } $res = array(); for ( $i = 0; $i < $max; $i+= $step) { ob_start(); $bar->advance(); // sleep( 1 ); $resTmp = ob_get_clean(); if ( trim( $resTmp ) !== '' ) { $res[] = $resTmp; } } $refFile = dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . $refFile . '.dat'; // Use the following line to regenerate test reference files // file_put_contents( $refFile, implode( PHP_EOL, $res ) ); $expected = ( file_exists( $refFile ) ? file_get_contents( $refFile ) : '' ); $this->assertEquals( $expected, implode( PHP_EOL, $res ), 'Progressbar not correctly generated for ' . $refFile . '.' ); } } ?> ConsoleTools-1.7.5/tests/progressmonitor_options_test.php000066400000000000000000000077561470000742400241250ustar00rootroot00000000000000 "%8.1f%% %s %s", ) ); $this->assertEquals( $fake, new ezcConsoleProgressMonitorOptions(), 'Default values incorrect for ezcConsoleProgressMonitorOptions.' ); } public function testNewAccess() { $opt = new ezcConsoleProgressMonitorOptions(); $this->assertEquals( $opt["formatString"], "%8.1f%% %s %s" ); } public function testGetAccessSuccess() { $opt = new ezcConsoleProgressMonitorOptions(); $this->assertEquals( "%8.1f%% %s %s", $opt->formatString ); } public function testGetAccessFailure() { $opt = new ezcConsoleProgressMonitorOptions(); $exceptionThrown = false; try { echo $opt->foo; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on get access of invalid property foo." ); } public function testSetAccessSuccess() { $opt = new ezcConsoleProgressMonitorOptions(); $opt->formatString = "foo %s"; $this->assertEquals( "foo %s", $opt->formatString ); } public function testSetAccessFailure() { $opt = new ezcConsoleProgressMonitorOptions(); $exceptionThrown = false; try { $opt->formatString = ""; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property formatString." ); $exceptionThrown = false; try { $opt->formatString = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property formatString." ); $exceptionThrown = false; try { $opt->foo = true; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on set access of invalid property foo." ); } public function testIsset() { $opt = new ezcConsoleProgressMonitorOptions(); $this->assertTrue( isset( $opt->formatString ) ); $this->assertFalse( isset( $opt->foo ) ); } } ?> ConsoleTools-1.7.5/tests/progressmonitor_test.php000066400000000000000000000220151470000742400223330ustar00rootroot00000000000000addEntry( $this->stati[$i][0], $this->stati[$i][1] ); } $res = ob_get_contents(); ob_end_clean(); // To prepare test files use this: // file_put_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testProgressMonitor1.dat', $res ); $this->assertEquals( file_get_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testProgressMonitor1.dat' ), $res, "Formated statusbar not generated correctly." ); } public function testProgressMonitor2() { $out = new ezcConsoleOutput(); $status = new ezcConsoleProgressMonitor( $out, 7 ); ob_start(); for ( $i = 0; $i < 7; $i++ ) { $status->addEntry( $this->stati[$i][0], $this->stati[$i][1] ); } $res = ob_get_contents(); ob_end_clean(); // To prepare test files use this: // file_put_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testProgressMonitor2.dat', $res ); $this->assertEquals( file_get_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testProgressMonitor2.dat' ), $res, "Formated statusbar not generated correctly." ); } public function testProgressMonitor3() { $out = new ezcConsoleOutput(); $status = new ezcConsoleProgressMonitor( $out, 7, array( 'formatString' => '%2$10s %1$6.2f%% %3$s' ) ); ob_start(); for ( $i = 0; $i < 7; $i++ ) { $status->addEntry( $this->stati[$i][0], $this->stati[$i][1] ); } $res = ob_get_contents(); ob_end_clean(); // To prepare test files use this: // file_put_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testProgressMonitor3.dat', $res ); $this->assertEquals( file_get_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testProgressMonitor3.dat' ), $res, "Formated statusbar not generated correctly." ); } public function testProgressMonitor4() { $out = new ezcConsoleOutput(); $out->formats->tag->color = 'red'; $out->formats->percent->color = 'blue'; $out->formats->percent->style = array( 'bold' ); $out->formats->data->color = 'green'; $status = new ezcConsoleProgressMonitor( $out, 7, array( 'formatString' => $out->formatText( '%2$10s', 'tag' ) . ' ' . $out->formatText( '%1$6.2f%%', 'percent' ) . ' ' . $out->formatText( '%3$s', 'data' ) ) ); ob_start(); for ( $i = 0; $i < 7; $i++ ) { $status->addEntry( $this->stati[$i][0], $this->stati[$i][1] ); } $res = ob_get_contents(); ob_end_clean(); // To prepare test files use this: // file_put_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testProgressMonitor4.dat', $res ); $this->assertEquals( file_get_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testProgressMonitor4.dat' ), $res, "Formated statusbar not generated correctly." ); } public function testGetAccessSuccess() { $out = new ezcConsoleOutput(); $status = new ezcConsoleProgressMonitor( $out, 7 ); $this->assertEquals( new ezcConsoleProgressMonitorOptions(), $status->options ); } public function testGetAccessFailure() { $out = new ezcConsoleOutput(); $status = new ezcConsoleProgressMonitor( $out, 7 ); try { echo $status->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "ezcBasePropertyNotFoundException not thrown on get access of invalid property ezcConsoleProgressMonitor->foo." ); } public function testSetAccessSuccess() { $out = new ezcConsoleOutput(); $status = new ezcConsoleProgressMonitor( $out, 7 ); $opt = new ezcConsoleProgressMonitorOptions(); $status->options = $opt; $this->assertSame( $opt, $status->options, "Value not correctly set for property ezcConsoleProgressMonitor->options." ); } public function testSetAccessFailure() { $out = new ezcConsoleOutput(); $status = new ezcConsoleProgressMonitor( $out, 7 ); $exceptionThrown = false; try { $status->options = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid value for ezcConsoleProgressMonitor->options." ); $exceptionThrown = false; try { $status->foo = 23; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on set access of invalid property ezcConsoleProgressMonitor->options." ); } public function testIssetAccess() { $out = new ezcConsoleOutput(); $status = new ezcConsoleProgressMonitor( $out, 7 ); $this->assertTrue( isset( $status->options ) ); $this->assertFalse( isset( $status->foo ) ); } public function testSetOptionsSuccess() { $out = new ezcConsoleOutput(); $optArr = array(); $optArr["formatString"] = "Foo bar"; $optObj = new ezcConsoleProgressMonitorOptions(); $optObj->formatString = "Foo bar"; $status = new ezcConsoleProgressMonitor( $out, 7 ); $status->setOptions( $optArr ); $this->assertEquals( $optObj, $status->options, "Options not set correctly from array." ); $status = new ezcConsoleProgressMonitor( $out, 7 ); $status->setOptions( $optObj ); $this->assertSame( $optObj, $status->options, "Options not set correctly from object." ); } public function testSetOptionsFailure() { $out = new ezcConsoleOutput(); $status = new ezcConsoleProgressMonitor( $out, 7 ); try { $status->setOptions( 23 ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "ezcBaseValueException not thrown on invalid parameter for setOptions()." ); } public function testGetOptions() { $out = new ezcConsoleOutput(); $status = new ezcConsoleProgressMonitor( $out, 7 ); $this->assertSame( $status->options, $status->getOptions() ); } } ?> ConsoleTools-1.7.5/tests/question_dialog_collection_validator_test.php000066400000000000000000000157751470000742400265440ustar00rootroot00000000000000assertEquals( $collection, $validator->collection ); $this->assertNull( $validator->default ); $this->assertEquals( ezcConsoleQuestionDialogCollectionValidator::CONVERT_NONE, $validator->conversion ); } public function testGetAccessCustomSuccess() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogCollectionValidator( $collection, "foo", ezcConsoleQuestionDialogCollectionValidator::CONVERT_UPPER ); $this->assertEquals( $collection, $validator->collection ); $this->assertEquals( "foo", $validator->default ); $this->assertEquals( ezcConsoleQuestionDialogCollectionValidator::CONVERT_UPPER, $validator->conversion ); } public function testGetAccessFailure() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogCollectionValidator( $collection ); try { echo $validator->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on invalid property foo." ); } public function testSetAccessSuccess() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogCollectionValidator( $collection ); $collectionNew = array( 23, 42 ); $validator->collection = $collectionNew; $validator->default = 23; $validator->conversion = ezcConsoleQuestionDialogCollectionValidator::CONVERT_LOWER; $this->assertEquals( $collectionNew, $validator->collection ); $this->assertEquals( 23, $validator->default ); $this->assertEquals( ezcConsoleQuestionDialogCollectionValidator::CONVERT_LOWER, $validator->conversion ); } public function testSetAccessFailure() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogCollectionValidator( $collection ); $exceptionCaught = false; try { $validator->collection = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property collection." ); $exceptionCaught = false; try { $validator->default = array(); } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property default." ); $exceptionCaught = false; try { $validator->conversion = "Foo"; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property conversion." ); $exceptionCaught = false; try { $validator->foo = "Foo"; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on access of nonexistent property foo." ); } public function testIssetAccess() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogCollectionValidator( $collection ); $this->assertTrue( isset( $validator->collection ), "Property collection not set." ); $this->assertTrue( isset( $validator->default ), "Property default not set." ); $this->assertTrue( isset( $validator->conversion ), "Property conversion not set." ); $this->assertFalse( isset( $validator->foo ), "Property foo set." ); } public function testValidate() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogCollectionValidator( $collection ); $this->assertTrue( $validator->validate( "foo" ) ); $this->assertFalse( $validator->validate( "test" ) ); } public function testFixup() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogCollectionValidator( $collection, null ); $this->assertEquals( "foo", $validator->fixup( "foo" ) ); $this->assertEquals( "FOO", $validator->fixup( "FOO" ) ); $validator->conversion = ezcConsoleQuestionDialogCollectionValidator::CONVERT_UPPER; $this->assertEquals( "FOO", $validator->fixup( "foo" ) ); $this->assertEquals( "FOO", $validator->fixup( "FOO" ) ); $validator->conversion = ezcConsoleQuestionDialogCollectionValidator::CONVERT_LOWER; $this->assertEquals( "foo", $validator->fixup( "foo" ) ); $this->assertEquals( "foo", $validator->fixup( "FOO" ) ); $this->assertEquals( "", $validator->fixup( "" ) ); $validator->default = "foo"; $this->assertEquals( "foo", $validator->fixup( "" ) ); } public function testGetResultString() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogCollectionValidator( $collection, null ); $this->assertEquals( "(foo/bar/baz)", $validator->getResultString() ); $validator->default = "foo"; $this->assertEquals( "(foo/bar/baz) [foo]", $validator->getResultString() ); } } ?> ConsoleTools-1.7.5/tests/question_dialog_mapping_validator_test.php000066400000000000000000000231531470000742400260310ustar00rootroot00000000000000assertEquals( $collection, $validator->collection ); $this->assertNull( $validator->default ); $this->assertEquals( ezcConsoleQuestionDialogMappingValidator::CONVERT_NONE, $validator->conversion ); $this->assertEquals( array(), $validator->map ); } public function testGetAccessCustomSuccess() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogMappingValidator( $collection, "foo", ezcConsoleQuestionDialogMappingValidator::CONVERT_UPPER, array( 'f' => 'foo' ) ); $this->assertEquals( $collection, $validator->collection ); $this->assertEquals( "foo", $validator->default ); $this->assertEquals( ezcConsoleQuestionDialogMappingValidator::CONVERT_UPPER, $validator->conversion ); $this->assertEquals( array( 'f' => 'foo' ), $validator->map ); } public function testGetAccessFailure() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogMappingValidator( $collection ); try { echo $validator->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on invalid property foo." ); } public function testSetAccessSuccess() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogMappingValidator( $collection ); $collectionNew = array( 23, 42 ); $validator->collection = $collectionNew; $validator->default = 23; $validator->conversion = ezcConsoleQuestionDialogMappingValidator::CONVERT_LOWER; $newMap = array( 'f' => 23, 'g' => 42 ); $validator->map = $newMap; $this->assertEquals( $collectionNew, $validator->collection ); $this->assertEquals( 23, $validator->default ); $this->assertEquals( ezcConsoleQuestionDialogMappingValidator::CONVERT_LOWER, $validator->conversion ); $this->assertEquals( $newMap, $validator->map ); } public function testSetAccessFailure() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogMappingValidator( $collection ); $exceptionCaught = false; try { $validator->collection = true; $this->fail( "Exception not thrown on invalid value for property collection." ); } catch ( ezcBaseValueException $e ) { } try { $validator->default = array(); $this->fail( "Exception not thrown on invalid value for property default." ); } catch ( ezcBaseValueException $e ) { } try { $validator->conversion = "Foo"; $this->fail( "Exception not thrown on invalid value for property conversion." ); } catch ( ezcBaseValueException $e ) { } try { $validator->map = "Foo"; $this->fail( "Exception not thrown on invalid value for property map." ); } catch ( ezcBaseValueException $e ) { } $exceptionCaught = false; try { $validator->foo = "Foo"; $this->fail( "Exception not thrown on access of nonexistent property foo." ); } catch ( ezcBasePropertyNotFoundException $e ) { } } public function testIssetAccess() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogMappingValidator( $collection ); $this->assertTrue( isset( $validator->collection ), "Property collection not set." ); $this->assertTrue( isset( $validator->default ), "Property default not set." ); $this->assertTrue( isset( $validator->conversion ), "Property conversion not set." ); $this->assertTrue( isset( $validator->map ), "Property map not set." ); $this->assertFalse( isset( $validator->foo ), "Property foo set." ); } public function testValidate() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogMappingValidator( $collection ); $this->assertTrue( $validator->validate( "foo" ) ); $this->assertFalse( $validator->validate( "test" ) ); } public function testFixup() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogMappingValidator( $collection, null ); $this->assertEquals( "foo", $validator->fixup( "foo" ), 'Fixup incorrect without conversion.' ); $this->assertEquals( "FOO", $validator->fixup( "FOO" ), 'Fixup incorrect without conversion.' ); $validator->conversion = ezcConsoleQuestionDialogMappingValidator::CONVERT_UPPER; $this->assertEquals( "FOO", $validator->fixup( "foo" ), 'Fixup incorrect with conversion to upper case.' ); $this->assertEquals( "FOO", $validator->fixup( "FOO" ), 'Fixup incorrect with conversion to upper case.' ); $validator->conversion = ezcConsoleQuestionDialogMappingValidator::CONVERT_LOWER; $this->assertEquals( "foo", $validator->fixup( "foo" ), 'Fixup incorrect with conversion to lower case.' ); $this->assertEquals( "foo", $validator->fixup( "FOO" ), 'Fixup incorrect with conversion to lower case.' ); $this->assertEquals( "", $validator->fixup( "" ) ); $validator->default = "foo"; $this->assertEquals( "foo", $validator->fixup( "" ) ); } public function testFixupWithMapping() { $collection = array( 'y', 'n' ); $validator = new ezcConsoleQuestionDialogMappingValidator( $collection, null, ezcConsoleQuestionDialogMappingValidator::CONVERT_NONE, array( 'yes' => 'y', 'no' => 'n', '1' => 'y', '0' => 'n', ) ); $this->assertEquals( "y", $validator->fixup( "yes" ) ); $this->assertEquals( "y", $validator->fixup( "1" ) ); $this->assertEquals( "YES", $validator->fixup( "YES" ) ); $this->assertEquals( "n", $validator->fixup( "no" ) ); $this->assertEquals( "n", $validator->fixup( "0" ) ); $this->assertEquals( "NO", $validator->fixup( "NO" ) ); $validator->conversion = ezcConsoleQuestionDialogMappingValidator::CONVERT_UPPER; $this->assertEquals( "YES", $validator->fixup( "yes" ) ); $this->assertEquals( "y", $validator->fixup( "1" ) ); $this->assertEquals( "YES", $validator->fixup( "YES" ) ); $this->assertEquals( "NO", $validator->fixup( "no" ) ); $this->assertEquals( "n", $validator->fixup( "0" ) ); $this->assertEquals( "NO", $validator->fixup( "NO" ) ); $validator->conversion = ezcConsoleQuestionDialogMappingValidator::CONVERT_LOWER; $this->assertEquals( "y", $validator->fixup( "yes" ) ); $this->assertEquals( "y", $validator->fixup( "1" ) ); $this->assertEquals( "y", $validator->fixup( "YES" ) ); $this->assertEquals( "n", $validator->fixup( "no" ) ); $this->assertEquals( "n", $validator->fixup( "0" ) ); $this->assertEquals( "n", $validator->fixup( "NO" ) ); $this->assertEquals( "", $validator->fixup( "" ) ); $validator->default = "no"; $this->assertEquals( "no", $validator->fixup( "" ) ); } public function testGetResultString() { $collection = array( "foo", "bar", "baz" ); $validator = new ezcConsoleQuestionDialogMappingValidator( $collection, null, ezcConsoleQuestionDialogMappingValidator::CONVERT_NONE, array( 'f' => 'foo', 'b' => 'bar' ) ); $this->assertEquals( "(foo/bar/baz)", $validator->getResultString() ); $validator->default = "foo"; $this->assertEquals( "(foo/bar/baz) [foo]", $validator->getResultString() ); } } ?> ConsoleTools-1.7.5/tests/question_dialog_options_test.php000066400000000000000000000127461470000742400240320ustar00rootroot00000000000000assertEquals( "Please enter a value: ", $opts->text ); $this->assertInstanceOf( "ezcConsoleQuestionDialogTypeValidator", $opts->validator ); $this->assertFalse( $opts->showResults ); $this->assertEquals( "default", $opts->format ); } public function testGetAccessCustomSuccess() { $opts = new ezcConsoleQuestionDialogOptions( array( "text" => "Do you have a question?", "validator" => new ezcConsoleQuestionDialogCollectionValidator( array( "a", "b" ) ), "showResults" => true, "format" => "test", ) ); $this->assertEquals( "Do you have a question?", $opts->text ); $this->assertInstanceOf( "ezcConsoleQuestionDialogCollectionValidator", $opts->validator ); $this->assertTrue( $opts->showResults ); $this->assertEquals( "test", $opts->format ); } public function testGetAccessFailure() { $opts = new ezcConsoleQuestionDialogOptions(); try { echo $opts->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on invalid property foo." ); } public function testSetAccessSuccess() { $opts = new ezcConsoleQuestionDialogOptions(); $opts->text = "Do you have a question?"; $opts->validator = new ezcConsoleQuestionDialogCollectionValidator( array( "a", "b" ) ); $opts->showResults = true; $opts->format = "test"; $this->assertEquals( "Do you have a question?", $opts->text ); $this->assertInstanceOf( "ezcConsoleQuestionDialogCollectionValidator", $opts->validator ); $this->assertTrue( $opts->showResults ); $this->assertEquals( "test", $opts->format ); } public function testSetAccessFailure() { $opts = new ezcConsoleQuestionDialogOptions(); $exceptionCaught = false; try { $opts->text = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property text." ); $exceptionCaught = false; try { $opts->validator = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property validator." ); $exceptionCaught = false; try { $opts->showResults = "Foo"; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property showResults." ); $exceptionCaught = false; try { $opts->format = true; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property format." ); $exceptionCaught = false; try { $opts->foo = "Foo"; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on access of nonexistent property foo." ); } public function testIssetAccess() { $opts = new ezcConsoleQuestionDialogOptions(); $this->assertTrue( isset( $opts->text ), "Property text not set." ); $this->assertTrue( isset( $opts->validator ), "Property validator not set." ); $this->assertTrue( isset( $opts->showResults ), "Property showResults not set." ); $this->assertTrue( isset( $opts->format ), "Property format not set." ); $this->assertFalse( isset( $opts->foo ), "Property foo set." ); } } ?> ConsoleTools-1.7.5/tests/question_dialog_regex_validator_test.php000066400000000000000000000143131470000742400255060ustar00rootroot00000000000000assertEquals( "//", $validator->pattern ); $this->assertNull( $validator->default ); } public function testGetAccessCustomSuccess() { $validator = new ezcConsoleQuestionDialogRegexValidator( "/[0-9]+/", 23 ); $this->assertEquals( "/[0-9]+/", $validator->pattern ); $this->assertEquals( 23, $validator->default ); } public function testGetAccessFailure() { $validator = new ezcConsoleQuestionDialogRegexValidator( "//" ); try { echo $validator->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on invalid property foo." ); } public function testSetAccessSuccess() { $validator = new ezcConsoleQuestionDialogRegexValidator( "//" ); $validator->pattern = "@^\s+$@"; $validator->default = 23.42; $this->assertEquals( "@^\s+$@", $validator->pattern ); $this->assertEquals( 23.42, $validator->default ); } public function testSetAccessFailure() { $validator = new ezcConsoleQuestionDialogRegexValidator( "//" ); $exceptionCaught = false; try { $validator->pattern = ""; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property type." ); $exceptionCaught = false; try { $validator->default = array(); } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property default." ); $exceptionCaught = false; try { $validator->foo = "Foo"; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on nonexistent property foo." ); } public function testIssetAccess() { $validator = new ezcConsoleQuestionDialogRegexValidator( "//" ); $this->assertTrue( isset( $validator->pattern ), "Property pattern not set." ); $this->assertTrue( isset( $validator->default ), "Property default not set." ); $this->assertFalse( isset( $validator->foo ), "Property foo set." ); } public function testValidate() { $validator = new ezcConsoleQuestionDialogRegexValidator( "//" ); $this->assertTrue( $validator->validate( "foo" ), "Value foo is invalid." ); $this->assertTrue( $validator->validate( true ) ); $this->assertTrue( $validator->validate( 23 ) ); $validator->pattern = "/^[0-9]+\.[a-z]+$/"; $this->assertTrue( $validator->validate( "123.abc" ), "Value 123.abc is invalid." ); $this->assertFalse( $validator->validate( "abc" ) ); $this->assertFalse( $validator->validate( 23 ) ); $validator->default = "foo"; $this->assertTrue( $validator->validate( "" ), "Empty value is invalid." ); } public function testFixup() { $validator = new ezcConsoleQuestionDialogRegexValidator( "//" ); $this->assertEquals( "23", $validator->fixup( "23" ) ); $this->assertEquals( "-23", $validator->fixup( "-23" ) ); $this->assertEquals( "foo", $validator->fixup( "foo" ) ); $this->assertEquals( "23.42", $validator->fixup( "23.42" ) ); $this->assertEquals( "-23.42", $validator->fixup( "-23.42" ) ); $this->assertEquals( "true", $validator->fixup( "true" ) ); $this->assertEquals( "false", $validator->fixup( "false" ) ); $this->assertEquals( "1", $validator->fixup( "1" ) ); $this->assertEquals( "0", $validator->fixup( "0" ) ); $this->assertEquals( "", $validator->fixup( "" ) ); $validator->default = "foo"; $this->assertEquals( "foo", $validator->fixup( "" ) ); } public function testGetResultString() { $validator = new ezcConsoleQuestionDialogRegexValidator( "//" ); $this->assertEquals( "(match //)", $validator->getResultString() ); $validator->default = "foo"; $this->assertEquals( "(match //) [foo]", $validator->getResultString() ); $validator->pattern = "/^[0-9]+\.[a-z]+$/"; $validator->default = null; $this->assertEquals( "(match /^[0-9]+\.[a-z]+$/)", $validator->getResultString() ); $validator->default = "foo"; $this->assertEquals( "(match /^[0-9]+\.[a-z]+$/) [foo]", $validator->getResultString() ); } } ?> ConsoleTools-1.7.5/tests/question_dialog_test.php000066400000000000000000000214371470000742400222540ustar00rootroot00000000000000assertSame( $output, $dialog->output ); $this->assertEquals( new ezcConsoleQuestionDialogOptions(), $dialog->options ); } public function testGetAccessFailure() { $output = new ezcConsoleOutput(); $dialog = new ezcConsoleQuestionDialog( $output ); $exceptionCaught = false; try { echo $dialog->foo; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on access of nonexistent property foo." ); } public function testSetAccessSuccess() { $output = new ezcConsoleOutput(); $dialog = new ezcConsoleQuestionDialog( $output ); $outputNew = new ezcConsoleOutput(); $optionsNew = new ezcConsoleQuestionDialogOptions(); $dialog->output = $outputNew; $dialog->options = $optionsNew; $this->assertSame( $outputNew, $dialog->output ); $this->assertSame( $optionsNew, $dialog->options ); } public function testSetAccessFailure() { $output = new ezcConsoleOutput(); $dialog = new ezcConsoleQuestionDialog( $output ); $exceptionCaught = false; try { $dialog->output = "Foo"; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for output." ); $exceptionCaught = false; try { $dialog->options = "Foo"; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for options." ); $exceptionCaught = false; try { $dialog->foo = "bar"; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on access of nonexistent property foo." ); $this->assertSame( $output, $dialog->output ); $this->assertEquals( new ezcConsoleQuestionDialogOptions(), $dialog->options ); } public function testIssetAccess() { $output = new ezcConsoleOutput(); $dialog = new ezcConsoleQuestionDialog( $output ); $this->assertTrue( isset( $dialog->options ), "Property options is not set." ); $this->assertTrue( isset( $dialog->output ), "Property options is not set." ); $this->assertFalse( isset( $dialog->foo ), "Property foo is set." ); } public function testBasicMethods() { $output = new ezcConsoleOutput(); $dialog = new ezcConsoleQuestionDialog( $output ); $this->assertFalse( $dialog->hasValidResult(), "Fresh dialog has valid result." ); $exceptionCaught = false; try { $dialog->getResult(); } catch ( ezcConsoleNoValidDialogResultException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Excption not thrown on getResult() without result." ); $dialog->reset(); $exceptionCaught = false; try { $dialog->getResult(); } catch ( ezcConsoleNoValidDialogResultException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Excption not thrown on getResult() without result." ); } /** * @group interactive */ public function testYesNoQuestionFactory() { $output = new ezcConsoleOutput(); $dialog = ezcConsoleQuestionDialog::YesNoQuestion( $output, "Is Jean-Luc a borg?", "y" ); $this->assertInstanceOf( "ezcConsoleQuestionDialogOptions", $dialog->options ); $this->assertEquals( "Is Jean-Luc a borg?", $dialog->options->text ); $this->assertTrue( $dialog->options->showResults ); $this->assertInstanceOf( "ezcConsoleQuestionDialogCollectionValidator", $dialog->options->validator ); $this->assertEquals( array( "y", "n" ), $dialog->options->validator->collection ); $this->assertEquals( "y", $dialog->options->validator->default ); $this->assertEquals( ezcConsoleQuestionDialogCollectionValidator::CONVERT_LOWER, $dialog->options->validator->conversion ); } /** * @group interactive */ public function testDialog1() { $this->runDialog( __METHOD__ ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "A\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "Y\n" ); $res[] = $this->readPipe( $this->pipes[1] ); // $this->saveDialogResult( __METHOD__, $res ); $this->assertEquals( $this->res, $res ); } /** * @group interactive */ public function testDialog2() { $this->runDialog( __METHOD__ ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "A\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "3.14\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "true\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "23\n" ); $res[] = $this->readPipe( $this->pipes[1] ); // $this->saveDialogResult( __METHOD__, $res ); $this->assertEquals( $this->res, $res ); } /** * @group interactive */ public function testDialog3() { $this->runDialog( __METHOD__ ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "A\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "y\n" ); $res[] = $this->readPipe( $this->pipes[1] ); // $this->saveDialogResult( __METHOD__, $res ); $this->assertEquals( $this->res, $res ); } /** * @group interactive */ public function testDialog4() { $this->runDialog( __METHOD__ ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "foo\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "foo.bar@\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "foo.bar@example\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "foo.bar@example.com\n" ); $res[] = $this->readPipe( $this->pipes[1] ); // $this->saveDialogResult( __METHOD__, $res ); $this->assertEquals( $this->res, $res ); } /** * @group interactive */ public function testDialog5() { $this->runDialog( __METHOD__ ); $res[] = $this->readPipe( $this->pipes[1] ); fputs( $this->pipes[0], "foo\n" ); $res[] = $this->readPipe( $this->pipes[1] ); fclose( $this->pipes[0] ); $res[] = $this->readPipe( $this->pipes[1] ); // $this->saveDialogResult( __METHOD__, $res ); $this->assertEquals( $this->res, $res ); } } ?> ConsoleTools-1.7.5/tests/question_dialog_type_validator_test.php000066400000000000000000000235121470000742400253560ustar00rootroot00000000000000assertEquals( ezcConsoleQuestionDialogTypeValidator::TYPE_STRING, $validator->type ); $this->assertNull( $validator->default ); } public function testGetAccessCustomSuccess() { $validator = new ezcConsoleQuestionDialogTypeValidator( ezcConsoleQuestionDialogTypeValidator::TYPE_INT, 23 ); $this->assertEquals( ezcConsoleQuestionDialogTypeValidator::TYPE_INT, $validator->type ); $this->assertEquals( 23, $validator->default ); } public function testGetAccessFailure() { $validator = new ezcConsoleQuestionDialogTypeValidator(); try { echo $validator->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on invalid property foo." ); } public function testSetAccessSuccess() { $validator = new ezcConsoleQuestionDialogTypeValidator(); $validator->type = ezcConsoleQuestionDialogTypeValidator::TYPE_FLOAT; $validator->default = 23.42; $this->assertEquals( ezcConsoleQuestionDialogTypeValidator::TYPE_FLOAT, $validator->type ); $this->assertEquals( 23.42, $validator->default ); } public function testSetAccessFailure() { $validator = new ezcConsoleQuestionDialogTypeValidator(); $exceptionCaught = false; try { $validator->type = "Foo"; } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property type." ); $exceptionCaught = false; try { $validator->default = array(); } catch ( ezcBaseValueException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on invalid value for property default." ); $exceptionCaught = false; try { $validator->foo = "Foo"; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionCaught = true; } $this->assertTrue( $exceptionCaught, "Exception not thrown on nonexistent property foo." ); } public function testIssetAccess() { $validator = new ezcConsoleQuestionDialogTypeValidator(); $this->assertTrue( isset( $validator->type ), "Property collection not set." ); $this->assertTrue( isset( $validator->default ), "Property default not set." ); $this->assertFalse( isset( $validator->foo ), "Property foo set." ); } public function testValidate() { $validator = new ezcConsoleQuestionDialogTypeValidator(); $this->assertTrue( $validator->validate( "foo" ) ); $this->assertFalse( $validator->validate( true ) ); $this->assertFalse( $validator->validate( "" ) ); $validator->type = ezcConsoleQuestionDialogTypeValidator::TYPE_INT; $this->assertTrue( $validator->validate( 23 ) ); $this->assertFalse( $validator->validate( true ) ); $this->assertFalse( $validator->validate( "" ) ); $validator->type = ezcConsoleQuestionDialogTypeValidator::TYPE_FLOAT; $this->assertTrue( $validator->validate( 23.42 ) ); $this->assertTrue( $validator->validate( 7E-10 ) ); $this->assertFalse( $validator->validate( true ) ); $this->assertFalse( $validator->validate( "" ) ); $validator->type = ezcConsoleQuestionDialogTypeValidator::TYPE_BOOL; $this->assertTrue( $validator->validate( true ) ); $this->assertFalse( $validator->validate( 23.42 ) ); $this->assertFalse( $validator->validate( "" ) ); $validator->default = "foo"; $this->assertTrue( $validator->validate( "" ) ); } public function testFixup() { $validator = new ezcConsoleQuestionDialogTypeValidator(); $this->assertEquals( "23", $validator->fixup( "23" ) ); $this->assertEquals( "-23", $validator->fixup( "-23" ) ); $this->assertEquals( "foo", $validator->fixup( "foo" ) ); $this->assertEquals( "23.42", $validator->fixup( "23.42" ) ); $this->assertEquals( "-23.42", $validator->fixup( "-23.42" ) ); $this->assertEquals( "true", $validator->fixup( "true" ) ); $this->assertEquals( "false", $validator->fixup( "false" ) ); $this->assertEquals( "1", $validator->fixup( "1" ) ); $this->assertEquals( "0", $validator->fixup( "0" ) ); $this->assertEquals( "", $validator->fixup( "" ) ); $validator->type = ezcConsoleQuestionDialogTypeValidator::TYPE_INT; $this->assertEquals( 23, $validator->fixup( "23" ) ); $this->assertEquals( -23, $validator->fixup( "-23" ) ); $this->assertEquals( "foo", $validator->fixup( "foo" ) ); $this->assertEquals( "23.42", $validator->fixup( "23.42" ) ); $this->assertEquals( "-23.42", $validator->fixup( "-23.42" ) ); $this->assertEquals( "true", $validator->fixup( "true" ) ); $this->assertEquals( "false", $validator->fixup( "false" ) ); $this->assertEquals( 1, $validator->fixup( "1" ) ); $this->assertEquals( 0, $validator->fixup( "0" ) ); $this->assertEquals( "", $validator->fixup( "" ) ); $validator->type = ezcConsoleQuestionDialogTypeValidator::TYPE_FLOAT; $this->assertEquals( (float) 23, $validator->fixup( "23" ) ); $this->assertEquals( (float) -23, $validator->fixup( "-23" ) ); $this->assertEquals( "foo", $validator->fixup( "foo" ) ); $this->assertEquals( 23.42, $validator->fixup( "23.42" ) ); $this->assertEquals( -23.42, $validator->fixup( "-23.42" ) ); $this->assertEquals( 7E-10, $validator->fixup( "7E-10" ) ); $this->assertEquals( 7e-10, $validator->fixup( "7e-10" ) ); $this->assertEquals( 4E3, $validator->fixup( "4E3" ) ); $this->assertEquals( "true", $validator->fixup( "true" ) ); $this->assertEquals( "false", $validator->fixup( "false" ) ); $this->assertEquals( 1, $validator->fixup( "1" ) ); $this->assertEquals( 0, $validator->fixup( "0" ) ); $this->assertEquals( "", $validator->fixup( "" ) ); $validator->type = ezcConsoleQuestionDialogTypeValidator::TYPE_BOOL; $this->assertEquals( "23", $validator->fixup( "23" ) ); $this->assertEquals( "-23", $validator->fixup( "-23" ) ); $this->assertEquals( "foo", $validator->fixup( "foo" ) ); $this->assertEquals( 23.42, $validator->fixup( "23.42" ) ); $this->assertEquals( -23.42, $validator->fixup( "-23.42" ) ); $this->assertEquals( true, $validator->fixup( "true" ) ); $this->assertEquals( false, $validator->fixup( "false" ) ); $this->assertEquals( true, $validator->fixup( "1" ) ); $this->assertEquals( false, $validator->fixup( "0" ) ); $this->assertEquals( "", $validator->fixup( "" ) ); $validator->default = "foo"; $this->assertEquals( "foo", $validator->fixup( "" ) ); } public function testGetResultString() { $validator = new ezcConsoleQuestionDialogTypeValidator(); $this->assertEquals( "()", $validator->getResultString() ); $validator->default = "foo"; $this->assertEquals( "() [foo]", $validator->getResultString() ); $validator->type = ezcConsoleQuestionDialogTypeValidator::TYPE_INT; $validator->default = null; $this->assertEquals( "()", $validator->getResultString() ); $validator->default = 23; $this->assertEquals( "() [23]", $validator->getResultString() ); $validator->type = ezcConsoleQuestionDialogTypeValidator::TYPE_FLOAT; $validator->default = null; $this->assertEquals( "()", $validator->getResultString() ); $validator->default = 23.42; $this->assertEquals( "() [23.42]", $validator->getResultString() ); $validator->type = ezcConsoleQuestionDialogTypeValidator::TYPE_BOOL; $validator->default = null; $this->assertEquals( "()", $validator->getResultString() ); $validator->default = true; $this->assertEquals( "() [1]", $validator->getResultString() ); } } ?> ConsoleTools-1.7.5/tests/statusbar_options_test.php000066400000000000000000000103771470000742400226520ustar00rootroot00000000000000 "+", "failureChar" => "-", ) ); $this->assertEquals( $fake, new ezcConsoleStatusbarOptions(), 'Default values incorrect for ezcConsoleStatusbarOptions.' ); } public function testNewAccess() { $opt = new ezcConsoleStatusbarOptions(); $this->assertEquals( "+", $opt->successChar ); $this->assertEquals( "-", $opt->failureChar ); $this->assertEquals( $opt["successChar"], "+" ); $this->assertEquals( $opt["failureChar"], "-" ); } public function testGetAccessSuccess() { $opt = new ezcConsoleStatusbarOptions(); $this->assertEquals( "+", $opt->successChar ); $this->assertEquals( "-", $opt->failureChar ); } public function testGetAccessFailure() { $opt = new ezcConsoleStatusbarOptions(); $exceptionThrown = false; try { echo $opt->foo; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on get access of invalid property foo." ); } public function testSetAccessSuccess() { $opt = new ezcConsoleStatusbarOptions(); $opt->successChar = "1"; $opt->failureChar = "0"; $this->assertEquals( "1", $opt->successChar ); $this->assertEquals( "0", $opt->failureChar ); } public function testSetAccessFailure() { $opt = new ezcConsoleStatusbarOptions(); $exceptionThrown = false; try { $opt->successChar = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property successChar." ); $exceptionThrown = false; try { $opt->failureChar = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property failureChar." ); $exceptionThrown = false; try { $opt->foo = true; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on set access of invalid property foo." ); } public function testIsset() { $opt = new ezcConsoleStatusbarOptions(); $this->assertTrue( isset( $opt->successChar ) ); $this->assertTrue( isset( $opt->failureChar ) ); $this->assertFalse( isset( $opt->foo ) ); } } ?> ConsoleTools-1.7.5/tests/statusbar_test.php000066400000000000000000000245041470000742400210740ustar00rootroot00000000000000stati as $statusVal ) { $status->add($statusVal); } $res = ob_get_contents(); ob_end_clean(); $this->assertEquals( file_get_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testStatusbar1.dat' ), $res, "Formated statusbar not generated correctly." ); // To prepare test files use this: // file_put_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testStatusbar1.dat', $res ); } public function testStatusbar2() { $out = new ezcConsoleOutput(); $out->options->useFormats = false; $status = new ezcConsoleStatusbar( $out ); ob_start(); foreach ( $this->stati as $statusVal ) { $status->add($statusVal); } $res = ob_get_contents(); ob_end_clean(); $this->assertEquals( file_get_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testStatusbar2.dat' ), $res, "Unformated statusbar not generated correctly." ); // To prepare test files use this: // file_put_contents( dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testStatusbar2.dat', $res ); } public function testGetAccessSuccess() { $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); $this->assertEquals( new ezcConsoleStatusbarOptions(), $status->options ); $this->assertEquals( "+", $status->successChar ); $this->assertEquals( "-", $status->failureChar ); } public function testGetAccessFailure() { $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); try { echo $status->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "ezcBasePropertyNotFoundException not thrown on get access to invalid property ezcConsoleStatusbar->foo." ); } public function testSetAccessSuccess() { $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); $refOpt = new ezcConsoleStatusbarOptions(); $refOpt->successChar = "*"; $refOpt->failureChar = "#"; $status->successChar = "*"; $status->failureChar = "#"; $this->assertEquals( $refOpt, $status->options, "Options not set correctly through direct set access on ezcConsoleStatusbar." ); $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); $status->options = $refOpt; $this->assertSame( $refOpt, $status->options, "Property ezcConsoleStatusbar->options not set correctly." ); } public function testSetAccessFailure() { $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); $exceptionThrown = false; try { $status->successChar = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid value for property ezcConsoleStatusbar->successChar." ); $exceptionThrown = false; try { $status->failureChar = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid value for property ezcConsoleStatusbar->failureChar." ); $exceptionThrown = false; try { $status->options = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid value for property ezcConsoleStatusbar->options." ); $exceptionThrown = false; try { $status->foo = 23; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBasePropertyNotFoundException not thrown on set access of invalid property ezcConsoleStatusbar->foo." ); } public function testIssetAccess() { $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); $this->assertTrue( isset( $status->successChar ) ); $this->assertTrue( isset( $status->failureChar ) ); $this->assertTrue( isset( $status->options ) ); $this->assertFalse( isset( $status->foo ) ); } public function testSetOptionsSuccess() { $refOpt = new ezcConsoleStatusbarOptions(); $refOpt->successChar = "*"; $refOpt->failureChar = "#"; $optArr = array( "successChar" => "*", "failureChar" => "#", ); $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); $status->setOptions( $optArr ); $this->assertEquals( $refOpt, $status->options, "Options not correctly set from array." ); $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); $status->setOptions( $refOpt ); $this->assertSame( $refOpt, $status->options, "Options not correctly set from array." ); } public function testSetOptionsFailure() { $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); try { $status->setOptions( 23 ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "ezcBaseValueException not thrown on invalid parameter to ezcConsoleStatusbar->setOptions()." ); } public function testGetOptions() { $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); $this->assertEquals( new ezcConsoleStatusbarOptions(), $status->getOptions() ); } public function testReset() { $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); ob_start(); foreach ( $this->stati as $statusVal ) { $status->add($statusVal); } ob_end_clean(); $counter = $this->readAttribute( $status, "counter" ); $this->assertEquals( 14, $counter[true], "Success values not counted correctly." ); $this->assertEquals( 11, $counter[false], "Failure values not counted correctly." ); $status->reset(); $counter = $this->readAttribute( $status, "counter" ); $this->assertEquals( 0, $counter[true], "Success values not reset correctly." ); $this->assertEquals( 0, $counter[false], "Failure values not reset correctly." ); } public function testGetSuccessCount() { $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); $this->assertEquals( 0, $status->getSuccessCount() ); ob_start(); foreach ( $this->stati as $statusVal ) { $status->add($statusVal); } ob_end_clean(); $this->assertEquals( 14, $status->getSuccessCount() ); } public function testGetFailureCount() { $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); $this->assertEquals( 0, $status->getFailureCount() ); ob_start(); foreach ( $this->stati as $statusVal ) { $status->add($statusVal); } ob_end_clean(); $this->assertEquals( 11, $status->getFailureCount() ); } public function testAddIncorrectStatus() { $out = new ezcConsoleOutput(); $status = new ezcConsoleStatusbar( $out ); set_error_handler( array( $this, "catchWarning" ), E_USER_WARNING ); ob_start(); $status->add( "foo" ); ob_end_clean(); restore_error_handler(); $this->assertTrue( $this->errorCaught, "Warning not caught on invalid status 'foo'." ); } public function catchWarning( $errno, $errstr, $errfile, $errline, $errcontext = null ) { $this->assertEquals( "Unknown status 'foo'.", $errstr ); $this->assertEquals( realpath( dirname( __FILE__ ) . "/../src/statusbar.php" ), $errfile ); $this->errorCaught = true; } } ?> ConsoleTools-1.7.5/tests/string_tools_test.php000066400000000000000000000056261470000742400216160ustar00rootroot00000000000000assertEquals( $expected, $actual ); } public function provideTestWordWrap() { if ( !isset( self::$provideTestWordWrap ) ) { self::$provideTestWordWrap = require dirname( __FILE__ ) . '/data/string_tools_wordwrap_data.php'; } return self::$provideTestWordWrap; } /** * testStrPad * * @param mixed $input * @param mixed $expected * @return void * * @dataProvider provideTestStrPad */ public function testStrPad( $input, $expected ) { $tools = new ezcConsoleStringTool(); $actual = call_user_func_array( array( $tools, 'strPad' ), $input ); $this->assertEquals( $expected, $actual ); } public function provideTestStrPad() { if ( !isset( self::$provideTestStrPad ) ) { self::$provideTestStrPad = require dirname( __FILE__ ) . '/data/string_tools_strpad_data.php'; } return self::$provideTestStrPad; } public static function suite() { return new PHPUnit\Framework\TestSuite( __CLASS__ ); } } ?> ConsoleTools-1.7.5/tests/table_cell_test.php000066400000000000000000000117361470000742400211550ustar00rootroot00000000000000assertEquals( $cell->content, "" ); $this->assertEquals( $cell->format, "default" ); $this->assertEquals( $cell->align, ezcConsoleTable::ALIGN_DEFAULT ); } public function testConstructorSuccessNonDefault() { $cell = new ezcConsoleTableCell( 'test', 'success', ezcConsoleTable::ALIGN_RIGHT ); $this->assertEquals( "test", $cell->content ); $this->assertEquals( "success", $cell->format ); $this->assertEquals( ezcConsoleTable::ALIGN_RIGHT, $cell->align ); } public function testConstructorFailure() { try { $cell = new ezcConsoleTableCell( 'test', 'success', 42 ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid align value." ); } public function testGetAccessSuccess() { $cell = new ezcConsoleTableCell(); $this->assertEquals( $cell->content, "" ); $this->assertEquals( $cell->format, "default" ); $this->assertEquals( $cell->align, ezcConsoleTable::ALIGN_DEFAULT ); } public function testGetAccessFailure() { $cell = new ezcConsoleTableCell(); try { echo $cell->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on get access of invalid property foo." ); } public function testSetAccessSuccess() { $cell = new ezcConsoleTableCell(); $cell->content = "aaa"; $cell->format = "bbb"; $cell->align = ezcConsoleTable::ALIGN_RIGHT; $this->assertEquals( $cell->content, "aaa" ); $this->assertEquals( $cell->format, "bbb" ); $this->assertEquals( $cell->align, ezcConsoleTable::ALIGN_RIGHT ); } public function testSetAccessFailure() { $cell = new ezcConsoleTableCell(); $exceptionThrown = false; try { $cell->content = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "No exception thrown on invalid value for ezcConsoleTableCell->content." ); $exceptionThrown = false; try { $cell->format = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "No exception thrown on invalid value for ezcConsoleTableCell->format." ); $exceptionThrown = false; try { $cell->align = "nonExistent"; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "No exception thrown on invalid value for ezcConsoleTableCell->align." ); $exceptionThrown = false; try { $cell->foo = "nonExistent"; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "No exception thrown on set access of invalid property ezcConsoleTableCell->foo." ); } public function testIssetAccess() { $cell = new ezcConsoleTableCell(); $this->assertTrue( isset( $cell->content ) ); $this->assertTrue( isset( $cell->format ) ); $this->assertTrue( isset( $cell->align ) ); $this->assertFalse( isset( $cell->foo ) ); } } ?> ConsoleTools-1.7.5/tests/table_options_test.php000066400000000000000000000306771470000742400217360ustar00rootroot00000000000000assertEquals( $fake, new ezcConsoleTableOptions(), 'Default values incorrect for ezcConsoleTableOptions.' ); } /** * testConstructorNew * * @access public */ public function testConstructorNew() { $fake = new ezcConsoleTableOptions( array( "colWidth" => "auto", "colWrap" => ezcConsoleTable::WRAP_AUTO, "defaultAlign" => ezcConsoleTable::ALIGN_LEFT, "colPadding" => " ", "widthType" => ezcConsoleTable::WIDTH_MAX, "lineVertical" => "-", "lineHorizontal" => "|", "corner" => "+", "defaultFormat" => "default", "defaultBorderFormat" => "default" ) ); $this->assertEquals( $fake, new ezcConsoleTableOptions(), 'Default values incorrect for ezcConsoleTableOptions.' ); } public function testCompatibility() { $old = new ezcConsoleTableOptions( array( 10, 20, 10 ), ezcConsoleTable::WRAP_CUT, ezcConsoleTable::ALIGN_CENTER, "-", ezcConsoleTable::WIDTH_FIXED, "_", "I", "x", "red", "blue" ); $new = new ezcConsoleTableOptions( array( "colWidth" => array( 10, 20, 10 ), "colWrap" => ezcConsoleTable::WRAP_CUT, "defaultAlign" => ezcConsoleTable::ALIGN_CENTER, "colPadding" => "-", "widthType" => ezcConsoleTable::WIDTH_FIXED, "lineVertical" => "_", "lineHorizontal" => "I", "corner" => "x", "defaultFormat" => "red", "defaultBorderFormat" => "blue" ) ); $this->assertEquals( $old, $new, "Old construction method did not produce same result as old one." ); } public function testAccess() { $opt = new ezcConsoleTableOptions(); $this->assertEquals( $opt->colWidth, "auto" ); $this->assertEquals( $opt->colWrap, ezcConsoleTable::WRAP_AUTO ); $this->assertEquals( $opt->defaultAlign, ezcConsoleTable::ALIGN_LEFT ); $this->assertEquals( $opt->colPadding, " " ); $this->assertEquals( $opt->widthType, ezcConsoleTable::WIDTH_MAX ); $this->assertEquals( $opt->lineVertical, "-" ); $this->assertEquals( $opt->lineHorizontal, "|" ); $this->assertEquals( $opt->corner, "+" ); $this->assertEquals( $opt->defaultFormat, "default" ); $this->assertEquals( $opt->defaultBorderFormat, "default" ); $this->assertEquals( $opt["colWidth"], "auto" ); $this->assertEquals( $opt["colWrap"], ezcConsoleTable::WRAP_AUTO ); $this->assertEquals( $opt["defaultAlign"], ezcConsoleTable::ALIGN_LEFT ); $this->assertEquals( $opt["colPadding"], " " ); $this->assertEquals( $opt["widthType"], ezcConsoleTable::WIDTH_MAX ); $this->assertEquals( $opt["lineVertical"], "-" ); $this->assertEquals( $opt["lineHorizontal"], "|" ); $this->assertEquals( $opt["corner"], "+" ); $this->assertEquals( $opt["defaultFormat"], "default" ); $this->assertEquals( $opt["defaultBorderFormat"], "default" ); } public function testConstructorFirstParameter() { $colWidthArray = new ezcConsoleTableOptions( array( 1, 2, 3 ) ); $optionsArray = new ezcConsoleTableOptions( array( "colWidth" => array( 1, 2, 3 ), ) ); $this->assertEquals( $colWidthArray, $optionsArray, "Did not detect options array correctly." ); } public function testTableConstructorCompatibility() { $out = new ezcConsoleOutput(); $old = new ezcConsoleTable( $out, 100, new ezcConsoleTableOptions( array( 1, 2, 3 ) ) ); $new = new ezcConsoleTable( $out, 100, array( "colWidth" => array( 1, 2, 3 ), ) ); $this->assertEquals( $old, $new, "Constructor calls did not produce same table objects." ); } public function testGetAccessSuccess() { $opt = new ezcConsoleTableOptions(); $this->assertEquals( $opt->colWidth, "auto" ); $this->assertEquals( $opt->colWrap, ezcConsoleTable::WRAP_AUTO ); $this->assertEquals( $opt->defaultAlign, ezcConsoleTable::ALIGN_LEFT ); $this->assertEquals( $opt->colPadding, " " ); $this->assertEquals( $opt->widthType, ezcConsoleTable::WIDTH_MAX ); $this->assertEquals( $opt->lineVertical, "-" ); $this->assertEquals( $opt->lineHorizontal, "|" ); $this->assertEquals( $opt->corner, "+" ); $this->assertEquals( $opt->defaultFormat, "default" ); $this->assertEquals( $opt->defaultBorderFormat, "default" ); } public function testGetAccessFailure() { $opt = new ezcConsoleTableOptions(); $exceptionThrown = false; try { echo $opt->foo; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on get access of invalid property foo." ); } public function testSetAccessSuccess() { $opt = new ezcConsoleTableOptions(); $opt->colWidth = 10; $opt->colWrap = ezcConsoleTable::WRAP_NONE; $opt->defaultAlign = ezcConsoleTable::ALIGN_CENTER; $opt->colPadding = "--"; $opt->widthType = ezcConsoleTable::WIDTH_FIXED; $opt->lineVertical = "_"; $opt->lineHorizontal = "/"; $opt->corner = "o"; $opt->defaultFormat = "foo"; $opt->defaultBorderFormat = "bar"; $this->assertEquals( $opt->colWidth, 10 ); $this->assertEquals( $opt->colWrap, ezcConsoleTable::WRAP_NONE ); $this->assertEquals( $opt->defaultAlign, ezcConsoleTable::ALIGN_CENTER ); $this->assertEquals( $opt->colPadding, "--" ); $this->assertEquals( $opt->widthType, ezcConsoleTable::WIDTH_FIXED ); $this->assertEquals( $opt->lineVertical, "_" ); $this->assertEquals( $opt->lineHorizontal, "/" ); $this->assertEquals( $opt->corner, "o" ); $this->assertEquals( $opt->defaultFormat, "foo" ); $this->assertEquals( $opt->defaultBorderFormat, "bar" ); } public function testSetAccessFailure() { $opt = new ezcConsoleTableOptions(); $exceptionThrown = false; try { $opt->colWidth = "foo"; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property colWidth." ); $exceptionThrown = false; try { $opt->colWrap = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property colWrap." ); $exceptionThrown = false; try { $opt->defaultAlign = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property defaultAlign." ); $exceptionThrown = false; try { $opt->colPadding = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property colPadding." ); $exceptionThrown = false; try { $opt->widthType = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property widthType." ); $exceptionThrown = false; try { $opt->lineVertical = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property lineVertical." ); $exceptionThrown = false; try { $opt->lineHorizontal = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property lineHorizontal." ); $exceptionThrown = false; try { $opt->corner = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property corner." ); $exceptionThrown = false; try { $opt->defaultFormat = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property defaultFormat." ); $exceptionThrown = false; try { $opt->defaultBorderFormat = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property defaultBorderFormat." ); $exceptionThrown = false; try { $opt->foo = true; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on set access of invalid property foo." ); } public function testIsset() { $opt = new ezcConsoleTableOptions(); $this->assertTrue( isset( $opt->colWidth ) ); $this->assertTrue( isset( $opt->colWrap ) ); $this->assertTrue( isset( $opt->defaultAlign ) ); $this->assertTrue( isset( $opt->colPadding ) ); $this->assertTrue( isset( $opt->widthType ) ); $this->assertTrue( isset( $opt->lineVertical ) ); $this->assertTrue( isset( $opt->lineHorizontal ) ); $this->assertTrue( isset( $opt->corner ) ); $this->assertTrue( isset( $opt->defaultFormat ) ); $this->assertTrue( isset( $opt->defaultBorderFormat ) ); $this->assertFalse( isset( $opt->foo ) ); } } ?> ConsoleTools-1.7.5/tests/table_row_test.php000066400000000000000000000317221470000742400210420ustar00rootroot00000000000000assertEquals( 2, count( $row ), "ezcConsoleTableRow not correctly created." ); } public function testCtorSuccess_2() { $row = new ezcConsoleTableRow(); $this->assertEquals( 0, count( $row ), "ezcConsoleTableRow not correctly created." ); } public function testCtorFailure() { $exceptionThrown = false; try { $row = new ezcConsoleTableRow( new ezcConsoleTableCell(), 'foo' ); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invald constructor parameter." ); } public function testAppend() { $row = new ezcConsoleTableRow(); $row[]->content = 'foo'; $this->assertTrue( $row[0] instanceof ezcConsoleTableCell, "ezcConsoleTableCell not correctly created on write access." ); } public function testOntheflyCreationRead_1() { $row = new ezcConsoleTableRow(); $this->assertTrue( $row[0] instanceof ezcConsoleTableCell, "ezcConsoleTableCell not correctly created on write access." ); } public function testOntheflyCreationRead_2() { $row = new ezcConsoleTableRow(); $row[0]; $row[1]; $row[2]; $this->assertTrue( count( $row ) == 3, "ezcConsoleTableCell not correctly created on write access." ); } public function testOntheflyCreationRead_3() { $row = new ezcConsoleTableRow(); $row[0]->content = 'test'; $row[1]->format = 'test'; $row[2]->align = ezcConsoleTable::ALIGN_CENTER; $this->assertTrue( count($row) == 3, "ezcConsoleTableCell not correctly created on write access." ); } public function testOntheflyCreationWrite_1() { $row = new ezcConsoleTableRow(); $row[0] = new ezcConsoleTableCell(); $row[0]->content = 'test'; $this->assertTrue( count($row) == 1 && $row[0] instanceof ezcConsoleTableCell && $row[0]->content === 'test', "ezcConsoleTableCell not correctly created on write access." ); } public function testNoOntheflyCreationIsset() { $row = new ezcConsoleTableRow(); $this->assertEquals( isset( $row[0] ), false, "ezcConsoleTableCell created on isset access." ); $this->assertEquals( count($row), 0, "ezcConsoleTableCell created on isset access." ); } public function testForeach_1() { $row = new ezcConsoleTableRow(); for ( $i = 0; $i < 10; $i++ ) { $row[$i]->content = 'Is '.$i; } $this->assertEquals( count( $row ), 10, "ezcConsoleTableCells not correctly created on write access." ); foreach ( $row as $id => $cell ) { $this->assertEquals( 'Is ' . $id, $cell->content, "Cell with wrong content found on iteration." ); } } public function testForeach_2() { $row = new ezcConsoleTableRow(); for ( $i = 0; $i < 20; $i += 2 ) { $row[$i]->content = 'Is '.$i; } $this->assertEquals( count( $row ), 19, "ezcConsoleTableCells." ); foreach ( $row as $id => $cell ); { $this->assertEquals( 'Is ' . $id, $cell->content, "Cell with wrong content found on iteration." ); } } public function testCount_1() { $row = new ezcConsoleTableRow(); $row[0]->content = "0"; $this->assertEquals( 1, count( $row ), "Did not count number of cells correctly" ); } public function testCount_2() { $row = new ezcConsoleTableRow(); $row[1]->content = "0"; $this->assertEquals( 2, count( $row ), "Did not count number of cells correctly" ); } public function testCount_3() { $row = new ezcConsoleTableRow(); $row[10]->content = "0"; $this->assertEquals( 11, count( $row ), "Did not count number of cells correctly" ); } public function testNotSetAllCellsProperties_1() { $row = new ezcConsoleTableRow(); for ( $i = 0; $i < 10; $i++ ) { $row[$i]->content = (string) $i; } $row->align = ezcConsoleTable::ALIGN_CENTER; foreach ( $row as $cell ) { $this->assertEquals( ezcConsoleTable::ALIGN_DEFAULT, $cell->align, "Did not set alignment correctly for all contained cells." ); } } public function testNotSetAllCellsProperties_2() { $row = new ezcConsoleTableRow(); for ( $i = 0; $i < 10; $i++ ) { $row[$i]->content = (string) $i; } $row->format = 'headline'; foreach ( $row as $cell ) { $this->assertEquals( 'default', $cell->format, "Did not set alignment correctly for all contained cells." ); } } public function testGetAccessSuccess() { $row = new ezcConsoleTableRow(); $this->assertEquals( "default", $row->borderFormat ); $this->assertEquals( "default", $row->format ); $this->assertEquals( ezcConsoleTable::ALIGN_DEFAULT, $row->align ); } public function testGetAccessFailure() { $row = new ezcConsoleTableRow(); try { echo $row->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "Exception not thrown on get access of invalid property foo." ); } public function testSetAccessSuccess() { $row = new ezcConsoleTableRow(); $row->borderFormat = "foo"; $row->format = "foo"; $row->align = ezcConsoleTable::ALIGN_RIGHT; $this->assertEquals( "foo", $row->borderFormat ); $this->assertEquals( "foo", $row->format ); $this->assertEquals( ezcConsoleTable::ALIGN_RIGHT, $row->align ); } public function testSetAccessFailure() { $row = new ezcConsoleTableRow(); $exceptionThrown = false; try { $row->borderFormat = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property borderFormat." ); $exceptionThrown = false; try { $row->format = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property format." ); $exceptionThrown = false; try { $row->align = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on invalid value for property align." ); $exceptionThrown = false; try { $row->foo = 23; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on set access to invalid property foo." ); } public function testIssetAccess() { $row = new ezcConsoleTableRow(); $this->assertTrue( isset( $row->format ) ); $this->assertTrue( isset( $row->borderFormat ) ); $this->assertTrue( isset( $row->align ) ); $this->assertFalse( isset( $row->foo ) ); } public function testOffsetExistsSuccess() { $row = new ezcConsoleTableRow( new ezcConsoleTableCell(), new ezcConsoleTableCell(), new ezcConsoleTableCell() ); $this->assertTrue( isset( $row[0] ) ); $this->assertTrue( isset( $row[1] ) ); $this->assertTrue( isset( $row[2] ) ); } public function testOffsetExistsFailure() { $row = new ezcConsoleTableRow( new ezcConsoleTableCell(), new ezcConsoleTableCell(), new ezcConsoleTableCell() ); try { isset( $row["foo"] ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid offset to offsetExists()." ); } public function testOffsetGetSuccess() { $row = new ezcConsoleTableRow( new ezcConsoleTableCell(), new ezcConsoleTableCell(), new ezcConsoleTableCell() ); $this->assertInstanceOf( "ezcConsoleTableCell", $row[0] ); $this->assertInstanceOf( "ezcConsoleTableCell", $row[1] ); $this->assertInstanceOf( "ezcConsoleTableCell", $row[2] ); } public function testOffsetGetFailure() { $row = new ezcConsoleTableRow( new ezcConsoleTableCell(), new ezcConsoleTableCell(), new ezcConsoleTableCell() ); try { var_dump( $row["foo"] ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid offset to offsetGet()." ); } public function testOffsetSetSuccess() { $row = new ezcConsoleTableRow( new ezcConsoleTableCell(), new ezcConsoleTableCell(), new ezcConsoleTableCell() ); $newCell = new ezcConsoleTableCell(); $row[1] = $newCell; $this->assertSame( $newCell, $row[1] ); $row[] = $newCell; $this->assertSame( $newCell, $row[3] ); } public function testOffsetSetFailure() { $row = new ezcConsoleTableRow( new ezcConsoleTableCell(), new ezcConsoleTableCell(), new ezcConsoleTableCell() ); $exceptionThrown = false; try { $row["foo"] = new ezcConsoleTableCell(); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on offsetSet() with invalid offset." ); $exceptionThrown = false; try { $row[1] = true; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "Exception not thrown on offsetSet() with invalid value." ); } public function testOffsetUnsetSuccess() { $row = new ezcConsoleTableRow( new ezcConsoleTableCell(), new ezcConsoleTableCell(), new ezcConsoleTableCell() ); unset( $row[1] ); $this->assertFalse( isset( $row[1] ) ); } public function testOffsetUnsetFailure() { $row = new ezcConsoleTableRow( new ezcConsoleTableCell(), new ezcConsoleTableCell(), new ezcConsoleTableCell() ); try { unset( $row["foo"] ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "Exception not thrown on invalid offset to offsetUnset()." ); } } ?> ConsoleTools-1.7.5/tests/table_test.php000066400000000000000000001040431470000742400201500ustar00rootroot00000000000000output = new ezcConsoleOutput(); $formats = array( 'red' => array( 'color' => 'red', 'style' => 'bold' ), 'blue' => array( 'color' => 'blue', 'style' => 'bold' ), 'green' => array( 'color' => 'green', 'style' => 'bold' ), 'magenta' => array( 'color' => 'magenta', 'style' => 'bold' ), ); foreach ( $formats as $name => $format ) { foreach ( $format as $type => $val ) { $this->output->formats->$name->$type = $val; } } } public function testTable1a() { $this->commonTableTest( __FUNCTION__, $this->tableData1, array( 'cols' => count( $this->tableData1[0] ), 'width' => 80 ), array( 'lineFormatHead' => 'green' ), array( 0 ) ); } public function testTable1b() { $this->commonTableTest( __FUNCTION__, $this->tableData1, array( 'cols' => count( $this->tableData1[0] ), 'width' => 40 ), array( 'lineFormatHead' => 'red', ), array( 0 ) ); } public function testTable2a() { $this->commonTableTest( __FUNCTION__, $this->tableData2, array( 'cols' => count( $this->tableData2[0] ), 'width' => 60 ), array( 'lineFormatHead' => 'magenta', 'defaultAlign' => ezcConsoleTable::ALIGN_RIGHT, 'widthType' => ezcConsoleTable::WIDTH_FIXED ) ); } public function testTable2b() { $this->commonTableTest( __FUNCTION__, $this->tableData2, array( 'cols' => count( $this->tableData2[0] ), 'width' => 60 ), array( 'lineFormatHead' => 'magenta', 'defaultAlign' => ezcConsoleTable::ALIGN_RIGHT ) ); } // Bug #8738: Unexpected behaviour with options->colPadding public function testTableColPadding1() { $this->commonTableTest( __FUNCTION__, $this->tableData2, array( 'width' => 100 ), array( 'defaultAlign' => ezcConsoleTable::ALIGN_CENTER, 'colPadding' => '~~~', 'widthType' => ezcConsoleTable::WIDTH_FIXED ) ); } // Bug #8738: Unexpected behaviour with options->colPadding public function testTableColPadding2() { $this->commonTableTest( __FUNCTION__, $this->tableData2, array( 'width' => 100 ), array( 'defaultAlign' => ezcConsoleTable::ALIGN_CENTER, 'colPadding' => '~~~' ) ); } public function testTable3a() { $this->commonTableTest( __FUNCTION__, $this->tableData3, array( 'cols' => count( $this->tableData3[0] ), 'width' => 120 ), array( 'lineFormatHead' => 'blue', 'defaultAlign' => ezcConsoleTable::ALIGN_CENTER, 'lineVertical' => '#', 'lineHorizontal' => '#', 'corner' => '#' ), array( 0, 3 ) ); } public function testTable3b() { $this->commonTableTest( __FUNCTION__, $this->tableData3, array( 'cols' => count( $this->tableData3[0] ), 'width' => 80 ), array( 'lineFormatHead' => 'magenta', 'lineVertical' => 'v', 'lineHorizontal' => 'h', 'corner' => 'c' ), array( 1, 2 ) ); } public function testTable3c() { $this->commonTableTest( __FUNCTION__, $this->tableData3, array( 'cols' => count( $this->tableData3[0] ), 'width' => 30 ), array( 'lineFormatHead' => 'magenta', 'colWrap' => ezcConsoleTable::WRAP_NONE ), array( 1, 2 ) ); } public function testTable3d() { $this->commonTableTest( __FUNCTION__, $this->tableData3, array( 'cols' => count( $this->tableData3[0] ), 'width' => 9 ), array( 'lineFormatHead' => 'magenta', 'colWrap' => ezcConsoleTable::WRAP_CUT, 'colWidth' => array( 3, 3, 3 ) ), array( 1, 2 ) ); } public function testTable4a() { $this->commonTableTest( __FUNCTION__, $this->tableData4, array( 'cols' => count( $this->tableData4[0] ), 'width' => 120 ), array( 'lineFormatHead' => 'blue', 'defaultAlign' => ezcConsoleTable::ALIGN_CENTER, 'colWrap' => ezcConsoleTable::WRAP_CUT ), array( 0 ) ); } public function testTable4b() { $this->commonTableTest( __FUNCTION__, $this->tableData4, array( 'cols' => count( $this->tableData4[0] ), 'width' => 120 ), array( 'lineFormatHead' => 'blue', 'defaultAlign' => ezcConsoleTable::ALIGN_LEFT, 'colWrap' => ezcConsoleTable::WRAP_AUTO ), array( 0 ) ); } public function testTable4c() { $this->commonTableTest( __FUNCTION__, $this->tableData4, array( 'cols' => count( $this->tableData4[0] ), 'width' => 120 ), array( 'lineFormatHead' => 'blue', 'defaultAlign' => ezcConsoleTable::ALIGN_CENTER, 'colWrap' => ezcConsoleTable::WRAP_CUT ), array( 0 ) ); } public function testTable5autowidth() { $this->commonTableTest( __FUNCTION__, $this->tableData5, array( 'cols' => count( $this->tableData4[0] ), 'width' => 120 ), array( 'widthType' => ezcConsoleTable::WIDTH_MAX ), array() ); } public function testTableWithoutBorders() { $this->commonTableTest( __FUNCTION__, $this->tableData4, array( 'cols' => count( $this->tableData4[0] ), 'width' => 120 ), array( 'lineVertical' => null, 'lineHorizontal' => null, 'corner' => null ), array( 0 ) ); } public function testTableWithSpaceBorders() { $this->commonTableTest( __FUNCTION__, $this->tableData4, array( 'cols' => count( $this->tableData4[0] ), 'width' => 120 ), array( 'lineVertical' => ' ', 'lineHorizontal' => ' ', 'corner' => ' ' ), array( 0 ) ); } public function testTableWithoutVerticalBorders() { $this->commonTableTest( __FUNCTION__, $this->tableData4, array( 'cols' => count( $this->tableData4[0] ), 'width' => 120 ), array( 'lineVertical' => null ), array( 0 ) ); } public function testTableWithoutHorizontalBorders() { $this->commonTableTest( __FUNCTION__, $this->tableData4, array( 'cols' => count( $this->tableData4[0] ), 'width' => 120 ), array( 'lineHorizontal' => null ), array( 0 ) ); } public function testEmptyTable() { $out = new ezcConsoleOutput(); $tbl = new ezcConsoleTable( $out, 80 ); $this->assertTableOutputEquals( __FUNCTION__, (string) $tbl ); } public function testEmptyTableNoBorders() { $out = new ezcConsoleOutput(); $tbl = new ezcConsoleTable( $out, 80 ); $tbl->options->lineVertical = null; $tbl->options->lineHorizontal = null; $tbl->options->corner = null; $this->assertEquals( '', (string) $tbl ); } public function testOneRowOneColumnTable() { $out = new ezcConsoleOutput(); $tbl = new ezcConsoleTable( $out, 80 ); $tbl[0][0]->content = 'foo'; $this->assertTableOutputEquals( __FUNCTION__, (string) $tbl ); } public function testUtf8TableHighlightNonUtf8() { $this->commonTableTest( __FUNCTION__, $this->tableData6, array( 'cols' => count( $this->tableData6[0] ), 'width' => 80 ), array( 'lineFormatHead' => 'red' ), array( 0 ) ); } public function testUtf8TableHighlightUtf8() { $data = array( 0 => $this->tableData6[1], 1 => $this->tableData6[0], ); $this->commonTableTest( __FUNCTION__, $data, array( 'cols' => count( $data[0] ), 'width' => 80 ), array( 'lineFormatHead' => 'red' ), array( 0 ) ); } public function testTableWithoutFormatting() { $this->output->options->useFormats = false; $this->commonTableTest( __FUNCTION__, $this->tableData2, array( 'cols' => count( $this->tableData2[0] ), 'width' => 80 ), array() ); } public function testTableConfigurationFailure1 () { // Missing 'cols' setting try { $table = new ezcConsoleTable( $this->output, null ); } catch (ezcBaseValueException $e) { $this->assertTrue( true, 'Wrong exception code thrown on missing setting.' ); return; } $this->fail( 'No or wrong exception thrown on missing setting.' ); } public function testTableConfigurationFailure2 () { // 'cols' setting wrong type try { $table = new ezcConsoleTable( $this->output, 'test' ); } catch (ezcBaseValueException $e) { $this->assertTrue( true, 'Wrong exception code thrown on missing setting.' ); return; } $this->fail( 'No or wrong exception thrown on wrong type for setting.' ); } public function testTableConfigurationFailure3 () { // 'cols' setting out of range try { $table = new ezcConsoleTable( $this->output, -10 ); } catch (ezcBaseValueException $e) { $this->assertTrue( true, 'Wrong exception code thrown on missing setting.' ); return; } $this->fail( 'No or wrong exception thrown on invalid value of setting.' ); } public function testArrayAccess() { $table = new ezcConsoleTable( $this->output, 100 ); $table[0]; } public function testSetAccessOptionsSuccess() { $table = new ezcConsoleTable( $this->output, 80 ); $table->options->colWidth = array( 1, 2, 3 ); $table->options->colWrap = ezcConsoleTable::WRAP_CUT; $table->options->defaultAlign = ezcConsoleTable::ALIGN_CENTER; $table->options->colPadding = ':'; $table->options->widthType = ezcConsoleTable::WIDTH_FIXED; $table->options->lineVertical = ':'; $table->options->lineHorizontal = '-'; $table->options->corner = 'o'; $table->options->defaultFormat = 'test'; $table->options->defaultBorderFormat = 'test2'; $this->assertEquals( array( 1, 2, 3 ), $table->options->colWidth ); $this->assertEquals( ezcConsoleTable::WRAP_CUT, $table->options->colWrap ); $this->assertEquals( ezcConsoleTable::ALIGN_CENTER, $table->options->defaultAlign ); $this->assertEquals( ':', $table->options->colPadding ); $this->assertEquals( ezcConsoleTable::WIDTH_FIXED, $table->options->widthType ); $this->assertEquals( ':', $table->options->lineVertical ); $this->assertEquals( '-', $table->options->lineHorizontal ); $this->assertEquals( 'o', $table->options->corner ); $this->assertEquals( 'test', $table->options->defaultFormat ); $this->assertEquals( 'test2', $table->options->defaultBorderFormat ); } public function testSetAccessOptionsSuccess2() { $opt = new ezcConsoleTableOptions( array( 1, 2, 3 ), ezcConsoleTable::WRAP_CUT, ezcConsoleTable::ALIGN_CENTER, ':', ezcConsoleTable::WIDTH_FIXED, ':', '-', 'o', 'test', 'test2' ); $this->assertEquals( array( 1, 2, 3 ), $opt->colWidth ); $this->assertEquals( ezcConsoleTable::WRAP_CUT, $opt->colWrap ); $this->assertEquals( ezcConsoleTable::ALIGN_CENTER, $opt->defaultAlign ); $this->assertEquals( ':', $opt->colPadding ); $this->assertEquals( ezcConsoleTable::WIDTH_FIXED, $opt->widthType ); $this->assertEquals( ':', $opt->lineVertical ); $this->assertEquals( '-', $opt->lineHorizontal ); $this->assertEquals( 'o', $opt->corner ); $this->assertEquals( 'test', $opt->defaultFormat ); $this->assertEquals( 'test2', $opt->defaultBorderFormat ); } public function testSetAccessOptionsSuccess3() { $opt = new ezcConsoleTableOptions( array( "colWidth" => array( 1, 2, 3 ), "colWrap" => ezcConsoleTable::WRAP_CUT, "defaultAlign" => ezcConsoleTable::ALIGN_CENTER, "colPadding" => ':', "widthType" => ezcConsoleTable::WIDTH_FIXED, "lineVertical" => ':', "lineHorizontal" => '-', "corner" => 'o', "defaultFormat" => 'test', "defaultBorderFormat" => 'test2' ) ); $this->assertEquals( array( 1, 2, 3 ), $opt->colWidth ); $this->assertEquals( ezcConsoleTable::WRAP_CUT, $opt->colWrap ); $this->assertEquals( ezcConsoleTable::ALIGN_CENTER, $opt->defaultAlign ); $this->assertEquals( ':', $opt->colPadding ); $this->assertEquals( ezcConsoleTable::WIDTH_FIXED, $opt->widthType ); $this->assertEquals( ':', $opt->lineVertical ); $this->assertEquals( '-', $opt->lineHorizontal ); $this->assertEquals( 'o', $opt->corner ); $this->assertEquals( 'test', $opt->defaultFormat ); $this->assertEquals( 'test2', $opt->defaultBorderFormat ); } public function testSetAccessOptionsFailure() { $table = new ezcConsoleTable( $this->output, 80 ); $exceptionThrown = false; try { $table->options->colWidth = 'test'; } catch ( ezcBaseValueException $e) { $exceptionThrown = true; } if ( !$exceptionThrown ) { $this->fail( 'No exception thrown on invalid setting for .'); } $exceptionThrown = false; try { $table->options->colWrap = 100; } catch ( ezcBaseValueException $e) { $exceptionThrown = true; } if ( !$exceptionThrown ) { $this->fail( 'No exception thrown on invalid setting for .'); } $exceptionThrown = false; try { $table->options->defaultAlign = 101; } catch ( ezcBaseValueException $e) { $exceptionThrown = true; } if ( !$exceptionThrown ) { $this->fail( 'No exception thrown on invalid setting for .'); } $exceptionThrown = false; try { $table->options->colPadding = 102; } catch ( ezcBaseValueException $e) { $exceptionThrown = true; } if ( !$exceptionThrown ) { $this->fail( 'No exception thrown on invalid setting for .'); } $exceptionThrown = false; try { $table->options->widthType = 103; } catch ( ezcBaseValueException $e) { $exceptionThrown = true; } if ( !$exceptionThrown ) { $this->fail( 'No exception thrown on invalid setting for .'); } $exceptionThrown = false; try { $table->options->lineVertical = 104; } catch ( ezcBaseValueException $e) { $exceptionThrown = true; } if ( !$exceptionThrown ) { $this->fail( 'No exception thrown on invalid setting for .'); } $exceptionThrown = false; try { $table->options->lineHorizontal = 105; } catch ( ezcBaseValueException $e) { $exceptionThrown = true; } if ( !$exceptionThrown ) { $this->fail( 'No exception thrown on invalid setting for .'); } $exceptionThrown = false; try { $table->options->corner = 106; } catch ( ezcBaseValueException $e) { $exceptionThrown = true; } if ( !$exceptionThrown ) { $this->fail( 'No exception thrown on invalid setting for .'); } $exceptionThrown = false; try { $table->options->defaultFormat = array(); } catch ( ezcBaseValueException $e) { $exceptionThrown = true; } if ( !$exceptionThrown ) { $this->fail( 'No exception thrown on invalid setting for .'); } $exceptionThrown = false; try { $table->options->defaultBorderFormat = true; } catch ( ezcBaseValueException $e) { $exceptionThrown = true; } if ( !$exceptionThrown ) { $this->fail( 'No exception thrown on invalid setting for .'); } $exceptionThrown = false; } public function testConstructorFailure() { try { $table = new ezcConsoleTable( $this->output, 80, 23 ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "ezcBaseValueException not thrown on invalid option parameter to constructor." ); } public function testSetOptionsSuccess() { $optArr = array( "colWidth" => array( 1, 2, 3 ), "colWrap" => ezcConsoleTable::WRAP_CUT, "defaultAlign" => ezcConsoleTable::ALIGN_CENTER, "colPadding" => ':', "widthType" => ezcConsoleTable::WIDTH_FIXED, "lineVertical" => ':', "lineHorizontal" => '-', "corner" => 'o', "defaultFormat" => 'test', "defaultBorderFormat" => 'test2' ); $optObj = new ezcConsoleTableOptions( array( 1, 2, 3 ), ezcConsoleTable::WRAP_CUT, ezcConsoleTable::ALIGN_CENTER, ':', ezcConsoleTable::WIDTH_FIXED, ':', '-', 'o', 'test', 'test2' ); $table = new ezcConsoleTable( $this->output, 80 ); $table->setOptions( $optArr ); $this->assertEquals( $optObj, $table->options ); $table = new ezcConsoleTable( $this->output, 80 ); $table->setOptions( $optObj ); $this->assertSame( $optObj, $table->options ); } public function testSetOptionsFailure() { $table = new ezcConsoleTable( $this->output, 80 ); try { $table->setOptions( 23 ); } catch ( ezcBaseValueException $e ) { return; } $this->fail( "ezcBaseValueException not thrown on invalid Parameter to setOptions()." ); } public function testGetOptions() { $optObj = new ezcConsoleTableOptions( array( 1, 2, 3 ), ezcConsoleTable::WRAP_CUT, ezcConsoleTable::ALIGN_CENTER, ':', ezcConsoleTable::WIDTH_FIXED, ':', '-', 'o', 'test', 'test2' ); $table = new ezcConsoleTable( $this->output, 80, $optObj ); $this->assertSame( $optObj, $table->getOptions() ); } public function testOutputTable() { $table = new ezcConsoleTable( $this->output, 80 ); for ( $i = 0; $i < count( $this->tableData1 ); $i++ ) { for ( $j = 0; $j < count( $this->tableData1[$i]); $j++ ) { $table[$i][$j]->content = $this->tableData1[$i][$j]; } } $table[0][0]->format = "red"; $table[0]->borderFormat = "green"; ob_start(); $table->outputTable(); $res = ob_get_clean(); $refFile = dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . 'testTable1a.dat'; $this->assertEquals( file_get_contents( $refFile ), $res, "Table not printed correctly on use of outputTable()" ); } public function testOffsetExistsSuccess() { $table = new ezcConsoleTable( $this->output, 80 ); $table[0]->borderFormat = "green"; $this->assertTrue( isset( $table[0] ) ); $this->assertFalse( isset( $table[1] ) ); } public function testOffsetExistsFailure() { $table = new ezcConsoleTable( $this->output, 80 ); $exceptionThrown = false; try { isset( $table[-10] ); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on negative offset." ); $exceptionThrown = false; try { isset( $table["foo"] ); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on character offset." ); } public function testOffsetGetSuccess() { $table = new ezcConsoleTable( $this->output, 80 ); $table[0] = new ezcConsoleTableRow(); $this->assertEquals( new ezcConsoleTableRow(), $table[0] ); // Test for bug #10710 $this->assertEquals( "test", ( $table[][0]->format = "test" ) ); } public function testOffsetGetFailure() { $table = new ezcConsoleTable( $this->output, 80 ); $exceptionThrown = false; try { echo $table["foo"]; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid string offset." ); $exceptionThrown = false; try { echo $table[-23]; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid int offset." ); } public function testOffsetSetSuccess() { $table = new ezcConsoleTable( $this->output, 80 ); $table[0] = new ezcConsoleTableRow(); $table[] = new ezcConsoleTableRow(); $this->assertTrue( isset( $table[0] ) ); $this->assertTrue( isset( $table[1] ) ); } public function testOffsetSetFailure() { $table = new ezcConsoleTable( $this->output, 80 ); $exceptionThrown = false; try { $table[-10] = new ezcConsoleTableRow(); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on negative offset." ); $exceptionThrown = false; try { $table["foo"] = new ezcConsoleTableRow(); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on character offset." ); $exceptionThrown = false; try { $table[10] = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid value." ); } public function testOffsetUnsetSuccess() { $table = new ezcConsoleTable( $this->output, 80 ); $table[0] = new ezcConsoleTableRow(); $table[] = new ezcConsoleTableRow(); $this->assertTrue( isset( $table[0] ) ); $this->assertTrue( isset( $table[1] ) ); unset( $table[0] ); unset( $table[1] ); $this->assertFalse( isset( $table[0] ) ); $this->assertFalse( isset( $table[1] ) ); } public function testOffsetUnsetFailure() { $table = new ezcConsoleTable( $this->output, 80 ); $exceptionThrown = false; try { unset( $table[-10] ); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on negative offset." ); $exceptionThrown = false; try { unset( $table["foo"] ); } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on character offset." ); } public function testIterator() { $table = new ezcConsoleTable( $this->output, 80 ); for ( $i = 0; $i < 10; ++$i ) { $table[$i]->borderFormat = "green"; } $refRow = new ezcConsoleTableRow(); $refRow->borderFormat = "green"; // First iteration $i = 0; foreach ( $table as $id => $row ) { $this->assertEquals( $i++, $id ); $this->assertEquals( $refRow, $row ); } $this->assertEquals( 10, $i, "Not iterated through all rows." ); // Second iteration $i = 0; foreach ( $table as $id => $row ) { $this->assertEquals( $i++, $id ); $this->assertEquals( $refRow, $row ); } $this->assertEquals( 10, $i, "Not iterated through all rows." ); } public function testGetAccessSuccess() { $table = new ezcConsoleTable( $this->output, 100 ); $this->assertEquals( 100, $table->width ); $this->assertEquals( new ezcConsoleTableOptions(), $table->options ); } public function testGetAccessFailure() { $table = new ezcConsoleTable( $this->output, 100 ); try { echo $table->foo; } catch ( ezcBasePropertyNotFoundException $e ) { return; } $this->fail( "ezcBasePropertyNotFoundException not thrown on get access to invalid property foo." ); } public function testSetAccessSuccess() { $table = new ezcConsoleTable( $this->output, 100 ); $opt = new ezcConsoleTableOptions(); $table->options = $opt; $table->width = 80; $this->assertEquals( 80, $table->width ); $this->assertSame( $opt, $table->options ); } public function testSetAccessFailure() { $table = new ezcConsoleTable( $this->output, 100 ); $exceptionThrown = false; try { $table->options = 23; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid value for property options." ); $exceptionThrown = false; try { $table->width = false; } catch ( ezcBaseValueException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBaseValueException not thrown on invalid value for property width." ); $exceptionThrown = false; try { $table->foo = true; } catch ( ezcBasePropertyNotFoundException $e ) { $exceptionThrown = true; } $this->assertTrue( $exceptionThrown, "ezcBasePropertyNotFoundException not thrown set access to invalid property foo." ); } public function testIssetAccess() { $table = new ezcConsoleTable( $this->output, 100 ); $this->assertTrue( isset( $table->width ) ); $this->assertTrue( isset( $table->options ) ); $this->assertFalse( isset( $table->foo ) ); } private function commonTableTest( $refFile, $tableData, $settings, $options, $headrows = array() ) { $table = new ezcConsoleTable( $this->output, $settings['width'] ); // Set options foreach ( $options as $key => $val ) { if ( $key == 'lineFormatHead' ) { continue; } $table->options->$key = $val; } // Add data for ( $i = 0; $i < count( $tableData ); $i++ ) { for ( $j = 0; $j < count( $tableData[$i]); $j++ ) { $table[$i][$j]->content = $tableData[$i][$j]; } } // Set a specific cell format $table[0][0]->format = 'red'; // Apply head format to head rows foreach ( $headrows as $row ) { $table[$row]->borderFormat = isset( $options['lineFormatHead'] ) ? $options['lineFormatHead'] : 'default'; } // For visual inspection, uncomment this block // echo "\n\n"; // echo "Old $refFile:\n:"; // echo file_get_contents( dirname( __FILE__ ) . '/data/' . $refFile . '.dat' ); // echo "New $refFile:\n:"; // echo implode( "\n", $table->getTable() ); // echo "\n\n"; $this->assertTableOutputEquals( $refFile, (string) $table ); } protected function assertTableOutputEquals( $expectedRef, $actualContent ) { $refFile = dirname( __FILE__ ) . '/data/' . ( ezcBaseFeatures::os() === "Windows" ? "windows/" : "posix/" ) . $expectedRef . '.dat'; // To prepare test files, uncomment this block // file_put_contents( $refFile, $actualContent ); if ( !file_exists( $refFile ) ) { // Default assert for new files. $this->assertEquals( '', $actualContent, "Asserted against empty string, since reference file '$refFile' does not exist." ); } else { $this->assertEquals( file_get_contents( $refFile ), $actualContent, 'Table not correctly generated for ' . $expectedRef . '.' ); } } } ?>